Skip to content

Commit 1bfc346

Browse files
authored
Merge pull request #4 from shendriksza/ota-support
Add ArduinoOta support
2 parents 40fa4b0 + 71805ab commit 1bfc346

File tree

6 files changed

+115
-6
lines changed

6 files changed

+115
-6
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ Firmware for the modular Split Flap Display created by [Morgan Manly](https://gi
1818

1919
## Supported boards
2020

21-
| Environment | Processor | Tested Boards |
22-
| -------------------- | ------------- | ------------------------------------------------------------ |
23-
| `esp32_c3` (default) | ESP32-C3FN4 | Teyleten Robot ESP32-C3-SuperMini<br>Waveshare ESP32-C3-Zero |
24-
| `esp32_s3` | ESP32-S3FH4R2 | Waveshare ESP32-S3-Zero<sup>\*</sup> |
21+
| Environment | Processor | Tested Boards |
22+
| -------------------- | ------------- | ------------------------------------------------------------------------ |
23+
| `esp32_c3` (default) | ESP32-C3FN4 | Teyleten Robot ESP32-C3-SuperMini<br>Waveshare ESP32-C3-Zero |
24+
| `esp32_s3` | ESP32-S3FH4R2 | Waveshare ESP32-S3-Zero<sup>\*</sup><br>ESP32-S3 Super Mini<sup>\*</sup> |
2525

2626
<sub>\* 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.</sub>
2727

@@ -46,6 +46,10 @@ Firmware for the modular Split Flap Display created by [Morgan Manly](https://gi
4646

4747
1. Enjoy!
4848

49+
### Using OTA to update the firmware
50+
51+
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
52+
4953
## Contributing
5054

5155
### Setup

platformio.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ build_flags=
3131
; '-D WIFI_SSID="MySsid"' ; hardcoded wifi credentials
3232
; '-D WIFI_PASS="123456"' ; ( for settings development )
3333

34+
; OTA-specific settings (can be combined with any env)
35+
[env:ota]
36+
upload_protocol=espota
37+
upload_port=splitflap.local
38+
; upload_flags = --auth=yourpassword
39+
3440
[env:esp32_c3]
3541
extends=env
3642
board=esp32-c3-devkitm-1
@@ -42,6 +48,9 @@ build_flags=
4248
'-D ARDUINO_USB_MODE=1'
4349
'-D ARDUINO_USB_CDC_ON_BOOT=1'
4450

51+
[env:esp32_c3_ota]
52+
extends=env:esp32_c3, env:ota
53+
4554
[env:esp32_s3]
4655
extends=env
4756
board=esp32-s3-fh4r2
@@ -51,3 +60,6 @@ build_flags=
5160
${env.build_flags}
5261
'-D SERIAL_SPEED=115200'
5362
'-D WIFI_TX_POWER=28'
63+
64+
[env:esp32_s3_ota]
65+
extends=env:esp32_s3, env:ota

src/SplitFlapDisplay.ino

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ JsonSettings settings = JsonSettings("config", {
1717
// General Settings
1818
{"name", JsonSetting("My Display")},
1919
{"mdns", JsonSetting("splitflap")},
20+
{"otaPass", JsonSetting("")},
2021
{"timezone", JsonSetting("Etc/UTC")},
2122
// Wifi Settings
2223
{"ssid", JsonSetting("")},
@@ -59,6 +60,7 @@ void setup() {
5960

6061
if (! webServer.connectToWifi()) {
6162
webServer.startAccessPoint();
63+
webServer.enableOta();
6264
webServer.startMDNS();
6365
webServer.startWebServer();
6466

@@ -71,6 +73,7 @@ void setup() {
7173
display.writeChar('X');
7274
}
7375
} else {
76+
webServer.enableOta();
7477
webServer.startMDNS();
7578
webServer.startWebServer();
7679

@@ -100,10 +103,12 @@ void loop() {
100103
default: break;
101104
}
102105

106+
webServer.handleOta();
103107
checkConnection();
104108

105109
reconnectIfNeeded();
106110

111+
webServer.checkRebootRequired();
107112
yield();
108113
}
109114

@@ -197,10 +202,12 @@ void reconnectIfNeeded() {
197202
display.writeString("");
198203
if (! webServer.connectToWifi()) {
199204
webServer.startAccessPoint();
205+
webServer.enableOta();
200206
webServer.endMDNS();
201207
webServer.startMDNS();
202208
display.writeChar('X');
203209
} else {
210+
webServer.enableOta();
204211
webServer.endMDNS();
205212
webServer.startMDNS();
206213
display.writeString("OK");

src/SplitFlapWebServer.cpp

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
#endif
1515

1616
SplitFlapWebServer::SplitFlapWebServer(JsonSettings &settings)
17-
: settings(settings), server(80), multiWordDelay(1000), attemptReconnect(false), multiWordCurrentIndex(0),
18-
numMultiWords(0), wifiCheckInterval(1000), connectionMode(0), checkDateInterval(250), centering(1) {
17+
: settings(settings), server(80), multiWordDelay(1000), rebootRequired(false), attemptReconnect(false),
18+
multiWordCurrentIndex(0), numMultiWords(0), wifiCheckInterval(1000), connectionMode(0), checkDateInterval(250),
19+
centering(1) {
1920
lastSwitchMultiTime = millis();
2021
}
2122

@@ -180,6 +181,63 @@ bool SplitFlapWebServer::loadWiFiCredentials() {
180181
return false; // Return false if no credentials were found
181182
}
182183

184+
void SplitFlapWebServer::checkRebootRequired() {
185+
if (rebootRequired) {
186+
Serial.println("Reboot required. Restarting...");
187+
delay(1000);
188+
ESP.restart();
189+
}
190+
}
191+
192+
void SplitFlapWebServer::handleOta() {
193+
ArduinoOTA.handle();
194+
}
195+
void SplitFlapWebServer::enableOta() {
196+
// Skip OTA initialisation if no password is set
197+
if (settings.getString("otaPass") == "") {
198+
return;
199+
}
200+
201+
ArduinoOTA.setHostname(settings.getString("mdns").c_str()); // otherwise mdns name gets overwritten with default
202+
ArduinoOTA.setPassword(settings.getString("otaPass").c_str());
203+
204+
ArduinoOTA
205+
.onStart([]() {
206+
String type;
207+
if (ArduinoOTA.getCommand() == U_FLASH) {
208+
type = "sketch";
209+
} else { // U_LITTLEFS
210+
type = "filesystem";
211+
LittleFS.end(); // Unmount the filesystem before update
212+
}
213+
Serial.println("Start updating " + type);
214+
})
215+
.onEnd([]() {
216+
Serial.println("\nEnd");
217+
LittleFS.begin(); // Remount filesystem
218+
})
219+
.onProgress([](unsigned int progress, unsigned int total) {
220+
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
221+
}).onError([](ota_error_t error) {
222+
Serial.printf("Error[%u]: ", error);
223+
LittleFS.begin(); // Remount filesystem
224+
if (error == OTA_AUTH_ERROR) {
225+
Serial.println("Auth Failed");
226+
} else if (error == OTA_BEGIN_ERROR) {
227+
Serial.println("Begin Failed");
228+
} else if (error == OTA_CONNECT_ERROR) {
229+
Serial.println("Connect Failed");
230+
} else if (error == OTA_RECEIVE_ERROR) {
231+
Serial.println("Receive Failed");
232+
} else if (error == OTA_END_ERROR) {
233+
Serial.println("End Failed");
234+
}
235+
});
236+
237+
ArduinoOTA.begin();
238+
Serial.println("OTA Initialized");
239+
}
240+
183241
bool SplitFlapWebServer::connectToWifi() {
184242
if (loadWiFiCredentials()) {
185243
unsigned long startAttemptTime = millis();
@@ -296,6 +354,7 @@ void SplitFlapWebServer::startWebServer() {
296354
Serial.println("Received settings update request");
297355
Serial.println(json.as<String>());
298356

357+
bool rebootRequired = false;
299358
bool reconnect = false;
300359
JsonDocument response;
301360
response["message"] = "Settings saved successfully!";
@@ -308,6 +367,11 @@ void SplitFlapWebServer::startWebServer() {
308367
json["ssid"].as<String>() + " network";
309368
}
310369

370+
if (json["otaPass"].is<String>() && json["otaPass"].as<String>() != settings.getString("otaPass")) {
371+
rebootRequired = true; // OTA password change can only be applied by rebooting
372+
response["message"] = "Settings updated successfully, OTA Password has changed. Rebooting...";
373+
}
374+
311375
if (json["mdns"].is<String>() && json["mdns"].as<String>() != settings.getString("mdns")) {
312376
reconnect = true;
313377
response["message"] =
@@ -338,6 +402,7 @@ void SplitFlapWebServer::startWebServer() {
338402

339403
request->send(200, "application/json", response.as<String>());
340404

405+
this->rebootRequired = rebootRequired;
341406
this->attemptReconnect = reconnect;
342407
}
343408
));

src/SplitFlapWebServer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <Arduino.h>
66
#include <ArduinoJson.h>
7+
#include <ArduinoOTA.h>
78
#include <ESPAsyncWebServer.h>
89
#include <ESPmDNS.h>
910
#include <LittleFS.h>
@@ -15,6 +16,7 @@ class SplitFlapWebServer {
1516
SplitFlapWebServer(JsonSettings &settings);
1617
void init();
1718
void setTimezone();
19+
void checkRebootRequired();
1820

1921
// Wifi Connectivity
2022
bool loadWiFiCredentials();
@@ -24,6 +26,8 @@ class SplitFlapWebServer {
2426
void startWebServer();
2527
void endMDNS();
2628
void startMDNS();
29+
void enableOta();
30+
void handleOta();
2731
void startAccessPoint();
2832
void checkWiFi();
2933
unsigned long getLastCheckWifiTime() { return lastCheckWifiTime; }
@@ -85,6 +89,7 @@ class SplitFlapWebServer {
8589
String inputString; // latest single input from user
8690
String writtenString; // string for whatever is currently written to the display
8791

92+
bool rebootRequired;
8893
bool attemptReconnect;
8994
unsigned long lastCheckWifiTime;
9095
int wifiCheckInterval;

src/web/settings.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,22 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
6868
x-text="errors.message"
6969
></div>
7070

71+
<label for="otaPass" class="block text-left text-lg mt-4"
72+
>OTA Password (will restart)</label
73+
>
74+
<input
75+
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
76+
type="text"
77+
x-model="settings.otaPass"
78+
placeholder="Enter OTA Password"
79+
/>
80+
<div
81+
class="w-full p-3 mt-2 text-sm text-white bg-red-500 rounded-md"
82+
x-cloak
83+
x-show="errors.key === 'otaPass'"
84+
x-text="errors.message"
85+
></div>
86+
7187
<label for="timezone" class="block text-left text-lg mt-4"
7288
>Timezone</label
7389
>

0 commit comments

Comments
 (0)