0

I am still not sure what should be the exact Title for this question, because I don't know where the problem is.

I am using #include <MQTTClient.h> mqtt library to subscribe on a topic. This topic is created dynamically depending on the name of the device. Currently I am testing with device name myDeviceAbc.

String baseTopic  = "arn:aws:iot:us-east-2:xxxxxxxxxxxx:thing";
String espInTopicStr  = baseTopic + "/" + config.machineName + "/espIn"; //config.machineName is a string "myDeviceAbc"

const char* espInTopic  = "";
espInTopic = espInTopicStr.c_str();

const char* hardCodedTopic = "arn:aws:iot:us-east-2:xxxxxxxxxxxx:thing/myDeviceAbc/espIn";

int rc = client.subscribe(espInTopic, MQTT::QOS0, messageArrived);
if (rc != 0) {
  Serial.print("rc from MQTT subscribe is ");
  Serial.println(rc);
  return;
}
Serial.println("MQTT subscribed");


Serial.print("size of hardcoded topic: ");
Serial.println(strlen(hardCodedTopic));
Serial.print("size of actual topic: ");
Serial.println(strlen(espInTopic)); 

Serial.print("hardCodedTopic >>>");
Serial.print(hardCodedTopic);   
Serial.print("<<<"); 
Serial.println("."); 

Serial.println("---");

Serial.print("espInTopic topic >>>");
Serial.print(espInTopic);   
Serial.print("<<<"); 
Serial.println(".");

I have defined two variables for same topic arn:aws:iot:us-east-2:xxxxxxxxxxxx:thing/myDeviceAbc/espIn . one is hardcoded hardCodedTopic and other is created dynamically espInTopic.

when I subscribe on espInTopic, I get MQTT subscribed message on serial console BUT nothing is received on this topic when I publish from AWS portal. but when I subscribe on hardCodedTopic I then receive all published messages. I cannot figure out what is the issue. I checked the mqttClient subscribe method inside the library header file, which is defined like this

int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh);

/** MQTT Unsubscribe - send an MQTT unsubscribe packet and wait for the unsuback
 *  @param topicFilter - a topic pattern which can include wildcards
 *  @return success code -
 */
int unsubscribe(const char* topicFilter);  

It does takes const char* as topic name. I though maybe due to conversion from String to char array, there might be some unknown characters appended in the char array espInTopic, but I also checked the size and content of both topics which is exactly the same.

size of hardcoded topic: 52
size of actual topic: 52
hardCodedTopic >>>arn:aws:iot:us-east-2:xxxxxxxxxxxx:thing/myDeviceAbc/espIn<<<.
---
espInTopic topic >>>arn:aws:iot:us-east-2:xxxxxxxxxxxx:thing/myDeviceAbc/espIn<<<.

Full Code:

#include <Arduino.h>
#include <Stream.h>
#include <EEPROM.h>
#include <ArduinoJson.h>
#include <SoftwareSerial.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFiMulti.h>
#include "helpers.h"
#include "global.h"

//AWS
#include "sha256.h"
#include "Utils.h"


//WEBSockets
#include <Hash.h>
#include <WebSocketsClient.h>


//MQTT PAHO
#include <SPI.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>


//AWS MQTT Websocket
#include "Client.h"
#include "AWSWebSocketClient.h"
#include "CircularByteBuffer.h"

#define ACCESS_POINT_NAME  "VM"        
#define ACCESS_POINT_PASSWORD  "12345678" 

extern "C" {
  #include "user_interface.h"
}

//AWS IOT config, change these:
char wifi_ssid[]       = "TALKTALKAF521F";
char wifi_password[]   = "xxxxxxxx";
char aws_endpoint[]    = "xxxxxxxxxxxxxxxx.iot.us-east-2.amazonaws.com";
char aws_key[]         = "xxxxxxxxxxxxxxxx";
char aws_secret[]      = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char aws_region[]      = "us-east-2";

String baseTopic  = "arn:aws:iot:us-east-2:xxxxxxxxxxxx:thing";
//const char* espInTopic  = "";        // was global before, but later I also tried to keep this local on subscribe() method 
const char* espOutTopic  = "";
int port = 443;

//MQTT config
const int maxMQTTpackageSize = 512;
const int maxMQTTMessageHandlers = 1;

const char response[] = "{\"response\": {\"request_id\":, \"data\":}}"; 

String nodeName = ""; // Name needs to be unique
String nodeMac = "";

ESP8266WiFiMulti WiFiMulti;

AWSWebSocketClient awsWSclient(1000);

