Browse Source

新增OTA升级

yangfei 1 year ago
parent
commit
fc6bfab9f9
5 changed files with 224 additions and 19 deletions
  1. 153 0
      OTAUpdater.cpp
  2. 27 0
      OTAUpdater.h
  3. 23 15
      WiFiConfig.cpp
  4. 3 2
      WiFiConfig.h
  5. 18 2
      esp32_c6.ino

+ 153 - 0
OTAUpdater.cpp

@@ -0,0 +1,153 @@
+#include "OTAUpdater.h"
+
+// 使用 extern 声明全局变量
+extern String serverAddress;
+extern String deviceID;
+
+OTAUpdater::OTAUpdater(uint16_t port, const String& currentVersion)
+    : _port(port), _currentVersion(currentVersion) {}  // 移除 _serverUrl 初始化
+
+void OTAUpdater::checkForUpdates() {
+    // 添加调试信息,打印设备ID
+    Serial.println("设备ID: " + deviceID);  // 确保设备ID被正确传递
+
+    // 检查 WiFi 连接状态
+    while (true) {
+        // 尝试连接 WiFi,超时时间为 30 秒
+        if (connectWiFiWithTimeout(30000)) {
+            break;  // 连接成功,退出循环
+        } else {
+            Serial.println("[信息] Wi-Fi 连接失败,进入配置模式...");
+            setupWiFiConfig();  // 进入配置模式,超时时间为 60 秒
+        }
+    }
+
+    Serial.println("正在检查更新...");
+
+    // 构建完整的 OTA 服务器 URL,使用全局变量 serverAddress 和 _port
+    String fullServerUrl = "http://" + serverAddress + ":" + String(_port);
+    Serial.println("OTA 服务器地址: " + fullServerUrl);
+
+    // 向服务器发送设备ID和当前固件版本,检查是否有更新
+    HTTPClient http;
+    String checkUpdateUrl = fullServerUrl + "/check-update";
+    http.begin(checkUpdateUrl);
+    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
+
+    // 发送设备ID和当前固件版本
+    String postData = "device_id=" + deviceID + "&version=" + _currentVersion;
+    int httpCode = http.POST(postData);
+
+    if (httpCode == HTTP_CODE_OK) {
+        String response = http.getString();
+        Serial.println("服务器响应: " + response);
+
+        // 如果服务器返回"update",则下载固件
+        if (response == "update") {
+            Serial.println("发现新固件,正在下载...");
+            if (downloadAndUpdateFirmware()) {
+                Serial.println("固件更新完成,即将重启...");
+                ESP.restart();
+            } else {
+                Serial.println("固件更新失败。");
+            }
+        } else {
+            Serial.println("无可用更新。");
+        }
+    } else {
+        Serial.println("检查更新失败,HTTP错误代码: " + String(httpCode));
+    }
+
+    http.end();
+}
+
+bool OTAUpdater::downloadAndUpdateFirmware() {
+    // 构建完整的 OTA 服务器 URL,使用全局变量 serverAddress 和 _port
+    String fullServerUrl = "http://" + serverAddress + ":" + String(_port);
+    String firmwareUrl = fullServerUrl + "/firmware";
+
+    HTTPClient http;
+    http.begin(firmwareUrl);
+
+    // 设置 HTTP 超时时间(单位:毫秒)
+    http.setTimeout(20000);  // 20秒超时
+
+    int httpCode = http.GET();
+
+    if (httpCode == HTTP_CODE_OK) {
+        // 获取固件文件大小
+        int contentLength = http.getSize();
+        if (contentLength <= 0) {
+            Serial.println("无效的固件文件大小。");
+            http.end();
+            return false;
+        }
+
+        // 开始更新
+        if (Update.begin(contentLength)) {
+            // 获取数据流
+            WiFiClient* stream = http.getStreamPtr();
+
+            // 设置数据流超时时间(单位:毫秒)
+            stream->setTimeout(20000);  // 20秒超时
+
+            // 记录下载开始时间
+            unsigned long startTime = millis();
+            size_t written = 0;
+
+            // 分块下载固件数据
+            while (written < contentLength) {
+                // 检查是否超时
+                if (millis() - startTime > 20000) {  // 20秒超时
+                    Serial.println("下载超时,中断更新。");
+                    http.end();
+                    Update.end(false);  // 结束更新并标记为失败
+                    return false;
+                }
+
+                // 读取数据块
+                size_t available = stream->available();
+                if (available > 0) {
+                    uint8_t buffer[128];  // 缓冲区大小
+                    size_t read = stream->readBytes(buffer, min(sizeof(buffer), available));
+                    written += Update.write(buffer, read);
+                } else {
+                    // 如果没有数据,等待一段时间
+                    delay(100);
+                }
+            }
+
+            // 检查是否下载完成
+            if (written == contentLength) {
+                Serial.println("固件写入成功。");
+            } else {
+                Serial.println("固件写入失败。");
+                http.end();
+                Update.end(false);  // 结束更新并标记为失败
+                return false;
+            }
+
+            // 结束更新
+            if (Update.end()) {
+                Serial.println("固件更新完成。");
+                http.end();
+                return true;
+            } else {
+                Serial.println("固件更新结束失败。");
+                http.end();
+                return false;
+            }
+        } else {
+            Serial.println("空间不足,无法开始OTA更新。");
+            http.end();
+            return false;
+        }
+    } else {
+        Serial.println("下载固件失败,HTTP错误代码: " + String(httpCode));
+        http.end();
+        return false;
+    }
+
+    http.end();
+    return false;
+}

