ESP32 私人成员已损坏

问题描述 投票:0回答:1

我有一个在 freeRTOS 上运行的 ESP32 程序,从 microSD 读取配置数据并将它们作为我的 API 类的凭据传递

RemoteLogger
:

#ifndef REMOTELOGGER_H
#define REMOTELOGGER_H

#include <PubSubClient.h>

#define API_TIMEOUT       5000
#define TINY_GSM_MODEM_SIM7600

#include <TinyGsmClient.h>
#include "structs.h"

struct MQTT{
  const char* broker;
  const char* topic;
  const char* user;
  const char* pass;
  uint16_t port;
};

struct API{
  const char* host;
  const char* user;
  const char* pass;
  uint16_t port;
};

class RemoteLogger : public PubSubClient{
public:
  RemoteLogger(TinyGsmClient& mqttClient, TinyGsmClient& apiClient)
              : PubSubClient(mqttClient), apiClient(&apiClient){}

  RemoteLogger& setCreds(MQTT& mqtt);
  RemoteLogger& setCreds(API& api);
  PubSubClient& setServer();
  boolean mqttConnect(const char* id);
  boolean mqttConnected();
  boolean publish(const char* payload, boolean retained);
  boolean subscribe();
  
  void retrieveToken();
  bool apiConnect();
  bool apiConnected();
  bool post(const char* request, const char* msg);
  bool authPost(const char* request, const char* msg);
  bool dataToApi(String& jsonPayload);
  bool errorToApi(String& jsonPayload);
private:
  TinyGsmClient* mqttClient;
  TinyGsmClient* apiClient;
  const char* token;
  MQTT mqtt;
  API api;
};

#endif

RemoteLogger& RemoteLogger::setCreds(MQTT& mqtt){
  this->mqtt = mqtt;
  return *this;
}

RemoteLogger& RemoteLogger::setCreds(API& api){
  this->api = api;
  return *this;
}

PubSubClient& RemoteLogger::setServer(){
  return PubSubClient::setServer(this->mqtt.broker, this->mqtt.port);
}

boolean RemoteLogger::mqttConnect(const char* id){
  return PubSubClient::connect(id, this->mqtt.user, this->mqtt.pass);
}

boolean RemoteLogger::mqttConnected(){
  return PubSubClient::connected();
}

boolean RemoteLogger::subscribe(){
  return PubSubClient::subscribe(this->mqtt.topic);
}

boolean RemoteLogger::publish(const char* payload, boolean retained){
  return PubSubClient::publish(this->mqtt.topic, payload, retained);
}

bool RemoteLogger::apiConnect(){
  if(apiClient->connect(this->api.host, this->api.port, 10) != 1){
    return false;
  } else {
    return true;
  }
}

bool RemoteLogger::apiConnected(){
  return apiClient->connected();
}
// POST request without authentication
bool RemoteLogger::post(const char* request, const char* msg){
  char header[1250];
  snprintf(header, sizeof(header),
          "POST %s HTTP/1.1\r\n"
          "Host: %s:%d\r\n"
          "Content-Type: application/json\r\n"
          "tenant: root\r\n"
          "Accept-Language: en-US\r\n"
          "Connection: keep-alive\r\n"
          "Content-Length: %d\r\n\r\n",
          request, this->api.host, this->api.port, strlen(msg));
  if(!apiClient){
    Serial.println("Client not initialized!");
    return false;
  }
  apiClient->print(header);
  Serial.print(header);
  apiClient->print(msg);
  return true;
}
// POST request with authentication
bool RemoteLogger::authPost(const char* request, const char* msg){
  char header[1250];
  snprintf(header, sizeof(header),
          "POST %s HTTP/1.1\r\n"
          "Host: %s:%d\r\n"
          "Content-Type: application/json\r\n"
          "Authorization: Bearer %s\r\n"
          "Accept-Language: en-US\r\n"
          "Connection: keep-alive\r\n"
          "Content-Length: %d\r\n\r\n",
          request, this->api.host, this->api.port, this->token, strlen(msg));
  if(!apiClient){
    Serial.println("Client not initialized!");
    return false;
  }
  apiClient->print(header);
  Serial.print(header);
  apiClient->print(msg);
  return true;
}

