ESP-NOW converting incoming bytearray problem

I have a Micropython ESP32 sending an ESP-NOW message to an Arduino ESP32. The message leaves the Micropython as bytearray(b'87') - a value of 87 - but comes out of the Arduino as data =14136. I'm using the stock receive code of:

int data=0;
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
   memcpy(&data, incomingData, sizeof(data));

If I convert the 'data' variable to a char I get the first value of the integer I want (8). I'm not sure if I'm running into a Micropython- Arduino type mismatch. Thanks

Should "data" be a structure?

I don't think so as I'm just sending an integer from Micropython. That example defines the struct on both ends to encode/decode the message. I suspect it maybe Micropython encoding with UTF-8. Bit of a hardware corner I'm backed into since the sender needs Micropython for it's asyncio properties, and the receiver is an LCD display without mature Micropython support.

Playing around more with it, if I modify 'data' from type int to String, I get the correct value, although the serial monitor output has trailing characters I assume from the rest of the bytearray.

String data;

void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  
  memcpy(&data, incomingData, sizeof(data));

Try this sketch

int n;
String s;

void assign87(byte *b) {
  *b++ = '8';
  *b++ = '7';
}

void setup() {
  Serial.begin(115200);
  // delay(1500);  // may need this
  assign87((byte *)&n);
  assign87((byte *)&s);
  Serial.println(n);
  Serial.println(n, HEX);
  Serial.println(s);
  Serial.println(s.length());
}

void loop() {
}

I get

14136
3738

0

Does the int make sense now? I did not reproduce your String result, but I can if I change the declaration to String s = "ABCD";

87CD
4

But only on ESP32, not on Uno. This is actually a trap, an invalid way of writing to the internal implementation of a String on some boards.

The data is coming across fine. You need to have a more accurate definition of the "shape" of the data, defined in C++. It could be a struct. In C++, an "array of bytes" that represents a string must be NUL-terminated. Details like that.

Thank you, that is helpful. Your code output line as is ' Serial.println(n);' matches 4 input test points I tried. Reversing your function to output '87' from an input of '14136' is not jumping off the page to me but I will look at it more deeply in the AM.

You are sending the data as a byte array but you are not receiving it as a byte array. So it's not surprising that the data isn't as expected.

void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
   char data[len+1]; // enough for the data + null terminator
   memcpy(&data, incomingData, len);
   data[len] = '/0'; // add null terminator

Thank you. I think that's correct as well but the code snippet seems to add the null terminator 0 in the middle of whatever is incoming from Micropython. The MP docs say it sends "msg : string or byte-string up to espnow.MAX_DATA_LEN (250) bytes long" and the print output in MP is bytearray(b'87').

@coaster what you have in Python is a byte string which can converted to a list then each character transmitted one at a time.

byte_string = b'87'

ser.write(list(byte_string))

One way of checking this out is using the following which will echo the value back to the sender

int data=0;

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
}

void loop() {
if (Serial.available () > 0){
    data=Serial.parseInt ();
    Serial.println(data);
}
}

I didn't get that result initially when I print the 'byte_string' list in Micropython, but I do get the behaviour you describe with the modify the line to list(byte_string.decode('utf-8')) .

@coaster yes I get what your saying, and really the list is unnecessary you can just write the value

byte_string = b'87'

print(byte_string )

and this will also be good for the serial write

byte_string = b'87'

ser.write(byte_string )

the thing that makes this work in the way I mentioned was having

data=Serial.parseInt();

which will capture and display your micropython int.

In case anyone comes across this problem of having Micropython talk to Arduino with ESP-NOW, the solution that seems to be working best for me was to convert to JSON. In Micropython the sender puts out a JSON object via ESP-NOW with the data I need:

            send_message = ({"lower_tank_percentage":lower_tank_percentage,"upper_tank_percentage":upper_tank_percentage,"battery_voltage":battery_voltage})
            
            esp_now.send(display_mac,ujson.dumps(send_message), True)

On the Arduino receiver side I set up ArduinJson:

#include <ArduinoJson.h>
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {

// Convert the incoming byte array to a string
  data = String((char*)incomingData);

  // Allocate memory for the JSON document
  StaticJsonDocument<250> doc; // Adjust the size based on your expected JSON structure

  // Deserialize the JSON string into the document
  DeserializationError error = deserializeJson(doc, data);


  // Access the values from the parsed JSON
  upper_tank_percentage = doc["upper_tank_percentage"].as<int>();
  lower_tank_percentage = doc["lower_tank_percentage"].as<int>(); // Make sure this key exists in your JSON
  battery_voltage = doc["battery_voltage"].as<int>(); // Ensure this key matches your JSON structure

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