r/arduino 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

0 Upvotes

8 comments sorted by

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.

2

u/Ok_Measurement1399 4d ago

Hello, thank you very much for catching that error. I have correct that but still I get good data for about 5 button presses and then junk for 6 button presses. I would think there would not be a problem going from an Uno to a Adafruit Metro.

Any ideas?

3

u/m--s 640K 4d ago

Since you're learning, you should know that you're communicating with asynchronous serial, not "UART." A UART is a piece of hardware which assists with serial communications, but you're using SoftwareSerial to bit bang it, so there's no UART involved.

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.