void RemoteLogger::retrieveToken(){
  if(!apiClient->connected()){
    Serial.println("Failed to connect to server.");
    return;
  }
  char tokenAuth[128];
  snprintf(tokenAuth, sizeof(tokenAuth),
          "{\"username\":\"%s\",\"password\":\"%s\"}",
          this->api.user, this->api.pass);
  this->post("/api/tokens", tokenAuth);

  Serial.println("Waiting for authentication response...");
  unsigned long startTime = millis();
  String response;
  while((millis() - startTime) < API_TIMEOUT){
    if(apiClient->available()){
      response = apiClient->readString();
      Serial.println(response);
      break;
    }
  }
  if(response.isEmpty()){
    Serial.println("Error: No response from server.");
    return;
  }
  int jsonStart = response.indexOf("{");
  if(jsonStart != -1){
    response = response.substring(jsonStart);
    int jsonEnd = response.lastIndexOf("}");
    if(jsonEnd != -1)
      response = response.substring(0, jsonEnd + 1);
  } else {
    response = "";
  }
  // Parse the token from the response (assumes JSON response format)
  JsonDocument jsonDoc;
  DeserializationError error = deserializeJson(jsonDoc, response);
  if (error){
    Serial.print("Failed to get token: ");
    Serial.println(error.c_str());
    return;
  }
  if(jsonDoc.containsKey("token")){
    this->token = jsonDoc["token"].as<const char*>();
    Serial.print("Extracted Token: ");
    Serial.println(this->token);
  } else {
    Serial.println("Error: 'token' field not found.");
  }
}

bool RemoteLogger::errorToApi(String& jsonPayload){
  if(this->token == NULL){
    Serial.println("Error: Token is NULL");
    return false;
  }
  Serial.println("Sending error chunk...");
  authPost("/api/v1/errorloggers/addlisterrorogger", jsonPayload.c_str());
  
  Serial.println("Waiting for server response...");
  unsigned long startTime = millis();
  String response;
  while((millis() - startTime) < API_TIMEOUT){
    if(apiClient->available()){
      response = apiClient->readString();
      Serial.println(response);
      break;
    }
  }
  if(response.isEmpty()){
    Serial.println("Error: No response from server");
    return false;
  }
  if(response.startsWith("HTTP/1.1 200")){
    Serial.println("Data successfully sent to API");
    return true;
  } else {
    Serial.println("Error: API response indicates failure");
  }
  return false;
}

bool RemoteLogger::dataToApi(String& jsonPayload){
  if(this->token == NULL){
    Serial.println("Error: Token is NULL");
    return false;
  }
  Serial.println("Sending data chunk...");
  authPost("/api/v1/dataloggers/addlistdatalogger", jsonPayload.c_str());
  
  Serial.println("Waiting for server response...");
  unsigned long startTime = millis();
  String response;
  while((millis() - startTime) < API_TIMEOUT){
    if(apiClient->available()){
      response = apiClient->readString();
      Serial.println(response);
      break;
    }
  }
  if(response.isEmpty()){
    Serial.println("Error: No response from server");
    return false;
  }
  if(response.startsWith("HTTP/1.1 200")){
    Serial.println("Data successfully sent to API");
    return true;
  } else {
    Serial.println("Error: API response indicates failure");
  }
  return false;
}

void callBack(char* topic, byte* msg, unsigned int len){
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String tempMsg;
  
  for (int i = 0; i < len; i++) {
    Serial.print((char)msg[i]);
    tempMsg += (char)msg[i];
  }
  Serial.println();
}

使用

ConfigManager
检索凭证:

#ifndef CONFIGMANAGER_H
#define CONFIGMANAGER_H

#include <vector>
#include "RemoteLogger.h"
#include "Modem.h"
#include "structs.h"

class ConfigManager{
public:
  ConfigManager(RemoteLogger& extRemote, Modem& extModem) : remote(extRemote), modem(extModem) {}

  const char* lastError;

  bool readGprs();
  bool readMqtt();
  bool readApi();
private:
  Modem& modem;
  RemoteLogger& remote;
  friend class Modem;
  friend class RemoteLogger;
};

#endif
#include <SD.h>
#include <ArduinoJson.h>
#include "ConfigManager.h"

