Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
28e24d5
TAS5805M driver improvements. Sleep mode when mute. State object for …
Nov 19, 2025
048fdc3
Set correct mute flag on DAC restore
Nov 19, 2025
515daca
Added revised state changes: independent MUTE flasg and DAC power mode
Nov 20, 2025
79e9950
Volume implementation without a lookup tables
Nov 20, 2025
52b4f1e
Changed lower border of volume slider to -60dB to provide better dyna…
Nov 20, 2025
1ae205e
Updated Ui to manage DSP settings and change DSP flow. Updated DSP co…
Nov 21, 2025
c33637e
post rebase cleanup
Nov 22, 2025
1505272
fix build errors
Nov 22, 2025
b908e45
refactored away DSP settings into dedicated component. aligned genera…
Nov 24, 2025
a2f126d
replaced hard coded flow count with DSP_FLOW_COUNT
Nov 24, 2025
30e3f64
Removed dsp_processor_switch_flow function
Nov 26, 2025
7da1391
First steps in tas5805m settings page (wip)
Nov 25, 2025
1a5397a
Add persistence for DAC mode, modulation mode, digital volume and ana…
Nov 26, 2025
1f6dc7e
Added UI for DAC settings: bridge mode, modulation mode, switching fr…
Nov 26, 2025
dd91fbe
Added mixer implementation
Nov 26, 2025
d1f6f64
Added EQ related code to the tas5805m driver
Nov 26, 2025
ece19f1
config changes
Nov 26, 2025
e7d67d1
Added EQ mode (with persistance and restore on boot)
Nov 27, 2025
d385ae1
Added 15-band equilizer
Nov 27, 2025
86d77da
Added mode switch: 15-band EQ or EQ presets
Nov 28, 2025
db76ced
Added mixer compensation gains
Nov 28, 2025
3d7092f
Apply tas5805m settings after DAc init passed (need I2S clock)
Nov 30, 2025
dc203ad
apply settings after I2S clock is present - fix
Dec 3, 2025
3cc92ec
dsiable eq controls, when eq support is disabled
Dec 4, 2025
eb06b6b
Bugs with eq restore and build error fixes
Dec 5, 2025
131d2f2
Eq vertical slider layout
Dec 5, 2025
f056afd
Added dedicated tab for the EQ and refactored json API
Jan 5, 2026
c13a7f9
Removed BQ editor, restored develop version of the dsp_processor
Jan 8, 2026
5672d93
merge cleanup
Jan 8, 2026
f7ecade
Post merge cleanup
Jan 8, 2026
e594659
Build and warnings fix
Jan 8, 2026
d544ea3
Added Faults read and reset every 5 seconds driver logic, and faults …
Jan 10, 2026
c96e664
Fixes for:
craigmillard86 Jan 10, 2026
8e07721
Fixes for Wifi Power Save disabling low latency audio, DSP UI setting…
craigmillard86 Jan 10, 2026
3505b14
reverting Player.c
craigmillard86 Jan 11, 2026
a46df4a
additional fix for sliders not updating correctly after initial page …
craigmillard86 Jan 11, 2026
01077fd
Removing the wifi power setting change from none back to min_modem.
craigmillard86 Jan 11, 2026
9df8db9
Added back removed wifi power lines in wifi_interface.c
craigmillard86 Jan 11, 2026
2a2a83f
Fix for the following crash that was happening because:
craigmillard86 Jan 11, 2026
3106588
Fix to enable channel gains to be restored and applied to all dsp typ…
craigmillard86 Jan 12, 2026
a1b29e5
Merge branch 'pr-183' into feature/eq-ui-fixes
craigmillard86 Jan 14, 2026
17f412e
Fix EQ UI gains showing 0 on page load before music starts
craigmillard86 Jan 14, 2026
c4b76d9
Add Advanced Bi-Amp Crossover with PEQ, loudness compensation, and UI…
craigmillard86 Jan 15, 2026
1f8d7f5
Add bi-amp preset export/import for shareable speaker configurations
craigmillard86 Jan 15, 2026
d54c0f6
Fix player reconnection crash and improve bi-amp UI controls
craigmillard86 Jan 16, 2026
55ae385
Expand PEQ to 6 bands and group loudness zones in UI
craigmillard86 Jan 16, 2026
3dd8da6
Fix bi-amp bugs, add security hardening, and improve documentation
craigmillard86 Jan 17, 2026
4bcce57
Improve mobile responsive UI and fix script loading race condition
craigmillard86 Jan 17, 2026
d9b6d5e
fix: Guard tas5805m_loudness_apply with CONFIG_DAC_TAS5805M_EQ_SUPPORT
craigmillard86 Jan 17, 2026
684157f
fix: Improve mobile UI loading reliability for DAC/EQ pages
craigmillard86 Jan 17, 2026
2eec835
fix: Improve HTTP server socket handling for mobile browsers
craigmillard86 Jan 17, 2026
dcb9615
fix: Add Connection: close to EQ settings handler and increase delay
craigmillard86 Jan 17, 2026
b7645ff
fix: Increase tas5805m_faults task stack size to prevent overflow
craigmillard86 Jan 17, 2026
615cbf2
Revert chkInFrames handling to return -1 instead of defaulting to 1152
craigmillard86 Jan 23, 2026
783db4d
fix: Make biamp conditional on EQ support being enabled
craigmillard86 Jan 26, 2026
b4ee16b
fix: Add stub biamp functions when EQ support is disabled
craigmillard86 Jan 26, 2026
6037764
fix: Add missing string.h include for memset
craigmillard86 Jan 26, 2026
8711a57
Merge branch 'develop' into feature/advanced-biamp
craigmillard86 Apr 3, 2026
6eec360
Address PR #187 review comments: security, robustness, and cleanup fixes
craigmillard86 Apr 23, 2026
9ff1a86
Fix concurrent schema/settings fetches bypassing ESP32 recovery delay
craigmillard86 Apr 23, 2026
f44edb2
Merge branch 'develop' into feature/advanced-biamp
craigmillard86 May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions components/custom_board/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
set(COMPONENT_REQUIRES audio_hal audio_board)
set(COMPONENT_PRIV_REQUIRES esp_peripherals)

# Add tas5805m_settings dependency when TAS5805M DAC is enabled
if(CONFIG_DAC_TAS5805M)
list(APPEND COMPONENT_PRIV_REQUIRES tas5805m_settings)
endif()

if(CONFIG_AUDIO_BOARD_CUSTOM)
message(STATUS "Current board name is " CONFIG_AUDIO_BOARD_CUSTOM)
set(COMPONENT_ADD_INCLUDEDIRS ./generic_board/include)
Expand Down Expand Up @@ -49,6 +54,7 @@ if(CONFIG_AUDIO_BOARD_CUSTOM)
if(CONFIG_DAC_TAS5805M)
message(STATUS "Selected DAC is " CONFIG_DAC_TAS5805M)
list(APPEND COMPONENT_ADD_INCLUDEDIRS ./tas5805m/include)
list(APPEND COMPONENT_ADD_INCLUDEDIRS ../tas5805m_settings/include)
list(APPEND COMPONENT_SRCS ./tas5805m/tas5805m.c)
endif()

Expand Down
155 changes: 143 additions & 12 deletions components/custom_board/tas5805m/tas5805m.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,18 @@
#include "i2c_bus.h"
#include "tas5805m_reg_cfg.h"
#include <math.h>
#include "freertos/semphr.h"
#include "freertos/portmacro.h"

#if CONFIG_DAC_TAS5805M
#include "tas5805m_settings.h"
#endif

static const char *TAG = "TAS5805M";

/* Mutex for thread-safe I2C access */
static SemaphoreHandle_t tas5805m_i2c_mutex = NULL;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have this anywhere. Do we really need this?


