DIY个硬件看门狗。有死机、失联的朋友感兴趣可以往下看。用飞牛NAS已有大半年了。我对NAS要求不高能远程随时查看备份的资料,偶尔备份下相册就行·为一苦恼的就是经常失联死机···每次都需要断 电重启··用着最稳定的版本是0.9.13,连续8天没死机失联··升级到0.9.15又开始失联··0.913的上一个版本基本每天得死机两次··一开始怀疑内存,有问题换了一根内存故障依旧,经过总结应该是兼容性问题,应为每个版本死机频率不一样,它是不定时的死机。配置如下

应用只装了相册和OFFICE在线预览其他都卸载了死机依旧。

确实是不想抛弃,只能硬解了·········做一个算是硬件看门狗。
原理很简单用ESP8266 每隔2分钟PING5次 飞牛NAS的IP如果5次都失败则重启NAS,延时10分钟继续循环每个2分钟ping,给nas启动预留时间。
所需原件如下,ESP8266继电器模块。电源从内部USB插针取5V电源。继电器输出接nas的rest实现失联死机自动重启。

以下为网页端显示看门狗状态

有感兴趣的,代码如下:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <ESP8266Ping.h>
// 服务器配置
ESP8266WebServer server(80);
// EEPROM配置
#define EEPROM_SIZE 96 // 存储SSID(32字节) + 密码(64字节)
// 配网模式AP名称和IP
const char* apSSID = "NasWatchDog";
IPAddress apIP(192, 168, 4, 1); //配网IP可自行修改
// 存储WiFi凭证
String savedSSID = "";
String savedPassword = "";
// Ping相关变量
const IPAddress targetIP(192, 168, 0, 113); // NAS目标IP地址
unsigned long lastPingTime = 0;
const unsigned long pingInterval = 120000; // 2分钟(毫秒)
const unsigned long failureDelay = 600000; // 10分钟(毫秒)
bool pingFailure = false;
unsigned long failureStartTime = 0;
const int pingCount = 5; // 每次发送5个Ping包
const int pingTimeout = 200; // 每个Ping的超时时间(毫秒)
// WiFi连接状态跟踪
bool isWiFiConnected = false; // 跟踪WiFi连接状态
int ledPin = 2; // LED引脚
// 呼吸灯变量
int brightness = 0; // 当前亮度值
int fadeAmount = 5; // 每次变化的步长
unsigned long previousFadeTime = 0;
const unsigned long fadeInterval = 30; // 呼吸灯更新间隔(毫秒)
void setup() {
digitalWrite(0, HIGH); // 初始化为高电平
pinMode(0, OUTPUT);
Serial.begin(115200);
delay(10);
// 初始化GPIO0
// 初始化LED引脚
pinMode(ledPin, OUTPUT);
**ogWrite(ledPin, 1023); // 初始化为熄灭状态(高电平)
// 初始化EEPROM
EEPROM.begin(EEPROM_SIZE);
// 尝试读取保存的WiFi
readCredentials();
// 尝试连接WiFi
if (savedSSID.length() > 0) {
Serial.print("正在连接 ");
Serial.println(savedSSID);
WiFi.begin(savedSSID.c_str(), savedPassword.c_str());
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
}
// 检查连接状态
isWiFiConnected = (WiFi.status() == WL_CONNECTED);
// 连接失败进入配网模式
if (!isWiFiConnected) {
startConfigPortal();
} else {
Serial.println("\n连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
// 设置工作模式下的路由
server.on("/", handleRoot);
server.on("/reset", handleReset);
server.begin();
// 初始化Ping计时器(仅在连接成功后)
lastPingTime = millis();
}
}
void loop() {
server.handleClient();
// 仅在成功连接WiFi时执行Ping
if (isWiFiConnected) {
handlePingLogic();
}
// 更新呼吸灯效果(仅在非失败状态)
updateBreathingLED();
}
// 更新呼吸灯效果
void updateBreathingLED() {
if (pingFailure) {
// Ping失败时LED常亮
**ogWrite(ledPin, 0); // 低电平点亮LED
return;
}
unsigned long currentMillis = millis();
if (currentMillis - previousFadeTime >= fadeInterval) {
previousFadeTime = currentMillis;
// 设置PWM亮度
**ogWrite(ledPin, 1023 - brightness); // 反转亮度(因为低电平点亮)
// 改变亮度值
brightness += fadeAmount;
// 反转亮度变化方向
if (brightness <= 0 || brightness >= 1023) {
fadeAmount = -fadeAmount;
}
}
}
// Ping逻辑处理
void handlePingLogic() {
unsigned long currentTime = millis();
// 处理失败状态
if (pingFailure) {
if (currentTime - failureStartTime >= failureDelay) {
pingFailure = false; // 结束失败状态
lastPingTime = currentTime; // 重置Ping计时器
Serial.println("故障延迟结束,恢复正常Ping检测");
}
return; // 在失败状态下跳过Ping
}
// 执行Ping
if (currentTime - lastPingTime >= pingInterval) {
Serial.print("正在Ping ");
Serial.print(targetIP);
Serial.println(" 5次...");
int successCount = 0;
// 发送5个Ping包 - 使用Ping库
for (int i = 0; i < pingCount; i++) {
bool success = Ping.ping(targetIP, 1); // 发送1个Ping包
if (success) {
successCount++;
Serial.print("Ping #");
Serial.print(i + 1);
Serial.println(" 成功");
} else {
Serial.print("Ping #");
Serial.print(i + 1);
Serial.println(" 失败");
}
delay(100); // 短暂延迟
}
// 检查所有Ping是否都失败
if (successCount == 0) {
Serial.println("全部5次Ping失败! 触发GPIO0");
digitalWrite(0, LOW); // 拉低GPIO0
delay(1000); // 保持1秒
digitalWrite(0, HIGH); // 恢复高电平
pingFailure = true;
failureStartTime = currentTime;
Serial.println("进入故障延迟状态");
} else {
Serial.print("Ping成功次数: ");
Serial.println(successCount);
}
lastPingTime = currentTime; // 更新最后一次Ping时间
}
}
// 启动配置门户
void startConfigPortal() {
Serial.println("\n启动配置模式");
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP(apSSID);
Serial.print("AP IP地址: ");
Serial.println(apIP);
// 设置配网路由
server.on("/", handleConfigRoot);
server.on("/configure", handleConfigure);
server.begin();
}
// 读取保存的凭证
void readCredentials() {
Serial.println("读取EEPROM");
savedSSID = "";
savedPassword = "";
// 读取SSID (32字节)
for (int i = 0; i < 32; ++i) {
char c = char(EEPROM.read(i));
if (c != 0 && c != 255) savedSSID += c;
}
// 读取密码 (64字节)
for (int i = 32; i < 96; ++i) {
char c = char(EEPROM.read(i));
if (c != 0 && c != 255) savedPassword += c;
}
Serial.println("保存的SSID: " + savedSSID);
Serial.println("保存的密码: " + savedPassword);
}
// 保存凭证到EEPROM
void saveCredentials(String ssid, String password) {
Serial.println("保存凭证");
// 清除EEPROM
for (int i = 0; i < EEPROM_SIZE; ++i) {
EEPROM.write(i, 0);
}
// 写入SSID
for (int i = 0; i < ssid.length(); ++i) {
EEPROM.write(i, ssid[i]);
}
// 写入密码
for (int i = 0; i < password.length(); ++i) {
EEPROM.write(32 + i, password[i]);
}
EEPROM.commit();
}
// 配网页面HTML
const char* configPage =
"<html>"
"<head>"
"<meta charset='UTF-8'>"
"<meta name='viewport' content='width=device-width, initial-scale=1'>"
"<style>"
"body{font-family:'Microsoft YaHei',Arial;text-align:center;margin-top:50px}"
"input{padding:10px;width:80%;max-width:300px;margin:10px}"
"button{padding:10px 20px;background:#4CAF50;color:white;border:none;cursor:pointer}"
"h1{color:#333}"
"</style>"
"</head>"
"<body>"
"<h1>WiFi 配置</h1>"
"<form action='/configure' method='POST'>"
"<input type='text' name='ssid' placeholder='WiFi名称' required><br>"
"<input type='password' name='password' placeholder='WiFi密码' required><br>"
"<button type='submit'>保存并连接</button>"
"</form>"
"</body>"
"</html>";
// 配置页面处理
void handleConfigRoot() {
server.send(200, "text/html", configPage);
}
// 处理配置请求
void handleConfigure() {
if (server.method() != HTTP_POST) {
server.send(405, "text/plain", "方法不允许");
return;
}
String newSSID = server.arg("ssid");
String newPassword = server.arg("password");
if (newSSID.length() == 0) {
server.send(400, "text/plain", "SSID不能为空");
return;
}
saveCredentials(newSSID, newPassword);
server.send(200, "text/html",
"<html>"
"<head><meta charset='UTF-8'></head>"
"<body style='font-family:\"Microsoft YaHei\",Arial;text-align:center;'>"
"<h1>凭证已保存!</h1>"
"<p>设备正在重启,将尝试连接到: " + newSSID + "</p>"
"<p>请等待约20秒,然后在路由器中检查已连接设备。</p>"
"</body></html>");
delay(2000);
ESP.restart();
}
// 工作模式首页
void handleRoot() {
String html = "<html>"
"<head><meta charset='UTF-8'></head>"
"<body style='font-family:\"Microsoft YaHei\",Arial;text-align:center;'>"
"<h1>看门狗 已连接!</h1>"
"<p>SSID: " + savedSSID + "</p>"
"<p>IP地址: " + WiFi.localIP().toString() + "</p>"
"<p>Ping状态: " + String(pingFailure ? "故障延迟中(10分钟)" : "活动状态") + "</p>"
"<p>Ping目标: 192.168.0.113</p>"
"<p><a href='/reset'>重置WiFi设置</a></p>"
"</body></html>";
server.send(200, "text/html", html);
}
// 重置WiFi设置
void handleReset() {
saveCredentials("", ""); // 清除凭证
server.send(200, "text/html",
"<html>"
"<head><meta charset='UTF-8'></head>"
"<body style='font-family:\"Microsoft YaHei\",Arial;text-align:center;'>"
"<h1>WiFi设置已重置!</h1>"
"<p>设备将重启进入配置模式</p>"
"</body></html>");
delay(2000);
ESP.restart();
}