bool ConfigManager::readGprs(){
  File file = SD.open("/config.json");
  if (!file) {
    lastError = "Failed to open config file";
    return false;
  }
  // Allocate a JSON document
  JsonDocument config;
  // Parse the JSON from the file
  DeserializationError error = deserializeJson(config, file);
  if (error) {
    lastError = ("Failed to get GPRS config: " + String(error.f_str())).c_str();
    return false;
  }
  file.close();

  GPRS gprs;
  gprs.apn = config["GprsConfiguration"]["Apn"].as<const char*>();
  gprs.simPin = config["GprsConfiguration"]["SimPin"].as<const char*>();
  gprs.user = config["GprsConfiguration"]["User"].as<const char*>();
  gprs.pass = config["GprsConfiguration"]["Password"].as<const char*>();

  modem.setCreds(gprs);
  return true;
}

bool ConfigManager::readMqtt(){
  File file = SD.open("/config.json");
  if (!file) {
    lastError = "Failed to open config file";
    return false;
  }
  JsonDocument config;
  DeserializationError error = deserializeJson(config, file);
  if (error) {
    lastError = ("Failed to get MQTT config: " + String(error.f_str())).c_str();
    return false;
  }
  file.close();

  MQTT mqtt;
  mqtt.topic = config["MqttConfiguration"]["Topic"].as<const char*>();
  mqtt.broker = config["MqttConfiguration"]["Broker"].as<const char*>();
  mqtt.user = config["MqttConfiguration"]["User"].as<const char*>();
  mqtt.pass = config["MqttConfiguration"]["Password"].as<const char*>();
  mqtt.port = config["MqttConfiguration"]["Port"].as<uint16_t>();

  remote.setCreds(mqtt);
  return true;
}

bool ConfigManager::readApi(){
  File file = SD.open("/config.json");
  if (!file) {
    lastError = "Failed to open config file";
    return false;
  }
  JsonDocument config;
  DeserializationError error = deserializeJson(config, file);
  if (error) {
    lastError = ("Failed to get API config: " + String(error.f_str())).c_str();
    return false;
  }
  file.close();

  API api;
  api.host = config["ApiConfiguration"]["Host"].as<const char*>();
  api.user = config["ApiConfiguration"]["Username"].as<const char*>();
  api.pass = config["ApiConfiguration"]["Password"].as<const char*>();
  api.port = config["ApiConfiguration"]["Port"].as<uint16_t>();

  remote.setCreds(api);
  return true;
}

main()
中运行一些功能后,凭据已损坏,要么是乱码,要么更改为从 microSD 读取的其他内容。

#include <Arduino.h>
#include <SD.h>
#include <Wire.h>
#include <EEPROM.h>
#include <ArduinoJson.h>
#include <esp_task_wdt.h>
#include "ConfigManager.h"
#include "RemoteLogger.h"

#define SIM_RXD           32
#define SIM_TXD           33
#define SIM_BAUD          115200
#define RTU_BAUD          9600
#define TASK_WDT_TIMEOUT  60

HardwareSerial SerialAT(1);
Modem modem(SerialAT, SIM_RXD, SIM_TXD, SIM_BAUD);
TinyGsmClient mqttClient(modem), apiClient(modem);
RemoteLogger remote(mqttClient, apiClient);
ConfigManager config(remote, modem);

uint8_t mac[6];
char macAdr[18];
int rowCount = 0;
int logCount = 0;
const int chunkSize = 5;

bool sendRows(File &data, String &timeStamp, int fileNo);
bool processCsv(fs::FS &fs, const char* path, int fileNo);
bool findTimestamp(File &data, String &timeStamp, size_t &filePtr);

