Skip to content

Commit 47e0c28

Browse files
authored
WiFi Over the air update (#21)
* Refactoring some of the Button and Page rendering to allow for a stack of modal page drawing * Arduino support for etlcpp library Not required for PlatformIO, but this adds support for Arduino IDE builds once the etlcpp library has been added. Test Plan (using Arduino IDE): ``` Sketch uses 612976 bytes (46%) of program storage space. Maximum is 1310720 bytes. Global variables use 91304 bytes (27%) of dynamic memory, leaving 236376 bytes for local variables. Maximum is 327680 bytes. ``` * Fixed build system failes. Added a build flag to PlatformIO to know when we're building with that. Added a new library to the Arduino github action so it can check out that library.. I still maintain that keeping this working in two build environments is making this project harder to maintain already :\ * - Added WiFi OTA and supporting tools * Added QR Code library to Codebase. QR Code library was not building with Arduino. Followed the recommended approach to copy/paste it in and rename it. It appears to be MIT license, so I think this is good. * [Feature] WiFi Manager * BugFix that may cause exception faults on two pages sharing no sub-menus * Added WiFi Manager to be able to configure WiFi without the needed for an app under "Manual" mode.
1 parent b336127 commit 47e0c28

17 files changed

Lines changed: 1707 additions & 34 deletions

.github/workflows/arduino.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ jobs:
3333
version: 1.2.12
3434
- name: AHT20
3535
version: 1.0.1
36+
- name: "Embedded Template Library ETL"
37+
version: 20.39.4
38+
- name: "WiFiManager"
39+
version: 2.0.17

platformio.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,24 @@ board_upload.maximum_size = 8388608
1414
build_flags =
1515
-D ARDUINO_USB_MODE=1
1616
-D ARDUINO_USB_CDC_ON_BOOT=1 # Required for USB Serial on boot (for debugging)
17+
-D PIO_BUILD_SYSTEM
1718

1819
# ~3.2Mb for A and B partitions to allow for updates
1920
board_build.partitions = default_8MB.csv
2021

2122
lib_extra_dirs = src/libraries
2223

24+
lib_deps =
25+
; STL like library for Arduino platform and embedded systems
26+
etlcpp/Embedded Template Library@^20.39.4
27+
28+
; WiFi Manager for configuring WiFi credentials over captive portal
29+
WiFiManager
30+
31+
# build_type = debug # Might be useful when looking at stack traces
32+
# Used to take exception traces and decode them to meaningful errors
33+
monitor_filters = esp32_exception_decoder
34+
2335
[platformio]
2436
src_dir = src/vario
2537

src/vario/PageMenuSystem.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "settings.h"
99
#include "power.h"
1010
#include "speaker.h"
11+
#include "PageMenuSystemWifi.h"
1112

1213

1314
enum system_menu_items {
@@ -143,7 +144,12 @@ void SystemMenuPage::setting_change(Button dir, ButtonState state, uint8_t count
143144
if (state == RELEASED) settings_toggleBoolOnOff(&ECO_MODE);
144145
break;
145146
case cursor_system_wifi:
146-
if (state == RELEASED) {}
147+
if (state != RELEASED) break;
148+
149+
// User has selected WiFi, show this page
150+
static PageMenuSystemWifi wifiPage;
151+
push_page(&wifiPage);
152+
redraw = true;
147153
break;
148154
case cursor_system_bluetooth:
149155
if (state == RELEASED) {}

src/vario/PageMenuSystem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <Arduino.h>
55
#include "menu_page.h"
6+
#include "PageMenuSystemWifi.h"
67

78
class SystemMenuPage : public SettingsMenuPage {
89
public:

src/vario/PageMenuSystemWifi.cpp

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#include "PageMenuSystemWifi.h"
2+
3+
#include "display.h"
4+
#include "fonts.h"
5+
#include "ota.h"
6+
#include "version.h"
7+
#include "WiFi.h"
8+
9+
/**************************
10+
* PageMenuSystemWifi (Top Level)
11+
*/
12+
void PageMenuSystemWifi::setting_change(Button dir, ButtonState state,
13+
uint8_t count) {
14+
if (state != RELEASED) return;
15+
16+
// Handle updating items
17+
switch (cursor_position) {
18+
case cursor_system_wifi_setup:
19+
push_page(&page_wifi_setup);
20+
break;
21+
case cursor_system_wifi_update:
22+
push_page(&page_wifi_update);
23+
break;
24+
}
25+
26+
// Call the parent class to handle the back button
27+
SimpleSettingsMenuPage::setting_change(dir, state, count);
28+
}
29+
30+
void PageMenuSystemWifi::draw_menu_input(int8_t cursor_position) {
31+
String ret((char)126); // Enter character by default
32+
switch (cursor_position) {
33+
case cursor_system_wifi_setup:
34+
if (wifi_state == WifiState::SMART_CONFIG_WAITING) {
35+
// Animation to show we're waiting for SmartConfig
36+
ret = ".";
37+
for (int i = 0; i < (millis() / 1000) % 3; i++) {
38+
ret += ".";
39+
}
40+
} else if (WiFi.status() == WL_CONNECTED) {
41+
ret = "*"; // Connected... Probably need a checkmark or
42+
// something
43+
}
44+
break;
45+
46+
case cursor_system_wifi_update:
47+
break;
48+
}
49+
50+
u8g2.print(ret);
51+
}
52+
53+
/**************************
54+
* PageMenuSystemWifiSetup (sub-page)
55+
*/
56+
void PageMenuSystemWifiSetup::shown() {
57+
SimpleSettingsMenuPage::shown();
58+
59+
if (WiFi.status() == WL_CONNECTED) {
60+
// If we're already connected, don't do anything
61+
*wifi_state = WifiState::CONNECTED;
62+
pop_page();
63+
return;
64+
}
65+
66+
// Start the WiFi process
67+
*wifi_state = WifiState::SMART_CONFIG_WAITING;
68+
WiFi.mode(WIFI_AP_STA);
69+
WiFi.beginSmartConfig();
70+
}
71+
72+
void PageMenuSystemWifiSetup::setting_change(Button dir, ButtonState state,
73+
uint8_t count) {
74+
// Call the parent class to handle the back button
75+
SimpleSettingsMenuPage::setting_change(dir, state, count);
76+
77+
if (state != RELEASED) return;
78+
79+
// Handle updating items
80+
switch (cursor_position) {
81+
case cursor_system_wifi_setup_iphone:
82+
push_page(&qr_iphone);
83+
break;
84+
case cursor_system_wifi_setup_android:
85+
push_page(&qr_android);
86+
break;
87+
case cursor_system_wifi_setup_manual:
88+
push_page(&manual);
89+
break;
90+
}
91+
}
92+
93+
void PageMenuSystemWifiSetup::loop() {
94+
// Check if we're connected, if so, update the state
95+
// and close the dialog
96+
if (WiFi.status() == WL_CONNECTED) {
97+
*wifi_state = WifiState::CONNECTED;
98+
pop_page();
99+
}
100+
}
101+
102+
/**************************
103+
* PageMenuSystemWifiManualSetup (sub-page)
104+
*/
105+
void PageMenuSystemWifiManualSetup::shown() {
106+
SimpleSettingsMenuPage::shown();
107+
WiFi.mode(WIFI_STA);
108+
wm.setConfigPortalBlocking(false);
109+
wm.autoConnect("Leaf");
110+
wm.setConfigPortalTimeout(60);
111+
}
112+
113+
void PageMenuSystemWifiManualSetup::loop() {
114+
// If we're connected, close the page
115+
if (WiFi.status() == WL_CONNECTED) {
116+
pop_page();
117+
return;
118+
}
119+
wm.process();
120+
}
121+
122+
void PageMenuSystemWifiManualSetup::draw_extra() {
123+
u8g2.setFont(leaf_6x12);
124+
auto y = 40;
125+
const auto OFFSET = 14; // Font is 10px high, allow for margin
126+
u8g2.setCursor(2, y);
127+
128+
// Instruction Page
129+
const char* lines[] = {
130+
"Join WiFi",
131+
"Network Called",
132+
"Leaf"
133+
};
134+
135+
for (auto line : lines) {
136+
u8g2.setCursor(2, y);
137+
u8g2.print(line);
138+
y += OFFSET;
139+
}
140+
}
141+
142+
143+
/**************************
144+
* PageMenuSystemWifiUpdate (sub-page)
145+
*/
146+
void PageMenuSystemWifiUpdate::shown() {
147+
SimpleSettingsMenuPage::shown();
148+
149+
// Reset the WiFi module into a disconnected
150+
// TODO: Only do this if not already connected
151+
WiFi.mode(WIFI_STA);
152+
WiFi.begin();
153+
*wifi_state = WifiState::CONNECTING;
154+
155+
log_lines.clear();
156+
log_lines.push_back("* Currently running: ");
157+
log_lines.push_back((String) " " + VERSION);
158+
log_lines.push_back("* Connecting to WiFi...");
159+
}
160+
161+
void PageMenuSystemWifiUpdate::draw_extra() {
162+
// Draw the current log
163+
u8g2.setFont(leaf_5h);
164+
auto y = 40;
165+
const auto OFFSET = 7; // Font is 5px high, allow for margin
166+
u8g2.setCursor(2, y);
167+
168+
for (auto line : log_lines) {
169+
u8g2.setCursor(2, y);
170+
u8g2.print(line);
171+
y += OFFSET;
172+
}
173+
}
174+
175+
void PageMenuSystemWifiUpdate::loop() {
176+
// Update the state of the OTA Updater
177+
switch (*wifi_state) {
178+
case WifiState::CONNECTING:
179+
if (WiFi.status() == WL_CONNECTED) {
180+
log_lines.push_back("* Checking for updates...");
181+
*wifi_state = WifiState::OTA_CHECKING_VERSION;
182+
}
183+
break;
184+
case WifiState::OTA_CHECKING_VERSION: {
185+
String latest_version;
186+
try {
187+
latest_version = getLatestVersion();
188+
} catch (const std::runtime_error& e) {
189+
log_lines.push_back("* Error checking for updates");
190+
log_lines.push_back("* ");
191+
log_lines.push_back(e.what());
192+
*wifi_state = WifiState::ERROR;
193+
break;
194+
}
195+
if (latest_version == VERSION) {
196+
log_lines.push_back("* Up to date!");
197+
*wifi_state = WifiState::OTA_UP_TO_DATE;
198+
} else {
199+
log_lines.push_back("* New version available!");
200+
log_lines.push_back("* Updating to: ");
201+
log_lines.push_back((String) " " + latest_version);
202+
log_lines.push_back("(this will take a while)");
203+
log_lines.push_back("* Device will reboot");
204+
log_lines.push_back(" when done");
205+
*wifi_state = WifiState::OTA_UPDATING;
206+
}
207+
} break;
208+
case WifiState::OTA_UPDATING:
209+
try {
210+
// TODO: Enable this
211+
PerformOTAUpdate();
212+
} catch (const std::runtime_error& e) {
213+
log_lines.push_back("* Error updating firmware");
214+
log_lines.push_back("* ");
215+
log_lines.push_back(e.what());
216+
*wifi_state = WifiState::ERROR;
217+
}
218+
break;
219+
}
220+
}

0 commit comments

Comments
 (0)