I'm trying to build a digital clock using ESP32. I cannot use the TM1637 display module because it's too small. So I'm using a generic common anode 4 digit 7 segment display. I used the Arduino SevSeg library on ESP32 and it worked perfectly. However, it consumed a lot of GPIO pins. Hence I used two 74HC595 registers and connected them exactly as described in the SevSegShift library (a fork of SevSeg library but with shift registers), like this:
Unfortunately, when I run the code, I get wrong output. No matter what number or characters I try to display, only the "b" segment of the corresponding digits are turned on, all other segments of all digits are turned off. For ex, if I try to display "1111", only the "b" segment of all digits are turned on, rest all segments of all digits are turned off. As a result only upper half of "1111" is displayed on the 7 segment display.
Here's the code:
#include <Arduino.h>
#include <SevSegShift.h>
#define SHIFT_PIN_DS 5 /* Data input PIN */
#define SHIFT_PIN_STCP 19 /* Shift Register Storage PIN */
#define SHIFT_PIN_SHCP 18 /* Shift Register Shift PIN */
SevSegShift sevseg(SHIFT_PIN_DS, SHIFT_PIN_SHCP, SHIFT_PIN_STCP); //Instantiate a seven segment controller object (with Shift Register functionality)
void setup() {
// put your setup code here, to run once:
byte numDigits = 4;
byte digitPins[] = {8+2, 8+5, 8+6, 2}; // of ShiftRegister(s) | 8+x (2nd Register)
byte segmentPins[] = {8+3, 8+7, 4, 6, 7, 8+4, 3, 5}; // of Shiftregister(s) | 8+x (2nd Register)
bool resistorsOnSegments = false;
byte hardwareConfig = COMMON_ANODE;
bool updateWithDelays = false;
bool leadingZeros = false;
bool disableDecPoint = false;
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
}
void loop() {
// put your main code here, to run repeatedly:
sevseg.setNumber(1111, 0);
sevseg.refreshDisplay();
delay(1);
}
What am I doing wrong? Is the circuit layout given above correct? More importantly, does the SevSegShift library work with ESP32?
Related
I have tried to create wireless PWM transmission using 433Mhz transmitter modules. I found this library for transmiting https://github.com/zeitgeist87/RFTransmitter and this library for reading PWM values on some pin https://github.com/xkam1x/Arduino-PWM-Reader.
I wrote code for sending PWM values:
#include "PWM.hpp"
#include <RFTransmitter.h>
#define NODE_ID 1
#define OUTPUT_PIN 11
RFTransmitter transmitter(OUTPUT_PIN, NODE_ID);
PWM my_pwm(2);
// the setup function runs once when you press reset or power the board
void setup() {
my_pwm.begin(true);
}
// the loop function runs over and over again forever
void loop() {
int pwmValue = my_pwm.getValue();
char stringValue[4];
itoa(pwmValue, stringValue, 10);
transmitter.send(stringValue, strlen(stringValue) + 1);
}
And similar code for receiving
#include <Servo.h>
#include <PinChangeInterruptHandler.h>
#include <RFReceiver.h>
int PWM_out_pin = 9;
Servo servo;
// Listen on digital pin 2
RFReceiver receiver(2);
void setup() {
servo.attach(PWM_out_pin);
receiver.begin();
}
void loop() {
char msg[MAX_PACKAGE_SIZE];
byte senderId = 0;
byte packageId = 0;
byte len = receiver.recvPackage((byte *)msg, &senderId, &packageId);
String *stringObject = new String(msg);
servo.writeMicroseconds(stringObject->toInt());
}
It works, but it has few problems.
First is that is not optimal. I transforming all to string. How can I send int values from PWM directly?
Second problem is that it has about 1 sec delay. Does it possible make it faster? I need it for realtime controlling servo.
Thanks.
How can I send int values from PWM directly?
transmitter.send((char*)pwmValue, sizeof(int));
Then on the receive side, you don't need to convert the data to a string.
servo.writeMicroseconds((int) *msg);
(int) and (char*) are C-style typecasts. Essentially telling the compiler that you want the data to be interpreted as a different variable type.
WARNING: Be careful with the "new" keyword, you are dynamically allocating memory. Rule of thumb in C/C++: Wherever you use the "new" keyword there should be a corresponding "delete" call that cleans up the memory.
In terms of making it faster, your C/C++ code here is not the limiting factor causing the 1 sec latency from command to action. Most likely there is a parameter in your RF transmitter transmit/receive stack that needs to be tweaked. Before sending PWM values, I would benchmark your latency of sending and printing out a simple string.
I have a device that measures radiation, using an Elegoo Uno (knockoff Arduino brand) and an RM-60 Radiation Monitor from Aware Electronics. I have had this working for almost a year and a half as part of a high-altitude balloon payload item at my university. I am currently revisiting it, as now I want to understand and cleanup my code.
The setup goes like this:
The RM-60 has four wires. Yellow and black go to ground, red to my 5v, and the green goes to my output (More documentation can be found online).
I have a pin attached to digital 2. I had read online that pins 2 & 3 can use attachInterrputs for the Uno. But for whatever reason, setting my pinMode() to 2 won't work. I have found that when I set my pin to 8, with my actual wire connecting to digital 2, I can read it fine.
This is what I am confused about. I felt that after learning how these inputs work, I am doing it right. But it's not working. So then why, when my pin set to 8, is the device running correctly? If I am doing this wrong (or inefficiently), what tips or pointers can you give me on how to optimize/repair this?
I have my previous code, compiled and tested from almost two years ago. It works as is, but I just don't understand why. I have looked online for similar projects, as several balloon teams around the world have used RM-60s to measure radiation. Following their pin layout and program, I have been unsuccessful.
//this is taking just the necessary lines to run the geigercounter.
//using geigerPin 8, it works. But why not when I change this to 2, where my
//wire actually is?
int count;
int geigerPin = 8;
int testVar = 0;
void setup() {
Serial.begin(9600);
pinMode(geigerPin, OUTPUT);
attachInterrupt(0, test, RISING);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(count * 6);
count = 0;
delay(10000);
}
void test() {
count++;
}
The data reads back to the Serial monitor every 10 seconds. The returned result should be the counts over ten seconds, multiplied by 6 to give us a counts-per-minute reading.
Please refer to the manual befor using any functions. The manual clearly states that your approach is not going to work.
From the Arduino Reference Manual:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) (recommended)
attachInterrupt(interrupt, ISR, mode) (not recommended)
attachInterrupt(pin, ISR, mode) (Not recommended. Additionally, this
syntax only works on Arduino SAMD Boards, Uno WiFi Rev2, Due, and
101.)
Example Code
const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}
void loop() {
digitalWrite(ledPin, state);
}
void blink() {
state = !state;
}
I have a sinewave look up table in the form of a list in my arduino program. I used a calculator to generate it. I have confirmed the length of the list with a python 2 shell,
>>>a=[all the variables separated by commas]
>>>len(a)
131300 is the value returned as the number of values in 'a'. So why when I run this code:
#include <Wire.h>//Include the Wire library to talk I2C
//This is the I2C Address of the MCP4725, by default (A0 pulled to GND).
#define MCP4725_ADDR 0x60
//Sinewave Tables were generated using this calculator:
//http://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml
int lookup = 0;//varaible for navigating through the tables
int sintab2[131300]=
{
/* This is where I put my list. */
};
void setup()
{
Wire.begin();
// Set A2 and A3 as Outputs to make them our GND and Vcc,
//which will power the MCP4725
pinMode(A2, OUTPUT);
pinMode(A3, OUTPUT);
digitalWrite(A2, LOW);//Set A2 as GND
digitalWrite(A3, HIGH);//Set A3 as Vcc
}
//---------------------------------------------------
void loop()
{
Wire.beginTransmission(MCP4725_ADDR);
Wire.write(64); // cmd to update the DAC
Wire.write(sintab2[lookup] >> 4); // the 8 most significant bits...
Wire.write((sintab2[lookup] & 15) << 4); // the 4 least significant bits...
Wire.endTransmission();
lookup = (lookup + 1) & 147454;
}
Why am I getting this error, the python shell is telling me my list is 131300 values long.
DAC_SINE.cpp:6607:1: error: too many initializers for 'int [16383]'
If it is needed I can post my list. The values I entered into the daycounter.com calculator with 131299 for the number of points, 4095 for the max amplitude, 20 numbers per row, and decimal format. Please help.
i have 25 relays which has min 15 different configurations, which have to be stored in a "array" or something simple...i have to switch those relays on/off (HiGH/LOW).
to use as less memory as possible i want to do it with a "trick" using bit's like:
char two = B1011011;
int mask = 1;
for(int i=0; i<7; i++){
if((mask & two) == 0) digitalWrite(pins[i], LOW); else
digitalWrite(pins[i], HIGH);
mask = mask << 1;
}
but char has only 8 bits and I NEED min 25 bits...
so now it the question NR.1, can i use uint32_t just in the same way as char just with 32 bits's? or something else?
uint32_t BIG1 = B10110110111011011111101101101
uint32_t BIG2 = B10110110111011011011101101101
uint32_t BIG3 = B10110110111111011011101101101
...
uint32_t BIG = B10110110111011011011101101101;//B... and 31 0s and 1s
uint32_t mask = 1;//????? not right???
for(int i=0; i<31; i++){
if((mask & two) == 0) digitalWrite(pins[i], LOW); else
digitalWrite(pins[i], HIGH);
mask = mask << 1;
};
what would be the mask then?
or is there a better/easyer/faster way to set OUTPUTS to the needed value?
Thank you in davance!
As I already told you in the other thread, the easiest and fastest way to do this is to deal with PORTS rather than individual pins.
For example, on the arduino UNO pins 0..7 map to port D pins 0..7, so when you do something like
uint8_t the_value_i_want = 0b01001000;
PORTD = the_value_i_want;
you write the pins 0..7 in a single instruction. Now, again with the uno, the complete mapping is
PORTD maps to Arduino digital pins 0 to 7
PORTB maps to Arduino digital pins 8 to 13. The two high bits (6 & 7) map to the crystal pins and are not usable
PORTC maps to Arduino analog pins 0 to 5. Bit 6 is the reset pin, so it's not usable, while bit 7 does not exist.
So things are a bit more complicated for the other ports. Well, the easiest way to handle this is making a function to mask the relevant bits. Just note that the masking is the same for port B and C, but this is just a coincidence.
#define PORT_B_C_MASK = 0x3F;
void write_with_mask(volatile uint8_t *p_register, uint8_t mask, uint8_t value)
{
*register = (*register | (value & mask)) & (value | ~mask);
}
Now you can write easily the instructions to write the value you want on the port. For instance, if you want to turn on pins 3, 6, 8 and 10, you just have to provide two values (one for port D, i.e pins 0..7, and one for port B, pins 8..13):
uint8_t the_value_i_want_8_13 = 0b000101;
uint8_t the_value_i_want_0_7 = 0b01001000;
write_with_mask(&PORTB,PORT_B_C_MASK,the_value_i_want_8_13);
PORTD = the_value_i_want_0_7;
Now, if you want to make a const matrix with all the possible values (again, this applies for the UNO only), you can just make a three-columns uint8_t matrix. Something like
int allvalues[][3] = { {0b001000, 0b001010, 0b00000001},
...};
In this case, with the first configuration (the reported one) pins A3, 0, 9, 11 will be turned on, the others will be off.
A possible function to apply this is
void apply_configuration(uint8_t index)
{
write_with_mask(&PORTC,PORT_B_C_MASK,allvalues[index][0]);
write_with_mask(&PORTB,PORT_B_C_MASK,allvalues[index][1]);
PORTD = allvalues[index][2];
}
This way you just have to provide the index for the configuration (the row) you want to apply.
If, for some reasons, you want to exclude some pins (e.g. pin 0 and 1, since they are the serial interface) you just have to include it in the mask. For instance:
#define PORT_B_MASK = 0x3F;
#define PORT_C_MASK = 0x0F;
#define PORT_D_MASK = 0xFC;
void apply_configuration(uint8_t index)
{
write_with_mask(&PORTC,PORT_C_MASK,allvalues[index][0]);
write_with_mask(&PORTB,PORT_B_MASK,allvalues[index][1]);
write_with_mask(&PORTD,PORT_D_MASK,allvalues[index][2]);
}
This way I excluded pins 0 and 1 (serial interface) and pins A4 and A5 (I2C interface).
Just one remark: I used the UNO as example, but you can use any board. Just look at the pin mapping to understand what is the association between ports and arduino pins.
I am trying to divide two integer values and store as a float.
void setup()
{
lcd.begin(16, 2);
int l1 = 5;
int l2 = 15;
float test = 0;
test = (float)l1 / (float)l2;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(test);
}
For some reason that I expect is fairly obvious I can't seem to store and display the correct value. 'test' variable is always set to 0.
How do I cast the integer values?
It must be your LCD print routine, and thus the casts you have used are correct.
I tried it out on an Arduino using serial printing instead of an LCD. The expected result appears in the serial monitor (started by menu Tools -> Serial Monitor) for the complete code example below:
Start...
5
15
0.33
0.33333334922790
The last result line confirms it is a 4 byte float with 7-8 significant digits.
Complete code example
/********************************************************************************
* Test out for Stack Overflow question "Divide two integers in Arduino", *
* <http://stackoverflow.com/questions/13792302/divide-two-integers-in-arduino> *
* *
********************************************************************************/
// The setup routine runs once when you press reset:
void setup() {
// Initialize serial communication at 9600 bits per second:
Serial.begin(9600);
//The question part, modified for serial print instead of LCD.
{
int l1 = 5;
int l2 = 15;
float test = 0;
test = (float)l1 / (float)l2;
Serial.println("Start...");
Serial.println("");
Serial.println(l1);
Serial.println(l2);
Serial.println(test);
Serial.println(test, 14);
}
} //setup()
void loop()
{
}
lcd.print doesn't know how to print a float, so you end up printing the integer instead.