Add Slice 4 tuner stack with AGENTS-compliant core types and HTTP API.

Introduces Si4684/ADAU1701 drivers, TunerService, FrequencyKHz,
SeekDirection, /api/tuner routes without file-scope globals, host tests,
and firmware blob tooling (binaries remain gitignored).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 16:17:56 +02:00
co-authored by Cursor
parent c83c9bd765
commit 81d404a1df
68 changed files with 15411 additions and 139 deletions
@@ -0,0 +1,118 @@
/**
* @file Adau1701Driver.cpp
* @brief Adau1701Driver 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 "adau1701/Adau1701Driver.hpp"
#include "SigmaStudioFW.h"
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
extern "C" {
/** @brief SigmaStudio default program download (generated C, not part of the C++ API). */
void adau1701_run_default_download(void);
} // extern "C"
namespace adau1701 {
namespace {
constexpr char kTag[] = "Adau1701";
constexpr int kI2cPort = 0;
} // namespace
Adau1701Driver::Adau1701Driver(Adau1701Pins pins)
: pins_(pins)
, booted_(false)
, i2cBus_(nullptr)
, i2cDev_(nullptr)
{
}
Adau1701Driver::~Adau1701Driver()
{
auto* dev = static_cast<i2c_master_dev_handle_t>(i2cDev_);
auto* bus = static_cast<i2c_master_bus_handle_t>(i2cBus_);
if (dev != nullptr) {
i2c_master_bus_rm_device(dev);
}
if (bus != nullptr) {
i2c_del_master_bus(bus);
}
}
std::expected<void, Adau1701Error> Adau1701Driver::boot()
{
if (booted_) {
return {};
}
gpio_config_t resetCfg = {};
resetCfg.pin_bit_mask = 1ULL << pins_.resetGpio;
resetCfg.mode = GPIO_MODE_OUTPUT;
if (gpio_config(&resetCfg) != ESP_OK) {
return std::unexpected(Adau1701Error::ResetFailed);
}
gpio_set_level(static_cast<gpio_num_t>(pins_.resetGpio), 0);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(static_cast<gpio_num_t>(pins_.resetGpio), 1);
vTaskDelay(pdMS_TO_TICKS(10));
i2c_master_bus_config_t busCfg = {};
busCfg.i2c_port = static_cast<i2c_port_num_t>(kI2cPort);
busCfg.sda_io_num = static_cast<gpio_num_t>(pins_.i2cSda);
busCfg.scl_io_num = static_cast<gpio_num_t>(pins_.i2cScl);
busCfg.clk_source = I2C_CLK_SRC_DEFAULT;
busCfg.glitch_ignore_cnt = 7;
busCfg.flags.enable_internal_pullup = true;
i2c_master_bus_handle_t bus = nullptr;
if (i2c_new_master_bus(&busCfg, &bus) != ESP_OK) {
ESP_LOGE(kTag, "i2c_new_master_bus failed");
return std::unexpected(Adau1701Error::I2cInitFailed);
}
i2cBus_ = bus;
i2c_device_config_t devCfg = {};
devCfg.dev_addr_length = I2C_ADDR_BIT_LEN_7;
devCfg.device_address = static_cast<uint16_t>(pins_.i2cAddr7);
devCfg.scl_speed_hz = 100000;
i2c_master_dev_handle_t dev = nullptr;
if (i2c_master_bus_add_device(bus, &devCfg, &dev) != ESP_OK) {
ESP_LOGE(kTag, "i2c_master_bus_add_device failed");
return std::unexpected(Adau1701Error::I2cInitFailed);
}
i2cDev_ = dev;
sigma_studio_bind_i2c(kI2cPort, static_cast<unsigned char>(pins_.i2cAddr7));
sigma_studio_set_device(dev);
adau1701_run_default_download();
booted_ = true;
ESP_LOGI(kTag, "SigmaStudio program loaded");
return {};
}
bool Adau1701Driver::isBooted() const noexcept
{
return booted_;
}
} // namespace adau1701
@@ -0,0 +1,60 @@
/**
* @file SigmaStudioFW.c
* @brief SigmaStudio SIGMA_WRITE_REGISTER_BLOCK for ESP-IDF I2C.
*
* 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 "SigmaStudioFW.h"
#include "driver/i2c_master.h"
#include <string.h>
static i2c_master_dev_handle_t s_dev = NULL;
void sigma_studio_bind_i2c(int port, unsigned char addr7)
{
(void)port;
(void)addr7;
}
void sigma_studio_set_device(void* i2cDevHandle)
{
s_dev = (i2c_master_dev_handle_t)i2cDevHandle;
}
void SIGMA_WRITE_REGISTER_BLOCK(unsigned char devAddress,
unsigned int address,
unsigned char length,
ADI_REG_TYPE* pData)
{
(void)devAddress;
if (s_dev == NULL || pData == NULL || length == 0U) {
return;
}
enum { kChunk = 64U };
unsigned int addr = address;
unsigned char remaining = length;
ADI_REG_TYPE* cursor = pData;
while (remaining > 0U) {
const unsigned char chunk =
remaining > kChunk ? kChunk : remaining;
unsigned char buf[2U + 64U];
buf[0] = (unsigned char)((addr >> 8) & 0xFFU);
buf[1] = (unsigned char)(addr & 0xFFU);
memcpy(buf + 2U, cursor, chunk);
i2c_master_transmit(s_dev, buf, (size_t)(2U + chunk), 1000);
addr += chunk;
cursor += chunk;
remaining = (unsigned char)(remaining - chunk);
}
}
@@ -0,0 +1,20 @@
/**
* @file adau1701_program.c
* @brief SigmaStudio default download for DigiRadio ADAU1701.
*
* 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 "SigmaStudioFW.h"
#include "DigiRadio_IC_1.h"
void adau1701_run_default_download(void)
{
default_download_IC_1();
}
@@ -1,33 +0,0 @@
/**
* @file component_stub.cpp
* @brief ADAU1701 driver component placeholder (Slice 5).
*
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
*
* Copyright 2026 Michele Bigi
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* @author Michele Bigi
* @date 2026-07-06
*/
namespace adau1701::detail {
/**
* @brief adau1701ComponentLinked — ensures the driver component links.
*
* @dname adau1701ComponentLinked
* @return n/a (type)
* @pubstate n/a
*
* @author Michele Bigi
* @date 2026-07-06
*/
void adau1701ComponentLinked() noexcept {}
} // namespace adau1701::detail