IPStack ipstack(awsWSclient);
MQTT::Client<IPStack, Countdown, maxMQTTpackageSize, maxMQTTMessageHandlers> client(ipstack);

//# of connections
long connection = 0;
unsigned long timer = 0;
unsigned long maxTimeOut = 7000;  //7 seconds 

//generate random mqtt clientID
char* generateClientID () {
  char* cID = new char[23]();
  for (int i=0; i<22; i+=1)
    cID[i]=(char)random(1, 256);
  return cID;
}

//count messages arrived
int arrivedcount = 0;



//callback to handle mqtt messages
void messageArrived(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;

    Serial.print("Message ");
    Serial.print(++arrivedcount);
    Serial.print(" arrived: qos ");
    Serial.print(message.qos);
    Serial.print(", retained ");
    Serial.print(message.retained);
    Serial.print(", dup ");
    Serial.print(message.dup);
    Serial.print(", packetid ");
    Serial.println(message.id);
    Serial.print("Payload ");
    char* msg = new char[message.payloadlen+1]();
    memcpy (msg,message.payload,message.payloadlen);

}

//connects to websocket layer and mqtt layer
bool connect () {

    if (client.isConnected ()) {    
        client.disconnect ();
    }  
    //delay is not necessary... it just help us to get a "trustful" heap space value
    delay (1000);
    Serial.print (millis ());
    Serial.print (" - conn: ");
    Serial.print (++connection);
    Serial.print (" - (");
    Serial.print (ESP.getFreeHeap ());
    Serial.println (")");

   int rc = ipstack.connect(aws_endpoint, port);
    if (rc != 1)
    {
      Serial.println("error connection to the websocket server");
      return false;
    } else {
      Serial.println("websocket layer connected");
    }


    Serial.println("MQTT connecting");
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.MQTTVersion = 4;
    char* clientID = generateClientID ();
    data.clientID.cstring = clientID;
    rc = client.connect(data);
    delete[] clientID;
    if (rc != 0)
    {
      Serial.print("error connection to MQTT server");
      Serial.println(rc);
      return false;
    }
    Serial.println("MQTT connected");
    return true;
}

//subscribe to a mqtt topic
void subscribe () {
   //subscript to a topic
  String espInTopicStr  = baseTopic + "/" + "myDeviceAbc" + "/espIn";
//  String espOutTopicStr = baseTopic + "/" + config.machineName + "/espOut";
  const char* espInTopic  = espInTopicStr.c_str();
//  espOutTopic = espOutTopicStr.c_str();

  const char* hardCodedTopic = "arn:aws:iot:us-east-2:xxxxxxxxxxxxxx:thing/myDeviceAbc/espIn";
  Serial.print("size of hardcoded topic: ");
  Serial.println(strlen(hardCodedTopic));
  Serial.print("size of actual topic: ");
  Serial.println(strlen(espInTopic));    

   Serial.print("hardCodedTopic >>>");
   Serial.print(hardCodedTopic);   
   Serial.print("<<<"); 
   Serial.println("."); 

   Serial.println("---");

   Serial.print("espInTopic topic >>>");
   Serial.print(espInTopicStr.c_str());   
   Serial.print("<<<"); 
   Serial.println(".");
   int rc = 99;
   while (rc != 0){
      rc = client.subscribe(hardCodedTopic, MQTT::QOS0, messageArrived);
      if (rc != 0) {
        Serial.print("rc from MQTT subscribe is ");
        Serial.println(rc);
        return;
      } else {
        Serial.println("");
        Serial.println("MQTT subscribed");
      }
      Serial.println("rc: " + String(rc));
      delay(500);     
   }

}

//send a message to a mqtt topic
void sendmessage (const char* topic, String msg) {
    //send a message
    MQTT::Message message;
    char buf[100];
    strcpy(buf, msg.c_str());    
    message.qos = MQTT::QOS0;
    message.retained = false;
    message.dup = false;
    message.payload = (void*)buf;
    message.payloadlen = strlen(buf)+1;
    int rc = client.publish(topic, message); 
}


