diff --git a/README.md b/README.md
index 1f9c0cb..d7d3abb 100644
--- a/README.md
+++ b/README.md
@@ -18,10 +18,10 @@ Firmware for the modular Split Flap Display created by [Morgan Manly](https://gi
## Supported boards
-| Environment | Processor | Tested Boards |
-| -------------------- | ------------- | ------------------------------------------------------------ |
-| `esp32_c3` (default) | ESP32-C3FN4 | Teyleten Robot ESP32-C3-SuperMini
Waveshare ESP32-C3-Zero |
-| `esp32_s3` | ESP32-S3FH4R2 | Waveshare ESP32-S3-Zero\* |
+| Environment | Processor | Tested Boards |
+| -------------------- | ------------- | ------------------------------------------------------------------------ |
+| `esp32_c3` (default) | ESP32-C3FN4 | Teyleten Robot ESP32-C3-SuperMini
Waveshare ESP32-C3-Zero |
+| `esp32_s3` | ESP32-S3FH4R2 | Waveshare ESP32-S3-Zero\*
ESP32-S3 Super Mini\* |
\* Requires manually resetting the board into firmware upload mode by holding BOOT, pressing & releasing RESET, then releasing BOOT prior to upload. After uploading is successful, either press & release RESET or power cycle the board to put it in normal operation mode.
@@ -46,6 +46,10 @@ Firmware for the modular Split Flap Display created by [Morgan Manly](https://gi
1. Enjoy!
+### Using OTA to update the firmware
+
+On the settings page set an OTA password to enable OTA updatable firmware. Use this same password for your `auth` flag in `platformio.ini`, and then use a device environment with `*_ota` appended (ie `esp32_s3_ota`) to upload a new firmware and/or filesystem
+
## Contributing
### Setup
diff --git a/platformio.ini b/platformio.ini
index 727041f..3248da8 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -31,6 +31,12 @@ build_flags=
; '-D WIFI_SSID="MySsid"' ; hardcoded wifi credentials
; '-D WIFI_PASS="123456"' ; ( for settings development )
+; OTA-specific settings (can be combined with any env)
+[env:ota]
+upload_protocol=espota
+upload_port=splitflap.local
+; upload_flags = --auth=yourpassword
+
[env:esp32_c3]
extends=env
board=esp32-c3-devkitm-1
@@ -42,6 +48,9 @@ build_flags=
'-D ARDUINO_USB_MODE=1'
'-D ARDUINO_USB_CDC_ON_BOOT=1'
+[env:esp32_c3_ota]
+extends=env:esp32_c3, env:ota
+
[env:esp32_s3]
extends=env
board=esp32-s3-fh4r2
@@ -51,3 +60,6 @@ build_flags=
${env.build_flags}
'-D SERIAL_SPEED=115200'
'-D WIFI_TX_POWER=28'
+
+[env:esp32_s3_ota]
+extends=env:esp32_s3, env:ota
diff --git a/src/SplitFlapDisplay.ino b/src/SplitFlapDisplay.ino
index f8c18f4..bf2eec5 100644
--- a/src/SplitFlapDisplay.ino
+++ b/src/SplitFlapDisplay.ino
@@ -17,6 +17,7 @@ JsonSettings settings = JsonSettings("config", {
// General Settings
{"name", JsonSetting("My Display")},
{"mdns", JsonSetting("splitflap")},
+ {"otaPass", JsonSetting("")},
{"timezone", JsonSetting("Etc/UTC")},
// Wifi Settings
{"ssid", JsonSetting("")},
@@ -59,6 +60,7 @@ void setup() {
if (! webServer.connectToWifi()) {
webServer.startAccessPoint();
+ webServer.enableOta();
webServer.startMDNS();
webServer.startWebServer();
@@ -71,6 +73,7 @@ void setup() {
display.writeChar('X');
}
} else {
+ webServer.enableOta();
webServer.startMDNS();
webServer.startWebServer();
@@ -100,10 +103,12 @@ void loop() {
default: break;
}
+ webServer.handleOta();
checkConnection();
reconnectIfNeeded();
+ webServer.checkRebootRequired();
yield();
}
@@ -197,10 +202,12 @@ void reconnectIfNeeded() {
display.writeString("");
if (! webServer.connectToWifi()) {
webServer.startAccessPoint();
+ webServer.enableOta();
webServer.endMDNS();
webServer.startMDNS();
display.writeChar('X');
} else {
+ webServer.enableOta();
webServer.endMDNS();
webServer.startMDNS();
display.writeString("OK");
diff --git a/src/SplitFlapWebServer.cpp b/src/SplitFlapWebServer.cpp
index 160267e..f041e56 100644
--- a/src/SplitFlapWebServer.cpp
+++ b/src/SplitFlapWebServer.cpp
@@ -14,8 +14,9 @@
#endif
SplitFlapWebServer::SplitFlapWebServer(JsonSettings &settings)
- : settings(settings), server(80), multiWordDelay(1000), attemptReconnect(false), multiWordCurrentIndex(0),
- numMultiWords(0), wifiCheckInterval(1000), connectionMode(0), checkDateInterval(250), centering(1) {
+ : settings(settings), server(80), multiWordDelay(1000), rebootRequired(false), attemptReconnect(false),
+ multiWordCurrentIndex(0), numMultiWords(0), wifiCheckInterval(1000), connectionMode(0), checkDateInterval(250),
+ centering(1) {
lastSwitchMultiTime = millis();
}
@@ -180,6 +181,63 @@ bool SplitFlapWebServer::loadWiFiCredentials() {
return false; // Return false if no credentials were found
}
+void SplitFlapWebServer::checkRebootRequired() {
+ if (rebootRequired) {
+ Serial.println("Reboot required. Restarting...");
+ delay(1000);
+ ESP.restart();
+ }
+}
+
+void SplitFlapWebServer::handleOta() {
+ ArduinoOTA.handle();
+}
+void SplitFlapWebServer::enableOta() {
+ // Skip OTA initialisation if no password is set
+ if (settings.getString("otaPass") == "") {
+ return;
+ }
+
+ ArduinoOTA.setHostname(settings.getString("mdns").c_str()); // otherwise mdns name gets overwritten with default
+ ArduinoOTA.setPassword(settings.getString("otaPass").c_str());
+
+ ArduinoOTA
+ .onStart([]() {
+ String type;
+ if (ArduinoOTA.getCommand() == U_FLASH) {
+ type = "sketch";
+ } else { // U_LITTLEFS
+ type = "filesystem";
+ LittleFS.end(); // Unmount the filesystem before update
+ }
+ Serial.println("Start updating " + type);
+ })
+ .onEnd([]() {
+ Serial.println("\nEnd");
+ LittleFS.begin(); // Remount filesystem
+ })
+ .onProgress([](unsigned int progress, unsigned int total) {
+ Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
+ }).onError([](ota_error_t error) {
+ Serial.printf("Error[%u]: ", error);
+ LittleFS.begin(); // Remount filesystem
+ if (error == OTA_AUTH_ERROR) {
+ Serial.println("Auth Failed");
+ } else if (error == OTA_BEGIN_ERROR) {
+ Serial.println("Begin Failed");
+ } else if (error == OTA_CONNECT_ERROR) {
+ Serial.println("Connect Failed");
+ } else if (error == OTA_RECEIVE_ERROR) {
+ Serial.println("Receive Failed");
+ } else if (error == OTA_END_ERROR) {
+ Serial.println("End Failed");
+ }
+ });
+
+ ArduinoOTA.begin();
+ Serial.println("OTA Initialized");
+}
+
bool SplitFlapWebServer::connectToWifi() {
if (loadWiFiCredentials()) {
unsigned long startAttemptTime = millis();
@@ -296,6 +354,7 @@ void SplitFlapWebServer::startWebServer() {
Serial.println("Received settings update request");
Serial.println(json.as());
+ bool rebootRequired = false;
bool reconnect = false;
JsonDocument response;
response["message"] = "Settings saved successfully!";
@@ -308,6 +367,11 @@ void SplitFlapWebServer::startWebServer() {
json["ssid"].as() + " network";
}
+ if (json["otaPass"].is() && json["otaPass"].as() != settings.getString("otaPass")) {
+ rebootRequired = true; // OTA password change can only be applied by rebooting
+ response["message"] = "Settings updated successfully, OTA Password has changed. Rebooting...";
+ }
+
if (json["mdns"].is() && json["mdns"].as() != settings.getString("mdns")) {
reconnect = true;
response["message"] =
@@ -338,6 +402,7 @@ void SplitFlapWebServer::startWebServer() {
request->send(200, "application/json", response.as());
+ this->rebootRequired = rebootRequired;
this->attemptReconnect = reconnect;
}
));
diff --git a/src/SplitFlapWebServer.h b/src/SplitFlapWebServer.h
index babfbac..a725527 100644
--- a/src/SplitFlapWebServer.h
+++ b/src/SplitFlapWebServer.h
@@ -4,6 +4,7 @@
#include
#include
+#include
#include
#include
#include
@@ -15,6 +16,7 @@ class SplitFlapWebServer {
SplitFlapWebServer(JsonSettings &settings);
void init();
void setTimezone();
+ void checkRebootRequired();
// Wifi Connectivity
bool loadWiFiCredentials();
@@ -24,6 +26,8 @@ class SplitFlapWebServer {
void startWebServer();
void endMDNS();
void startMDNS();
+ void enableOta();
+ void handleOta();
void startAccessPoint();
void checkWiFi();
unsigned long getLastCheckWifiTime() { return lastCheckWifiTime; }
@@ -85,6 +89,7 @@ class SplitFlapWebServer {
String inputString; // latest single input from user
String writtenString; // string for whatever is currently written to the display
+ bool rebootRequired;
bool attemptReconnect;
unsigned long lastCheckWifiTime;
int wifiCheckInterval;
diff --git a/src/web/settings.html b/src/web/settings.html
index ead2ca5..ee0a28f 100644
--- a/src/web/settings.html
+++ b/src/web/settings.html
@@ -68,6 +68,22 @@
x-text="errors.message"
>
+
+
+
+