更新:WiFiClientSecureTelegram和WiFiClientMQTT导致内存泄漏

问题描述

标题很容易解释,就像我在Exception 28 thrown on ESP8266-01 when connected to Adafruit MQTT and Telegram

上发布的其他问题一样

有人可能会说这是一个相同的问题,但这实际上是一个后续问题,我认为这是一个独立的问题,因为它也可能对其他人有帮助,并且在其他地方并没有真正涉及到互联网。我还将发布整个代码(就像我在上一个问题上所做的一样),尽管StackOverflow建议发布最少的代码来复制错误,因为我觉得完整地复制它是必需的。 (私人数据由PRIVATE一词代替)

#include "UniversalTelegramBot.h"
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>

#define BOTtoken "PRIVATE"
#define fanPin 2
#define myChatId "PRIVATE"
#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME  "PRIVATE"
#define AIO_KEY  "PRIVATE"

char ssid[] = "PRIVATE";
char password[] = "PRIVATE";
bool fanState;
unsigned long lastTimeBotRan = 0;
unsigned long checkTime = 1000;
int numNewMessages;
unsigned long timerStartPoint = 0;
bool timerStart;
String chat_id;
String text;
int messagesNumber;
String timerString;
String  Request;
const unsigned long rst = 300000;
boolean MQTT_connect();


WiFiClientSecure telegramClient;
WiFiClient MQTTClient;
UniversalTelegramBot bot(BOTtoken,telegramClient);
Adafruit_MQTT_Client mqtt(&MQTTClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY);
Adafruit_MQTT_Subscribe PRIVATE = Adafruit_MQTT_Subscribe(&mqtt,AIO_USERNAME "/feeds/PRIVATE");

void checkUpdates(int numNewMessages) {
  for (int i = 0; i < numNewMessages; i++) {
    chat_id = String(bot.messages[i].chat_id);
    text = bot.messages[i].text;
    String from_name = bot.messages[i].from_name;

    if (chat_id != myChatId) {
      bot.sendMessage(chat_id,"Unauthorized user,please refrain from texting this bot again.","");
      continue;
    }

    if (text == "/start") {
      bot.sendMessage(chat_id,"Welcome " + from_name + "!\n"
                              "Control your fan remotely!\n\n"
                              "/fanon: switch the fan ON\n"
                              "/fanoff: switch the fan OFF\n"
                              "/state: get the current state of the fan"
                              "/timer15: run fan for 15 minutes"
                              "/timer30: run fan for 30 minutes"
                              "/timer60: run fan for 1 hour"
                              "/timer: run fan for the amount of time you specify","Markdown");
    }

    if (text == "/fanon") {
      digitalWrite(fanPin,HIGH);
      Serial.println("Fan on");
      fanState = true;
      bot.sendMessage(chat_id,"Your fan is ON","");
    }

    if (text == "/fanoff") {
      fanState = false;
      timerStart = false;
      digitalWrite(fanPin,LOW);
      Serial.println("Fan off");
      bot.sendMessage(chat_id,"Your fan is OFF","");
    }

    if (text == "/state") {
      if (digitalRead(2) == HIGH) {
        bot.sendMessage(chat_id,"");
      } else {
        bot.sendMessage(chat_id,"");
      }
    }

    if (text == "/timer15") {
      timerStartPoint = millis();
      digitalWrite(fanPin,HIGH);
      timerStart = true;
      Serial.print("(/timer15) Fan on at ");
      Serial.println(timerStartPoint);
      bot.sendMessage(chat_id,"Your fan will run for 15 minutes","");
      launchTimer(15);
    }

    if (text == "/timer30") {
      digitalWrite(fanPin,HIGH);
      timerStart = true;
      timerStartPoint = millis();
      Serial.print("(/timer30) Fan on at ");
      Serial.println(timerStartPoint);
      bot.sendMessage(chat_id,"Your fan will run for 30 minutes","");
      launchTimer(30);
    }

    if (text == "/timer60") {
      digitalWrite(fanPin,HIGH);
      timerStart = true;
      timerStartPoint = millis();
      Serial.print("(/timer60) Fan on at ");
      Serial.println(timerStartPoint);
      bot.sendMessage(chat_id,"Your fan will run for 1 hour","");
      launchTimer(60);
    }

    if (text == "/timer") {
      messagesNumber = bot.last_message_received + 1;
      bot.sendMessage(chat_id,"How long do you want the fan to run for? (in minutes)","");
      Serial.println(messagesNumber);

      while (messagesNumber == (bot.last_message_received + 1)) {
        checkUpdates(bot.getUpdates(bot.last_message_received + 1));
        timerString = bot.messages[i].text;
        yield();
      }

      if (messagesNumber < (bot.last_message_received + 1)) {
        unsigned long timer = timerString.toInt();
        Serial.println(timer);
        digitalWrite(fanPin,HIGH);
        timerStart = true;
        timerStartPoint = millis();
        Serial.print("(/timer) Fan on at ");
        Serial.println(timerStartPoint);
        bot.sendMessage(chat_id,"Your fan will run for " + timerString + " minutes","");
        launchTimer(timer);
      }
    }
    text = "";
  }
}

