Merge branch 'cursor/igiRadio-ios-app' into HEAD
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/DeviceIdentity.hpp"
|
||||
#include "core/ISecureStore.hpp"
|
||||
#include "core/WifiCredentials.hpp"
|
||||
#include "net/NetError.hpp"
|
||||
|
||||
@@ -94,8 +96,20 @@ public:
|
||||
* @brief connect — join the network described by creds.
|
||||
*
|
||||
* @dname connect
|
||||
* @param creds Validated domain credentials from ISecureStore.
|
||||
* @param hostname STA hostname / mDNS label (no .local suffix).
|
||||
* @param creds Validated domain credentials from
|
||||
* ISecureStore.
|
||||
* @param hostname STA hostname / mDNS label (no .local
|
||||
* suffix).
|
||||
* @param bleFallbackStore When set alongside bleFallbackIdentity,
|
||||
* a link lost for over a minute *after*
|
||||
* this call already returned successfully
|
||||
* starts BLE provisioning (additive,
|
||||
* STA reconnect attempts keep running) so
|
||||
* the app can reconfigure Wi-Fi without a
|
||||
* power cycle. Null skips this behaviour.
|
||||
* @param bleFallbackIdentity Device identity for the BLE fallback's
|
||||
* advertising name; must outlive this
|
||||
* StaClient when bleFallbackStore is set.
|
||||
* @return Ok on success, or NetError::StaConnectTimeout /
|
||||
* NetError::StaConnectFailed.
|
||||
* @pubstate writes connected_ on success; uses creds via Secret.
|
||||
@@ -105,7 +119,9 @@ public:
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, NetError>
|
||||
connect(const core::WifiCredentials& creds,
|
||||
std::string_view hostname = {});
|
||||
std::string_view hostname = {},
|
||||
core::ISecureStore* bleFallbackStore = nullptr,
|
||||
const core::DeviceIdentity* bleFallbackIdentity = nullptr);
|
||||
|
||||
private:
|
||||
bool connected_;
|
||||
|
||||
@@ -189,7 +189,8 @@ startStaMode(core::ISecureStore& store, tuner::TunerService& tuner,
|
||||
|
||||
StaClient sta;
|
||||
if (auto staResult =
|
||||
sta.connect(creds, deviceIdentity.hostname());
|
||||
sta.connect(creds, deviceIdentity.hostname(), &store,
|
||||
&deviceIdentity);
|
||||
!staResult) {
|
||||
ESP_LOGW(kTag, "STA connect failed for SSID %.*s — credentials kept in NVS",
|
||||
static_cast<int>(creds.ssid().value().size()),
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "net/StaClient.hpp"
|
||||
|
||||
#include "net/BleProvisioning.hpp"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
@@ -39,10 +41,20 @@ constexpr int kFailedBit = BIT1;
|
||||
constexpr int kMaxConnectRetries = 10;
|
||||
constexpr TickType_t kConnectTimeout = pdMS_TO_TICKS(45000);
|
||||
constexpr TickType_t kRetryDelay = pdMS_TO_TICKS(800);
|
||||
/** How long a post-boot link loss must persist before BLE fallback starts. */
|
||||
constexpr TickType_t kBleFallbackThreshold = pdMS_TO_TICKS(60000);
|
||||
|
||||
EventGroupHandle_t s_wifiEventGroup = nullptr;
|
||||
int s_connectRetries = 0;
|
||||
|
||||
// Set once by connect() on success when the caller opted in; used only by
|
||||
// the post-boot branch of wifiEventHandler() below (s_wifiEventGroup is
|
||||
// null there, since the bounded connect() call has already returned).
|
||||
core::ISecureStore* s_bleFallbackStore = nullptr;
|
||||
const core::DeviceIdentity* s_bleFallbackIdentity = nullptr;
|
||||
TickType_t s_disconnectedSinceTick = 0;
|
||||
bool s_bleFallbackActive = false;
|
||||
|
||||
/**
|
||||
* @brief disconnectReasonString — map ESP-IDF Wi-Fi disconnect reason codes.
|
||||
*
|
||||
@@ -136,6 +148,32 @@ void wifiEventHandler(void* arg,
|
||||
return;
|
||||
}
|
||||
|
||||
// Post-boot link loss (the bounded connect() call above already
|
||||
// succeeded once): esp_wifi_connect() below retries forever on its
|
||||
// own, which used to be the whole story -- if the AP never comes
|
||||
// back (moved, replaced, password changed), the device retried
|
||||
// silently forever with no way for the app to reach it and no way
|
||||
// for the user to reconfigure Wi-Fi short of a power cycle. After
|
||||
// a sustained ~1 minute outage, start BLE provisioning alongside
|
||||
// the ongoing reconnect attempts (does not stop them) so the app
|
||||
// can push new credentials over Bluetooth.
|
||||
if (s_disconnectedSinceTick == 0) {
|
||||
s_disconnectedSinceTick = xTaskGetTickCount();
|
||||
} else if (!s_bleFallbackActive && s_bleFallbackStore != nullptr
|
||||
&& s_bleFallbackIdentity != nullptr
|
||||
&& (xTaskGetTickCount() - s_disconnectedSinceTick)
|
||||
>= kBleFallbackThreshold) {
|
||||
ESP_LOGW(kTag, "STA link lost for over a minute — starting BLE "
|
||||
"provisioning fallback");
|
||||
if (auto bleResult = ble_provisioning::start(
|
||||
*s_bleFallbackStore, *s_bleFallbackIdentity);
|
||||
!bleResult) {
|
||||
ESP_LOGW(kTag, "BLE fallback provisioning failed to start");
|
||||
} else {
|
||||
s_bleFallbackActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGW(kTag, "STA link lost — reconnecting");
|
||||
esp_wifi_connect();
|
||||
} else if (eventBase == IP_EVENT && eventId == IP_EVENT_STA_GOT_IP) {
|
||||
@@ -145,6 +183,7 @@ void wifiEventHandler(void* arg,
|
||||
ESP_LOGI(kTag, "STA IP " IPSTR, IP2STR(&event->ip_info.ip));
|
||||
}
|
||||
applyStaLinkTuning();
|
||||
s_disconnectedSinceTick = 0;
|
||||
if (s_wifiEventGroup != nullptr) {
|
||||
xEventGroupSetBits(s_wifiEventGroup, kConnectedBit);
|
||||
}
|
||||
@@ -212,12 +251,20 @@ StaClient& StaClient::operator=(StaClient&& other) noexcept
|
||||
}
|
||||
|
||||
std::expected<void, NetError>
|
||||
StaClient::connect(const core::WifiCredentials& creds, std::string_view hostname)
|
||||
StaClient::connect(const core::WifiCredentials& creds,
|
||||
std::string_view hostname,
|
||||
core::ISecureStore* bleFallbackStore,
|
||||
const core::DeviceIdentity* bleFallbackIdentity)
|
||||
{
|
||||
if (connected_) {
|
||||
return {};
|
||||
}
|
||||
|
||||
s_bleFallbackStore = bleFallbackStore;
|
||||
s_bleFallbackIdentity = bleFallbackIdentity;
|
||||
s_disconnectedSinceTick = 0;
|
||||
s_bleFallbackActive = false;
|
||||
|
||||
std::string hostLabel;
|
||||
if (!hostname.empty()) {
|
||||
hostLabel.assign(hostname.begin(), hostname.end());
|
||||
|
||||
@@ -5,9 +5,48 @@ un suggerimento. Se qualcosa non è chiaro o sembra in conflitto con codice
|
||||
esistente, chiedi prima di improvvisare una soluzione diversa — non inventare
|
||||
comportamenti non specificati qui.
|
||||
|
||||
Dispositivo di test reale: `http://192.168.1.62` (mDNS `digiradio-CC4DB4.local`).
|
||||
Verifica ogni funzionalità contro il dispositivo vero prima di considerarla
|
||||
finita, non solo con dati mock.
|
||||
Dispositivo di test reale: `http://192.168.1.62` (mDNS **`igiradio-CC4DB4.local`**
|
||||
— vedi §0bis, è cambiato oggi). Verifica ogni funzionalità contro il
|
||||
dispositivo vero prima di considerarla finita, non solo con dati mock.
|
||||
|
||||
---
|
||||
|
||||
## 0bis. Identità dispositivo rinominata "DigiRadio" → "igiRadio" — OBBLIGATORIO
|
||||
|
||||
Oggi il firmware è stato rinominato per coerenza con il nome dell'app. Tre
|
||||
stringhe sono cambiate:
|
||||
|
||||
| Cosa | Prima | Ora |
|
||||
|---|---|---|
|
||||
| SSID WiFi di setup | `DigiRadio-<seriale>` | `igiRadio-<seriale>` |
|
||||
| Nome Bluetooth | `DigiRadio` | `igiRadio` |
|
||||
| Hostname mDNS | `digiradio-<seriale>.local` | `igiradio-<seriale>.local` |
|
||||
|
||||
**Cerca in tutto il progetto app ogni occorrenza letterale di `"DigiRadio"` o
|
||||
`"digiradio"`** usata per riconoscere/filtrare il dispositivo (discovery WiFi
|
||||
locale, scan Bluetooth, matching SSID durante il provisioning) — non solo
|
||||
testo di UI. Candidati sospetti da controllare: `DigiRadioDiscoveryService`,
|
||||
`BLEProvisioningService`, `LocalNetworkScanner`. Se il filtro cerca ancora
|
||||
"DigiRadio", l'app smette di trovare il dispositivo reale.
|
||||
|
||||
---
|
||||
|
||||
## 0ter. Nuovo: fallback Bluetooth se il WiFi cade durante l'uso normale
|
||||
|
||||
Prima, se il WiFi si perdeva DOPO essersi già connesso una volta (router
|
||||
riavviato, password cambiata, spostato di posto), il firmware ritentava
|
||||
all'infinito senza mai avvisare né dare un modo per riconfigurare — bisognava
|
||||
staccare la corrente. Ora, dopo **circa un minuto** di disconnessione
|
||||
continua, il dispositivo attiva automaticamente il provisioning Bluetooth
|
||||
(stesso protocollo BLE già usato al primo setup) **senza smettere di
|
||||
ritentare il WiFi in background**.
|
||||
|
||||
Non serve un nuovo endpoint HTTP (il dispositivo potrebbe non essere
|
||||
raggiungibile via WiFi in quel momento, è proprio il punto). **Azione
|
||||
consigliata**: se l'app ha già una schermata "Cambia rete WiFi" che passa da
|
||||
Bluetooth (dovrebbe già esistere per il primo setup), verifica che scatti
|
||||
anche quando l'app perde la connessione HTTP al dispositivo per un po' — è
|
||||
il segnale che potrebbe essere entrato in questa modalità.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user