It defines the above structure. Inside that structure is a String type. a) In use the structure seems to stay the same size, 56 bytes, despite having a dynamically sized member. b) The string may be filled with any number (tried up to 200) of chars and returned with a serial.print(). c) the message is sent as expected and printed by the receiver, only up to 10 chars. 11 or more breaks the receiver and an empty string is printed, even though it was fine on the sender. d) it does not "overrun" the bool after it in the structure.
so, I ask:
How does this even work? Wouldn't the structures on both sides need to have defined lengths to line up and unpack properly at the other end?
How does the String member get packed into the structure if it is dynamically allocated and expandable? I expanded it with the loop that added a character with every iteration. It works on the sender and can be printed to serial. I can fill it with hundreds of chars and still get 56 bytes from sizeof(myData).
As stated, when I access myData.d directly I get all the data. When I send the entire structure (56 bytes??) all data remains intact except for myData.d which behaves properly until 11 chars. At 11 chars it is received blank Try the examples, make the string "12345678901" and it fails to receive. Why? and Why 10 chars specifically? Data placed after it in the structure is unaffected.
I have been at this for to days, so I am reaching out. Hello by the way. It's my first post.
In the struct d is an object of the String class. Rather than being the text that holds, d is a container for it and that text is not actually in the struct. As a result the struct itself has a fixed size
Please post your 2 sketches, using code tags when you do
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-now-esp8266-nodemcu-arduino-ide/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <ESP8266WiFi.h>
#include <espnow.h>
// REPLACE WITH RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
char a[32];
int b;
float c;
String d;
bool e;
} struct_message;
// Create a struct_message called myData
struct_message myData;
unsigned long lastTime = 0;
unsigned long timerDelay = 2000; // send readings timer
// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
Serial.print("Last Packet Send Status: ");
if (sendStatus == 0){
Serial.println("Delivery success");
}
else{
Serial.println("Delivery fail");
}
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
// Register peer
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
void loop() {
if ((millis() - lastTime) > timerDelay) {
// Set values to send
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = "Hello";
myData.e = false;
// Send message via ESP-NOW
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
lastTime = millis();
}
}
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-now-esp8266-nodemcu-arduino-ide/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <ESP8266WiFi.h>
#include <espnow.h>
// REPLACE WITH RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
char a[32];
int b;
float c;
String d;
bool e;
} struct_message;
// Create a struct_message called myData
struct_message myData;
unsigned long lastTime = 0;
unsigned long timerDelay = 2000; // send readings timer
// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
Serial.print("Last Packet Send Status: ");
if (sendStatus == 0){
Serial.println("Delivery success");
}
else{
Serial.println("Delivery fail");
}
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
// Register peer
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
void loop() {
if ((millis() - lastTime) > timerDelay) {
// Set values to send
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = "Hello";
myData.e = false;
// Send message via ESP-NOW
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
lastTime = millis();
}
}
I can't see how that code is supposed to work, the only way I can see it did in their test is down to luck.
I guess what they did is load the sender sketch, and then load the receiver sketch. Since both sketches use the same variables, the memory allocation is the same for both sketches. So when the receiver sketch de-references the String pointer, it will point to the memory that was initialized when the sender sketch was loaded.
Exactly. As I am understanding it, the purpose of the struct is to pack several pieces of data into a formatted "packet" that can be disassembled on the other side using the same struct definition. For this to work don't all elements in the structure have to be of fixed size?
Could the author of this code simply missed that and gotten lucky that the code works as published? I broke it when I tried to send a bigger string, then went down a rabbit hole with many tunnels.
I just want to understand the code and why it behaves the way it does. Why does it work perfectly up to 10 chars? It packs and sends and unpacks on the other side, till I send 11.
I am new to cpp and the learning curve is steep.
I did what I wanted by making an oversized char string, putting it at the end of the structure, and added a length member.
Ok, That's what I was expecting too, but I'm a newbie. It did send the actual string data, not a pointer, up until 11 chars was too much. Then nothing in the string.
I though i was doing something wrong or missing something. Thank you.
Yes, and they are all of a fixed size. However, as has been pointed out the String is held as a pointer to the data and so is more than likely not consistent at both ends of the link
An array of chars is a much better solution even if t0eh String worked
this is flat in memory (consecutive bytes) so there is no problem
the issue is when you have a non trivial data type with the actual values not being stored in the same place.
Sending such structures requires serialising and unserializing the data
are you interested in examining all the detailsabout ESP-NOW and what datatypes can be used in a struct / can not be used in a struct
or
do you want to realise some project that exchanges data over ESP-NOW?
If you want to realise a project simply don't use String but instead use arrays of chars
(another name for them is "c_string" ) or use the library SafeString.h
Show us how it’s flat…. It’s not…so the string manipulation is not the issue there. Also the constructors of the class are meant to be called through cumbersome macros, making it thus more difficult than necessary to use that type as part of a struct.
Try it.
In the code I would use SafeStrings for string-manipulation and when it comes to send the ESP-NOW-Data I would copy the SafeString to a classical char-array.