M1 step 2: Wi-Fi Service, Saved Networks, NTP, Settings page

- lib/wifi (host-tested): SavedNetworks (up to 8, validated, hidden
  flag, persisted) and WifiController (joins the strongest Saved
  Network, tries hidden ones in turn, backoff 10/30/60 s, connect
  timeout, Monitoring override, radio off without Saved Networks)
- WifiService carries out the controller's actions, sets the EU
  country code, and syncs the Clock over SNTP without touching the TZ
- Wi-Fi On/Off setting; Settings > Wi-Fi page: status, add from a scan
  or a hidden network, forget
- Status Bar: W + signal bars, W? while searching, MON while monitoring
- Serial: wifi add / wifi status (replaces the step 1 heap probe)

Verified on the device: joins the test network ~5 s after boot, clock
set over NTP, ~129 KB free heap while connected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-02 14:46:07 +02:00
co-authored by Claude Opus 5.5
parent d8fe676c65
commit 1a0baf4ac7
22 changed files with 1108 additions and 48 deletions
@@ -89,6 +89,14 @@ void test_brightness_steps_with_left_right_within_bounds() {
TEST_ASSERT_EQUAL(10, f.settings.getInt(Setting::Brightness));
}
void test_wifi_is_a_page() {
Fixture f;
int wifi = f.row(SettingsMenu::Row::Wifi);
TEST_ASSERT_TRUE(wifi >= 0);
TEST_ASSERT_EQUAL(static_cast<int>(SettingsMenu::Kind::Page), static_cast<int>(f.menu.kind(wifi)));
TEST_ASSERT_EQUAL_STRING("On", f.menu.value(wifi).c_str());
}
void test_names_are_text_rows_with_their_byte_limits() {
Fixture f;
int shortName = f.row(SettingsMenu::Row::ShortName);
@@ -108,6 +116,7 @@ int main() {
RUN_TEST(test_choosing_applies_the_setting);
RUN_TEST(test_choice_that_breaks_a_rule_is_refused_with_a_reason);
RUN_TEST(test_brightness_steps_with_left_right_within_bounds);
RUN_TEST(test_wifi_is_a_page);
RUN_TEST(test_names_are_text_rows_with_their_byte_limits);
return UNITY_END();
}