在ESP8266中读取const*的const*时崩溃



我正在制作一个系统,该系统通过软件读取Arduino Uno的传感器值,并通过MQTT发布。但是,我遇到的问题我认为是更笼统的,我必须承认我是新手c。

我正在阅读数据,然后将其分为两个const*变量,这些变量已在我的程序的顶部定义。

当我读回我从串行连接中解析的保存的"数据"one_answers"主题"变量时,我只会获得垃圾输出,通常是重新启动设备的崩溃。

它在从读取函数的读取功能中成功打印它们,但稍后无法正确阅读。它可以与数据的保存方式有关吗?我可以明确分配变量的内存吗?

我正在使用降低波特速率和适当电压供应的ESP8266(ESP07)芯片。它似乎运行良好且稳定。

#include <StringSplitter.h>
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include <time.h>
//const char* ssid = "xxxx";
//const char* password =  "xxxx";
const char* ssid = "xxxx";
const char* password =  "xxxx";
const char* mqttServer = "xxxx;
const int mqttPort = xxxx;
const char* mqttUser = "xxxx";
const char* mqttPassword = "xxxx";
int timezone = 1;
int dst = 0;

数据存储在这里:

char* data;
char* topic;
boolean newData = false;
boolean unpublishedData = false;
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
  Serial.begin(19200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
  configTime(timezone * 3600, dst * 0, "pool.ntp.org", "time.nist.gov");

  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
    if (client.connect("ESP8266Client", mqttUser, mqttPassword )) {
      Serial.println("connected");
    } else {
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
    // wait and determine if we have a valid time from the network.
    time_t now = time(nullptr);
    Serial.print("Waiting for network time.");
    while (now <= 1500000000) {
      Serial.print(".");
      delay(300); // allow a few seconds to connect to network time.
      now = time(nullptr);
    }
  }
  Serial.println("");
  time_t now = time(nullptr);
  Serial.println(ctime(&now));
  String datatext = "val: ";
  String timetext = ", time: ";
  String dataToBeSent = "test";
  String timeToBeSent = ctime(&now);
  String publishString = datatext + dataToBeSent + timetext + timeToBeSent;
  Serial.println("Attempting to publish: " + publishString);
  client.publish("trykk/sensor0", (char*) publishString.c_str());
  client.subscribe("trykk/sensor0");
}
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  Serial.println("-----------------------");
}
void loop() {
  client.loop();
  recvWithStartEndMarkers();
  showNewData();
  publishReceived();
}
void publishReceived() {
  if (unpublishedData) {
    Serial.println("Hello from inside the publisher loop!");
    time_t now = time(nullptr);
    char* timeNow = ctime(&now);

它在这里失败,阅读数据:

    char publishText[30]; //TODO: make it JSON
    strcpy( publishText, data );
    strcat( publishText, " " );
    strcat( publishText, timeNow );
    Serial.print("publishText: ");
    Serial.println(publishText);
    Serial.print("topic: "); 
    Serial.println(topic);
    client.publish(topic, publishText);
    client.subscribe(topic);
    unpublishedData = false;
  } else if (!data) {
    Serial.println("No data saved to array.");
  } else if (!topic) {
    Serial.println("No topic saved to array.");
  }
}
void recvWithStartEndMarkers() {
  int numChars = 32;
  char receivedChars[numChars];
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  if (Serial.available() > 0) {
    Serial.println("Hello from inside the receive loop!");
    delay(100);
    while (Serial.available() > 0 && newData == false) {
      rc = Serial.read();
      Serial.println("Reading from data line.");
      if (recvInProgress == true) {
        if (rc != endMarker) {
          receivedChars[ndx] = rc;
          ndx++;
          if (ndx >= numChars) {
            ndx = numChars - 1;
          }
        }
        else {
          Serial.println("Found the end marker.");
          receivedChars[ndx] = ''; // terminate the string
          recvInProgress = false;
          ndx = 0;
          newData = true;
          unpublishedData = true;

此部分将值正确打印回我:

          //Split the string
          Serial.print("ESP debug: read: ");
          Serial.println(receivedChars);
          const char s[2] = ":";
          *data = strtok(receivedChars, s);
          Serial.print(data);
          Serial.print(" ");
          *topic = strtok(NULL, s);
          Serial.println(topic);
        }
      }
      else if (rc == startMarker) {
        recvInProgress = true;
        Serial.println("Found start marker");
      }
    }
  }
}
//This is gutted as it gave me problems reading the variables
void showNewData() {
  if (newData == true) {
    Serial.print("This just in ... ");
    Serial.print("Topic: ");
    Serial.print("stuff");
    Serial.print(", data: ");
    Serial.println("more stuff");
    newData = false;
  }
}

来自您的代码:

char* data;
...
*data = strtok(receivedChars, s);

strtok 返回 char*,但是您进行 *data = strtok(...) data 本身是(非初始化的) char *,这是不一致的,您有第一个"机会"来崩溃是因为您在随机地址写了。

如果您没有崩溃,并且您的程序可以继续 data 不会自行修改并保持非初始化。

strcpy( publishText, data );
...
Serial.print(data);

当您使用 data 作为char*进行Serial.print(data);strcpy( publishText, data );时,您会从随机(无效)地址读取,从而产生崩溃。

要纠正*data = strtok(receivedChars, s);

替换CC_7

在将strtok的结果固定为data之后,如Bruno的答案所示,还有另一个可能导致崩溃的错误。

您的功能loop()首先调用recvWithStartEndMarkers(),然后是publishReceived()

void loop() {
  client.loop();
  recvWithStartEndMarkers();
  showNewData();
  publishReceived();
}

在函数recvWithStartEndMarkers中,您将一些数据读取到本地数组receivedChars中,将其馈送到strtok中,然后编写一个从strtok返回到全局变量data的指针。

void recvWithStartEndMarkers() {
  int numChars = 32;
  char receivedChars[numChars]; /* this is a local variable with automatic storage */
  /* ... */
    while (Serial.available() > 0 && newData == false) {
      /* ... */
          receivedChars[ndx] = rc;
          ndx++;
          if (ndx >= numChars) {
            ndx = numChars - 1;
          }
          /* ... */
          receivedChars[ndx] = ''; // terminate the string
          /* Now there is a terminated string in the local variable */
          /* ... */
          //Split the string
          /* ... */
          const char s[2] = ":";
          data = strtok(receivedChars, s); /* strtok modifies the input in receivedChars and returns a pointer to parts of this array. */ 
          /* ... */
}

离开功能后,receivedChars的内存不再有效。这意味着data将指向堆栈上的此无效内存。

后来,您希望访问函数publishReceived()中的全局变量data。访问此内存是未指定的行为。您可能仍然可以获得数据,您可能会得到其他内容,或者您的程序可能会崩溃。

void publishReceived() {
  /* ... */
    char publishText[30]; //TODO: make it JSON
    strcpy( publishText, data ); /* This will try to copy whatever is now in the memory that was part of receivedChars inside recvWithStartEndMarkers() but may now contain something else, e.g. local data of function publishReceived(). */
  /* ... */

要解决此问题,您可以在recvWithStartEndMarkers()中使用strdup

data = strtok(receivedChars, s);
if(data != NULL) data = strdup(data);

然后,当您不再需要数据或再次致电recvWithStartEndMarkers()之前,您必须在某个地方进行free(data)

或使data数组并在recvWithStartEndMarkers()中使用strncpy

最新更新