Fix ADAU1701 SerialInputRegister IBP override causing persistent hiss

The 2026-08-16 IBP=1 override (commit 6974095) was applied alongside a
separate, simultaneous fix to Si4684's PIN_CONFIG_ENABLE and credited
with turning static into audible music. SerialInputRegister (0x081F) is
a single register shared by every SDATA_INx pin on the ADAU1701, so the
override also applied to the ESP32 streaming input, not just Si4684's.
Live A/B testing today (DAB, FM, and ESP32 web-radio, all isolated via
mixer gain, with and without ADAU1701 DSP bypass, with and without BT
A2DP codec changes) narrowed the hiss to this one shared register.
Removing the override and leaving IBP at its compiled default (0x00)
resolved the hiss on both the Si4684 and ESP32 paths, confirmed by ear.

Also:
- Add a runtime HTTP API (GET/POST /api/bluetooth/a2dp-codec) to change
  the BT1035 A2DP codec bitmask without reflashing, used to rule out
  AAC/SBC codec choice as a contributing cause.
- Extend the ADAU1701 boot-time EQ diagnostic to read back all 6 bands
  (previously band 0 only) using the correct 5.23 fixed-point format.
- Note EEPROM persistence for ANTCAP/crystal calibration as TODO.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 14:37:57 +02:00
co-authored by Claude Sonnet 5
parent fae2305164
commit 38e768edbb
12 changed files with 460 additions and 99 deletions
@@ -96,6 +96,33 @@ parseBluetoothAutoReconnectJson(std::string_view json)
return static_cast<std::uint8_t>(raw);
}
std::expected<std::uint8_t, ParseError>
parseBluetoothA2dpCodecConfigJson(std::string_view json)
{
if (json.find('{') == std::string_view::npos) {
return std::unexpected(ParseError::InvalidJson);
}
const std::string needle = "\"codec_mask\":";
const std::size_t start = json.find(needle);
if (start == std::string_view::npos) {
return std::unexpected(ParseError::MissingField);
}
char* end = nullptr;
const unsigned long raw =
std::strtoul(json.data() + start + needle.size(), &end, 10);
if (end == json.data() + start + needle.size() || raw > 63U) {
return std::unexpected(ParseError::InvalidJson);
}
return static_cast<std::uint8_t>(raw);
}
std::string serializeBluetoothA2dpCodecJson(Bt1035A2dpCodec codec)
{
std::ostringstream out;
out << "{\"codec\":\"" << a2dpCodecToken(codec) << "\"}";
return out.str();
}
std::string serializeBluetoothScanJson(
const std::vector<Bt1035ScannedDevice>& devices)
{
+14 -3
View File
@@ -106,8 +106,10 @@ std::string buildBt1035AtLine(Bt1035AtCommand command)
return "AT\r\n";
case Bt1035AtCommand::I2sMode:
return "AT+AUXCFG=3\r\n";
case Bt1035AtCommand::I2sSlave48k24:
return "AT+I2SCFG=35\r\n";
case Bt1035AtCommand::I2sSlave48k32:
return "AT+I2SCFG=67\r\n";
case Bt1035AtCommand::A2dpCodecConfig:
return buildBt1035A2dpCodecConfigLine(kBt1035A2dpCodecConfigParam);
case Bt1035AtCommand::PairDiscoverable:
return "AT+PAIR=1\r\n";
case Bt1035AtCommand::PairHidden:
@@ -128,6 +130,14 @@ std::string buildBt1035AtLine(Bt1035AtCommand command)
return "AT\r\n";
}
std::string buildBt1035A2dpCodecConfigLine(std::uint8_t bitmask)
{
if (bitmask > 63U) {
bitmask = 63U;
}
return "AT+A2DPCFG=" + std::to_string(bitmask) + "\r\n";
}
std::string buildBt1035SetAutoConnLine(std::uint8_t times)
{
if (times > 15U) {
@@ -150,7 +160,8 @@ std::array<Bt1035AtCommand, kBt1035BootInitCommandCount> bootInitSequence() noex
return std::array<Bt1035AtCommand, kBt1035BootInitCommandCount>{
Bt1035AtCommand::Ping,
Bt1035AtCommand::I2sMode,
Bt1035AtCommand::I2sSlave48k24,
Bt1035AtCommand::I2sSlave48k32,
Bt1035AtCommand::A2dpCodecConfig,
};
}