I'm working on a home automation system, one part of which includes an Arduino Mega2560 connected via USB to a RaspberryPi 3 Model B. The Arduino receives two short, simple commands over serial from the Pi; the first command sets 'regions' of led lights, and the second sets colors on the regions previously described. Syntax is as follows for both commands, where bracketed items are single bytes, len+off are 3 ascii bytes interpreted as base-10, and r+g+b are 2 ascii bytes each interpreted as hex. The color set command supports a variable number of regions as shown:
Region Set Command
!c[regionId][stripId][len2][len1][len0][off2][off1][off0]\r
Color Set Command
!b[r1][r0][g1][g0][b1][b0][numRegions][regionId0]([regionId1]...)\r
Each command sends positive/negative acknowledgements so you'd think I'd be able to tell what's going wrong, but I get unexpected behavior when I try to set light colors with the second command, however, despite getting acknowledgements as expected. So far, the only reasonable conclusion I can come to is that for the global array regions discards changes and stays zero always. For example, this exchange results in no lights lit (comments are not in the actual exchange)
Pi sends: !c00144000\r // create regionId 0 on stripId 0; length 144 offset 0
Pi recieves: O // positive confirmation?
Pi sends: !b00009910\r // set regionId 0 to color 0x000099 (blue)
Pi recieves: O // positive confirmation, but no lights?
Pi sends: !x\r // invalid command; triggers default in switch
Pi recieves: X // as expected, all lights turn red
Here's the stripped down source for the Arduino:
#include "Arduino.h"
#include "FastLED.h"
#define WAIT while(!Serial.available()) ;;
// Forward Declarations
const int max_strips = 2;
const int max_strip_length = 300;
const int max_leds = max_strips * max_strip_length;
CRGB leds[max_strips][max_strip_length];
const int max_regions = 20;
int regions[max_regions][3];
const int baud_rate = 9600;
unsigned char buffer[64];
unsigned char currentByte = 0;
unsigned char incommingByte;
unsigned char startChar = '!';
unsigned char endChar = '\r';
bool store = false;
// Helper Functions
void set(CRGB c) {
for(int i = 0; i < max_strips; i++) {
for(int j = 0; j < max_strip_length; j++) {
leds[i][j] = c;
}
}
}
void set(int stripId, int length, int offset, CRGB c) {
for(int i = offset; i < offset+length; i++) {
leds[stripId][i] = c;
}
}
void setup() {
Serial.begin(baud_rate);
for(int i = 0; i < max_strips; i++) {
switch(i) {
case 0: {
FastLED.addLeds<WS2812B, 2, GRB>(leds[0], max_strip_length);
} break;
case 1: {
FastLED.addLeds<WS2812B, 3, GRB>(leds[1], max_strip_length);
} break;
}
}
set(CRGB::Black);
}
void loop() {
if(Serial.available() > 0) {
unsigned char incomingByte = Serial.read();
if(incomingByte == startChar) {
currentByte = 0;
store = true;
}
if(store) {
if(incomingByte == endChar) {
buffer[currentByte++] = incomingByte;
store = false;
switch(buffer[1]) {
case 'b': {
int red = (buffer[2] - '0')*16 + (buffer[3] - '0');
int green = (buffer[4] - '0')*16 + (buffer[5] - '0');
int blue = (buffer[6] - '0')*16 + (buffer[7] - '0');
int numRegions = buffer[8] - '0';
for(int i = 0; i < numRegions; i++) {
int regionId = buffer[9+i] - '0';
if(regionId >= max_regions) {
Serial.write('S');
return;
}
// Never sets anything
set(regions[regionId][0], regions[regionId][1], regions[regionId][2], CRGB(red, green, blue));
}
Serial.write('O');
} break;
case 'c': {
int regionId = buffer[2] - '0';
int stripId = buffer[3] - '0';
int length = (buffer[4] - '0')*100 + (buffer[5] - '0')*10 + (buffer[6] -'0');
int offset = (buffer[7] - '0')*100 + (buffer[8] - '0')*10 + (buffer[9] -'0');
if(regionId >= max_regions) {
Serial.write('R');
return;
}
if(stripId >= max_strips) {
Serial.write('S');
return;
}
regions[regionId][0] = stripId; // WE LOSE THESE VALUES??
regions[regionId][1] = length; // ??
regions[regionId][2] = offset; // ??
Serial.write('O');
} break;
default: {
set(CRGB::Red);
Serial.write('X');
} break;
}
currentByte = 0;
}else if(currentByte > 64){
store = false;
currentByte = 0;
} else {
buffer[currentByte++] = incomingByte;
}
}
}
FastLED.show();
}
I'd appreciate any advice as to what I'm missing here, and regardless, thanks for reading this far!
This was occurring because I have a lot of garbage showing up on the serial interface for the Arduino I'm testing on. Slowing the baud rate down to 2400bps resolves the issue on this device. Other Arduinos in the system do fine with much higher serial rates -- I suspect there's a hardware problem with the crystal on this particular Arduino so clock speeds aren't matching up correctly between the two sides of the serial connection.
Related
I'm working on a circuit that has two separate 4-bit binary counters with LEDs. I press a button and one counter begins counting to 15 in binary. I press a second button and the first counter pauses where it is and the second group of LEDs begin counting to 15 in binary. I got both counters working, but I can't get the first group to pause and the second to begin. I've tried using if statements with a boolean flag, but it messes up the first group of LEDs. How can I get ledPins1[] to pause when button2 is pressed, then resume when ledPins2[] finish?
int ledPin1[] = {2,3,4,5};
int ledPin2[] = {7,8,9,10};
int button1 = 11;
int button2 = 12;
boolean button1Last = LOW;
boolean button1Current = LOW;
boolean button2Last = LOW;
boolean button2Current = LOW;
void setup()
{
pinMode(button1, INPUT);
pinMode(button2, INPUT);
for(int i=0; i<4; i++)
{
pinMode(ledPin1[i], OUTPUT);
}
for(int i=0; i<4; i++)
{
pinMode(ledPin2[i], OUTPUT);
}
}
boolean waitForButtonPush1 (boolean lastStartSwitchState1)
{
boolean currentStartSwitchState1 = digitalRead(button1);
if(lastStartSwitchState1 != currentStartSwitchState1) delay(20);
currentStartSwitchState1 = digitalRead(button1);
return currentStartSwitchState1;
}
boolean waitForButtonPush2 (boolean lastStartSwitchState2)
{
boolean currentStartSwitchState2 = digitalRead(button2);
if(lastStartSwitchState2 != currentStartSwitchState2) delay(20);
currentStartSwitchState2 = digitalRead(button2);
return currentStartSwitchState2;
}
void loop()
{
button1Current = waitForButtonPush1(button1Last);
if(button1Last == LOW && button1Current == HIGH)
{
for (byte counter =0;counter<=15; counter++)
{
displayBinary(counter);
delay(500);
}
}
button2Current = waitForButtonPush2(button2Last);
if(button2Last == LOW && button2Current == HIGH)
{
for (byte counter =0;counter<=15; counter++)
{
displayBinary2(counter);
delay(500);
}
}
}
void displayBinary(byte numToShow)
{
for (int i =0;i<4;i++)
{
if (bitRead(numToShow, i)==1)
{
digitalWrite(ledPin1[i], HIGH);
}
else
{
digitalWrite(ledPin1[i], LOW);
}
}
}
void displayBinary2(byte numToShow)
{
for (int i =0;i<4;i++)
{
if (bitRead(numToShow, i)==1)
{
digitalWrite(ledPin2[i], HIGH);
}
else
{
digitalWrite(ledPin2[i], LOW);
}
}
}
Welcome to the world of embedded devices!
Getting a small microprocessor to do several things at the same time is a bit tricky.
The key is to never block. No calls to delay(), no sending large buffers on the serial port at 9600 bauds in one go, etc...
There are some simple techniques to do it, one of the most commonly used is finite state machines.
Let's analyse your app a bit.
2 similar dssplay counters, with delay
2 buttons, buttons usually need to be debounced, that also involves a delay.
Some code, for you to tinker with:
// ****************************
// pinout
static const byte ledPin1[] = { 2, 3, 4, 5 };
static const byte ledPin2[] = { 7, 8, 9, 10 };
constexpr byte button1 = 11; // using constexpr for these saves 2 bytes of RAM.
constexpr byte button2 = 12;
// ****************************
// Counter data
static constexpr unsigned int led_delay = 500; // 500 ms, for all counters.
// constexpr ?? arduino supports c++17. Not all features in the main .ino
// module and all features in .cpp modules.
// Hint: you could have a member variable in the structure below for delay,
// this would allow for counters running at different speeds, or add buttons
// to increase/decrease speed.
// we have only 2 states, but you could add more, like running
// backwards, or run a different chase pattern maybe?
enum class led_counter_state : byte
{
stopped,
running,
};
struct led_counter_data_t
{
led_counter_state state; // STATE
byte counter; // counter current value
unsigned int timestamp; // used for timing.
const byte* leds; // LED pins.
};
static led_counter_data_t led_counter[2];
void led_display_init()
{
for (byte i = 0; i < 2; ++i)
{
led_counter[i].state = led_counter_state::stopped;
led_counter[i].counter = 0;
led_counter[i].timestamp = 0;
}
led_counter[0].leds = ledPin1;
led_counter[1].leds = ledPin2;
}
// ****************************
// LED cotrrol
static void leds_display_value(const led_counter_data_t& cntr)
{
for (byte i = 0, val = cntr.counter; i < 4; ++i, val >>= 1)
digitalWrite(cntr.leds[i], val & 0x01);
}
static void leds_control(led_counter_data_t& cntr)
{
const auto now = (unsigned int)millis(); // keep track of time.
switch(cntr.state)
{
default: // something is wrong.. stop.
cntr.state = led_counter_state::stopped;
// fall through ...
case led_counter_state::stopped:
return; // if not running, do nothing
case led_counter_state::running:
if (now - cntr.timestamp >= led_delay) // check delay
{
if (++cntr.counter > 15) // advance counter.
cntr.counter = 0;
leds_display_value(cntr); // show value.
cntr.timestamp = now; // keep track of time.
}
break;
}
}
static void leds_start(led_counter_data_t& cntr)
{
if (cntr.state != led_counter_state::stopped)
return;
cntr.state = led_counter_state::running;
if (++cntr.counter > 15) // advance counter.
cntr.counter = 0;
led_display_value(cntr); // show value.
cntr.timestamp = (unsigned int)millis();
}
static void leds_stop(led_counter_data_t& cntr)
{
cntr.state = led_counter_state::stopped;
}
// ****************************
// switch inputs data
static constexpr byte switch_debounce_delay = 30; // 30ms is a good value for
// debouncing
struct switch_data_t
{
byte sw1_state : 1; // no need to waste more than 1 bit per switch
byte sw2_state : 1;
byte timestamp; // we'll only count to 30 ms, so 1 byte timestamp will do
};
static switch_data_t switch_data;
// ****************************
// switch inputs code
static void control_inputs()
{
const auto now = (byte)millis();
if (now - switch_data.timestamp < switch_debounce_delay)
return;
switch_data.timestamp = now;
// All switch control logic is regrouped here, and isolated
// form other control code, this makes the logic easier to
// write, read, and debug.
bool b = digitalRead(button1);
if (b & !switch_data.sw1_state) // button was pushed right now.
{
if (led_counter[0].state == led_counter_state::stopped)
{
leds_start(led_counter[0]); // start counter 1
leds_stop(led_counter[1]); // stop counter 2
}
else
{
leds_stop(led_counter[0]); // stop counter 1
}
}
switch_data.sw1_state = b;
b = digitalRead(button2);
if (b & !switch_data.sw2_state) // button was pushed right now.
{
if (led_counter[1].state == led_counter_state::stopped)
{
leds_start(led_counter[1]); // start counter 2
leds_stop(led_counter[0]); // stop counter 1
}
else
{
leds_stop(led_counter[1]); // stop counter 2
}
}
switch_data.sw2_state = b;
}
// ****************************
void setup()
{
pinMode(button1, INPUT);
pinMode(button2, INPUT);
for (byte i = 0; i < 4; ++i)
{
digitalWrite(ledPin1[i], LOW);
pinMode(ledPin1[i], OUTPUT);
digitalWrite(ledPin2[i], LOW);
pinMode(ledPin2[i], OUTPUT);
}
led_display_init();
}
// ****************************
// The goal, always, is to exit loop() as fast as possible, so
// everything will run smoothly, and appear to run simultaneously.
void loop()
{
control_inputs();
leds_control(led_counter[0]);
leds_control(led_counter[1]);
}
I do not have an arduino with me, so I did not comppile nor ran this, but it should be pretty close. Let me know if you're having issues or have any questions.
i need to add millis in my code but i dont know exactly where should be ok to add millis or where is it acctually needed.Because i have used only delay till now can you help me and tell me where can millis be implemented in this piece of code.If it is possible to add millis somewhere in or after the button modes or switch case because it would be nicer if the modes changed properly because for now their changing either when the button is pressed normal or somtimes you need to pres the button twice to change to the next mode.I would apriciate all the help.
const int BUTTON_SWITCH = 8;
const int BUTTON_ALARM = 9;
const int KNOB = A0;
const int TEMP = A1;
const int tempRES = 10000; // the resistance of the NTC at 25'C is 10k ohm
const int NTC_MATERIAL_CONSTANT = 3950;
const int RED_LED = 4;
const int BUZZER = 3;
int index = 1;
double value;
int state = 0;
unsigned long time_now = 0;
int period = 1000;
char incomingOption;
#include "Display.h"
void setup() {
Serial.begin(9600);
pinMode(BUTTON_SWITCH, INPUT_PULLUP);
pinMode(BUTTON_ALARM, INPUT_PULLUP);
pinMode(RED_LED, OUTPUT);
}
float get_temperature()
{
float temperature, resistance;
int value;
value = analogRead(TEMP);
resistance = (float)value * tempRES / (1024 - value); // Calculate resistance
/* Calculate the temperature according to the following formula. */
temperature = 1 / (log(resistance / tempRES) / NTC_MATERIAL_CONSTANT + 1 / 298.15) - 273.15;
return temperature;
}
void loop() {
if (digitalRead(BUTTON_SWITCH) == LOW) { // button for switching the 3 modes
index = index + 1;
}
if (digitalRead(BUTTON_ALARM) == LOW) { // button for the alarm
displayAlarm();
}
if (Serial.available()) {
// Read entire buffer up to newline character
// Since on the C# side, serialPort1.WriteLine appends a newline character
String respond = Serial.readStringUntil('\n');
if (respond == "RESET") {
digitalWrite(RED_LED, LOW);
digitalWrite(BUZZER, LOW);
}
}
if (index > 3) { // when the code is on the last mode press the button to turn back to the first mode
index = 1;
}
get_temperature();
float celcius;
celcius = get_temperature();
if(celcius<16 || celcius>27){ // if the temperature becomes less than 16 degrees or goes higher than 27 degrees turn on the alarm
displayAlarm();
}
switch (index) {
case 1: displayTime(); break; // switch between the 3 different modes
case 2: displayTemp(); break;
case 3: displayAngle(); break;
}
}
void displayTime() {
float timer = Serial.parseFloat(); // take the current time from the c# application and display it on the arduino board
Display.show(timer);
}
void displayTemp() {
float celcius;
celcius = get_temperature(); // mode for displaying the current temperature
Display.show(celcius);
}
void displayAngle() {
int value = analogRead(KNOB); // read and save analog value from the potentionmeter
value = map(value, 0, 1023, 0, 30); // Map value 0-1023 to 0-30
Display.show(value);
}
void displayAlarm() {
Serial.println("Alarm");
digitalWrite(RED_LED, HIGH);
tone(BUZZER, 1500, 700);
}
I want to write some text into the flash memory of an Arduino ESP32. It works kinda but not as I want it to.
void writeString(const char* toStore, int startAddr) {
int i = 0;
for (; i < LENGTH(toStore); i++) {
EEPROM.write(startAddr + i, toStore[i]);
}
EEPROM.write(startAddr + i, '\0');
EEPROM.commit();
}
My call
writeString("TEST_STRING_TO_WRITE", 0);
only writes TEST into the memory. I do not understand why. Is that because of the _? Or am I missing something different?
Here is the used LENGTH macro
#define LENGTH(x) (sizeof(x)/sizeof(x[0]))
and the method I use to read the string from the memory again (which seems to work correctly):
String readStringFromFlash(int startAddr) {
char in[128];
char curIn;
int i = 0;
curIn = EEPROM.read(startAddr);
for (; i < 128; i++) {
curIn = EEPROM.read(startAddr + i);
in[i] = curIn;
}
return String(in);
}
Where on earth did you get that LENGTH macro from? It’s surreal.
sizeof will not do what you want here. It’s a compile-time function that computes the storage requirements of its argument. In this case it should return the length in bytes of a character pointer, not the string it points to.
You want to use strlen(), assuming your char* is a properly terminated C string. Add one to make sure the ‘\0’ at the end gets stored, too.
#define LENGTH(x) (strlen(x) + 1)
Below is the code to demonstrate the storing as well as retrieving of the string ssid in the EEPROM (permanent storage).
#include "EEPROM.h"
int addr = 0;
#define EEPROM_SIZE 64
// the sample text which we are storing in EEPROM
char ssid[64] = "CARNIVAL OF RUST";
void setup() {
Serial.begin(115200);
Serial.println("starting now...");
if (!EEPROM.begin(EEPROM_SIZE)) {
Serial.println("failed to init EEPROM");
while(1);
}
// writing byte-by-byte to EEPROM
for (int i = 0; i < EEPROM_SIZE; i++) {
EEPROM.write(addr, ssid[i]);
addr += 1;
}
EEPROM.commit();
// reading byte-by-byte from EEPROM
for (int i = 0; i < EEPROM_SIZE; i++) {
byte readValue = EEPROM.read(i);
if (readValue == 0) {
break;
}
char readValueChar = char(readValue);
Serial.print(readValueChar);
}
}
void loop() {
}
I am using keil c51 compiler. I transmit data from my pc to MCU using serial port it works best.
When I transmit data from my MCU to PC then also it works best.
But when I transmit data to MCU and then store it to buffer character pointer and again from that character pointer buffer I transmit return to PC then it does not work and give garbage values?
My code for both function as below.
#include <REG51.H>
#include "uart.c"
void delay_ms(unsigned int x) // delays x msec (at fosc=11.0592MHz)
{
unsigned char j=0;
while(x-- > 0)
{
for (j=0; j<125; j++){;}
}
}
sbit SW = P3^2;
sbit LED = P3^3;
bit x = 0;
void main ()
{
char *buf;
int len=0;
int len1 = 0;
uart_init();
while(1){
if(RI == 1){
UART_RxString(buf,&len);
buf -= (len-1) ;
x = 1;
}
if(x == 1 && SW == 0){
UART_TxString(buf,&len1);
x = 0;
}
}
}
And below are the functions.
1.
void UART_TxString(char *string_ptr, int *l)
{
int count = 0;
while(*string_ptr){
UART_TxChar(*string_ptr++);
count++;
}
*l = count;
}
void UART_RxString(char *string_ptr, int *l)
{
char ch;
int count = 0;
while(1)
{
ch=UART_RxChar(); //Reaceive a char
//UART_TxChar(ch); //Echo back the received char
count++;
if((ch=='\r') || (ch=='\n')) //read till enter key is pressed
{ //once enter key is pressed
*string_ptr=0; //null terminate the string
break; //and break the loop
}
*string_ptr=ch; //copy the char into string.
string_ptr++; //and increment the pointer
}
*l = count;
}
I communicate with Arduino via Serial using a program that sends a series of bytes.
In order for the Arduino to realize it is receiving a message rather than junk, I have tagged the start of my byte array with the chars 'S' 'T' 'A' 'R' 'T'. After this will eventually follow a series of bytes that will be assigned to internal variables (not yet implemented).
The Arduino must read each byte sequentially and compare it to the byte array and if all are present in the correct order it will continue with the next part of the program, otherwise it will should discard current byte and wait for more bytes to arrive.
I am trying to implement it in the most efficient and readable way rather than using a series of nested if statements.
So far I have got:
byte inByte = 0;
byte handShake[] = {'S','T','A','R','T'};
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
for (int x =0; x < sizeof(handShake) ; x++)
{
inByte = Serial.read();
Serial.println(x);
if (inByte == handShake[x])
{
if (x == (sizeof(handShake)-1)) {setArduino();}
}
else break;
}
}
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
This however doesn't seem to get past the second byte and I'm not sure why.
Worked it out :
Here is the answer:
byte inByte = 0;
char handShake[] = {'S','T','A','R','T'};
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
for (int x =0; x < sizeof(handShake) ; x++)
{
inByte = Serial.read();
Serial.println(x);
if (inByte == handShake[x])
{
if (x == (sizeof(handShake)-1)) {setArduino();}
while(!Serial.available()) {delay(1);}
}
else {break;}
}
}
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
This may not be the most efficient way perhaps, but I can't see a problem with it currently.
Better answer : This allows the rest of the loop to iterate while waiting for the message to finish and if the full handshake message isn't received the counter will reset.
byte inByte = 0;
char handShake[] = {'S','T','A','R','T'};
int messageIndex = 0;
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
inByte = Serial.read();
Serial.println(messageIndex);
if (inByte == handShake[messageIndex])
{
messageIndex++;
if (messageIndex == sizeof(handShake)) {messageIndex = 0; setArduino();}
}
else {messageIndex=0;}
}
// Other code while waiting for message to finish
Serial.println("tick");
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
You could try to calculate your message. CRC is old and good solution. I use it and it works perfect for me. I am not sure what kind of device are you communicating with.
//define
const uint32_t Polynomial = 0xEDB88320;
const uint16_t NumBytes = 256;
uint8_t data[NumBytes];
/// compute CRC32
uint32_t crc32_bitwise(const void* data, uint16_t length, uint32_t previousCrc32 = 0)
{
uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF
uint8_t* current = (uint8_t*) data;
while (length--)
{
crc ^= *current++;
for (uint8_t j = 0; j < 8; j++)
{
uint8_t lowestBit = crc & 1;
crc >>= 1;
if (lowestBit)
crc ^= Polynomial;
}
}
return ~crc; // same as crc ^ 0xFFFFFFFF
}
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
when you need to calculate CRC
uint32_t crc = crc32_bitwise(data_bytes, sizeof(data_bytes));
data_bytes is byte array.
Then you can get all settings or message in byte data[x] and calculate CRC. Then you can add CRC to the message and send message byte data[x+sizeof(CRC)]
P.S. Use byte instead of int. For ex. for(byte x =0; x<sizeof(handShake); x++)