void setup() {
  Serial.begin(115200);
  EEPROM.begin(EEPROM_SIZE);

  esp_efuse_mac_get_default(mac);
  sprintf(macAdr, "%02X:%02X:%02X:%02X:%02X:%02X",
          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.printf("ESP32 MAC Address: %s\r\n", macAdr);
  /***************************** Set Up microSD *********************************/
  if(!SD.begin(5)){
    Serial.println("Card Mount Failed");
  }
  uint8_t cardType = SD.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
  }
  // Check SD card type
  Serial.print("SD Card Type: ");
  if(cardType == CARD_MMC){
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }
  // Check SD card size
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
  /************************** Get Config Credentials ****************************/
  if(!config.readGprs()){
    Serial.println(config.lastError);
  }
  if(!config.readMqtt()){
    Serial.println(config.lastError);
  }
  if(!config.readApi()){
    Serial.println(config.lastError);
  }
  /**************************** Initialize 4G Modem *****************************/
  Serial.println("Initializing modem...");
  if(!modem.init()){
    Serial.println("Restarting modem...");
    modem.restart();
  }
  remote.setServer();
  remote.setBufferSize(1024);
  remote.setCallback(callBack);
  /******************* Connect to API & check for unlogged data *****************/
  processCsv(SD, "/error.csv", 0);
  processCsv(SD, "/probe1.csv", 1);
}
/***************************** Support functions ********************************/
// Function to skip rows until the last pushed timestamp is found
bool findTimestamp(File &data, String &timeStamp, size_t &filePtr){
  if(data.seek(filePtr, SeekSet)){
    while(data.available()){
      String line = data.readStringUntil('\n');
      line.trim();
      if(timeStamp == "" || line.startsWith(timeStamp)){
        return true;
      }
    }
  }
  return false;
}

// Read and process rows from the CSV file
bool sendRows(File &data, String &timeStamp, int fileNo){
  Serial.println("Sending rows");
  const int chunkSize = 5;
  const int maxRetries = 5;
  int retries = 0;
  int rowCount = 0;
  bool success = true;
  String jsonPayload;
  String rows[chunkSize];
  // Helper lambda to process and send rows
  auto processAndSend = [&](bool isFinalChunk){
    jsonPayload = (fileNo == 0) ? errorToJson(rows, rowCount) : dataToJson(rows, rowCount);
    Serial.println(jsonPayload);
    retries = 0;
    while(retries < maxRetries){
      //Serial.println(remote.token);
      bool sent = (fileNo == 0) ? remote.errorToApi(jsonPayload) : remote.dataToApi(jsonPayload);
      if(sent){
        timeStamp = readCsv(rows[rowCount - 1]);
        rowCount = 0;
        return true;
      } else {
        Serial.println(isFinalChunk ? "Failed to send final chunk. Retrying..." 
                                    : "Failed to send data chunk. Retrying...");
        retries++;
      }
    }
    Serial.println("Max retries reached. Aborting...");
    return false;
  };
  // Main loop to read and process rows
  while(data.available()){
    String line = data.readStringUntil('\n');
    line.trim();
    rows[rowCount++] = line;
    // Process a chunk when full
    if(rowCount == chunkSize){
      if(!processAndSend(false)){
        success = false;
        rowCount = 0;
        break;
      }
    }
  }
  // Process remaining rows if any
  if(rowCount > 0){
    if(!processAndSend(true)){
      success = false;
    }
  }
  return success;
}

bool processCsv(fs::FS &fs, const char* path, int fileNo){
  File data = openFile(fs, path);
  if (!data){
    return false;
  }
  String timeStamp = readFlash<String>(fileNo * 20 + fileNo * sizeof(size_t));
  size_t filePtr = readFlash<size_t>(fileNo * 20 + fileNo * sizeof(size_t) + 20);
  if(!findTimestamp(data, timeStamp, filePtr)){
    data.close();
    return false;
  }
  if(sendRows(data, timeStamp, fileNo)){
    saveToFlash(fileNo * 20 + fileNo * sizeof(size_t), timeStamp);
    saveToFlash(fileNo * 20 + fileNo * sizeof(size_t) + 20, data.position());
  }
  data.close();
  return true;
}

为什么会出现这种情况?我该如何解决这个问题?

oop esp32
1个回答
0
投票

api
方法中的API
ConfigManager::readApi
对象是一个局部变量。当该方法返回时,分配给
api
的内存将被释放。由于
api.host
api.user
api.pass
指向 JsonDocument 管理的内存,因此在函数退出后,它们的数据不再有效。

解决方案:需要确保函数返回后字符串和对象仍然存在。例如,将字符串存储在全局可访问的对象中或动态分配它们。

© www.soinside.com 2019 - 2024. All rights reserved.