+ 27 - 0
OTAUpdater.h

@@ -0,0 +1,27 @@
+#ifndef OTA_UPDATER_H
+#define OTA_UPDATER_H
+
+#include <WiFi.h>
+#include <HTTPClient.h>
+#include <Update.h>
+
+// 使用 extern 声明全局变量 deviceID 和 serverAddress
+extern String deviceID;
+extern String serverAddress;
+
+void setupWiFiConfig();
+bool connectWiFiWithTimeout(uint32_t timeoutMs);
+
+class OTAUpdater {
+public:
+    OTAUpdater(uint16_t port, const String& currentVersion);  // 移除 serverUrl 参数
+    void checkForUpdates();
+
+private:
+    uint16_t _port;  // 端口号
+    String _currentVersion;  // 当前固件版本
+
+    bool downloadAndUpdateFirmware();
+};
+
+#endif

+ 23 - 15
WiFiConfig.cpp

@@ -5,27 +5,31 @@
 Preferences preferences;
 Preferences preferences;
 
 
 // 持久化存储的参数
 // 持久化存储的参数
-String deviceID;
-String serverAddress;
-uint16_t serverPort;
+String deviceID; //MAC地址 id
+String serverAddress;   //mqtt服务器地址
+uint16_t serverPort; //mqtt端口
+uint16_t otaPort;  // 新增 OTA 端口号
 
 
 // 加载服务器参数
 // 加载服务器参数
 void loadServerParams() {
 void loadServerParams() {
   preferences.begin("settings", true);
   preferences.begin("settings", true);
-  serverAddress = preferences.getString("serverAddress", "192.168.3.31");
-  serverPort = preferences.getUInt("serverPort", 1883);
+  serverAddress = preferences.getString("serverAddress", "192.168.3.31"); //mqtt地址
+  serverPort = preferences.getUInt("serverPort", 1883); //mqtt端口 
+  otaPort = preferences.getUInt("otaPort", 2999);        // 新增 OTA 端口号,默认 2999
   preferences.end();
   preferences.end();
   Serial.println("[信息] 加载服务器参数:");
   Serial.println("[信息] 加载服务器参数:");
-  Serial.println("服务器地址: " + serverAddress);
-  Serial.println("服务器端口: " + String(serverPort));
+  Serial.println("服务器地址: " + serverAddress); // 打印服务器地址
+  Serial.println("服务器端口: " + String(serverPort)); // 打印 MQTT 端口号
+  Serial.println("OTA 端口: " + String(otaPort));  // 打印 OTA 端口号
 }
 }
 
 
 // 保存参数回调
 // 保存参数回调
 void saveParamsCallback() {
 void saveParamsCallback() {
   Serial.println("[信息] 正在保存自定义参数...");
   Serial.println("[信息] 正在保存自定义参数...");
   preferences.begin("settings", false);
   preferences.begin("settings", false);
-  preferences.putString("serverAddress", serverAddress);
-  preferences.putUInt("serverPort", serverPort);
+  preferences.putString("serverAddress", serverAddress); // 保存 MQTT 服务器地址
+  preferences.putUInt("serverPort", serverPort); // 保存 MQTT 端口号
+  preferences.putUInt("otaPort", otaPort);  // 保存 OTA 端口号
   preferences.end();
   preferences.end();
   Serial.println("[信息] 参数保存成功。");  
   Serial.println("[信息] 参数保存成功。");  
 }
 }
