Skip to content

Commit b3a4e2c

Browse files
Consolidated OTA updates, fixed webserver restarting commands
1 parent c142010 commit b3a4e2c

File tree

4 files changed

+79
-73
lines changed

4 files changed

+79
-73
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# ESP LED Matrix Display
2-
2+
![GitHub](https://img.shields.io/github/license/TheLogicMaster/ESP-LED-Matrix-Display)
33
## About
44
This project is intended to provide an graphically customizeable and functional firmware to drive LED Matrix panels using
55
ESP8266 based microcontrollers. It's powered by the [PxMatrix](https://github.com/2dom/PxMatrix) driver and is
@@ -18,20 +18,22 @@ is a placeholder until something clever comes along.
1818
* OTA Updating
1919
* Raw configuration editor
2020
* YouTube subscriber counter
21+
* Tetris Animation font
2122

2223
## Demo
24+
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/TheLogicMaster/ESP-LED-Matrix-Display/Build%20Demo)
25+
2326
A demo of the web dashboard is available [Here](https://thelogicmaster.github.io/ESP-LED-Matrix-Display/). Example data
2427
is provided in place of the normal display API calls. Aside from configuration and image saving, the dashboard should
2528
be fully functional.
2629

2730
## Hardware
28-
ESP32 and ESP8266 are now fully supported, though the sunrise/weather features aren't availible to ESP32 yet, since the
29-
WeatherStation library isn't ESP32 friendly at the moment. 4MB of RAM is required to use both the web dashboard
31+
ESP32 and ESP8266 are now fully supported. 4MB of RAM is required to use both the web dashboard
3032
and OTA updates. This was developed primarily on a P3 32x64 panel, but other sizes should be supported. Wiring is required to
3133
be like [this](https://github.com/2dom/PxMatrix#set-up-and-cabling). A photoresistor/resistor is supported on pin
3234
A0 in [this](https://www.instructables.com/id/NodeMCU-With-LDR/) configuration to control the display brightness. There
3335
are pre-configured Platformio environments for Wemos D1 Mini, NodeMCU V2, NodeMCU 32S, and the Wemos D1 Mini Lite. Pre-built update
34-
binaries are availible for D1 Mini and NodeMCU V2. The D1 Mini Lite has 1MB of flash memory, so OTA updates are not
36+
binaries are availible for D1 Mini, NodeMCU 32S, and NodeMCU V2. The D1 Mini Lite has 1MB of flash memory, so OTA updates are not
3537
supported and features like HTTPS and built-in images are disabled.
3638

3739
## Installation

led-matrix-dashboard/src/components/Controls.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export default {
2424
'Restart Display': {url: '/restart', tooltip: 'Restart the display'},
2525
'Delete all Images': {url: '/deleteAllImages', tooltip: 'Delete all custom images'},
2626
'Reset Display Configuration': {url: '/resetConfiguration', tooltip: 'Reset display configuration file'},
27-
'Factory Reset Display': {url: '/factoryReset', tooltip: 'Reset display configuration and erase custom images'}
27+
'Factory Reset Display': {url: '/factoryReset', tooltip: 'Reset display configuration and erase custom images'},
28+
'Format Filesystem': {url: '/formatFilesystem', tooltip: 'Format display filesystem'}
2829
}
2930
}),
3031
methods: {

led-matrix-dashboard/src/components/OTAUpdate.vue

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,6 @@ export default {
7575
if (files.length > 0)
7676
this.binary = files[0]
7777
},
78-
async abortUpdate() {
79-
try {
80-
this.$axios.get('/abortUpdate').then()
81-
return true
82-
} catch (error) {
83-
console.error(error)
84-
return false
85-
}
86-
},
8778
async update() {
8879
this.updateDialog = false
8980
let binary = this.binary
@@ -128,8 +119,6 @@ export default {
128119
}
129120
130121
try {
131-
await this.$axios.post('/beginUpdate')
132-
await this.sleep(1000)
133122
let form = new FormData()
134123
form.append(this.selected, binary, this.selected)
135124
this.progress = 0
@@ -144,15 +133,12 @@ export default {
144133
if (response.data.includes('OK')) {
145134
this.info('Success', 'Successfully updated display', true)
146135
this.waitForPromiseSuccess(this.getStats).then()
147-
} else {
136+
} else
148137
this.error('Error', 'Failed to update display: ' + response.data)
149-
await this.waitForPromiseSuccess(this.abortUpdate)
150-
}
151138
} catch (error) {
152139
this.error('Error', 'Failed to update display: ' + error)
153140
this.progress = -1;
154141
console.error(error)
155-
await this.waitForPromiseSuccess(this.abortUpdate)
156142
}
157143
},
158144
onUploadProgress(event) {

led-matrix-display/main.cpp

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
#define SHAPE_CIRCLE 2
4343
#define SHAPE_PIXEL 3
4444
#define STATE_NORMAL 0
45-
#define STATE_UPDATING 1
45+
#define STATE_DISABLED 1
4646

4747
// Configuration
4848
#define ROUNDED_RECT_RADIUS 3 // The radius to use for rounded rectangles
@@ -161,8 +161,6 @@
161161
#include <WiFiClientSecure.h>
162162
#include <HTTPClient.h>
163163
#include <WiFiMulti.h>
164-
//#define sint16_t signed short
165-
//#define sint32_t signed long
166164
#else // ESP8266 Specific
167165
#include <ESPAsyncTCP.h>
168166
#include <LittleFS.h>
@@ -187,7 +185,7 @@
187185
#include "TinyIcons.h"
188186
#endif
189187
#if USE_SUNRISE
190-
#include <SunMoonCalc.h>
188+
#include "SunMoonCalc.h"
191189
#endif
192190
#if USE_TETRIS
193191
#include <TetrisMatrixDraw.h>
@@ -321,6 +319,8 @@ bool needsConfig; // If a configuration is needed
321319
bool needsUpdate; // If a full update is needed
322320
bool needsRestart; // If a system reboot is needed
323321
bool needsTimezone; // If the timezone needs to be set still
322+
bool needsFormat; // If a FS format is needed
323+
bool needsFactoryReset; // If a factory reset is needed
324324
uint8_t theHour12;
325325
uint8_t theHour;
326326
uint8_t theMinute;
@@ -335,6 +335,7 @@ time_t sunSetTime;
335335
uint8_t scanPattern;
336336
uint8_t muxPattern;
337337
uint8_t muxDelay;
338+
AsyncClient* finalClient; // Client to wait for closing before restart
338339

339340
#ifdef ESP32
340341
#define P_LAT 22
@@ -433,6 +434,7 @@ uint16_t parseHexColorString(std::string s) {
433434
}
434435

435436
void closeFiles() {
437+
DEBUG("Closing widget files\n");
436438
for (uint i = 0; i < widgets.size(); i++)
437439
widgets[i].file.close();
438440
}
@@ -650,12 +652,22 @@ bool getConfig() {
650652

651653
#pragma region Web Server
652654

655+
void restartAfterDisconnect(AsyncWebServerRequest *request) {
656+
needsRestart = true;
657+
finalClient = request->client();
658+
}
659+
653660
void closeConnection(AsyncWebServerRequest *request) {
654661
AsyncWebServerResponse *response = request->beginResponse(200);
655662
response->addHeader("Connection", "close");
656663
request->send(response);
657664
}
658665

666+
void closeAndRestart(AsyncWebServerRequest *request) {
667+
closeConnection(request);
668+
restartAfterDisconnect(request);
669+
}
670+
659671
void onStartAccessPoint(AsyncWiFiManager *wiFiManager) {
660672
Serial.println(F("Entering AP config mode"));
661673
display.fillScreen(BLUE);
@@ -681,15 +693,6 @@ String getContentType(String filename) {
681693
return F("text/plain");
682694
}
683695

684-
int getHeaderCacheVersion(AsyncWebServerRequest *request) {
685-
if (!request->hasHeader(F("If-None-Match")))
686-
return -1;
687-
for (int i = 0; i < request->headers(); i++ )
688-
if (request->headerName(i).equals(F("If-None-Match")))
689-
return request->header(i).toInt();
690-
return -1;
691-
}
692-
693696
bool sendFile(AsyncWebServerRequest *request, String path, bool cache = true) {
694697
if (path.endsWith(F("/")))
695698
return false;
@@ -794,24 +797,7 @@ void serveNotFound(AsyncWebServerRequest *request) {
794797
}
795798

796799
void serveRestart(AsyncWebServerRequest *request) {
797-
closeConnection(request);
798-
needsRestart = true;
799-
}
800-
801-
// Todo: Replace start and abort OTA functions with direct update functions
802-
void startOTA(AsyncWebServerRequest *request) {
803-
state = STATE_UPDATING;
804-
disableDisplay = true;
805-
LittleFS.end();
806-
request->send(200);
807-
}
808-
809-
void abortOTA(AsyncWebServerRequest *request) {
810-
state = STATE_NORMAL;
811-
disableDisplay = false;
812-
LittleFS.begin();
813-
request->send(200);
814-
needsUpdate = true;
800+
closeAndRestart(request);
815801
}
816802

817803
void serveStats(AsyncWebServerRequest *request) {
@@ -977,8 +963,7 @@ void serveImageData(AsyncWebServerRequest *request) {
977963
}
978964

979965
void serveUploadImage(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
980-
for (uint i = 0; i < widgets.size(); i++)
981-
widgets[i].file.close();
966+
closeFiles();
982967
String imageFilename = "/images/" + request->arg("name");
983968

984969
if (!index) {
@@ -1027,6 +1012,7 @@ void serveImage(AsyncWebServerRequest *request) {
10271012
}
10281013

10291014
void serveRenameImage(AsyncWebServerRequest *request) {
1015+
closeFiles();
10301016
String name = request->arg(F("name"));
10311017
String newName = request->arg(F("newName"));
10321018
if (!LittleFS.exists("/images/" + name)) {
@@ -1045,8 +1031,7 @@ void serveRenameImage(AsyncWebServerRequest *request) {
10451031
}
10461032

10471033
void serveDeleteImage(AsyncWebServerRequest *request) {
1048-
for (uint i = 0; i < widgets.size(); i++)
1049-
widgets[i].file.close();
1034+
closeFiles();
10501035
std::string name = request->arg(F("name")).c_str();
10511036
std::map <std::string, FSImage> fsImageData = getFSImageData();
10521037
fsImageData.erase(name);
@@ -1058,8 +1043,8 @@ void serveDeleteImage(AsyncWebServerRequest *request) {
10581043
}
10591044

10601045
void deleteAllImages() {
1061-
for (uint i = 0; i < widgets.size(); i++)
1062-
widgets[i].file.close();
1046+
DEBUG("Deleting all custom images\n");
1047+
closeFiles();
10631048
writeDefaultImageData();
10641049
Dir dir = getImageDir();
10651050
LittleFS.rmdir(F("/images"));
@@ -1081,33 +1066,43 @@ void serveDeleteAllImages(AsyncWebServerRequest *request) {
10811066
}
10821067

10831068
void serveResetConfiguration(AsyncWebServerRequest *request) {
1069+
closeFiles();
10841070
writeDefaultConfig();
1085-
closeConnection(request);
1086-
needsRestart = true;
1071+
closeAndRestart(request);
10871072
}
10881073

10891074
void serveOK(AsyncWebServerRequest *request) {
10901075
request->send(200);
10911076
}
10921077

10931078
void serveFactoryReset(AsyncWebServerRequest *request) {
1094-
writeDefaultConfig();
1095-
deleteAllImages();
1096-
AsyncWiFiManager wifiManager(&server, &dnsServer);
1097-
wifiManager.resetSettings();
1098-
needsRestart = true;
1099-
closeConnection(request);
1079+
needsFactoryReset = true;
1080+
closeAndRestart(request);
1081+
}
1082+
1083+
void serveFormatFilesystem(AsyncWebServerRequest *request) {
1084+
needsFormat = true;
1085+
closeAndRestart(request);
11001086
}
11011087

11021088
void serveOTA(AsyncWebServerRequest *request) {
1103-
needsRestart = !Update.hasError();
1104-
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", needsRestart ? "OK" : String(Update.getError(), 10));
1089+
bool success = !Update.hasError();
1090+
if (!success) {
1091+
state = STATE_NORMAL;
1092+
disableDisplay = false;
1093+
LittleFS.begin();
1094+
needsUpdate = true;
1095+
} else
1096+
restartAfterDisconnect(request);
1097+
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", success ? "OK" : String(Update.getError(), 10));
11051098
response->addHeader("Connection", "close");
11061099
request->send(response);
11071100
}
11081101

11091102
void serveOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
11101103
if (!index) {
1104+
state = STATE_DISABLED;
1105+
disableDisplay = true;
11111106
#ifdef ESP8266
11121107
Update.runAsync(true);
11131108
#endif
@@ -1130,7 +1125,7 @@ void serveOTAUpload(AsyncWebServerRequest *request, String filename, size_t inde
11301125
Update.printError(Serial);
11311126
if (final) {
11321127
if(Update.end(true))
1133-
Serial.printf("Update Success: %uB\n", index+len);
1128+
Serial.printf("Update Success: %uB\n", index + len);
11341129
else
11351130
Update.printError(Serial);
11361131
}
@@ -1170,8 +1165,6 @@ void setupWebserver() {
11701165
server.addHandler(new AsyncCallbackJsonWebHandler("/config", serveSaveConfig, MAX_CONFIG_FILE_SIZE));
11711166
server.on("/update", HTTP_POST, serveOTA, serveOTAUpload);
11721167
server.on("/recovery", HTTP_GET, serveRecovery);
1173-
server.on("/abortUpdate", abortOTA);
1174-
server.on("/beginUpdate", startOTA);
11751168
server.on("/fullRefresh", serveFullUpdate);
11761169
server.on("/forceUpdate", serveForceUpdate);
11771170
server.on("/refreshWeather", serveWeatherUpdate);
@@ -1187,6 +1180,7 @@ void setupWebserver() {
11871180
server.on("/deleteAllImages", serveDeleteAllImages);
11881181
server.on("/renameImage", serveRenameImage);
11891182
server.on("/factoryReset", serveFactoryReset);
1183+
server.on("/formatFilesystem", serveFormatFilesystem);
11901184
server.on("/resetConfiguration", serveResetConfiguration);
11911185
server.serveStatic("/", LittleFS, "/", CACHE_DASHBOARD ? "no-cache" : NULL);
11921186
server.onNotFound(serveNotFound);
@@ -2026,6 +2020,29 @@ void updateArduinoOTA() {
20262020
#endif
20272021
}
20282022

2023+
void updateRestartFlags() {
2024+
if (needsRestart && (!finalClient || finalClient->disconnected())) {
2025+
if (needsFormat) {
2026+
closeFiles();
2027+
LittleFS.end();
2028+
state = STATE_DISABLED;
2029+
disableDisplay = true;
2030+
DEBUG("Formatting filesystem\n");
2031+
LittleFS.format();
2032+
}
2033+
2034+
if (needsFactoryReset) {
2035+
writeDefaultConfig();
2036+
deleteAllImages();
2037+
DEBUG("Erasing WiFi settings\n");
2038+
AsyncWiFiManager wifiManager(&server, &dnsServer);
2039+
wifiManager.resetSettings();
2040+
}
2041+
2042+
ESP.restart();
2043+
}
2044+
}
2045+
20292046
void setup() {
20302047
Serial.begin(115200);
20312048
Serial.println();
@@ -2124,6 +2141,7 @@ void setup() {
21242141
display.showBuffer();
21252142
needsConfig = true;
21262143
while (needsConfig) {
2144+
updateRestartFlags();
21272145
updateArduinoOTA();
21282146
updateTime();
21292147
yield();
@@ -2139,13 +2157,12 @@ void setup() {
21392157
}
21402158

21412159
void loop() {
2142-
if (needsRestart)
2143-
ESP.restart();
2160+
updateRestartFlags();
21442161

21452162
switch (state) {
21462163
default:
21472164
Serial.printf("Unknown state: %i/n", state);
2148-
case STATE_UPDATING:
2165+
case STATE_DISABLED:
21492166
break;
21502167
case STATE_NORMAL:
21512168
updateTime();

0 commit comments

Comments
 (0)