问题描述
标题很容易解释,就像我在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)和WiFiClientSecure(连接到我的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 !!!