Datatypes question ESP-now sending mqtt topic to gateway

Hello all, i have a question maybe about datatypes… I’m using several ESP8266 and ESP-now protocol with several temp sensors, i’m using a sketch based on an Andreas spiess sketch to receive messages from remote sensors and then send out by mqtt.

My first issue is that i want to send the mqtt topic from the remote sensor to the gateway, so that each separate sensor will instruct the ‘mqtt gateway’ which topic it will forward the data on…

My problem is that i have a poor understanding of datatypes. The sketch that i base this on uses a struct, and i can successfully transfer the temperature and a station number in 2 seperate floats, but i just don’t have a clue where to start with the topic, i’ve tried Char* and i’ve tried String, but the Char crashed the sketch on the gateway, and the String outputs a weird symbol on the receiving side like this → ▒ ←

Programming with arduino ide.

This is the sketch coming from an individual temp node

#include <ESP8266WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_PIN 2

OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);

float tempSensor1;

uint8_t sensor1[8] = { 0x28, 0xFF, 0x25, 0x95, 0x92, 0x16, 0x04, 0x42  }; // 28FF259592160442


extern "C" {
  
#include <espnow.h>
#include "user_interface.h"
}

uint8_t remoteMac[] = {0x1A, 0xFE, 0x34, 0xE1, 0x1E, 0xC2};

#define WIFI_CHANNEL 1
#define SLEEP_SECS 20  // sleep in seconds, 900 seconds is 15 minutes
#define SEND_TIMEOUT 100  // 245 millis seconds timeout

// keep in sync with slave struct
struct __attribute__((packed)) SENSOR_DATA {
  float station;
  float temp1;
//  
  String topic1;
} sensorData;

void setup() {
  Serial.begin(115200); Serial.println();
  sensors.begin();
  readSensor();

  if (esp_now_init() != 0) {
    Serial.println("*** ESP_Now init failed");
    gotoSleep();
  }
 
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
  esp_now_add_peer(remoteMac, ESP_NOW_ROLE_SLAVE, WIFI_CHANNEL, NULL, 0);
  esp_now_register_send_cb([](uint8_t* mac, uint8_t sendStatus) {
    //Serial.printf("send_cb, send done, status = %i\n", sendStatus);
    gotoSleep();
  });

  uint8_t bs[sizeof(sensorData)];
  memcpy(bs, &sensorData, sizeof(sensorData));
  esp_now_send(remoteMac, bs, sizeof(sensorData));
 
}

void loop() {
  if (millis() > SEND_TIMEOUT) {
    gotoSleep();
  }
}

void readSensor() {
  
  sensors.requestTemperatures();
  sensorData.station = 1;
  sensorData.temp1 = sensors.getTempC(sensor1);
  sensorData.topic1 = "ESP/hello";
  Serial.printf("temp1=%01f\n",  sensorData.temp1);
  Serial.println(sensorData.topic1);
}

void gotoSleep() {

  int sleepSecs = SLEEP_SECS + ((uint8_t)RANDOM_REG32 / 8);
  Serial.printf("Awake for %i ms, going to sleep for %i secs...\n", millis(), sleepSecs);
  ESP.deepSleep(sleepSecs * 1000000, RF_NO_CAL);
}

And this is the "gateway / Relay " code.

/**
 * 
 * Author: Andreas Spiess, 2017
 * Slightly adapted by Diy_bloke 2020
  This sketch receives ESP-Now message and sends it as an MQTT messge
  It is heavily based on of Anthony's gateway setch sketch

https://github.com/HarringayMakerSpace/ESP-Now
Anthony Elder
 */
#include <ESP8266WiFi.h>
#include "PubSubClient.h"

extern "C" {
  #include "user_interface.h"
  #include <espnow.h>
}


//-------- Customise the above values --------

//#define COMMANDTOPIC "ESPNow/command"
#define SERVICETOPIC "ESPNow/service"
#define SENDTOPIC "ESPNow/cheese"

/* Set a private Mac Address
 *  http://serverfault.com/questions/40712/what-range-of-mac-addresses-can-i-safely-use-for-my-virtual-machines
 * Note: the point of setting a specific MAC is so you can replace this Gateway ESP8266 device with a new one
 * and the new gateway will still pick up the remote sensors which are still sending to the old MAC 
 */
uint8_t mac[] = {0xEC, 0xFA, 0xBC, 0x9B, 0xF5, 0x6D};// your MAC
void initVariant() {
  //wifi_set_macaddr(SOFTAP_IF, &mac[0]);
}


char *ssid      = "***********";               // Set you WiFi SSID
char *password  = "*********";               // Set you WiFi password


IPAddress server(192, 168, 1, 77);//your MQTT Broker address

//const char deviceTopic[] = "ESPNOW/";    //I guess this isn't used

WiFiClient wifiClient;
PubSubClient client(server, 1883, wifiClient);

String deviceMac;

// keep in sync with ESP_NOW sensor struct
struct __attribute__((packed)) SENSOR_DATA {
  float station;
  float temp1;
  String topic1;
} sensorData;

volatile boolean haveReading = false;

//char* SENDTOPIC = sensorData.topic;

int heartBeat;
//////////////////////////////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  Serial.begin(115200); 
  Serial.println();
  Serial.println();
  Serial.println("ESP_Now Controller");
    Serial.println();

  WiFi.mode(WIFI_AP);
  Serial.print("This node NOW mac: "); Serial.println(WiFi.softAPmacAddress());
  Serial.print("This node STA mac: "); Serial.println(WiFi.macAddress());

  initEspNow();  
  Serial.println("Setup done");
}


void loop() {
  if (millis()-heartBeat > 30000) {
    Serial.println("Waiting for ESP-NOW messages...");
    heartBeat = millis();
  }

  if (haveReading) {
    haveReading = false;   //<<<<<<<<<<<<<<<<<<<<<<<
    wifiConnect();
    reconnectMQTT();
    sendToBroker();
    client.disconnect();
    delay(200);
    ESP.restart(); // <----- Reboots to re-enable ESP-NOW
  }
}

void sendToBroker() {  //   

  Serial.println(sensorData.temp1);
  Serial.println(sensorData.topic1);
  String payload = "{";
  payload += "\"station\":\"" + String(sensorData.station);
  payload += "\",\"temp\":\""+String(sensorData.temp1);
  payload += "\",\"topic\":\""+String(sensorData.topic1);  // i hope this wont need to be sent
  payload += "\"}";
  Serial.println(payload);
  publishMQTT(SENDTOPIC,payload);
}

void initEspNow() {
  if (esp_now_init()!=0) {
    Serial.println("*** ESP_Now init failed");
    ESP.restart();
  }

  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  esp_now_register_recv_cb([](uint8_t *mac, uint8_t *data, uint8_t len) {

    deviceMac = "";
    deviceMac += String(mac[0], HEX);
    deviceMac += String(mac[1], HEX);
    deviceMac += String(mac[2], HEX);
    deviceMac += String(mac[3], HEX);
    deviceMac += String(mac[4], HEX);
    deviceMac += String(mac[5], HEX);
    
    memcpy(&sensorData, data, sizeof(sensorData));

 //   Serial.println("Message received from device: "); 
    
    Serial.println(deviceMac);
    Serial.print("Received Topic: ");
    Serial.print(sensorData.topic1);
    Serial.println("#");
    //    Serial.printf(" Temp=%0.2f, Sta=%0.0f, Topic=%s\n",        sensorData.temp1, sensorData.station, sensorData.topic1);    

    haveReading = true;
  });
}

void wifiConnect() {
  WiFi.mode(WIFI_STA);
  Serial.println();
  Serial.print("Connecting to "); Serial.print(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
     delay(250);
     Serial.print(".");
  }  
  Serial.print("\nWiFi connected, IP address: "); Serial.println(WiFi.localIP());
}

void publishMQTT(String topic, String message) {
  Serial.println("Publish");
  if (!client.connected()) {
    reconnectMQTT();
  }
  client.publish(SENDTOPIC, message.c_str());
}

void reconnectMQTT() {
  Serial.println(" Loop until we're reconnected");
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP-NOWClient-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    
      if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish(SERVICETOPIC, "I am alive");
      // ... and resubscribe
      //  client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc = ");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

Sorry it’s very messy, i’m still copying pasting to get things working, and usually tidy up after

You can use an array of char. The struct must be identical on the send and receive side

Ok, thanks, i will try that. i'm pretty sure the structs are identical so i will look at the char array.

:smiley: :smiley:

So.. i tried to create a char array

#define WIFI_CHANNEL 1
#define SLEEP_SECS 20  // sleep in seconds, 900 seconds is 15 minutes
#define SEND_TIMEOUT 100  // 245 millis seconds timeout
char mqttTopic[] = "ESP/now";         // Char array to contain the topic i want to copy to the struct

// keep in sync with slave struct
struct __attribute__((packed)) SENSOR_DATA {
  float station;
  float temp1;
  char topic1[15];              //  Char array i tried to create inside the struct
} sensorData;

  strcpy (topic1,mqttTopic);    //  My attempt to initialise the array with the topic( i also tried this inside setup)

void setup() {
  Serial.begin(115200); Serial.println();
  sensors.begin();
  readSensor();

So when i tried to strcpy the mqttTopic to the topic1, i got an error that 'topic1' was not declared in this scope

and when i tried to do it before setup i got this error
"expected constructor, destructor, or type conversion before '(' token"

How can i do this better please ?

maybe i found it…
it should be

  strcpy (sensorData.topic1,mqttTopic);

now to test

To explain a bit more about Strings.

Strings are zero-terminated as indication for “End-of-String” this means the length of the string can vary.
For sending bytes with ESP-NOW you specify the number of bytes that shall be copied sended.

For sending the data the command memcpy is used

memcpy(bs, &sensorData, sizeof(sensorData));

last paremeter is the number of bytes. sizeof(sensorData)

if the size of the String varies a different number of bytes is copied. same thing on the receiver side

    memcpy(&sensorData, data, sizeof(sensorData));

How should this ever work properly?

back to a good solution:
to be sure that the sender-sketch and the receiver-sketch have really the same structure you should use the char-array this way

                   //1234567
char mqttTopic[8] = "ESP/now";

you should define the size of the char-array explicitly. the number between “
and it should be one byte longer than the characters you want to store in it; to have space for value zero as the “string”-terminator

as alternatives for a char-array you can take a look into PString and into SafeString

best regards Stefan