Public Access
- 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
100 lines
3.3 KiB
C++
100 lines
3.3 KiB
C++
#include "wifi_controller.h"
|
|
|
|
namespace roro {
|
|
|
|
namespace {
|
|
const uint32_t kRetryDelaysMs[] = {10000, 30000, 60000};
|
|
const int kRetrySteps = sizeof(kRetryDelaysMs) / sizeof(kRetryDelaysMs[0]);
|
|
} // namespace
|
|
|
|
WifiController::Step WifiController::wait(uint32_t nowMs) {
|
|
state_ = State::Waiting;
|
|
retryAtMs_ = nowMs + kRetryDelaysMs[retries_ < kRetrySteps ? retries_ : kRetrySteps - 1];
|
|
retries_++;
|
|
return act(Action::None);
|
|
}
|
|
|
|
WifiController::Step WifiController::update(uint32_t nowMs, bool enabled) {
|
|
if (state_ == State::Monitoring) return act(Action::None);
|
|
if (!enabled || saved_.count() == 0) { // nothing to join: keep the radio off
|
|
if (state_ == State::Off) return act(Action::None);
|
|
state_ = State::Off;
|
|
ssid_.clear();
|
|
return act(Action::RadioOff);
|
|
}
|
|
switch (state_) {
|
|
case State::Off:
|
|
state_ = State::Scanning;
|
|
return act(Action::StartScan);
|
|
case State::Waiting:
|
|
if (static_cast<int32_t>(nowMs - retryAtMs_) < 0) return act(Action::None);
|
|
state_ = State::Scanning;
|
|
return act(Action::StartScan);
|
|
case State::Connecting:
|
|
if (nowMs - sinceMs_ < kConnectTimeoutMs) return act(Action::None);
|
|
wait(nowMs);
|
|
return act(Action::Disconnect);
|
|
default:
|
|
return act(Action::None);
|
|
}
|
|
}
|
|
|
|
WifiController::Step WifiController::scanDone(const std::vector<ScanResult>& results, uint32_t nowMs) {
|
|
if (state_ != State::Scanning) return act(Action::None);
|
|
const ScanResult* best = nullptr;
|
|
for (auto& r : results)
|
|
if (saved_.find(r.ssid) && (!best || r.rssi > best->rssi)) best = &r;
|
|
const SavedNetwork* net = best ? saved_.find(best->ssid) : nullptr;
|
|
if (!net) {
|
|
// Hidden networks never appear in scans: try them one at a time.
|
|
for (int i = 0; i < saved_.count() && !net; i++) {
|
|
const SavedNetwork& candidate = saved_.at((nextHidden_ + i) % saved_.count());
|
|
if (candidate.hidden) {
|
|
net = &candidate;
|
|
nextHidden_ = (nextHidden_ + i + 1) % saved_.count();
|
|
}
|
|
}
|
|
}
|
|
if (!net) return wait(nowMs);
|
|
|
|
state_ = State::Connecting;
|
|
ssid_ = net->ssid;
|
|
sinceMs_ = nowMs;
|
|
return Step{Action::Connect, net->ssid, net->password};
|
|
}
|
|
|
|
WifiController::Step WifiController::connected(uint32_t) {
|
|
if (state_ != State::Connecting) return act(Action::None);
|
|
state_ = State::Connected;
|
|
retries_ = 0;
|
|
return act(Action::None);
|
|
}
|
|
|
|
WifiController::Step WifiController::disconnected(uint32_t nowMs) {
|
|
if (state_ == State::Connected) {
|
|
state_ = State::Scanning;
|
|
return act(Action::StartScan);
|
|
}
|
|
if (state_ == State::Connecting) return wait(nowMs); // e.g. wrong password
|
|
return act(Action::None);
|
|
}
|
|
|
|
WifiController::Step WifiController::requestMonitor() {
|
|
state_ = State::Monitoring;
|
|
ssid_.clear();
|
|
return act(Action::StartMonitor);
|
|
}
|
|
|
|
WifiController::Step WifiController::releaseMonitor(bool enabled) {
|
|
if (state_ != State::Monitoring) return act(Action::None);
|
|
retries_ = 0;
|
|
if (enabled) {
|
|
state_ = State::Scanning;
|
|
return act(Action::StopMonitorAndScan);
|
|
}
|
|
state_ = State::Off;
|
|
return act(Action::StopMonitorAndOff);
|
|
}
|
|
|
|
} // namespace roro
|