I have been successfully communicating using text based serial between by ESP32-CAM and an Arduino Uno, but I would like to make it faster by changing it to binary, but I am getting some odd output.
On the ESP32 I am sending using:
struct data
{
int x = 0;
int y = 0;
};
data d;
d.x = -512;
d.y = 512;
Serial.write((byte*)&d, sizeof(data));
And on the Arduino Uno I am receiving using this code (this is using software Serial rather than hardware, but I don't think that should make a difference):
struct data
{
int x = 0;
int y = 0;
};
data d;
if (esp32Serial.available() > 0) {
esp32Serial.readBytes((byte*)&d, sizeof(data));
Serial.print("x:");
Serial.print(d.x);
Serial.print("y:");
Serial.println(d.y);
}
But I actually get the output:
x:-1y:512
x:0y:-512
Instead of what I would expect:
x:-512y:512
x:-512y:512
Is there some sort of byte swapping I need to do? Or some sort of offset? Or a serial timing issue? I think both systems are little endian?
Note that I have a delay(1000) in my loop() function so I don't think this data is being sent to quickly (although the actual program will be faster) and I am using 115200 baud.
struct __attribute__((__packed__)) data
{
int16_t x = 0;
int16_t y = 0;
};
the packed attrib makes sure the data packed to bytes, think the default is word which works fine with the structure as it sits but if you add data this may change, so as a rule of thumb of the structure will be streamed, then pack it..
oh, a quick serial.print of the sizeof struct would have shown your orig issue..
But I also seems to be getting some corruption too. Is there anything I can do about this? I have tried adding a temporary buffer that I copy into rather than sending the data directly (is this the correct way to do this?):
hmm I must have got something wrong just now (maybe I didn't restart the ESP32 properly?) as it has gone back to the correct numbers but the wrong way around!
I have worked out what the problem was. It was a synchronisation issue. I was just continuously writing a stream of data to UART and the receiver might read an arbitrary point in that stream. Adding a third member to the struct made that more obvious!
There seem to be 2 ways around this - either I have a "header" byte that I look out for before doing a proper read. The only trouble with this is that my data is going to be -512 to 512.
Or I add some 2 way communication and send an explicit poll to the ESP32 to tell it to send a packet of data. This one might be the better approach.
Exactly! So, add sync pattern before sending the structure as natural binary.
In the following sketches, ESP32 is sendig struct over its UART2 Port and UNO is receiving via its sep32Serial(6, 7) Port.
Sender Sketch:
struct __attribute__((packed, aligned(1))) data
{
int16_t x; //MCU independent data size; int is 32-bit for ESP32, 8-bit for UNO
int16_t y;
float z;
};
data d;
uint8_t syncHeader[] = {0xDE, 0xAD, 0xBE, 0xEF}; // Synchronization header
//uint8_t *dataPtr = reinterpret_cast<uint8_t*>(&d);
void setup()
{
Serial.begin(9600);
Serial2.begin(9600);
d.x = -512;
d.y = 512;
d.z = 32.75;
}
void loop()
{
for (size_t i = 0; i < sizeof syncHeader; i++)
{
Serial2.write(syncHeader[i]);
}
Serial2.write(sizeof d); //number of bytes in the struct
Serial.println(sizeof d); //debugg
Serial2.write((byte*)&d, sizeof d);
delay(1000);
}
Receiver Sketch:
#include<SoftwareSerial.h>
SoftwareSerial esp32Serial(6, 7); //SRX = 6, STX = 7
struct __attribute__((packed, aligned(1))) data
{
int16_t x; //MCU independent data size; int is 32-bit for ESP32, 8-bit for UNO
int16_t y;
float z;
};
data d;
void setup()
{
Serial.begin(9600);
esp32Serial.begin(9600);
}
void loop()
{
byte n = esp32Serial.available();
{
if (n != 0)
{
if (n >= 4)
{
//Serial.println(n);//debugg
uint32_t syncPatt =
(uint32_t)esp32Serial.read() << 24 | (uint32_t)esp32Serial.read() << 16
| (uint32_t)esp32Serial.read() << 8 | esp32Serial.read();
//Serial.println(syncPatt, HEX);//debugg
delay(1); //without delay, the Receiver does not work! Why?
if (syncPatt == 0xDEADBEEF)
{
byte p = esp32Serial.read(); //number of bytes in struct
byte m = esp32Serial.readBytes((byte*)&d, p);
Serial.print("x = "); Serial.print(d.x); Serial.print(' ');
Serial.print("y = "); Serial.print(d.y); Serial.print(' ');
Serial.print("z = "); Serial.print(d.z, 2); Serial.println(' ');
Serial.println("===========================");
}
}
}
}
}
Output:
===========================
x = -512 y = 512 z = 32.75
===========================
x = -512 y = 512 z = 32.75
===========================
x = -512 y = 512 z = 32.75
===========================
You may read this thread (post #69@J-M-L) to see the details on why sync pattern is needed for natural binary transmission and also for ASCII transmission.
It is also needed for text transmission but usually it’s easier to have a start or end marker as just one byte as you have bytes that are not part of the alphabet / numbers .
For example \n is often used to denote the end of the line and to separate two payloads - but other bytes have special meaning in communication and could be put to good use as well - they are known as control flow bytes
Cheers! I forgot to the add the above as I was busy to understand your way of decteing the sync pattern. I am editing my post.
The receiver sketch of my post #11 does not work without the insertion of some delay time. I am implementing your way of detecting the sysn patter and see if it helps; else, your intervention is expected.
It's not as simple as that. Since binary data can contain any value, it's entirely possible for the sync byte(s) to end up in the payload data stream. Check out the source code for the SerialTranser Library for one technique to get around this problem.
sure - it's not 100% fool proof. Remember it's only to denote the start of a payload, so you might get a false start if this happens in the middle of a payload but then if the payload also include a size and a CRC you'll know it was a bogus packet. is some cases where you stream constant updates of your humidity and temperature, that might not be such a big issue.
I agree there are safer strategies when needed - a typical one is to use a start marker (say 0x00) and to have an escape sequence byte say (0x01) and two replacement values (one for the escape sequence byte (say 0x02) and one for the start marker (say 0x03)
when you stream your payload, you send the start marker 0x00
then before writing the bytes of the payload you check them.
if it's the start marker (0x00) you send the escape sequence followed by its replacement values so 2 bytes : 0x01 0x03
if it's the escape sequence byte, similarly you send the the escape sequence followed by its replacement 0x01 0x02
any other byte goes unchanged
on the receiving end
if you see 0x00 you know it's the start of the payload
if you see 0x01 (the escape sequence) you ignore it but know that the next byte is either 0x02 or 0x03.
if you get 0x02 you store 0x01 in the received payload
if you get 0x03 you store 0x00 in the received payload
if you see anything else you store it directly in the received payload (if you get 0x02 or 0x03 without having received first the escape byte then it's the real value of that byte)
That's pretty fast and only costs one look ahead byte in terms of latency from time to time. (that "random" latency can create issues possibly)