目录
目前使用树莓派装haos来做智能家居服务器,不知道是硬件问题还是哪里的设定问题,时不时挂掉,访问不了,需要手动断电重启,才能恢复,奈何找不到原因,本来是使用了一个米家智能插座来手动断电重启,但需要到用的时候才发现访问不了,需要进入米家重启,体验太差,所以想着如果可以有一个设备可以自己侦测是否正常,如果有问题,自动断电重启,这样会好很多。
方案一:使用esphome 来控制断电重启,最方便的一种,但目前没找到,使用esphome 来检测homeassistant是否挂掉的方法,故放弃。
方案二: 接入米家,继续使用米家插座来控制断电重启,也是需要先侦测到访问异常,再通过改造米家传感器接入,相对复杂,
方案三:借助blinker平台接入,本身使用arduino编程,相对熟悉很多,使用esp8266 ping库直接来侦测访问是否异常,点灯app可以控制断电以及各种状态,
选择方案三来做,可以在之前浇水的代码上修改来实现相关功能,目前已实现如下功能

- /* *****************************************************************
- Blinker 库下载地址:
- https://github.com/blinker-iot/blinker-library/archive/master.zip
- Blinker 是一套跨硬件、跨平台的物联网解决方案,提供APP端、设备端、
- 服务器端支持,使用公有云服务进行数据传输存储。可用于智能家居、
- 数据监测等领域,可以帮助用户更好更快地搭建物联网项目。
- 如果使用 ESP8266 接入 Blinker,
- 请确保安装了 2.7.4 或更新的 ESP8266/Arduino 支持包。
- https://github.com/esp8266/Arduino/releases
- 如果使用 ESP32 接入 Blinker,
- 请确保安装了 1.0.5 或更新的 ESP32/Arduino 支持包。
- https://github.com/espressif/arduino-esp32/releases
- 文档: https://diandeng.tech/doc
- * *****************************************************************/
-
- #define BLINKER_WIFI
-
- #include
- #include
- #include
- #include
-
- #include
- #include
- extern "C"
- {
- #include
// needed for icmp packet definitions - }
-
- // Set global to avoid object removing after setup() routine
- Pinger pinger;
-
- auto timer = timer_create_default(); // create a timer with default settings
- bool ip_timer_flag = true;
- bool ip_app_check_flag = true;
- bool ip_lost_flag = false;
- bool loss_true = false;
- bool otaflag = false;
- uint32_t read_time = 0;
- uint16_t blinker_year, error_time_final, error_time = 0;
- uint8_t blinker_month, blinker_mday, blinker_wday, blinker_hour, blinker_min, blinker_sec;
- uint8_t a0 = 1, a1 = 1, b0 = 1, b1 = 1, c0 = 1, c1 = 1, d0 = 0, d1 = 1;
- uint16_t check_timer_time = 30, check_error_time = 8, clen_error_timer = 600;
- String fh, fh0;
-
- char auth[] = "*****************";
- char ssid[] = "************";
- char pswd[] = "****************";
- //---------------------------------引脚配置-----------------------------
- #define VCCPIN D3 // 指示灯
- #define BUTTON_1 "ButtonKey"
- #define BUTTON_2 "onoff"
- #define BUTTON_3 "reset"
- #define BUTTON_4 "status"
- #define BUTTON_5 "ota"
- #define TEXTE_time "tex-time"
- #define RUN_time "tex-run"
- #define TEXTE_ip "tex-ip"
- BlinkerButton Button1(BUTTON_1);
- BlinkerButton Button2(BUTTON_2);
- BlinkerButton Button3(BUTTON_3);
- BlinkerButton Button4(BUTTON_4);
- BlinkerButton Button5(BUTTON_5);
- BlinkerText Text_ip(TEXTE_ip);
- BlinkerText Text_time(TEXTE_time);
- BlinkerText Text_runtime(RUN_time);
- BlinkerNumber Number1("num-xinhao"); // 定义信号强度键名
- BlinkerNumber Number2("num-error"); // 定义信号强度键名
- BlinkerNumber Number3("error"); // 定义信号强度键名
-
- void button1_callback(const String &state)
- {
- // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
- BLINKER_LOG("get button state: ", state);
-
- if (state == BLINKER_CMD_ON)
- {
- BLINKER_LOG("Toggle on!");
- ip_app_check_flag = true;
- a0 = 1;
- a1 = 1;
- // Button1.icon("icon_1");
- Button1.color("#FF6666");
- Button1.text("检测");
- Button1.print("on");
- }
- else if (state == BLINKER_CMD_OFF)
- {
- BLINKER_LOG("Toggle off!");
- ip_app_check_flag = false;
- a0 = 0;
- a1 = 1;
- // Button1.icon("icon_1");
- Button1.color("#FF6600");
- Button1.text("不检测");
- Button1.print("off");
- }
- }
-
- void button2_callback(const String &state)
- {
- // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
- BLINKER_LOG("get button state: ", state);
-
- if (state == BLINKER_CMD_ON)
- {
- BLINKER_LOG("Toggle on!");
- digitalWrite(VCCPIN, LOW);
- b0 = 1;
- b1 = 1;
- Button2.color("#FF6666");
- Button2.text("打开");
- Button2.print("on");
- }
- else if (state == BLINKER_CMD_OFF)
- {
- BLINKER_LOG("Toggle off!");
- digitalWrite(VCCPIN, HIGH);
- b0 = 0;
- b1 = 1;
- Button2.color("#FF6600");
- Button2.text("关闭");
- Button2.print("off");
- }
- }
-
- void button3_callback(const String &state)
- {
- // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
- BLINKER_LOG("get button state: ", state);
-
- if (state == BLINKER_CMD_BUTTON_TAP)
- {
- BLINKER_LOG("Button tap!");
- error_time_final = 0;
- // Button3.text("Your button name or describe");
- Button3.print();
- }
- }
- void button4_callback(const String &state)
- {
- // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
- BLINKER_LOG("get button state: ", state);
- }
- void button5_callback(const String &state)
- {
- BLINKER_LOG("button5_callback get button state: ", state);
- if (state == BLINKER_CMD_ON)
- {
- d0 = 1;
- d1 = 1;
- otaflag = true;
- Button5.color("#00b0FF");
- Button5.text("OTA");
- Button5.print("on");
- }
- else if (state == BLINKER_CMD_OFF)
- {
- d0 = 0;
- d1 = 1;
- otaflag = false;
- Button5.color("#607E8B");
- Button5.text("NO OTA");
- Button5.print("off");
- }
- }
- void heartbeat()
- {
- huidiaoneirong();
- }
- /*********************************************************************************************************
- 按键状态回调函数
- *********************************************************************************************************/
- void huidiaoneirong()
- {
- Number1.icon("fal fa-wifi");
- Number1.print(WiFi.RSSI());
- Number2.print(error_time_final);
- Number3.print(error_time);
- Text_ip.print(WiFi.localIP().toString().c_str());
- Text_time.print(fh);
- Text_runtime.print(fh0);
- if ((a1 == 1) && (a0 == 1))
- {
- a1 = 0;
- Button1.color("#66CC66");
- Button1.text("检测");
- Button1.print("on");
- }
- else if ((a1 == 1) && (a0 == 0))
- {
- a1 = 0;
- Button1.color("#FF6600");
- Button1.text("不检测");
- Button1.print("off");
- }
- if ((b1 == 1) && (b0 == 1))
- {
- b1 = 0;
- Button2.print("on");
- Button2.color("#66CC66");
- Button2.text("供电开");
- }
- else if ((b1 == 1) && (b0 == 0))
- {
- b1 = 0;
- Button2.color("#FF6600");
- Button2.text("供电关");
- Button2.print("off");
- }
- if ((c1 == 1) && (c0 == 1))
- {
- c1 = 0;
- Button4.print("on");
- Button4.color("#66CC66");
- Button4.text("正常连接");
- }
- else if ((c1 == 1) && (c0 == 0))
- {
- c1 = 0;
- Button4.color("#FF6600");
- Button4.text("异常连接");
- Button4.print("off");
- }
- if ((d1 == 1) && (d0 == 1))
- {
- d1 = 0;
- Button5.color("#00b0FF");
- Button5.text("OTA");
- Button5.print("on");
- }
- else if ((d1 == 1) && (d0 == 0))
- {
- d1 = 0;
- Button5.color("#607E8B");
- Button5.text("NO OTA");
- Button5.print("off");
- }
- }
- void dataRead(const String &data)
- {
- BLINKER_LOG("Blinker readString: ", data);
-
- Blinker.vibrate();
-
- uint32_t BlinkerTime = millis();
-
- Blinker.print("millis", BlinkerTime);
- }
-
- void setup()
- {
- Serial.begin(115200);
- BLINKER_DEBUG.stream(Serial);
-
- pinMode(LED_BUILTIN, OUTPUT);
- digitalWrite(LED_BUILTIN, LOW);
- delay(500);
- digitalWrite(LED_BUILTIN, HIGH);
- delay(500);
- digitalWrite(LED_BUILTIN, LOW);
- delay(500);
- digitalWrite(LED_BUILTIN, HIGH);
- delay(500);
- digitalWrite(LED_BUILTIN, LOW);
- delay(500);
- digitalWrite(LED_BUILTIN, HIGH);
- Blinker.begin(auth, ssid, pswd);
- Blinker.attachData(dataRead);
- Blinker.attachHeartbeat(heartbeat);
- Button1.attach(button1_callback);
- Button2.attach(button2_callback);
- Button3.attach(button3_callback);
- Button4.attach(button4_callback);
- Button5.attach(button5_callback);
- pinsetup();
- ping_ip_set();
- // ota_set();
- }
-
- void loop()
- {
-
- Blinker.run();
- get_time_now(); // 更新系统时间
- get_time_now_to_app(); // 运行时间
- checek_ip();
- }
- /************************
- void ota_set()
- {
- // ArduinoOTA.setHostname("ESP8266");
- // ArduinoOTA.setPassword("1234");
- ArduinoOTA.begin();
- ArduinoOTA.onStart([]() {
- // display.clear();
- // display.setFont(ArialMT_Plain_10);
- // display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
- // display.drawString(display.getWidth() / 2, display.getHeight() / 2 - 10, "OTA Update");
- // display.display();
- Serial.println("OTA start");
- });
- ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
- // display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) );
- // display.display();
- Serial.println(progress / (total / 100));
- });
- ArduinoOTA.onEnd([]() {
- // display.clear();
- // display.setFont(ArialMT_Plain_10);
- // display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
- // display.drawString(display.getWidth() / 2, display.getHeight() / 2, "Restart");
- // display.display();
- Serial.println("OTA Restart");
- });
- Serial.println("OTA ready");
- }
- void otaloop() {
- ArduinoOTA.handle();
- //Serial.println("OTA start");
- }
- *******************************/
- /*********************************************************************************************************
- 获取时间
- *********************************************************************************************************/
- void get_time_now()
- {
-
- blinker_year = Blinker.year(); // 年
- blinker_month = Blinker.month(); // 月
- blinker_mday = Blinker.mday(); // 日
- blinker_wday = Blinker.wday(); // 星期
- blinker_hour = Blinker.hour(); // 时
- blinker_min = Blinker.minute(); // 分
- blinker_sec = Blinker.second(); // 秒
- }
- /*********************************************************************************************************
- 获取运行时间并用app显示
- *********************************************************************************************************/
- void get_time_now_to_app()
- {
- String tianbl, shibl, fenbl, miaobl;
- uint8_t tian1 = 0, shi1 = 0, fen1 = 0, miao1 = 0;
- time_t run_time = Blinker.runTime(); // 获取运行时间,单位为秒
- tian1 = run_time / 86400; // 转化为天
- shi1 = (run_time - tian1 * 86400) / 3600; // 转化为时
- fen1 = (run_time - tian1 * 86400 - shi1 * 3600) / 60; // 转化为分
- miao1 = run_time - tian1 * 86400 - shi1 * 3600 - fen1 * 60; // 转化为秒
- if (tian1 < 10)
- {
- tianbl = String("") + "0" + tian1;
- }
- else
- {
- tianbl = String("") + tian1;
- }
- if (shi1 < 10)
- {
- shibl = String("") + "0" + shi1;
- }
- else
- {
- shibl = String("") + shi1;
- }
- if (fen1 < 10)
- {
- fenbl = String("") + "0" + fen1;
- }
- else
- {
- fenbl = String("") + fen1;
- }
- if (miao1 < 10)
- {
- miaobl = String("") + "0" + miao1;
- }
- else
- {
- miaobl = String("") + miao1;
- }
-
- if (tian1 == 0 & shi1 == 0 & fen1 == 0)
- {
- fh0 = String("") + miaobl + "秒";
- }
- else if (tian1 == 0 & shi1 == 0)
- {
- fh0 = String("") + fenbl + "分" + miaobl + "秒";
- }
- else if (tian1 == 0)
- {
- fh0 = String("") + shibl + "时" + fenbl + "分" + miaobl + "秒";
- }
- else
- {
- fh0 = String("") + tianbl + "天" + shibl + "时" + fenbl + "分" + miaobl + "秒";
- }
- }
- /*********************************************************************************************************
- 获取最近一次出错时间
- *********************************************************************************************************/
- void error_time_record()
- {
- String month12, day12, hourbl2, minbl2;
-
- if (blinker_month < 10)
- {
- month12 = String("") + "0" + blinker_month;
- }
- else
- {
- month12 = String("") + blinker_month;
- }
-
- if (blinker_mday < 10)
- {
- day12 = String("") + "0" + blinker_mday;
- }
- else
- {
- day12 = String("") + blinker_mday;
- }
-
- if (blinker_hour < 10)
- {
- hourbl2 = String("") + "0" + blinker_hour;
- }
- else
- {
- hourbl2 = String("") + blinker_hour;
- }
- if (blinker_min < 10)
- {
- minbl2 = String("") + "0" + blinker_min;
- }
- else
- {
- minbl2 = String("") + blinker_min;
- }
-
- fh = String("") + month12 + "/" + day12 + "/" + hourbl2 + ":" + minbl2;
- }
- /*********************************************************************************************************
- ping检测
- *********************************************************************************************************/
- void checek_ip()
- {
- if (ip_timer_flag == true) // 检测ip timer
- {
- Serial.println("");
- Serial.println("###########################################################################");
- Serial.printf(" 检测时间 %d : %d : %d \n", blinker_hour, blinker_min, blinker_sec);
- Serial.println("###########################################################################");
- ip_timer_flag = false;
- if (ip_app_check_flag == true) // app控制检测标志
- {
- ping_ip(); // 检测ip主程序
- }
- Blinker.run();
- }
- timer.tick();
- }
- void pinsetup()
- {
- pinMode(VCCPIN, OUTPUT);
- digitalWrite(VCCPIN, LOW); // 蜂鸣器关闭
- timer.every(check_timer_time * 1000, ip_check_timer); // 开启打印寄存器
- }
-
- bool ip_check_timer(void *)
- {
- ip_timer_flag = true;
- return true; // repeat? true
- }
-
- void delay_blinker(int n)
- {
- for (uint16_t i = 0; i < n; i++)
- {
- Blinker.delay(500);
- Blinker.run();
- timer.tick();
- }
- }
- void ping_ip()
- {
- // int16_t check_timer_time = 10, check_error_time= 5, clen_error_timer = 300;
- Serial.printf("检测间隔时间: %d s 判断异常次数 : %d 清除异常时间 : %d s \n", check_timer_time, clen_error_timer / check_timer_time, clen_error_timer);
- Serial.printf("访问 192.168.31.43\n");
- Serial.printf("当前是否访问正常 : %d 访问异常次数 : %d 最终访问异常次数 : %d\n", ip_lost_flag, error_time, error_time_final);
-
- if (pinger.Ping("192.168.31.43") == false)
- {
- }
- delay_blinker(8);
- if (loss_true == true) // 侦测到异常
- {
-
- error_time++;
- Serial.println("###########################################################################");
- Serial.printf("访问出错: %d 次\n", error_time);
-
- if ((ip_lost_flag == false) && (error_time == (clen_error_timer / check_timer_time))) // 超过异常次数时断电恢复
- {
- c0 = 0; // app 显示访问异常
- c1 = 1; // app s设定显示访问异常
- error_time = 0;
- digitalWrite(VCCPIN, HIGH);
- Blinker.delay(1500);
- digitalWrite(VCCPIN, LOW);
- ip_lost_flag = true; // 访问异常标志位打开
- error_time_final++; // 异常次数+1
- error_time_record(); // 记录异常时间
- Serial.println("###########################################################################");
- Serial.printf(" 第 %d 次访问出错\n", error_time_final);
- Serial.println("###########################################################################");
- }
-
- /*************************************************************************************************
- if (read_time == 0 || (millis() - read_time) >= clen_error_timer * 1000) // 超过一定时间,容错清0
- {
- read_time = millis();
- error_time = 0;
- Serial.println("############################超时容错清0####################################");
- }
- *************************************************************************************************/
- }
- else
- {
- error_time = 0; // 错误次数清0,重新计数
- if (ip_lost_flag == true) // 如果是异常,第一次恢复后计数清0以及标志位恢复
- {
-
- Serial.println("###########################################################################");
- Serial.println(" 恢复正常访问");
- Serial.println("###########################################################################");
- c0 = 1; // app 显示访问正常
- c1 = 1; // app s设定显示访问正常
- error_time = 0;
- ip_lost_flag = false;
- }
- }
- }
-
- void ping_ip_set()
- {
- // Begin serial connection at 9600 baud
- Serial.begin(115200);
-
- // Connect to WiFi access point
- bool stationConnected = WiFi.begin("************", "********************");
-
- // Check if connection errors
- if (!stationConnected)
- {
- Serial.println("Error, unable to connect specified WiFi network.");
- }
- // Wait connection completed
- Serial.print("Connecting to AP...");
- while (WiFi.status() != WL_CONNECTED)
- {
- delay(500);
- Serial.print(".");
- }
- Serial.print("Ok\n");
-
- pinger.OnReceive([](const PingerResponse &response)
- {
- if (response.ReceivedResponse)
- {
- Serial.printf("来自 %s: bytes=%d time=%lums TTL=%d\n",response.DestIPAddress.toString().c_str(),response.EchoMessageSize - sizeof(struct icmp_echo_hdr),response.ResponseTime,response.TimeToLive);
- }
- else
- {
- Serial.printf("请求超时\n");
- }
-
- // Return true to continue the ping sequence.
- // If current event returns false, the ping sequence is interrupted.
- return true; });
-
- pinger.OnEnd([](const PingerResponse &response)
- {
- // Evaluate lost packet percentage
- float loss = 100;
- if (response.TotalReceivedResponses > 0)
- {
- loss = (response.TotalSentRequests - response.TotalReceivedResponses) * 100 / response.TotalSentRequests;
- }
- if ( loss != 100) {
-
- loss_true = false;
- } else {
-
- loss_true = true;
- }
-
- // Print packet trip data
- Serial.printf("统计信息 %s: 数据包: 发送 = %lu, 接收 = %lu, 丢失 = %lu (%.2f%% 丢失)\n",response.DestIPAddress.toString().c_str(), response.TotalSentRequests, response.TotalReceivedResponses, response.TotalSentRequests - response.TotalReceivedResponses, loss);
- //Serial.printf(" Packets: Sent = %lu, Received = %lu, Lost = %lu (%.2f%% loss),\n", response.TotalSentRequests, response.TotalReceivedResponses, response.TotalSentRequests - response.TotalReceivedResponses, loss);
-
- // Print time information
- if (response.TotalReceivedResponses > 0)
- {
- Serial.printf("网络延迟: 最短 = %lums, 最长 = %lums, 平均 = %.2fms\n",response.MinResponseTime,response.MaxResponseTime,response.AvgResponseTime);
- //Serial.printf(" Minimum = %lums, Maximum = %lums, Average = %.2fms\n",response.MinResponseTime,response.MaxResponseTime,response.AvgResponseTime);
- }
-
- // Print host data
- //Serial.printf("Destination host data:\n");
- //Serial.printf(" IP address: %s\n", response.DestIPAddress.toString().c_str());
- if (response.DestMacAddress != nullptr)
- {
- Serial.printf( " MAC address: " MACSTR "\n",MAC2STR(response.DestMacAddress->addr));
- }
- if (response.DestHostname != "")
- {
- //Serial.printf(" DNS name: %s\n",response.DestHostname.c_str());
- }
-
- return true; });
-
- // Ping default gateway
- Serial.printf("\n\nPinging default gateway with IP %s\n", WiFi.gatewayIP().toString().c_str());
- if (pinger.Ping(WiFi.gatewayIP()) == false)
- {
- Serial.println("Error during last ping command.");
- }
- }
串口log打印如下:
可以协助判断运行状态,经过这两三天的调试,确认检测时间30s,也就是没个30s ping树莓派ip一次,会连续判断20次,如果20次都是访问不了的,就控制继电器断电,然后再上电,如果有20次判断有访问正常,则重新计数访问异常次数。

由于esp01 relay需要接线,需要改装数据线,所以想着画块板子通过两条usb线连接,方便后续改变,板子已下单,待收到后验证,

板子回来了,实际效果图如下:

优化的部分暂时未想好,目前可以基本满足需求。