How to send and receive struct data packet over serial

Hi,
I'm developing ESP32 to ESP32 serial communication module for switching 32 relays(receiver end).
the DataPacket format is as follows

struct DataPacket
{
   int arr[];
   int arrSize;
}

I want to transmit this struct to receiver side over serial
I'm a beginner and can someone help me to solve this Data packet transmission over serial problem?

The struct you have defined has no specification for the number of array-elements.

There are a lot of options to do this.

If you really want to transmit it over a serial interface you would iterate through all array-elements creating a comma-separated string.
printing the string to serial receive the whole string on the other side and then using strtok to separate the numbers into the same struct on the receiverside.

As you are using an ESP32 how about using the wireless ESP-NOW-protocol?
ESP-NOW allows to send up to 250 bytes per message.
And you can directly send the struct as is. You just need to do a memcopy from the received data to a variable of type DataPacket

There is some effort to setup the ESP-NOW but there are a lot of examples online how to do that.
I would recommend to use this tutorial

as a starting point and then adapt it in small steps to your needs.

best regards Stefan

1 Like

Hi,
Thank you Stefan

actually that's a common approach for defining a generic struct for a variable length packets (e.g. ethernet). however, the size must precede the data.

a packet of fixed size would be cast as that type of struct and passed to some output function. the output function would read the size and transmit the corresponding # of by byte (4 * (size+1)). you would use serial.write (buf, len)

2 Likes

Why not use something like

Serial.print(32);
Serial.print(',');
Serial.println ("11110000111100001111000011110000");
1 Like

there are benefits to sending structured data in binary in the form suggested. the data[] could be a structure that can be copied directly into the struct on the receiving side and avoid parsing a string

I agree, this is a good approach, especially serial.write (buf, len) if a person knows how structures are located in memory and how to get data later.
However, beginners can use any approach they understand.

@gcjr
how about posting a rough sketchy sketch that show sthe basic principle of what you mean.

Anyway defining a structure of variable length must be carefully designed.
This is not what I would recommend to a newcomer
best regards Stefan

consider the code (run on my laptop) below and producing the following output

the code defines two structures for data, A_s and B_s, to be transmitted using a common routine passed a argument of type Generic_s. the send() routine doesn't care about the format of the data, just the # of integers.

in both A_s and B_s, the data is not just composed of integers. and when using mixed types, the structure must be "packed", to avoid "unused" byte between fields. the field within a structure used for this purpose are often organized to line up on integer boundaries

send: 9
     0064 0009 2211 0001
     20776f68 20776f6e 776f7262 6f63206e
     0077
send: 3
     0065 0003 2178934

#include <stdlib.h>

struct A_s {
    int32_t     type;
    int32_t     len;

    uint32_t    time;
    int32_t     eventId;
    char        desc [20];
};

#define LEN_A   (sizeof(A_s)/sizeof(int32_t))

struct B_s {
    int32_t     type;
    int32_t     len;

    uint8_t     id;
    uint8_t     parameter1;
    uint8_t     parameter2;
    uint8_t     parameter3;
};

#define LEN_B   (sizeof(B_s)/sizeof(int32_t))

struct Generic_s {
    int32_t     type;
    int32_t     len;

    int32_t     data [];
};

enum { TYPE_A = 100, TYPE_B };

// -------------------------------------
A_s  a = { TYPE_A, LEN_A, 021021,  1, "how now brown cow" };
B_s  b = { TYPE_B, LEN_B, 0x34, 0x89, 0x17, 2 };

// -------------------------------------
void
send (
    Generic_s   *buf )
{
    int  *p   = (int *) buf;
    int  nInt = buf->len;

    printf ("%s: %d", __func__, nInt);

    for (unsigned n = 0; n < nInt; n++)  {
        if (! (n % 4))
            printf ("\n    ");
        printf (" %04x", p [n]);
    }
    printf ("\n");
}

// -----------------------------------------------------------------------------
int 
main ()
{
    send ((Generic_s *) &a);
    send ((Generic_s *) &b);
}

hopefully it's obvious why there is a type field

Don't you know how to format code? :roll_eyes:
Do you think a newbie will quickly understand how your code works?

some "`" got deleted in error

Even though it's not used in the code?

it's necessary in the receiver.

Your code doesn't work in Arduino.
Let's ask the author of the post if your code helped solve his problem?
If not, then why is it here?

i was asked to demonstrate what i described

gcjr
thank you for posting. Aha .... --- OK -- ... hm ....
The code makes intensive use of pointers.
It uses main. So it seems not an arduino-sketch but a classical C(c++-code.
That's all I understand.

I'm a somehow a little bit advanced programmer but this is above my head.
Again thank you for posting an example to my request.
maybe I should stop hijacking the thread and concentrate on suggestions for the thread-opener.

@Vijan Vijan,
what do you think about transferring the data wireless?
It is just another option.
Both options wired-serial and wireless ESP-NOW have their pro's and con's
best regards Stefan

no.
how else are you going to learn except to ask questions and at the very least become aware of techniques that may help in the future.

Both casts result in undefined behavior, a is neither a Generic_s, nor an array of integers, and the types are not similar: reinterpret_cast conversion - cppreference.com

This works as far as I have tested it
Sender

#include <SoftwareSerial.h>

SoftwareSerial softOut(11, 12); //Rx, Tx

struct dataLayout //struct to hold the data
{
  char x[20];
  byte y;
} dataOut; //working copy of the struct
const byte structSize = sizeof(dataOut);

union unionLayout //union to hold data in parallel
{
  dataLayout dataOut;
  char buffer[structSize];
};

unionLayout dataToSend;   //working copy of the union

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  softOut.begin(9600);
  strcpy(dataToSend.dataOut.x, "Hello");  //put some values in the struct
  dataToSend.dataOut.y = 65;
}

void loop()
{
  for (int bufferIndex = 0; bufferIndex < structSize; bufferIndex++)
  {
    softOut.write(dataToSend.buffer[bufferIndex]);
    Serial.print(dataToSend.buffer[bufferIndex]);
  }
  delay(1000);
}

Receiver

#include <SoftwareSerial.h>

SoftwareSerial softIn(11, 12); //Rx, Tx

struct dataLayout //struct to hold the data
{
  char x[20];
  byte y;
} dataIn; //working copy of the struct
const byte structSize = sizeof(dataIn);

union unionLayout //union to hold data in parallel
{
  dataLayout dataIn;
  char buffer[structSize];
};

unionLayout dataToReceive;   //working copy of the union

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  softIn.begin(9600);
}

void loop()
{
  if (softIn.available() == structSize)
  {
    for (int bufferIndex = 0; bufferIndex < structSize; bufferIndex++)
    {
      dataToReceive.buffer[bufferIndex] = softIn.read();
    }
    Serial.println(dataToReceive.dataIn.x);
    Serial.println(dataToReceive.dataIn.y);
  }
}

It may be full of holes and certainly needs careful management to ensure that both ends remain in step should the data layout change

2 Likes