diff --git a/Software/components/core/src/WebRadioJson.cpp b/Software/components/core/src/WebRadioJson.cpp index 5f4c870..3a94b3e 100644 --- a/Software/components/core/src/WebRadioJson.cpp +++ b/Software/components/core/src/WebRadioJson.cpp @@ -19,6 +19,7 @@ namespace { constexpr std::size_t kMaxUrlLength = 200U; constexpr std::string_view kHttpPrefix = "http://"; +constexpr std::string_view kHttpsPrefix = "https://"; [[nodiscard]] std::string_view extractJsonString(std::string_view json, std::string_view key) @@ -84,8 +85,9 @@ std::expected parseWebRadioConfigJson( } const std::string_view url = extractJsonString(json, "url"); - if (url.empty() || url.size() > kMaxUrlLength - || url.substr(0, kHttpPrefix.size()) != kHttpPrefix) { + const bool isHttp = url.substr(0, kHttpPrefix.size()) == kHttpPrefix; + const bool isHttps = url.substr(0, kHttpsPrefix.size()) == kHttpsPrefix; + if (url.empty() || url.size() > kMaxUrlLength || !(isHttp || isHttps)) { return std::unexpected(ParseError::InvalidJson); } diff --git a/Software/components/core/test/web_radio_json_test.cpp b/Software/components/core/test/web_radio_json_test.cpp index 891a111..9507fc6 100644 --- a/Software/components/core/test/web_radio_json_test.cpp +++ b/Software/components/core/test/web_radio_json_test.cpp @@ -40,9 +40,18 @@ namespace { return EXIT_FAILURE; } const auto badScheme = core::parseWebRadioConfigJson( - R"({"enabled":true,"url":"https://example.com/x.mp3"})"); + R"({"enabled":true,"url":"ftp://example.com/x.mp3"})"); if (badScheme) { - std::cerr << "non-http scheme should fail\n"; + std::cerr << "non-http(s) scheme should fail\n"; + return EXIT_FAILURE; + } + // Most public internet radio streams are HTTPS-only; rejecting the + // scheme outright (as this parser used to) made streaming unusable for + // essentially any real station. + const auto httpsOk = core::parseWebRadioConfigJson( + R"({"enabled":true,"url":"https://example.com/x.mp3"})"); + if (!httpsOk || httpsOk->url != "https://example.com/x.mp3") { + std::cerr << "https url should parse\n"; return EXIT_FAILURE; } const auto malformed = core::parseWebRadioConfigJson("not json"); diff --git a/Software/docs/app-cursor-brief-2026-08-25.md b/Software/docs/app-cursor-brief-2026-08-25.md index ec6a1e5..c7ecc05 100644 --- a/Software/docs/app-cursor-brief-2026-08-25.md +++ b/Software/docs/app-cursor-brief-2026-08-25.md @@ -176,26 +176,6 @@ quando arriva un nuovo nome/messaggio RDS durante la riproduzione. --- -## 5. Stile UI — Apple, minimalista ma con una sezione grafica curata - -- Componenti nativi SwiftUI: `Picker` segmented per la sorgente, `Slider` con - `.tint()` per volume/bass/stereo, `List`/`Form` in stile Impostazioni per le - stazioni e le opzioni tecniche. Niente controlli custom pesanti o griglie di - bottoni non standard. -- Organizza per tab/sezione logica, non tutto in una schermata: - - **Ascolto**: sorgente attiva, volume, stazione corrente. - - **Suono**: EQ 6 bande, Bass Boost, Stereo Spread. - - **Stazioni**: lista unificata FM+DAB (vedi §4), con scan. - - **Bluetooth**: pairing, dispositivo connesso. - - **Diagnostica**: tono di test, dettagli tecnici/versione firmware — non - mescolare con i controlli quotidiani. -- Una sezione "grafica" curata è benvenuta (es. una card "Now Playing" con - sfondo sfumato/blur, animazione leggera sul cambio sorgente). Ora **puoi** - agganciarla a dati reali di livello audio — vedi §7, l'endpoint VU-meter è - disponibile da oggi. - ---- - ## 7. VU-meter — nuovo, disponibile da oggi ``` @@ -225,7 +205,44 @@ uscita), non un'animazione finta. --- -## 8. Checklist di autoverifica prima di considerare il lavoro finito +## 8. Scan FM completo — NON è bloccato, è solo lento (86s misurati) + +``` +POST /api/tuner/scan/full +``` + +Fa una scansione dell'intera banda FM (fino a 60 canali candidati, con +pausa+lettura RDS per ciascuno) e risponde **una sola volta alla fine**, +misurato: **~86 secondi** per uno scan completo. Non è un bug, è il tempo +reale che serve per farlo bene (RDS incluso). + +**Se l'app usa un timeout HTTP standard (30-60s), questa richiesta scade +prima che il firmware finisca** — la request fallisce lato client, ma il +firmware nel frattempo continua e completa comunque (il risultato però va +perso perché il client non lo aspetta più). Sembra "bloccato", ma non lo è. + +**Azione richiesta**: per questa chiamata specifica, imposta un timeout di +almeno **120 secondi** sulla request HTTP, e mostra un indicatore "scansione +in corso..." per tutta la durata (non un caricamento breve). `POST +/api/tuner/scan` (senza `/full`, per una singola stazione con filtro nome) +è invece rapido, timeout normale va bene. + +--- + +## 8bis. Streaming web radio — bug corretto, ora accetta HTTPS + +`POST /api/streaming {"enabled":true,"url":"..."}` **prima rifiutava +categoricamente qualsiasi URL `https://`** (errore `invalid_json`), accettando +solo `http://` — dato che quasi tutte le radio via internet reali sono +HTTPS-only, questo probabilmente era il motivo per cui "qualsiasi cosa si +faccia" dava errore. Corretto oggi: ora accetta sia `http://` che `https://`, +e il firmware verifica il certificato TLS con la CA bundle integrata di +ESP-IDF. Nessun cambio di contratto per l'app — stessa forma JSON di prima, +semplicemente ora funziona anche con URL HTTPS. + +--- + +## 9. Checklist di autoverifica prima di considerare il lavoro finito - [ ] Cambiare sorgente da Radio a Bluetooth nell'app cambia davvero l'audio sul dispositivo reale (non solo lo stato locale dell'app). diff --git a/Software/main/CMakeLists.txt b/Software/main/CMakeLists.txt index a46d76e..2793976 100644 --- a/Software/main/CMakeLists.txt +++ b/Software/main/CMakeLists.txt @@ -11,7 +11,7 @@ idf_component_register( "$<$:esp32_i2s_test_tone.cpp>" INCLUDE_DIRS "." REQUIRES core net secure_store adau1701 si4684 tuner audio bt1035 bluetooth station integration ota eeprom24aa webradio driver - PRIV_REQUIRES esp_timer esp_http_client + PRIV_REQUIRES esp_timer esp_http_client mbedtls ) if(CONFIG_TEST_FIRMWARE) diff --git a/Software/main/web_radio_stream.cpp b/Software/main/web_radio_stream.cpp index 4f68a26..3fa874b 100644 --- a/Software/main/web_radio_stream.cpp +++ b/Software/main/web_radio_stream.cpp @@ -13,6 +13,7 @@ #include "esp32_i2s_sink.hpp" #include "webradio/WebRadioService.hpp" +#include "esp_crt_bundle.h" #include "esp_http_client.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" @@ -48,6 +49,12 @@ struct InputBuffer { esp_http_client_config_t cfg{}; cfg.url = url.c_str(); cfg.timeout_ms = kHttpTimeoutMs; + // Most public internet radio streams are HTTPS-only today; esp_http_client + // needs an explicit trust anchor for TLS verification or the handshake + // fails outright. CONFIG_MBEDTLS_CERTIFICATE_BUNDLE is already enabled + // (sdkconfig), so attach ESP-IDF's built-in CA bundle -- this is a no-op + // for plain http:// URLs. + cfg.crt_bundle_attach = esp_crt_bundle_attach; esp_http_client_handle_t client = esp_http_client_init(&cfg); if (client == nullptr) { ESP_LOGE(kTag, "esp_http_client_init failed");