Arduino UART Packet Handling

I have a question about how to use uart serial communnication on arduino
Sorry if this is simple I am new to UART

I'm pretty sure I'm overthinking this issue and complicating it

The arduino acts as the host, and has another device send it data via the uart protocol. The packet format is as so:

Header
4 bytes
ASCII character

Payload Length
4 bytes
uint32

Payload
x bytes (determined from payload length)
Binary Data

So the transmission from the device would be hex characters, but they need to be handled differently (ASCII Character, UINT32 and Binary Data)

How could I get the arduino to handle the different hex data differently?

Thanks much :slight_smile:

Welcome to the forum

I suggest that you post a couple of example messages and the translation of what they contain

1 Like

Sorry, but I don't think so. A "hex" character is a single byte from "0" to "9" and from "A" to "F".
The only part containing ASCII characters is the header, and if it's just a sequence of 4 characters it doesn't matter if ASCII or not, unless they're a hex representation of a 2-bytes integer. With an example we can better understand this section.
The payload length is an uint32 and it's not a problem getting that value for Arduino (it's an "unsigned long", aka "uint_32_t"), you just need to know the bytes order (big-endian or little-endian).
The Payload is just a byte array ("byte[]") buffer, once you know the length you read each received byte into it. If you already know the maximum length possible you can allocate the buffer to that length, if not you should dynamically allocate the appropriate memory space, up to the available RAM memory of your board (which one?).

@docdoc ok thank you for the information!

Here is an example of a transmission from an uno to the device

Header: INIT
0x49 0x4E 0x49 0x54

Payload Length (1)
0x01 0x00 0x00 0x00

Payload
0x01 (value means use default baud)

So: 0x49 0x4E 0x49 0x54 0x01 0x00 0x00 0x00 0x01

the order is Little Endian, so the first bit in the Payload Length dictates the payload length

Also the payload may be string at times (if i send the command to request firmware string for example). Is this also possible?

Also, I'm unsure what you mean in your first paragraph. What do you mean it doesnt matter if ascii or not?

Sorry for bad english and thank you :slight_smile:

do you know what can be the the maximum length of your payload?

coz if you want to store/manupilate the payload, depending on how big your payload can get, you may potentially have issues processing if on 'small' memory boards (e.g UNO). please keep that in mind.

as to detecting and receiving the data packet itself that would be pretty standard IMHO :wink:

so something like this maybe:
(Compiles, NOT tested!)

/*Header: INIT
  0x49 0x4E 0x49 0x54

  Payload Length (1)
  0x01 0x00 0x00 0x00

  Payload
  0x01 (value means use default baud)

  So: 0x49 0x4E 0x49 0x54 0x01 0x00 0x00 0x00 0x01
*/
#include <SoftwareSerial.h>

#define HEADER 0x01
#define LENGTH 0x02
#define DATA 0x03
#define TIMEOUT 100 //100ms

SoftwareSerial mySerial(2, 3); // RX, TX

uint32_t payload_length = 0, oldTime;
char payload_len[3], payload_data[128];
uint8_t fsm = HEADER, rx_cnt = 0;


void setup() {
  Serial.begin (115200); //for print monitor

  mySerial.begin(9600); //serial connection between devices
  oldTime = millis();
}

void loop() {
  if (mySerial.available()) {
    uint8_t c = mySerial.read();

    if (fsm == HEADER) {
      Serial.println("Packet Header Received");
      if (rx_cnt == 0 && c == 0x49) ++rx_cnt;
      else if (rx_cnt == 1 && c == 0x4E) ++rx_cnt;
      else if (rx_cnt == 2 && c == 0x49) ++rx_cnt;
      else if (rx_cnt == 3 && c == 0x54) {
        fsm = LENGTH;
        payload_length = 0;
        rx_cnt = 0;
      }
      else {
        fsm = HEADER;
        payload_length = 0;
        rx_cnt = 0;
      }
    }
    else if (fsm == LENGTH) {
      Serial.println("Packet Length Received");
      if (rx_cnt < 2) {
        payload_len[rx_cnt++] = c;
      }
      else if (rx_cnt == 3) {
        payload_length |= c;
        for (int8_t i = 2; i >= 0; --i) {
          payload_length = ((payload_length << 8) | payload_len[i]);
        }
        fsm = DATA;
        rx_cnt = 0;
      }
      else {
        fsm = HEADER;
        payload_length = 0;
        rx_cnt = 0;
      }
    }
    else if (fsm == DATA) {
      Serial.println("Receiving Packet Data");
      if (rx_cnt < payload_length) {
        payload_data[rx_cnt++];
      }
      else {
        Serial.println("Packet Receipt COMPLETE");
        fsm = HEADER;
        payload_length = 0;
        rx_cnt = 0;
      }
    }
    oldTime = millis();
  }

  if (millis() - oldTime > TIMEOUT) {
    fsm = HEADER;
    payload_length = 0;
    rx_cnt = 0;
  }

}

Hope that helps...

a protocol I have used over Bluetooth and other byte oriented serial communications is
SYN DLE STX byte stuffed data CRC DLE ETX

the data can be of any length and contain any byte value therefore DLEs in the data stream have to be byte stuffed

Please try

This often recommended thread

on the basics of serial communications.

It answers questions you don't even know you have yet. :wink:

a7

At best that is confusing, at worst it's simply incorrect. A single byte is a binary value between 0x00 and 0xFF (0 - 255 decimal). If it's also a '"hex" character is a single byte from "0" to "9" and from "A" to "F"', then it is, in fact, an ASCII representation.

compare your hex values to this ascii chart

serial prints display binary character values as ascii characters

Thanks, it's clearer now. And please forget the "hex characters" term, always speak about "bytes" (here represented by hex values just to let "humans" understand the contents).

Good, but I'd make some changes:

uint32_t payload_length = 0, oldTime;
char payload_len[3], payload_data[128];

First, the payload length is 4 bytes, not 3. Second, the payload content and lenght should be "byte" (unsigned byte) not "char":

uint32_t payload_length = 0, oldTime;
byte payload_len[4], payload_data[128];

The FSM implementation is a good practice and correct. We (I) don't really know if that 4 header bytes are fixed, anyway it looks to me like your code should work.

That's what I meant. The OP wrote the header is "ASCII character" and "the transmission from the device would be hex characters", so my goal was to let the OP be more clear where "hex characters" term is wrong. Then, after the packet example (that's what I asked for), everything becomes clear to us.
So, what is confusing (and perhaps incorrect) is his post, not my reply, and if you reply to me specifying "a hex characteris, in fact, an ASCII representation" is totally superfluous.

Detect the Header (the synchronization pattern), count message length, receive/store data bytes.

char mySync[5];  //to old ASCII codes Header
byte myData[50]; //to hold received binary data bytes
int i = 0;

void loop()
{
    byte n = SUART.available(); //SUART = Software UART Port
    if(n == 4)
    {
        for(i = 0; i < 4; i++)
        {
            mySync[i] = SUART.read();
        }
        mySync[i] = '\0';   //null-charcater
        if(strcmp(mySync, "INIT") == 0) //0x49 = 'I', 0x4E='N', 0x49 = 'I', 0x54= 'T'
        {
            uint32_t length = SUART.read();
            length = SUART.read() << 8 | length; //xxyy
            length = (uint32_t)SUART.read() << 16 | length; // zzyyxx
            length = (uint32_t)SUART.read() << 24 | length; // mmzzyyxx
            //------------------------------------------------------------------
            byte m = SUART.readBytes(myData, length); // m = bytes stored
        }  
   }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.