Add Slice 5 ADAU1701 runtime audio control (firmware 0.5.0).
Safeload mixer/EQ/master on the ADAU1701, persist AudioProfile in NVS, expose /api/audio routes and web UI controls, with host tests and manual sync. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
idf_component_register(
|
||||
SRCS "src/NvsSecureStore.cpp"
|
||||
SRCS
|
||||
"src/NvsSecureStore.cpp"
|
||||
"src/NvsAudioProfileStore.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES core nvs_flash
|
||||
)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file NvsAudioProfileStore.hpp
|
||||
* @brief NVS-backed IAudioProfileStore for user audio settings.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/IAudioProfileStore.hpp"
|
||||
|
||||
namespace secure_store {
|
||||
|
||||
/**
|
||||
* @brief NvsAudioProfileStore — persists AudioProfile JSON in NVS.
|
||||
*
|
||||
* @dname NvsAudioProfileStore
|
||||
* @return n/a (type)
|
||||
* @pubstate Uses namespace digiradio, key audio_profile_json.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
class NvsAudioProfileStore final : public core::IAudioProfileStore {
|
||||
public:
|
||||
[[nodiscard]] bool hasProfile() const override;
|
||||
|
||||
[[nodiscard]] std::expected<void, core::StoreError> saveProfile(
|
||||
const core::AudioProfile& profile) override;
|
||||
|
||||
[[nodiscard]] std::expected<core::AudioProfile, core::StoreError>
|
||||
loadProfile() const override;
|
||||
|
||||
[[nodiscard]] std::expected<void, core::StoreError> clearProfile() override;
|
||||
};
|
||||
|
||||
} // namespace secure_store
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @file NvsAudioProfileStore.cpp
|
||||
* @brief NvsAudioProfileStore implementation.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
|
||||
#include "secure_store/NvsAudioProfileStore.hpp"
|
||||
|
||||
#include "core/AudioProfileJson.hpp"
|
||||
|
||||
#include "nvs.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace secure_store {
|
||||
|
||||
namespace {
|
||||
constexpr char kNamespace[] = "digiradio";
|
||||
constexpr char kProfileKey[] = "audio_profile_json";
|
||||
} // namespace
|
||||
|
||||
bool NvsAudioProfileStore::hasProfile() const
|
||||
{
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kNamespace, NVS_READONLY, &handle) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t len = 0;
|
||||
const esp_err_t err = nvs_get_str(handle, kProfileKey, nullptr, &len);
|
||||
nvs_close(handle);
|
||||
return err == ESP_OK && len > 1U;
|
||||
}
|
||||
|
||||
std::expected<void, core::StoreError> NvsAudioProfileStore::saveProfile(
|
||||
const core::AudioProfile& profile)
|
||||
{
|
||||
const std::string json = core::serializeAudioProfileJson(profile);
|
||||
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kNamespace, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
return std::unexpected(core::StoreError::IoFailed);
|
||||
}
|
||||
|
||||
esp_err_t err = nvs_set_str(handle, kProfileKey, json.c_str());
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
return std::unexpected(core::StoreError::IoFailed);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::expected<core::AudioProfile, core::StoreError>
|
||||
NvsAudioProfileStore::loadProfile() const
|
||||
{
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kNamespace, NVS_READONLY, &handle) != ESP_OK) {
|
||||
return std::unexpected(core::StoreError::NotFound);
|
||||
}
|
||||
|
||||
std::size_t len = 0;
|
||||
if (nvs_get_str(handle, kProfileKey, nullptr, &len) != ESP_OK || len == 0U) {
|
||||
nvs_close(handle);
|
||||
return std::unexpected(core::StoreError::NotFound);
|
||||
}
|
||||
|
||||
std::vector<char> buf(len);
|
||||
if (nvs_get_str(handle, kProfileKey, buf.data(), &len) != ESP_OK) {
|
||||
nvs_close(handle);
|
||||
return std::unexpected(core::StoreError::IoFailed);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (auto parsed = core::parseAudioProfileJson(buf.data()); parsed) {
|
||||
return *parsed;
|
||||
}
|
||||
return std::unexpected(core::StoreError::InvalidData);
|
||||
}
|
||||
|
||||
std::expected<void, core::StoreError> NvsAudioProfileStore::clearProfile()
|
||||
{
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kNamespace, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
return std::unexpected(core::StoreError::IoFailed);
|
||||
}
|
||||
|
||||
esp_err_t err = nvs_erase_key(handle, kProfileKey);
|
||||
if (err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
return std::unexpected(core::StoreError::IoFailed);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace secure_store
|
||||
Reference in New Issue
Block a user