void launchTimer(unsigned long timeInMinutes) {
  unsigned long timeInMillis = timeInMinutes * 60 * 1000;

  while (timerStart) {
    checkUpdates(bot.getUpdates(bot.last_message_received + 1));
    if (MQTT_connect()) {
      Adafruit_MQTT_Subscribe *subscription_name;
      while ((subscription_name = mqtt.readSubscription(4000))) {
        if (subscription_name == &PRIVATE) {
          Request = ((char *)PRIVATE.lastread);
          if (Request == "fanon") {
            digitalWrite(fanPin,HIGH);
            Serial.println("(Control panel) Fan on");
            fanState = true;
            bot.sendMessage(myChatId,"Fan turned on through Control Panel","");
          }
          if (Request == "fanoff") {
            fanState = false;
            timerStart = false;
            Serial.println("(Control panel) Fan off");
            digitalWrite(fanPin,LOW);
            bot.sendMessage(myChatId,"Fan turned off through Control Panel","");
          }
          if (Request == "timer15") {
            timerStartPoint = millis();
            digitalWrite(fanPin,HIGH);
            timerStart = true;
            Serial.print("(CP /timer15) Fan on at ");
            Serial.println(timerStartPoint);
            bot.sendMessage(myChatId,"Fan turned on for 15 minutes through Control Panel","");
            launchTimer(15);
          }
          if (Request == "timer30") {
            digitalWrite(fanPin,HIGH);
            timerStart = true;
            timerStartPoint = millis();
            Serial.print("(CP /timer30) Fan on at ");
            Serial.println(timerStartPoint);
            bot.sendMessage(myChatId,"Fan turned on for 30 minutes through Control Panel","");
            launchTimer(30);
          }
          if (Request == "timer60") {
            digitalWrite(fanPin,HIGH);
            timerStart = true;
            timerStartPoint = millis();
            Serial.print("(CP /timer60) Fan on at ");
            Serial.println(timerStartPoint);
            bot.sendMessage(myChatId,"Fan turned on for 1 hour through Control Panel","");
            launchTimer(60);
          }
        }
      }
    }
    if (millis() - timerStartPoint > timeInMillis) {
      digitalWrite(fanPin,LOW);
      timerStart = false;
      Serial.print("Timer run out at ");
      Serial.println(millis());
      bot.sendMessage(myChatId,"Fan turned off because timer ran out","");
    }
    yield();
  }
}

boolean MQTT_connect() {
  int8_t ret;
  if (mqtt.connected()) {
    return true;
  }  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) {
    mqtt.disconnect();
    delay(2000);
    retries--;
    if (retries == 0) {
      return false;
    }
  } return true;
}


void setup() {
  Serial.begin(115200);
  telegramClient.setInsecure();

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);

  Serial.print("Connecting Wifi: ");
  Serial.println(ssid);
  WiFi.begin(ssid,password);

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  pinMode(fanPin,OUTPUT);
  delay(10);
  digitalWrite(fanPin,LOW);


  Request = "";
  mqtt.subscribe(&PRIVATE);

  if (MQTT_connect()) {
    Serial.println("mqtt connected");
  }
  else {
    Serial.println("mqtt connection failed");
  }

}

