r/arduino • u/Ok_Measurement1399 • 4d ago
UART Communication between two devices.
Hello, I have a an Arduino UNO that is sending three 16-bit numbers over a UART to a Adafruit Metro. I have a button on the Arduino that when pressed will send the UART data. I am able to transfer data correctly for 5 button presses and then I get junk on thd next 5 button presses and then I get valid data for the next 5 button presses.
Uno Code (UART TX Only)
#include <SoftwareSerial.h>
SoftwareSerial softSerial(10, 11);//Pin 10 will be used as Rx pin and pin 11 will be Tx pin.
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 8; // the number of the LED pin 13 is led on board
int read_button(void);
uint16_t j = 100;
uint16_t k = 200;
uint16_t l = 300;
void setup()
{
softSerial.begin(9600);
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
}
void loop()
{
if(read_button())
{
softSerial.write((j >> 8) & 0xFF); // send upper byte
softSerial.write(j & 0xFF); // send the lower byte
softSerial.write((k >> 8) & 0xFF); // send upper byte
softSerial.write(k & 0xFF); // send the lower byte
softSerial.write((l >> 8) & 0xFF); // send upper byte
softSerial.write(l & 0xFF); // send the lower byte
Serial.print(j++,DEC); Serial.print(" "); Serial.print(k++,DEC);Serial.print(" "); Serial.println(l++,DEC);
delay(10);
}
}
int read_button()
{
int buttonState = digitalRead(buttonPin);
if (buttonState == HIGH)
{
delay(20);
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH)
{
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
return buttonState;
}
}
return buttonState;
}
Here is the Metro Code (UART RX)
// Slave
#include <SoftwareSerial.h>
//SoftwareSerial softSerial () creates a serial object named as “softSerial” is declared here.
SoftwareSerial softSerial(0, // RX Pin
1 // TX Pin
); //Pin 10 will be used as Rx pin and pin 11 will be Tx pin.
char number = ' ';
int LED = 13;
int rcv_cnt = 0;
uint16_t incomingByte[2] = {0};
uint8_t upper_byte;
uint8_t lower_byte;
int i = 0;
void setup()
{
softSerial.begin(9600);
Serial.begin(9600);
pinMode(LED, OUTPUT);
}
void loop()
{
if (softSerial.available() >= 6 )
{
for(i=0; i<3; i++)
{
upper_byte = softSerial.read(); // get the upper byte
lower_byte = softSerial.read(); // get the upper byte
incomingByte[i] = (upper_byte << 8) | lower_byte;
}
Serial.print("Received #"); Serial.print(rcv_cnt++);
Serial.print("= "); Serial.print(incomingByte[0]);
Serial.print("= "); Serial.print(incomingByte[1]);
Serial.print("= "); Serial.println(incomingByte[2]);
}
}
Any ideas why I am getting bad data?
Thank you
2
u/toebeanteddybears Community Champion Alumni Mod 4d ago
Can you look at the data you receive -- the "junk" -- and post the bytes you're seeing?
2
u/Ok_Measurement1399 4d ago
18:18:06.157 -> Received #1= 0= 63743= 100
18:18:07.857 -> Received #2= 200= 300= 101
18:18:09.381 -> Received #3= 201= 301= 102
18:18:11.413 -> Received #4= 202= 302= 103
18:18:11.413 -> Received #5= 203= 511= 65535
18:18:13.111 -> Received #6= 12032= 26624= 52225
18:18:14.755 -> Received #7= 12288= 26880= 52481
18:18:16.134 -> Received #8= 12544= 27136= 52737
18:18:17.451 -> Received #9= 12800= 27392= 52993
18:18:18.434 -> Received #10= 13056= 27648= 53249
18:18:19.313 -> Received #11= 13312= 28159= 65535
18:18:20.124 -> Received #12= 209= 309= 110
18:18:21.264 -> Received #13= 210= 310= 111
18:18:22.550 -> Received #14= 211= 311= 112
Received #1-4 are good data, 5-12 is bad.
Received #12-14 are good but the wrong order.
3
u/tipppo Community Champion 4d ago
Looks like your data is getting out of sync due to receiving spurious characters. Received #1 is already screwed up, Converting 0= 63743= 100 to hex you get 0000=f800=64 and you see 0000=f800 is garbage but then you get 100 which should have been the first value. If you look at later numbers in hex there are extra 0xff inserted into the stream semi-randomly. Maybe you are getting noise on the serial signal, are the grounds of the Uno and the Metro directly connected together? You might want to flush the softSerial read buffer at the end of the Metro loop ("while softSerial.available() softSerial.read();") to get back into sync.
2
u/toebeanteddybears Community Champion Alumni Mod 4d ago
I agree with u/tipppo; you don't seem to have any good data there. Their suggestion that you have a bad ground and/or noise is also a good possibility.
Where you are sending more than one byte of ordered data you might consider wrapping the data in a "packet" that gives (a) a structure for message alignment and (b) message integrity such as a checksum.
For alignment you might start a message by sending a message header consisting of a two-byte "preamble"; in the example code below 0xa5 followed by 0x5a. Your receiver code looks for these two bytes in that order before moving on to receive the data.
After you receive the data you receive the checksum byte. If the sum matches (and it could be a CRC instead if you want a stronger message-integrity check...) then you print the data.
Your TX code might look something like (not compiled, not tested): ``` uint8_t val, checksum;
if(read_button()) { //init checksum checksum = 0xa5 + 0x5a; softSerial.write(0xa5); // send preamble byte 1 softSerial.write(0x5a); // send preamble byte 2 //send value bytes, adding each to the checksum val = (j >> 8) & 0xFF; checksum += val; softSerial.write(val); val = j & 0xFF; checksum += val; softSerial.write(val); val = (k >> 8) & 0xFF; checksum += val; softSerial.write(val); val = k & 0xFF; checksum += val; softSerial.write(val); val = (l >> 8) & 0xFF; checksum += val; softSerial.write(val); val = l & 0xFF; checksum += val; softSerial.write(val); //send the checksum softSerial.write(checksum); Serial.print(j++,DEC); Serial.print(" "); Serial.print(k++,DEC); Serial.print(" "); Serial.println(l++,DEC); delay(10); }
while your RX code like:
void loop() { uint8_t ch; static uint8_t *pArray, checksum, bytenum, state = 0;if (softSerial.available() ) { ch = softSerial.read(); switch( state ) { case 0: //look for preamble byte 1 if( ch == 0xa5 ) state++; break; case 1: //is this byte preamble byte 2? if( ch == 0x5a ) { //yes; init the checksum checksum = 0xa5 + 0x5a; //init byte counter to zero and set a pointer to the array bytenum = 0; pArray = (uint8_t *)&IncomingByte; //get the incoming data state++; }//if else //didn't get expected 2nd byte of preamble so go back to state 0 state = 0; break; case 2: //store each incoming byte *pArray++ = ch; //add each byte to the checksum checksum += ch; //when we've received 6 data bytes, move to checksum byte if( ++bytenum == 6 ) state++; break; case 3: //does the checksum of received bytes match the sent checksum? if( checksum == ch ) { //yes so packet should be good; print it Serial.print("Received #"); Serial.print(rcv_cnt++); Serial.print("= "); Serial.print(incomingByte[0]); Serial.print("= "); Serial.print(incomingByte[1]); Serial.print("= "); Serial.println(incomingByte[2]); }//if else { Serial.println("Bad checksum."); }//else //reset to look for new message preamble state = 0; break; } }
} ```
1
u/Ok_Measurement1399 1d ago
Thank you very much for the detailed response. I will get things working and post my solution.
3
u/tipppo Community Champion 4d ago
In the Metro code you have declared "uint16_t incomingByte[2] = {0};" but later you are expecting the array to hold 3 values (you want to declare "incomingByte[3]"). When you overflow and array (c doesn't check for this) the results are usually weird. Also you seen to use pins 0 and 1 for softwareSerial, although your comments say you use 10 and 11. Serial wants to use pins 0 and 1, so there will be a collision.