ESP 8266看门狗的问题已经困扰了我整整两天的时间,改进以后解决问题的方法:
- 如果半个小时之类解决不了的问题,不要再尝试用自己的知识去解决而是去网上查阅相关的资料。
- 谷歌上什么都有,但是不要认为都是正确的,即使别人用了错误的方法似乎解决了问题(其实没有解决,以后还会发生),比如针对这个问题很多人说是供电出现问题,给一个好的供电即可,但是这个并不是解决方法,只是重新烧录之后程序没有接收到导致触发错误的命令看起来似乎暂时的解决问题。
错误截图:
这里才是文章开始
索引:
- 什么是看门狗(Watch Dog Timer)
- 程序错误分析
- 如果不是的程序错误,那么如果解决这个问题。
第一部分 什么是看门狗
概述
看门狗(以下简称WDT)是嵌入式系统底层的的东西,对于系统起到了非常重要的作用,相当于系统警察,当系统发生严重的错误(比如进入死循环不能恢复的时候),WTD能够让系统重启。。WTD的应用主要是在嵌入式操作系统中,避免系统在无人干预的时候长时间挂起的情况。作用是:保证系统在出现不可恢复的错误时,能够自动让系统重启。本质上就是一个可以在一定时间内被复位的计数器,当看门狗启动后计数器开始计数,经过一定的时间,如果计数器没有复位,计数器达到指定的数值就会发出复位信号,很多设备包括cpu接收到这个信号会复位重启(俗称“被狗咬”)。为了保证看门狗不发出复位信号,就需要在看门狗允许的时间内对看门狗计数器清零(俗称“喂狗”),计数器重新计数。如果系统正常并保证按时“喂狗”,那么就相安无事,一旦程序卡死,没有“喂狗”,系统就会“被咬”复位
通俗的解释
- 你在围着一座小山跑 — 程序在按照预定流程执行
- 每次经过山脚下的某个地方,你都给大狼狗一根骨头 — 喂狗
- 跑着跑着,你跑岔道了,跑到了不该去的山顶 — 程序跑飞了
- 或者你在跑步的途中睡着了 — 程序死机
- 这个时候这条大狼狗因为一直没有食物饿疯了,挣脱锁链来追你 — 看么狗定时器溢出
- 你被它吓了一跳,从山上滚下来,起来一看又回到了出发地点,于是又从头考试跑 — 看门狗重置
所以,其实就是有一只狗一直盯着系统的定时器,如果定时器的时间值到了,你还没喂它东西的话,它就会把你的系统重启,因为它认为这么久不喂它吃东西程序肯定跑飞了(其实有可能是有比较长的延时)。所以为了保持系统的正常运行,就要在定时器到之前不断的给它喂东西吃。
WTD模块的主要功能
在比较高级的嵌入式芯片中,都有一个WTD模块,如果在MCU/MPU中没有集成WTD模块,那么一般会在此嵌入式系统中加一个专门的看门狗芯片来实现WTD机制,此模块的主要功能:
- 提供WDT控制寄存器和配置寄存器,供开发人员根据系统需要进行灵活配置
- 提供一接口,使应用软件能够定时给WDT“喂狗”
- 当系统进入不可恢复的错误时候,能够产生一个不可屏蔽中断来通知系统自动重启,只有相应的复位信号才能清除它。
WTD的实现方式:
对于不同的硬件芯片有不同的方式,很多芯片比如ARM11都已经集成了专门的WDT模块,并且可以自行配置。相同的一点是:设置好相应的寄存器,激活WDT后,需要应用程序周期性的服务WDT,即“喂狗”。很多单片机自带看门狗电路,可是进行WDT的启停,能够用指令禁止看门狗是为了使用用户程序开发阶段,但同时这给程序跑飞留了后门,在看么狗启动时或启动前遇到干扰使程序跑飞,则看门狗启动失败,无法使其执行监控职能。
第二部分 错误分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
while(Serial.available()>0){ /* 处理串口数据开始 */ delay(100); Serial.readBytesUntil(character,buffer,18); Serial.print("Received:"); Serial.println(buffer); /* 处理串口数据结束 */ /* 向服务器发送数据开始 */ Serial.print("connecting to "); Serial.println(host); // Use WiFiClient class to create TCP connections WiFiClient client; if (!client.connect(host, httpPort)) { Serial.println("connection failed"); return; } Serial.print("Requesting URL: "); Serial.println(path); String rest = "?temp=777&hum=321"; client.print(String("GET ") + path + rest + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); Serial.println("Data has been sent to destination"); delay(1000); // Read all the lines of the reply from server and print // them to Serial while(client.available()){ String line = client.readStringUntil('\r'); Serial.print(line); } Serial.println(); Serial.println("closing connection"); /* 向服务器发送数据结束 */ } |
在我程序的loop中有这样的一段程序造成了“被狗咬”情况,在这一段程序中判断串口是否有缓存,如果有的话就把串口缓存读取出来并发送到服务器,但是一个重点是没有清除串口的缓存(实际上我是在这个循环的外面清除的串口缓存,应该在循环内的最后进行清除),导致串口一直有缓存,程序一直在这个while(Serial.available()>0)的循环出不去,这个时候就喂不了狗了,就会导致“被狗咬”,WDT被重置。
第三部分 如果不是程序错误 那么如何解决这个问题
有的时候即使程序中没有出现我上述的错误也会触发看门狗,这是为什么呢?这个很有可能是在程序中出现了长时间的延迟,比如说这一段出现十秒延迟的程序就会导致“被狗咬”:
1 2 3 4 5 6 7 8 9 10 11 |
void setup() { Serial.begin(9600); Serial.println("Begin testing"); } unsigned long prevTime; void loop() { prevTime = millis(); while(millis() - prevTime < 10000); } |
实际上,程序会在loop()结束时或者delay()的时候更新看门狗计时器,如果很长一段时间没有更新看门狗计时器就会被重置。
解决方法一:在延迟的域类呼叫yield()或者delay()
1 2 3 4 5 6 7 8 9 10 11 |
void setup() { Serial.begin(9600); Serial.println("Begin testing"); } unsigned long prevTime; void loop() { prevTime = millis(); while(millis() - prevTime < 10000) {yield();} } |
解决方法二:手动喂狗
使用ESP.wdtFeed()在while中手动喂狗,ESP是内置的函数,不需要引入库。
1 2 3 4 5 6 7 8 |
void setup() { //ESP.wdtDisable(); while (1){ESP.wdtFeed();}; } void loop(){} |
解决方法三:终极解决方法 -> 提高程序质量
看门狗相当于是系统的警察,上面的两个解决方法虽然解决了(抑制了)“被狗咬”的问题,可其实在程序中我们不应该出现触发看门狗的情况,因为这样会导致使用了上述两种解决方法的程序跑飞的时候不能够启动看门狗的保护机制(除非100%确定这段程序没有问题),进一步的,可以从以下的几点进行提高:
- 尽量少的使用while循环,因为一旦传入的参数不正确非常容易导致进入死循环从而触发看门狗的保护机制。
- 不要出现过长时间的延迟,这样也会触发看门狗超时从而导致“被狗咬”
参考文献: