From f47c2b7e6cc39412a055d694ce481b2230d54c8c Mon Sep 17 00:00:00 2001 From: Ignacio Capurro Date: Tue, 18 Mar 2025 23:22:33 -0300 Subject: [PATCH] OTA support over wifi AP --- examples/shotStopper/ota_setup.h | 80 ++++++++++++++++++++++++++++ examples/shotStopper/shotStopper.ino | 14 +++++ 2 files changed, 94 insertions(+) create mode 100644 examples/shotStopper/ota_setup.h diff --git a/examples/shotStopper/ota_setup.h b/examples/shotStopper/ota_setup.h new file mode 100644 index 0000000..972a703 --- /dev/null +++ b/examples/shotStopper/ota_setup.h @@ -0,0 +1,80 @@ +#include +#include +#include + +#define WIFI_SSID "shotStopperAP" +#define WIFI_PASS "" + +bool otaMode = false; + +AsyncWebServer server(80); + +void restartESP(void *param) { + delay(2000); // Give client time to receive response + ESP.restart(); +} + +void setupOTA() { + // Serve a simple webpage for OTA update + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/html", "

ESP32 OTA Update V6

" + "
" + "
" + "
"); + }); + + // OTA Upload Handler + server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request) { + request->send(200, "application/json", "{\"status\": \"success\"}"); + xTaskCreate(restartESP, "restartTask", 1024, NULL, 1, NULL); + }, [](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { + if (!index) { + Serial.printf("Start OTA Update: %s\n", filename.c_str()); + if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { + Update.printError(Serial); + } + } + if (Update.write(data, len) != len) { + Update.printError(Serial); + } + if (final) { + if (Update.end(true)) { + Serial.println("Update Complete!"); + } else { + Update.printError(Serial); + } + } + }); +} + +void startAPMode() { + if (!otaMode) { + // Start Wi-Fi in AP mode + WiFi.softAP(WIFI_SSID, WIFI_PASS); + Serial.print("AP Mode Started: "); + Serial.println(WIFI_SSID); + setupOTA(); + server.begin(); + Serial.println("Web Server Started on 192.168.4.1"); + otaMode = true; + } +} + +// Function to stop Wi-Fi AP mode +void stopAPMode() { + if (otaMode) { + WiFi.softAPdisconnect(true); + server.end(); + Serial.println("AP Mode Stopped"); + otaMode = false; + } +} + +bool checkOTAMode(bool otaModeRequested) { + if (otaModeRequested && !otaMode) { + startAPMode(); + } else if (!otaModeRequested && otaMode) { + stopAPMode(); + } + return otaMode || otaModeRequested; +} diff --git a/examples/shotStopper/shotStopper.ino b/examples/shotStopper/shotStopper.ino index 3a5fd8f..32265bd 100644 --- a/examples/shotStopper/shotStopper.ino +++ b/examples/shotStopper/shotStopper.ino @@ -23,6 +23,7 @@ #include #include +#include "ota_setup.h" #define FIRMWARE_VERSION 1 #define MAX_OFFSET 5 // In case an error in brewing occured @@ -78,6 +79,7 @@ uint8_t maxShotDurationS = 50; // Primarily useful for latching switches, s // looses control of the paddle once the system // latches. uint8_t dripDelayS = 3; // Time after the shot ended to measure the final weight +bool otaModeRequested = false; // Set to true if OTA mode is being requested. typedef enum {BUTTON, WEIGHT, TIME, UNDEF} ENDTYPE; @@ -132,6 +134,7 @@ BLEByteCharacteristic maxShotDurationSCharacteristic("0xFF16", BLEWrite | BLERe BLEByteCharacteristic dripDelaySCharacteristic("0xFF17", BLEWrite | BLERead); BLEByteCharacteristic firmwareVersionCharacteristic("0xFF18", BLERead); BLEByteCharacteristic scaleStatusCharacteristic("0xFF19", BLERead | BLENotify); +BLEByteCharacteristic otaModeRequestedCharacteristic("0xFF20", BLEWrite | BLERead); enum ScaleStatus { STATUS_DISCONNECTED = 0, @@ -193,6 +196,7 @@ void initializeBLE() { shotStopperService.addCharacteristic(dripDelaySCharacteristic); shotStopperService.addCharacteristic(firmwareVersionCharacteristic); shotStopperService.addCharacteristic(scaleStatusCharacteristic); + shotStopperService.addCharacteristic(otaModeRequestedCharacteristic); BLE.addService(shotStopperService); weightCharacteristic.writeValue(goalWeight); momentaryCharacteristic.writeValue(momentary ? 1 : 0); @@ -203,6 +207,7 @@ void initializeBLE() { dripDelaySCharacteristic.writeValue(dripDelayS); firmwareVersionCharacteristic.writeValue(FIRMWARE_VERSION); scaleStatusCharacteristic.writeValue(STATUS_DISCONNECTED); + otaModeRequestedCharacteristic.writeValue(otaModeRequested ? 1 : 0); BLE.advertise(); Serial.println("Bluetooth® device active, waiting for connections..."); BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); @@ -256,6 +261,11 @@ void pollAndReadBLE() { EEPROM.write(DRIP_DELAY_S_ADDR, dripDelayS); updated = true; } + if (otaModeRequestedCharacteristic.written()) { + otaModeRequested = otaModeRequestedCharacteristic.value() != 0; + Serial.print("OTA mode requested: "); + Serial.println(otaModeRequested); + } if (updated) { EEPROM.commit(); } @@ -287,6 +297,10 @@ void loop() { // Check for setpoint updates pollAndReadBLE(); + if (checkOTAMode(otaModeRequested)) { + return; + } + // Connect to scale if (!scale.isConnected()) { updateScaleStatus(STATUS_DISCONNECTED);