void loop() {
  if (millis() - lastTimeBotRan > checkTime) {
    numNewMessages = bot.getUpdates(bot.last_message_received + 1);

    while (numNewMessages) {
      checkUpdates(numNewMessages);
      numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    }
    lastTimeBotRan = millis();
  }

  delay(1000);

  if (MQTT_connect()) {
    Adafruit_MQTT_Subscribe *subscription_name;
    while ((subscription_name = mqtt.readSubscription(4000))) {
      if (subscription_name == &PRIVATE) {
        Request = ((char *)PRIVATE.lastread);
        if (Request == "fanon") {
          digitalWrite(fanPin,HIGH);
          Serial.println("(Control panel) Fan on");
          fanState = true;
          bot.sendMessage(myChatId,"");
        }
        if (Request == "fanoff") {
          fanState = false;
          timerStart = false;
          Serial.println("(Control panel) Fan off");
          digitalWrite(fanPin,LOW);
          bot.sendMessage(myChatId,"");
        }
        if (Request == "timer15") {
          timerStartPoint = millis();
          digitalWrite(fanPin,HIGH);
          timerStart = true;
          Serial.print("(CP /timer15) Fan on at ");
          Serial.println(timerStartPoint);
          bot.sendMessage(myChatId,"");
          launchTimer(15);
        }
        if (Request == "timer30") {
          digitalWrite(fanPin,HIGH);
          timerStart = true;
          timerStartPoint = millis();
          Serial.print("(CP /timer30) Fan on at ");
          Serial.println(timerStartPoint);
          bot.sendMessage(myChatId,"");
          launchTimer(30);
        }
        if (Request == "timer60") {
          digitalWrite(fanPin,HIGH);
          timerStart = true;
          timerStartPoint = millis();
          Serial.print("(CP /timer60) Fan on at ");
          Serial.println(timerStartPoint);
          bot.sendMessage(myChatId,"");
          launchTimer(60);
        }
      }
    }
  }

  if (!mqtt.ping()) {
    mqtt.disconnect();
  }
}

现在,这是问题所在:我想实例化WiFiClient(连接到Adafruit MQTT)和W​​iFiClientSecure(连接到我的Telegram机器人),而不会导致崩溃,最后是Exception 28 stacktrace(其中有异常)我已经在链接的另一个问题中发布了“正常”版本和解码版本。我知道,经过数小时的研究,我无法实例化两个WiFiClientSecure实例,因为它会超出ESP01上的可用堆,而且我也知道,单独的WiFiClient或单独的WiFiClientSecure都不会碰到上述异常。 / p>

我没有发现WiFiClient和WiFiClientSecure不能一起使用,但是也许我遗漏了一些东西。因此,我的问题是:是否可以在同一代码中将WiFiClient和WiFiClientSecure分别用于两个不同的目的(分别是MQTT和Telegram)?

我希望阅读任何建议,因为我目前对创意不知所措。在此先感谢所有将提供帮助的人。

编辑:对于任何有兴趣的人,这是我的最新发现:

  • 将ESP01的CPU从80 MHz超频到160 MHz有助于缓解崩溃,实际上,它在8小时的时间内仅崩溃了一次。每30分钟从一次崩溃到8小时肯定是向前迈出的一步;
  • 我发现(感谢romkey的建议),它的WiFiClient和WiFiClientSecure不兼容,问题是由空闲堆引起的。通过删除字符串并将它们转换为char数组来释放堆,使程序在整个13小时内都不会崩溃。我可能会对此进行进一步测试,并将其保留24小时以上,以查看它是否真的可以解决。万一我会更新。

更新:发现了问题并更改了标题。问题完全出在两个库的堆使用上。这两个库都使用大量的堆空间,并且在一段时间后用完并导致异常28,因为据我所知,ESP尝试从其内存的错误部分读取数据,从而导致崩溃。我将在仅3分钟的时间内附加堆的串行输出:

41552
19448
20008
20120
20312
20200
20120
20120
20120
20312
20120
20120
20120
20312
20312
20312
20312
20312
20312
20504
20312
20312
19640

如您所见,第一个实例的堆显示超过40k,这是正常的,因为最大显示为51k-ish。然后它跳到20000,然后耗尽,即使ESP在几分钟后回收了自己的内存。

解决方法

我也在我的 ESP8266 多传感器检查和发布项目中同时使用了 WiFiClient 和 WiFiClientSecure。 虽然我的 WifiClient 一直在运行(对于 MQTT 和 ThingSpeak),但我决定将安全连接(很少需要发布到 GoogleSheets - 每 30 分钟)。 整个项目代码太大了(有5570行),所以这里是其中的一部分,并附有介绍堆使用的注释:

// heap size before call of the function: 37112
int SendGSCRPT() {
  showHeap();   // 35312
  WiFiClientSecure GoogleClient;
  showHeap();   // 27512
  GoogleClient.setInsecure();
  if (!GoogleClient.connect("script.google.com",443)) {
    GoogleClient.stop();
    return 0;
  }
  showHeap();  // 9840 !!!
  char sGog[200];
  snprintf(sGog,sizeof(sGog),"GET %s HTTP/1.1\r\nHost: script.google.com\r\nUser-Agent: PRIVATE\r\nConnection: close\r\n\r\n","PRIVATE");
  GoogleClient.print(sGog);
  Serial.println("request sent!");
  int res = 1;
  GoogleClient.stop();
  showHeap(); // 30104
  return res;
}
// heap size after function releases all the memory taken: 37112 again !!!

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...