OTA: quiet the audio sources during a firmware upload
An upload during an active Spotify session crawled (~10 kB/s, 197 s) and ended in a reset (seen twice). Workaround: - aes67_ota_on_update(cb): cb(true) right before an accepted upload writes flash, cb(false) if it then fails. - The player pauses Spotify (spotify_pause -> SpircHandler::setPause, the app follows) and suspends HLS fetching (hls_suspend); resumed if the upload fails. AES67 TX keeps running (silence). - Verified: upload during Spotify playback paused the session and installed in 25.8 s (normal for 2 MB), clean reboot and self-test. Root cause still open (noted in CLAUDE.md with the other cspot items). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,26 @@
|
||||
|
||||
static const char *TAG = "ota";
|
||||
|
||||
#define MAX_UPDATE_CBS 4
|
||||
static aes67_ota_update_cb_t s_update_cbs[MAX_UPDATE_CBS];
|
||||
static int s_update_n;
|
||||
|
||||
esp_err_t aes67_ota_on_update(aes67_ota_update_cb_t cb)
|
||||
{
|
||||
if (s_update_n >= MAX_UPDATE_CBS) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
s_update_cbs[s_update_n++] = cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void notify_update(bool starting)
|
||||
{
|
||||
for (int i = 0; i < s_update_n; i++) {
|
||||
s_update_cbs[i](starting);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_pending(void)
|
||||
{
|
||||
esp_ota_img_states_t st;
|
||||
@@ -114,6 +134,7 @@ static esp_err_t ota_post(httpd_req_t *req)
|
||||
esp_ota_handle_t ota = 0;
|
||||
size_t total = 0, fill = 0;
|
||||
const char *err_msg = NULL;
|
||||
bool notified = false;
|
||||
int64_t t0 = esp_timer_get_time();
|
||||
|
||||
while (total < req->content_len) {
|
||||
@@ -137,6 +158,8 @@ static esp_err_t ota_post(httpd_req_t *req)
|
||||
}
|
||||
const esp_app_desc_t *d = (const esp_app_desc_t *)(buf + DESC_OFFSET);
|
||||
ESP_LOGI(TAG, "image %.32s %.32s", d->project_name, d->version);
|
||||
notify_update(true); // project quiets streaming/decoding before the flash writes
|
||||
notified = true;
|
||||
if (esp_ota_begin(dst, OTA_WITH_SEQUENTIAL_WRITES, &ota) != ESP_OK) {
|
||||
err_msg = "ota begin failed";
|
||||
break;
|
||||
@@ -154,15 +177,20 @@ static esp_err_t ota_post(httpd_req_t *req)
|
||||
if (ota) {
|
||||
esp_ota_abort(ota);
|
||||
}
|
||||
if (notified) {
|
||||
notify_update(false);
|
||||
}
|
||||
// Rejected before the whole body was read: close instead of draining it.
|
||||
httpd_resp_set_hdr(req, "Connection", "close");
|
||||
return reject(req, err_msg);
|
||||
}
|
||||
esp_err_t err = esp_ota_end(ota); // verifies the image (checksum/hash, chip)
|
||||
if (err != ESP_OK) {
|
||||
notify_update(false);
|
||||
return reject(req, err == ESP_ERR_OTA_VALIDATE_FAILED ? "image verification failed" : "ota end failed");
|
||||
}
|
||||
if (esp_ota_set_boot_partition(dst) != ESP_OK) {
|
||||
notify_update(false);
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "could not set boot partition");
|
||||
}
|
||||
ESP_LOGW(TAG, "installed to %s in %.1f s, rebooting", dst->label,
|
||||
|
||||
@@ -2,8 +2,16 @@
|
||||
// Core component: must not depend on main/ (project code).
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
// Registers /api/ota routes. If the running image is pending verification, starts the
|
||||
// self-test: IP address + own web server answering within 60 s, else roll back.
|
||||
esp_err_t aes67_ota_init(void);
|
||||
|
||||
// Called with true right before an accepted upload starts writing flash, and with false if that
|
||||
// upload then fails (on success the device reboots). Lets the project quiet heavy work (streaming,
|
||||
// decoding) during the flash writes. Up to 4 callbacks.
|
||||
typedef void (*aes67_ota_update_cb_t)(bool starting);
|
||||
esp_err_t aes67_ota_on_update(aes67_ota_update_cb_t cb);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Spotify Connect (cspot). Project code; GPL-3.0 through cspot.
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -24,6 +25,8 @@ typedef void (*spotify_event_cb_t)(spotify_event_t ev, int value);
|
||||
// Registers the zeroconf endpoints (/spotify_info) and, when source.mode is spotify/auto and the
|
||||
// client credentials are set, advertises the device and starts the session task.
|
||||
esp_err_t spotify_init(spotify_pcm_cb_t pcm, spotify_event_cb_t event);
|
||||
// Pause/resume the running session (the Spotify app follows). No-op without a session.
|
||||
void spotify_pause(bool pause);
|
||||
// Current state for status.spotify_state.
|
||||
const char *spotify_state(void);
|
||||
|
||||
|
||||
@@ -30,9 +30,19 @@ static std::shared_ptr<cspot::LoginBlob> s_blob;
|
||||
static std::atomic<bool> s_got_blob{false};
|
||||
static std::atomic<const char *> s_state{"disabled"};
|
||||
static std::atomic<uint64_t> s_pcm_bytes{0};
|
||||
static std::shared_ptr<cspot::SpircHandler> s_handler; // running session, else null
|
||||
static spotify_pcm_cb_t s_pcm_cb;
|
||||
static spotify_event_cb_t s_event_cb;
|
||||
|
||||
extern "C" void spotify_pause(bool pause)
|
||||
{
|
||||
auto h = s_handler;
|
||||
if (h) {
|
||||
h->setPause(pause);
|
||||
ESP_LOGI(TAG, "%s", pause ? "paused" : "resumed");
|
||||
}
|
||||
}
|
||||
|
||||
const char *spotify_state(void)
|
||||
{
|
||||
return s_state.load();
|
||||
@@ -225,6 +235,7 @@ static void session(void)
|
||||
}
|
||||
ESP_LOGI(TAG, "event %s", event_name(ev->eventType));
|
||||
});
|
||||
s_handler = handler;
|
||||
s_state = "connected";
|
||||
ESP_LOGI(TAG, "connected as %s", ctx->config.username.c_str());
|
||||
while (true) {
|
||||
@@ -246,6 +257,7 @@ static void spotify_task(void *arg)
|
||||
s_state = "error";
|
||||
ESP_LOGE(TAG, "session ended: %s", e.what());
|
||||
}
|
||||
s_handler.reset();
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user