void setup() {

    wifi_set_sleep_type(NONE_SLEEP_T);
    EEPROM.begin(1024);  
//    for (int i = 0; i < 1024; ++i) { EEPROM.write(i, 0); }   
    Serial.begin(115200);
    ardMegaSerial.begin(9600);

    delay (2000);

    //////////////////////////////////////////
  if (!ReadConfig())
  {
    // DEFAULT CONFIG
    config.configMode = true;
    config.ssid = "N.A";
    config.password = "N.A";       
    config.machineName = "N.A"; 
    config.dhcp = true;
    config.IP[0] = 192;config.IP[1] = 168;config.IP[2] = 1;config.IP[3] = 100;
    config.Netmask[0] = 255;config.Netmask[1] = 255;config.Netmask[2] = 255;config.Netmask[3] = 0;
    config.Gateway[0] = 192;config.Gateway[1] = 168;config.Gateway[2] = 1;config.Gateway[3] = 1;
    WriteConfig(true);
    Serial.println("default configs applied");
    //////////starting web config server hotspot
        Serial.println("No configuration stored, starting access point.");   
        ESP.eraseConfig();
        WiFi.disconnect();
        WiFi.setAutoConnect(false);
        WiFi.setAutoReconnect(false);  
        WiFi.mode(WIFI_AP_STA);
        nodeName = String(ACCESS_POINT_NAME) + String("-") + String(WiFi.softAPmacAddress().c_str());
        WiFi.softAP(nodeName.c_str() , ACCESS_POINT_PASSWORD);
        Serial.print("Connect to ssid " + nodeName + " to configure Node on IP: ");   
        Serial.println(WiFi.softAPIP());
        ConfigureWifi();
        server.on ( "/favicon.ico",   []() { Serial.println("favicon.ico"); server.send ( 200, "text/html", "" );   }  );
        server.on ( "/", send_gateway_node_config_html  );
        server.on ( "/admin/values", send_gateway_node_config_values_html );
        server.on ( "/admin/connectionstate", send_connection_state_values_html );    
        server.on ( "/style.css", []() { Serial.println("style.css"); server.send ( 200, "text/plain", PAGE_Style_css );  } );
        server.on ( "/microajax.js", []() { Serial.println("microajax.js"); server.send ( 200, "text/plain", PAGE_microajax_js );  } );
        server.onNotFound ( []() { Serial.println("Page Not Found"); server.send ( 400, "text/html", "Page not Found" );   }  );
        server.begin();
        Serial.println( "HTTP server started" );     
  } else {
      nodeName = String(ACCESS_POINT_NAME) + String("-") + String(WiFi.softAPmacAddress().c_str());
      Serial.println("Device already configured");
      Serial.println("Wifi SSID: " + config.ssid);
      Serial.println("Wifi password: " + config.password); 
      Serial.println("Node name: " + config.machineName); 

      setupWifiAndAws();     
  }

    //////////////////////////////////////////


}


void setupWifiAndAws() {
    char ssidBfr[500];
    strcpy(ssidBfr, config.ssid.c_str()); 
    char passBfr[100];
    strcpy(passBfr, config.password.c_str());  
    //fill with ssid and wifi password
    WiFiMulti.addAP(ssidBfr, passBfr);
    Serial.println ("connecting to wifi");
    while(WiFiMulti.run() != WL_CONNECTED) {
        delay(100);
        Serial.print (".");
    }
    Serial.println("\nconnected");
    ardMegaSerial.println("ESP Started\n");
    //fill AWS parameters    
    awsWSclient.setAWSRegion(aws_region);
    awsWSclient.setAWSDomain(aws_endpoint);
    awsWSclient.setAWSKeyID(aws_key);
    awsWSclient.setAWSSecretKey(aws_secret);
    awsWSclient.setUseSSL(true);

    if (connect ()) {
        String espOutTopicStr = baseTopic + "/" + config.machineName + "/espOut";
        espOutTopic = espOutTopicStr.c_str();
        subscribe();
        sendmessage (espOutTopic, "{\"device\":\"connected\"}}");
    }  
}



void loop() {

  receiveFromArduino();
  //responseTimeOutCheck();
  //keep the mqtt up and running
  if (awsWSclient.connected ()) {    
      client.yield(50);
  } else {
    //handle reconnection
    if (connect ()){
      subscribe ();      
    }
  }

}
3
  • in that case, the difference should be clearly shown in Serial.print(espInTopic) ? but I will paste in my full code today. Mar 21, 2019 at 9:33
  • how can I check weather its valid later ? Mar 21, 2019 at 12:07
  • its not global. Mar 21, 2019 at 12:38

1 Answer 1

1

The c_str() function of the String class returns a pointer to current internal char array. This pointer is valid only until the String exists and is not changed.

Variable espInTopicStr is defined in a function and is destroyed at the end of the function. It frees the memory allocated for the internal char array.

You send a pointer to the internal char array of the espInTopicStr to client.subscribe. The MQTT library doesn't copy that char array, it only stores the pointer. Later the library uses the pointer to call the topic, but the memory on that location already changed and doesnt't contain the name of the topic.

1
  • placing the string espInTopicStr global, worked !... thank you so much Juraj for pointing out the issue :) Mar 21, 2019 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.