Сбой при чтении const * в c на ESP8266

#c #arduino #esp8266 #software-serial

#c #arduino #esp8266 #программное обеспечение-последовательный

Вопрос:

Я создаю систему, которая считывает значение датчика из Arduino Uno через SoftwareSerial и публикует его через MQTT. Однако проблема, с которой я сталкиваюсь, я думаю, более общая, я должен признать, что я новичок в c.

Я считываю данные и разделяю их на две переменные const *, которые определены в верхней части моей программы.

Когда я считываю сохраненные переменные «data» и «topic», которые я проанализировал из последовательного соединения, я получаю только вывод мусора и обычно сбой, который перезапускает устройство.

Он успешно печатает их внутри функции чтения из серийного номера, но позже это не может быть правильно прочитано. Может ли это иметь какое-то отношение к тому, как сохраняются данные? Могу ли я явно выделить некоторую память для переменных?

Я использую чип 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(amp;now));

  String datatext = "val: ";
  String timetext = ", time: ";
  String dataToBeSent = "test";
  String timeToBeSent = ctime(amp;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(amp;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 amp;amp; 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;
  }
}
  

Комментарии:

1. Совершенно непонятно, о чем вы спрашиваете. Второму (большому) блоку кода предшествует «Данные хранятся здесь»: — какие данные, где хранятся? При чем здесь const* в названии? К какому символу это относится — у вас их несколько const* , и неясно, возникли ли у вас какие-либо проблемы с их «чтением»?

2. У вас «опечатка», просто замените *data = strtok(receivedChars, s); на data = strtok(receivedChars, s); в recvWithStartEndMarkers , смотрите мой ответ

Ответ №1:

Из вашего кода :

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

strtok возвращает a char* но вы делаете это, *data = strtok(...) пока данные сами по себе являются a (неинициализированными) char * , это не согласованно, и у вас есть первый «шанс» на сбой, потому что вы пишете по случайному адресу.

Если у вас нет сбоя, и ваша программа может продолжить, данные не изменяются сами по себе и остаются неинициализированными.

В

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

Когда вы используете данные в качестве char* выполнения Serial.print(data); , и strcpy( publishText, data ); вы читаете со случайного (и, безусловно, недопустимого) адреса, что приводит к сбою.

Для исправления просто замените *data = strtok(receivedChars, s); на data = strtok(receivedChars, s);

Ответ №2:

После исправления присвоения результата strtok data , как показано в ответе Бруно, есть еще одна ошибка, которая может привести к сбою.

Сначала 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 amp;amp; 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 будет указывать на эту недопустимую память в стеке.

Позже вы захотите получить доступ к глобальной переменной data в функции publishReceived() . Доступ к этой памяти является неуказанным поведением. Вы все еще можете получить данные, вы можете получить что-то еще или ваша программа может аварийно завершиться.

 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(). */
  /* ... */
  

Чтобы исправить это, вы могли бы использовать strdup в recvWithStartEndMarkers() :

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

Затем вам нужно free(data) куда-нибудь, когда вам больше не нужны данные или перед повторным вызовом recvWithStartEndMarkers() .

Или создайте data массив и используйте strncpy в recvWithStartEndMarkers() .

Комментарии:

1. Большое вам спасибо. Ссылка, выходящая за рамки, была источником моих проблем! Другая ошибка была просто из эксперимента, и я забыл изменить ее обратно, компилятор бы ее поймал.