#define TAS5805M_SET_BOOK_AND_PAGE(BOOK, PAGE) \
do { \
tas5805m_write_byte(TAS5805M_REG_PAGE_SET, TAS5805M_REG_PAGE_ZERO); \
Expand Down Expand Up @@ -157,6 +166,11 @@ void i2c_master_init() {

// Reading of TAS5805M-Register
esp_err_t tas5805m_read_byte(uint8_t register_name, uint8_t *data) {
if (tas5805m_i2c_mutex && xSemaphoreTakeRecursive(tas5805m_i2c_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGE(TAG, "%s: Failed to acquire I2C mutex", __func__);
return ESP_ERR_TIMEOUT;
}

int ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
Expand All @@ -179,11 +193,18 @@ esp_err_t tas5805m_read_byte(uint8_t register_name, uint8_t *data) {
ret = i2c_master_cmd_begin(I2C_TAS5805M_MASTER_NUM, cmd, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd);
ESP_LOGV(TAG, "%s: Read 0x%02x from register 0x%02x", __func__, *data, register_name);

if (tas5805m_i2c_mutex) xSemaphoreGiveRecursive(tas5805m_i2c_mutex);
return ret;
}

// Writing of TAS5805M-Register
esp_err_t tas5805m_write_byte(uint8_t register_name, uint8_t value) {
if (tas5805m_i2c_mutex && xSemaphoreTakeRecursive(tas5805m_i2c_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGE(TAG, "%s: Failed to acquire I2C mutex", __func__);
return ESP_ERR_TIMEOUT;
}

int ret = 0;
ESP_LOGV(TAG, "%s: Writing 0x%02x to register 0x%02x", __func__, value, register_name);

Expand All @@ -203,6 +224,92 @@ esp_err_t tas5805m_write_byte(uint8_t register_name, uint8_t value) {

i2c_cmd_link_delete(cmd);

if (tas5805m_i2c_mutex) xSemaphoreGiveRecursive(tas5805m_i2c_mutex);
return ret;
}

esp_err_t tas5805m_write_bytes(uint8_t *reg,
int regLen, uint8_t *data, int datalen)
{
if (tas5805m_i2c_mutex && xSemaphoreTakeRecursive(tas5805m_i2c_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGE(TAG, "%s: Failed to acquire I2C mutex", __func__);
return ESP_ERR_TIMEOUT;
}

int ret = ESP_OK;
ESP_LOGV(TAG, "%s: 0x%02x <- [%d] bytes", __func__, *reg, datalen);
for (int i = 0; i < datalen; i++)
{
ESP_LOGV(TAG, "%s: 0x%02x", __func__, data[i]);
}

i2c_cmd_handle_t cmd = i2c_cmd_link_create();
ret |= i2c_master_start(cmd);
ret |= i2c_master_write_byte(cmd, TAS5805M_ADDRESS << 1 | WRITE_BIT, ACK_CHECK_EN);
ret |= i2c_master_write(cmd, reg, regLen, ACK_CHECK_EN);
ret |= i2c_master_write(cmd, data, datalen, ACK_CHECK_EN);
ret |= i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_TAS5805M_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);

// Check if ret is OK
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "%s: Error during I2C transmission: %s", __func__, esp_err_to_name(ret));
}

i2c_cmd_link_delete(cmd);

if (tas5805m_i2c_mutex) xSemaphoreGiveRecursive(tas5805m_i2c_mutex);
return ret;
}

esp_err_t tas5805m_read_bytes(uint8_t *reg, int regLen, uint8_t *data, int datalen)
{
if (tas5805m_i2c_mutex && xSemaphoreTakeRecursive(tas5805m_i2c_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGE(TAG, "%s: Failed to acquire I2C mutex", __func__);
return ESP_ERR_TIMEOUT;
}

int ret = ESP_OK;
ESP_LOGV(TAG, "%s: 0x%02x -> [%d] bytes", __func__, *reg, datalen);

i2c_cmd_handle_t cmd = i2c_cmd_link_create();
ret |= i2c_master_start(cmd);
ret |= i2c_master_write_byte(cmd, TAS5805M_ADDRESS << 1 | WRITE_BIT, ACK_CHECK_EN);
ret |= i2c_master_write(cmd, reg, regLen, ACK_CHECK_EN);
ret |= i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_TAS5805M_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);

if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: Error during I2C write phase: %s", __func__, esp_err_to_name(ret));
if (tas5805m_i2c_mutex) xSemaphoreGiveRecursive(tas5805m_i2c_mutex);
return ret;
}

vTaskDelay(1 / portTICK_PERIOD_MS);

cmd = i2c_cmd_link_create();
ret |= i2c_master_start(cmd);
ret |= i2c_master_write_byte(cmd, TAS5805M_ADDRESS << 1 | READ_BIT, ACK_CHECK_EN);
if (datalen > 1) {
ret |= i2c_master_read(cmd, data, datalen - 1, ACK_VAL);
}
ret |= i2c_master_read_byte(cmd, data + datalen - 1, NACK_VAL);
ret |= i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_TAS5805M_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);

if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: Error during I2C read phase: %s", __func__, esp_err_to_name(ret));
} else {
for (int i = 0; i < datalen; i++) {
ESP_LOGV(TAG, "%s: [%d] = 0x%02x", __func__, i, data[i]);
}
}

i2c_cmd_link_delete(cmd);

if (tas5805m_i2c_mutex) xSemaphoreGiveRecursive(tas5805m_i2c_mutex);
return ret;
}

Expand Down Expand Up @@ -282,6 +389,17 @@ esp_err_t tas5805m_read_bytes(uint8_t *reg, int regLen, uint8_t *data, int datal
esp_err_t tas5805m_init() {
ESP_LOGD(TAG, "%s: Initializing TAS5805M", __func__);
int ret = 0;

/* Create I2C mutex if not already created (recursive to allow nested calls).
* Safe without locking: called from app_main before concurrent access. */
if (tas5805m_i2c_mutex == NULL) {
tas5805m_i2c_mutex = xSemaphoreCreateRecursiveMutex();
if (tas5805m_i2c_mutex == NULL) {
ESP_LOGE(TAG, "%s: Failed to create I2C mutex", __func__);
return ESP_ERR_NO_MEM;
}
}

// Init the I2C-Driver
i2c_master_init();

Expand Down Expand Up @@ -346,7 +464,7 @@ esp_err_t tas5805m_init() {
BaseType_t task_ret = xTaskCreate(
tas5805m_fault_monitor_task,
"tas5805m_faults",
3 * 1024,
4096,
NULL,
5,
&tas5805m_fault_monitor_task_handle
Expand Down Expand Up @@ -419,6 +537,10 @@ esp_err_t tas5805m_set_volume(int vol) {
esp_err_t ret = tas5805m_write_byte(TAS5805M_DIG_VOL_CTRL_REGISTER, reg_val);
if (ret == ESP_OK) {
tas5805m_state.volume = vol;
#if CONFIG_DAC_TAS5805M
/* Apply loudness compensation based on new volume level */
tas5805m_loudness_apply(vol);
#endif
} else {
ESP_LOGW(TAG, "%s: Failed to write volume (reg 0x%02x): %s", __func__, reg_val, esp_err_to_name(ret));
}
Expand Down Expand Up @@ -1176,36 +1298,42 @@ esp_err_t tas5805m_read_biquad_coefficients(tas5805m_eq_chan_t channel, int band
return ESP_ERR_INVALID_ARG;
}

ESP_LOGD(TAG, "%s: Reading biquad coefficients for channel %d, band %d",
ESP_LOGD(TAG, "%s: Reading biquad coefficients for channel %d, band %d",
__func__, channel, band);

esp_err_t ret = ESP_OK;
uint8_t page, offset;
uint32_t raw_value;

// Read each coefficient
float *coeffs[] = {b0, b1, b2, a1, a2};
const char *names[] = {"B0", "B1", "B2", "A1", "A2"};

for (int i = 0; i < TAS5805M_EQ_KOEF_PER_BAND; i++) {
ret = tas5805m_get_biquad_register(channel, band, i, &page, &offset);
if (ret != ESP_OK) {
return ret;
}

TAS5805M_SET_BOOK_AND_PAGE(TAS5805M_REG_BOOK_EQ, page);

ret = tas5805m_read_bytes(&offset, 1, (uint8_t *)&raw_value, sizeof(raw_value));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: Failed to read coefficient %s: %s",
ESP_LOGE(TAG, "%s: Failed to read coefficient %s: %s",
__func__, names[i], esp_err_to_name(ret));
break;
}

*coeffs[i] = tas5805m_q5_27_to_float(raw_value);
ESP_LOGD(TAG, "%s: %s = %f (raw: 0x%08X)", __func__, names[i], *coeffs[i], (unsigned int)raw_value);
}


// TAS5805M uses addition convention for feedback: y = b0*x + b1*x1 + b2*x2 + a1*y1 + a2*y2
// Standard DSP uses subtraction: y = b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2
// Negate a1 and a2 to convert from TAS5805M convention back to standard DSP convention
*a1 = -(*a1);
*a2 = -(*a2);

TAS5805M_SET_BOOK_AND_PAGE(TAS5805M_REG_BOOK_CONTROL_PORT, TAS5805M_REG_PAGE_ZERO);
return ret;
}
Expand All @@ -1224,7 +1352,10 @@ esp_err_t tas5805m_write_biquad_coefficients(tas5805m_eq_chan_t channel, int ban
uint8_t page, offset;
uint32_t raw_value;

float coeffs[] = {b0, b1, b2, a1, a2};
// TAS5805M uses addition convention for feedback: y = b0*x + b1*x1 + b2*x2 + a1*y1 + a2*y2
// Standard DSP uses subtraction: y = b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2
// So we negate a1 and a2 to convert from standard DSP convention to TAS5805M convention
float coeffs[] = {b0, b1, b2, -a1, -a2};
const char *names[] = {"B0", "B1", "B2", "A1", "A2"};
Comment thread
craigmillard86 marked this conversation as resolved.

for (int i = 0; i < TAS5805M_EQ_KOEF_PER_BAND; i++) {
Expand Down Expand Up @@ -1302,8 +1433,8 @@ uint32_t tas5805m_float_to_q5_27(float value)
int32_t fixed_val = (int32_t)(value * (1 << 27));
uint32_t le_val = tas5805m_swap_endian_32((uint32_t)fixed_val);

// ESP_LOGD(TAG, "%s: value=%f -> fixed_val=%d, le_val=0x%08X",
// __func__, value, fixed_val, (unsigned int)le_val);
ESP_LOGD(TAG, "%s: value=%f -> fixed_val=%ld, le_val=0x%08lX",
__func__, value, (long)fixed_val, (unsigned long)le_val);

return le_val;
}
Expand Down
5 changes: 4 additions & 1 deletion components/lightsnapcast/player.c
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,10 @@ int start_player() {
esp_pm_lock_acquire(player_pm_lock_handle);
#endif

if (pcmChkQHdl == NULL)
// create message queue to inform task of changed settings
snapcastSettingQueueHandle = xQueueCreate(1, sizeof(uint8_t));

if (pcmChkQHdl == NULL)
Comment thread
craigmillard86 marked this conversation as resolved.
{

int entries = ceil(((float)scSet->sr / (float)scSet->chkInFrames) *
Expand Down
2 changes: 1 addition & 1 deletion components/tas5805m_settings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
idf_component_register(
SRCS "tas5805m_settings.c"
SRCS "tas5805m_settings.c" "tas5805m_biamp.c"
INCLUDE_DIRS "include"
REQUIRES nvs_flash custom_board json
)
Loading
Loading