Mqtt не подписывается на тему, преобразованную из массива String в массив символов.

Я до сих пор не уверен, каким должен быть точный заголовок для этого вопроса, потому что не знаю, в чем проблема.

Я использую библиотеку #include <MQTTClient.h> mqtt для подписки на тему. Эта тема создается динамически в зависимости от названия устройства. В настоящее время я тестирую имя устройства 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(".");

Я определил две переменные для одной темы arn:aws:iot:us-east-2:xxxxxxxxxxxx:thing/myDeviceAbc/espIn . один жестко запрограммирован hardCodedTopic, а другой создается динамически espInTopic.

Когда я подписываюсь на espInTopic, я получаю сообщение MQTT подписка на последовательной консоли, НО ничего не получено по этой теме, когда я публикую с портала AWS. но когда я подписываюсь на hardCodedTopic, я получаю все опубликованные сообщения. Я не могу понять, в чем проблема. Я проверил метод подписки mqttClient внутри заголовочного файла библиотеки, который определен следующим образом

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

/** Отмена подписки MQTT — отправить пакет отмены подписки MQTT и дождаться отмены подписки.
* @param themeFilter — шаблон темы, который может включать подстановочные знаки.
* @return код успеха -
*/
int unsubscribe(const char* topicFilter);  

В качестве имени темы принимает const char*. Хотя, возможно, из-за преобразования из массива String в массив символов в массив символов espInTopic могут быть добавлены неизвестные символы, но я также проверил размер и содержимое обеих тем, которые абсолютно одинаковы.

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<<<.

Полный код:

#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"


//ВЕБ-сокеты
#include <Hash.h>
#include <WebSocketsClient.h>


//MQTT ПАОЗ
#include <SPI.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>


//Веб-сокет AWS MQTT
#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, измените их:
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 = ""; // раньше был глобальным, но позже я также попытался сохранить его локальным в методе subscribe()
const char* espOutTopic  = "";
int port = 443;

//Конфигурация MQTT
const int maxMQTTpackageSize = 512;
const int maxMQTTMessageHandlers = 1;

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

String nodeName = ""; // Имя должно быть уникальным
String nodeMac = "";

ESP8266WiFiMulti WiFiMulti;

AWSWebSocketClient awsWSclient(1000);

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

//# подключений
long connection = 0;
unsigned long timer = 0;
unsigned long maxTimeOut = 7000;  //7 секунд

//генерируем случайный 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;
}

//считаем поступившие сообщения
int arrivedcount = 0;



// обратный вызов для обработки сообщений mqtt
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);

}

//подключается к уровню веб-сокета и уровню mqtt
bool connect () {

    if (client.isConnected ()) {    
        client.disconnect ();
    }  
    //задержка не обязательна... она просто поможет нам получить "доверительное" значение пространства в куче
    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;
}

//подписаться на тему mqtt
void subscribe () {
   //подписка к теме
  String espInTopicStr  = baseTopic + "/" + "myDeviceAbc" + "/espIn";
// Строка 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);     
   }

}

//отправляем сообщение в тему mqtt
void sendmessage (const char* topic, String msg) {
    //Отправить сообщение
    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())
  {
    // КОНФИГ ПО УМОЛЧАНИЮ
    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");
    //////////запускаем точку доступа сервера веб-конфигурации
        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());  
    //заполняем пароль ssid и wifi
    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");
    //заполняем параметры AWS
    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();
  //поддерживаем работоспособность mqtt
  if (awsWSclient.connected ()) {    
      client.yield(50);
  } else {
    //обработка повторного подключения
    if (connect ()){
      subscribe ();      
    }
  }

}

, 👍0

Обсуждение

в этом случае разница должна быть четко показана в Serial.print(espInTopic)? но сегодня я вставлю свой полный код., @Anum Sheraz

как я могу проверить погоду, она действительна позже?, @Anum Sheraz

это не глобально., @Anum Sheraz


1 ответ


Лучший ответ:

1

Функция c_str() класса String возвращает указатель на текущий внутренний массив символов. Этот указатель действителен только до тех пор, пока строка не существует и не изменяется.

Переменная espInTopicStr определяется в функции и уничтожается в конце функции. Он освобождает память, выделенную для внутреннего массива символов.

Вы отправляете указатель на внутренний массив символов espInTopicStr в client.subscribe. Библиотека MQTT не копирует этот массив символов, а сохраняет только указатель. Позже библиотека использует указатель для вызова темы, но память в этом месте уже изменилась и не содержит названия темы.

,

поместив глобальную строку espInTopicStr, сработало!... большое спасибо, Юрай, за указание на проблему :), @Anum Sheraz