@@ -33,15 +37,20 @@ void saveParamsCallback() {
 // WiFi 配网功能
 // WiFi 配网功能
 void setupWiFiConfig() {
 void setupWiFiConfig() {
   WiFiManager wifiManager;
   WiFiManager wifiManager;
-setConstantOn(true);  // LED 常亮
+  setConstantOn(true);  // LED 常亮
+
   WiFiManagerParameter customServer("server", "服务器地址", serverAddress.c_str(), 40);
   WiFiManagerParameter customServer("server", "服务器地址", serverAddress.c_str(), 40);
   WiFiManagerParameter customPort("port", "服务器端口", String(serverPort).c_str(), 6);
   WiFiManagerParameter customPort("port", "服务器端口", String(serverPort).c_str(), 6);
+  WiFiManagerParameter customOtaPort("otaPort", "OTA 端口", String(otaPort).c_str(), 6);  // 新增 OTA 端口配置
   wifiManager.addParameter(&customServer);
   wifiManager.addParameter(&customServer);
   wifiManager.addParameter(&customPort);
   wifiManager.addParameter(&customPort);
+  wifiManager.addParameter(&customOtaPort);  // 添加 OTA 端口配置
 
 
-  wifiManager.setSaveParamsCallback([]() {
+  // 修复:捕获 customOtaPort
+  wifiManager.setSaveParamsCallback([&customOtaPort]() {
     serverAddress = WiFiManagerParameter("server", "服务器地址", serverAddress.c_str(), 40).getValue();
     serverAddress = WiFiManagerParameter("server", "服务器地址", serverAddress.c_str(), 40).getValue();
     serverPort = String(WiFiManagerParameter("port", "服务器端口", String(serverPort).c_str(), 6).getValue()).toInt();
     serverPort = String(WiFiManagerParameter("port", "服务器端口", String(serverPort).c_str(), 6).getValue()).toInt();
+    otaPort = String(customOtaPort.getValue()).toInt();  // 保存 OTA 端口号
   });
   });
 
 
   wifiManager.setConfigPortalTimeout(50);  // 配置模式超时时间为 60 秒
   wifiManager.setConfigPortalTimeout(50);  // 配置模式超时时间为 60 秒
@@ -49,15 +58,14 @@ setConstantOn(true);  // LED 常亮
   if (!wifiManager.startConfigPortal("人体存在感应器_Config")) {
   if (!wifiManager.startConfigPortal("人体存在感应器_Config")) {
     Serial.println("[错误] 配置模式超时或未完成配置!");
     Serial.println("[错误] 配置模式超时或未完成配置!");
     feedWatchdog();
     feedWatchdog();
-    // 关闭 LED
-setConstantOn(false); 
+    setConstantOn(false);  // 关闭 LED
   } else {
   } else {
     serverAddress = customServer.getValue();
     serverAddress = customServer.getValue();
     serverPort = String(customPort.getValue()).toInt();
     serverPort = String(customPort.getValue()).toInt();
+    otaPort = String(customOtaPort.getValue()).toInt();  // 保存 OTA 端口号
     saveParamsCallback();
     saveParamsCallback();
     loadServerParams();  // 重新加载保存的服务器配置
     loadServerParams();  // 重新加载保存的服务器配置
-    // 关闭 LED
-setConstantOn(false); 
+    setConstantOn(false);  // 关闭 LED
   }
   }
 }
 }
 
 

+ 3 - 2
WiFiConfig.h

@@ -5,7 +5,8 @@
 #include <Preferences.h>
 #include <Preferences.h>
 extern String deviceID;
 extern String deviceID;
 extern String serverAddress;  // 声明外部变量
 extern String serverAddress;  // 声明外部变量
-extern uint16_t serverPort;
+extern uint16_t serverPort;   // MQTT 端口号
+extern uint16_t otaPort;      // 新增 OTA 端口号
 
 
 // WiFi 配置和服务器参数设置
 // WiFi 配置和服务器参数设置
 void loadServerParams();
 void loadServerParams();
@@ -16,4 +17,4 @@ void feedWatchdog();
 void setConstantOn(bool constantOn);  // 设置 LED 常亮或关闭
 void setConstantOn(bool constantOn);  // 设置 LED 常亮或关闭
 void addBlinkTask(int blinkCount);  // 添加闪烁任务,接受一个 int 参数
 void addBlinkTask(int blinkCount);  // 添加闪烁任务,接受一个 int 参数
 
 
-#endif
+#endif

+ 18 - 2
esp32_c6.ino

@@ -2,6 +2,7 @@
 #include "MQTTHandler.h"
 #include "MQTTHandler.h"
 #include "LEDHandler.h"  // 引入 LEDHandler
 #include "LEDHandler.h"  // 引入 LEDHandler
 #include "WatchdogHandler.h"  // 引入 WatchdogHandler
 #include "WatchdogHandler.h"  // 引入 WatchdogHandler
+#include "OTAUpdater.h"  // 引入 OTAUpdater
 
 
 #define BUTTON_PIN 4      // 重置引脚4  //高低电平3  //代开发信号5
 #define BUTTON_PIN 4      // 重置引脚4  //高低电平3  //代开发信号5
 #define WIFI_CONNECT_TIMEOUT 30000
 #define WIFI_CONNECT_TIMEOUT 30000
@@ -12,6 +13,10 @@
 
 
 // 数据量统计
 // 数据量统计
 unsigned long lastCheckTime = 0;  // 上次检查时间
 unsigned long lastCheckTime = 0;  // 上次检查时间
+// 当前固件版本
+ const String currentFirmwareVersion = "1.0.3";  // 设备当前的固件版本
+// const String currentFirmwareVersion = "1.0.2 - " __DATE__ " " __TIME__;  // 自动生成版本号
+
 
 
 long unsigned int lastDebounceTimeNew = 0;
 long unsigned int lastDebounceTimeNew = 0;
 int lastButtonStateNew = HIGH;
 int lastButtonStateNew = HIGH;
@@ -19,6 +24,9 @@ bool resetModeActivated = false;
 
 
 int lastGPIO18State = LOW;  // 保存上一次 GPIO18 的电平状态
 int lastGPIO18State = LOW;  // 保存上一次 GPIO18 的电平状态
 
 
+// 创建 OTAUpdater 实例,使用全局变量 serverAddress 和 otaPort
+OTAUpdater otaUpdater(otaPort, currentFirmwareVersion);
+
 void setup() {
 void setup() {
   Serial.begin(115200);
   Serial.begin(115200);
   delay(1000); // 等待串口初始化完成
   delay(1000); // 等待串口初始化完成
@@ -48,6 +56,9 @@ void setup() {
     }
     }
   }
   }
 
 
+  // 检查固件更新
+  otaUpdater.checkForUpdates();
+
   // 初始化 MQTT 客户端
   // 初始化 MQTT 客户端
   mqttSetup();
   mqttSetup();
 
 
@@ -108,8 +119,13 @@ void loop() {
     feedWatchdog();
     feedWatchdog();
     addBlinkTask(1);
     addBlinkTask(1);
 
 
-    //Serial.println("[信息] 主循环运行中……");
-    //Serial.println("[信息] 已永久存储 targetDeviceID: " + targetDeviceID);
+  }
+
+  // 每隔一段时间检查一次更新
+  static unsigned long lastCheck = 0;
+  if (millis() - lastCheck > 300000) {  // 每5分钟检查一次
+    lastCheck = millis();
+    otaUpdater.checkForUpdates();
   }
   }
 
 
   // LED 闪烁逻辑
   // LED 闪烁逻辑