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:
@@ -182,69 +182,137 @@ namespace adau1701
|
||||
return replay;
|
||||
}
|
||||
|
||||
// Diagnostic: log EQ band 0's live Param RAM contents. This band is
|
||||
// the fixed high-pass (SigmaStudio band 1) and applyEq() always
|
||||
// skips it -- no runtime code path ever safeloads it, so whatever
|
||||
// landed here at program-load time is what plays, permanently,
|
||||
// regardless of the audio-profile API (which reports a fictional
|
||||
// value for it). Read straight from the chip rather than trusting
|
||||
// the compiled source, in case the two have ever diverged.
|
||||
// Diagnostic (2026-08-24, extended from band-0-only): log EVERY EQ
|
||||
// band's live Param RAM contents (bands 0-5, 5 coefficients each --
|
||||
// B0,B1,B2,A0,A1 per paramAddrEqBandBase's 5-word stride). Band 0
|
||||
// is the fixed high-pass that applyEq() always skips (whatever
|
||||
// landed at program-load time plays permanently); bands 1-5 are
|
||||
// runtime-safeloaded whenever the audio profile has a nonzero
|
||||
// gain, but with the factory-default flat profile (all gains 0)
|
||||
// they should read back as the identity biquad (B0=0x00800000=1.0,
|
||||
// rest 0) written by designFlatEq(). A single steady test tone at
|
||||
// one frequency cannot reveal a bad coefficient elsewhere in a
|
||||
// band's response curve -- reading the actual RAM contents is the
|
||||
// only way to confirm what's really there, not what the software
|
||||
// believes it wrote. Decode as 5.23 (28-bit, matches the ADAU1701's
|
||||
// real Param RAM format, NOT a naive 32-bit Q8.23 -- see the
|
||||
// 2026-08-23 band-0 false alarm in
|
||||
// docs/si4684-rf-investigation-report.md for why that distinction
|
||||
// matters).
|
||||
for (unsigned band = 0U; band < 6U; ++band)
|
||||
{
|
||||
const unsigned baseAddr = paramAddrEqBandBase(0U);
|
||||
const unsigned baseAddr = paramAddrEqBandBase(
|
||||
static_cast<std::uint8_t>(band));
|
||||
for (unsigned i = 0U; i < 5U; ++i)
|
||||
{
|
||||
unsigned char raw[4U] = {0U, 0U, 0U, 0U};
|
||||
if (sigma_i2c_read(baseAddr + i, raw, sizeof(raw)) == 0)
|
||||
{
|
||||
const std::int32_t fixpoint =
|
||||
std::int32_t fixpoint =
|
||||
static_cast<std::int32_t>(
|
||||
(static_cast<std::uint32_t>(raw[0]) << 24) |
|
||||
(static_cast<std::uint32_t>(raw[1]) << 16) |
|
||||
(static_cast<std::uint32_t>(raw[2]) << 8) |
|
||||
static_cast<std::uint32_t>(raw[3]));
|
||||
// Sign-extend from bit 27 (28-bit/5.23 format).
|
||||
if ((fixpoint & (1 << 27)) != 0)
|
||||
{
|
||||
fixpoint -= (1 << 28);
|
||||
}
|
||||
ESP_LOGI(kTag,
|
||||
"EQ band0 param[%u] addr=0x%04X raw=0x%08X "
|
||||
"EQ band%u param[%u] addr=0x%04X raw=0x%08X "
|
||||
"value=%f",
|
||||
i, baseAddr + i,
|
||||
static_cast<unsigned>(fixpoint),
|
||||
band, i, baseAddr + i,
|
||||
static_cast<unsigned>(
|
||||
(static_cast<std::uint32_t>(raw[0]) << 24)
|
||||
| (static_cast<std::uint32_t>(raw[1]) << 16)
|
||||
| (static_cast<std::uint32_t>(raw[2]) << 8)
|
||||
| static_cast<std::uint32_t>(raw[3])),
|
||||
static_cast<double>(fixpoint) /
|
||||
static_cast<double>(1U << 23));
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(kTag, "EQ band0 param[%u] read-back failed", i);
|
||||
ESP_LOGW(kTag, "EQ band%u param[%u] read-back failed",
|
||||
band, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SerialInputRegister (0x081F) override: bit3 IBP=1, matching the
|
||||
// BCLK edge the Si4684's I2S output actually changes data on
|
||||
// (compiled DSP program default is 0x00 = IBP=0, which produced
|
||||
// pure static on a strong locked signal). ILP=1 was also tried
|
||||
// (0x18) and made it worse (pure white noise again) — IBP alone
|
||||
// (0x08) is the correct override, confirmed live: real, recognizable
|
||||
// music instead of static/noise on a locked FM station. Redundant
|
||||
// with the R3_HWCONFIGURATION program write above (SigmaStudio's
|
||||
// own export already places 0x08 at this address) — kept as a
|
||||
// belt-and-braces re-assert in case that specific chunk silently
|
||||
// failed; now checked/verified like everything else instead of
|
||||
// fire-and-forget.
|
||||
// SerialInputRegister (0x081F) IBP override -- REMOVED 2026-08-24,
|
||||
// CONFIRMED LIVE as the root cause of the multi-day hiss/
|
||||
// unintelligible-speech investigation. History: on 2026-08-16
|
||||
// (commit 6974095) this was set to IBP=1 (0x08) alongside a
|
||||
// SEPARATE, simultaneous fix to Si4684's PIN_CONFIG_ENABLE (which
|
||||
// had been forcing the chip's analog DAC fallback instead of real
|
||||
// I2S output). Both fixes landed in the same commit and were
|
||||
// tested together: "static" became "music", credited to IBP=1.
|
||||
// But SerialInputRegister is a SINGLE register shared by every
|
||||
// SDATA_INx pin on the ADAU1701 (confirmed against the datasheet
|
||||
// 2026-08-24: one INPUT_BCLK/INPUT_LRCLK clock pair serves all
|
||||
// four SDATA_INx pins) -- so the override also applied to the
|
||||
// ESP32 leg, not just Si4684's. The PIN_CONFIG_ENABLE fix alone
|
||||
// was what turned static into music (Si4684 finally sending valid
|
||||
// I2S data at all); IBP=1 had never been validated in isolation
|
||||
// and was in fact marginal/wrong for both legs. With IBP left at
|
||||
// the compiled default (0x00, IBP=0) and PIN_CONFIG_ENABLE
|
||||
// independently correct, live listening confirmed clean audio on
|
||||
// both the Si4684 (DAB) and ESP32 (web radio) paths -- the hiss
|
||||
// is gone. Left commented out below rather than deleted, in case
|
||||
// a future hardware revision needs it revisited.
|
||||
//
|
||||
// {
|
||||
// const unsigned char deviceAddr =
|
||||
// static_cast<unsigned char>(pins_.i2cAddr7 << 1);
|
||||
// ADI_REG_TYPE serialInFix = 0x08U;
|
||||
// if (SIGMA_WRITE_REGISTER_BLOCK(deviceAddr, 0x081FU, 1U,
|
||||
// &serialInFix) != 0)
|
||||
// {
|
||||
// ESP_LOGE(kTag, "SerialInputRegister override failed after retries");
|
||||
// return std::unexpected(Adau1701Error::DownloadFailed);
|
||||
// }
|
||||
// if (sigma_verify_block(0x081FU, &serialInFix, 1U) != 0)
|
||||
// {
|
||||
// ESP_LOGW(kTag,
|
||||
// "SerialInputRegister read-back mismatch -- ACKed "
|
||||
// "but did not land as 0x08");
|
||||
// }
|
||||
// }
|
||||
|
||||
// Limiter1/Limiter2 threshold override, 2026-08-24: compiled
|
||||
// program ships both at 0x00800000 = 1.0 linear = 0 dBFS (an
|
||||
// RMS-detecting limiter, per Analog Devices' own SigmaStudio
|
||||
// Limiter cell docs), with zero headroom anywhere upstream (every
|
||||
// mixer/EQ/master-volume gain in the compiled program is unity).
|
||||
// A quiet synthetic test tone (well under 0 dBFS RMS) never
|
||||
// engaged it and sounded clean; real loudness-normalized FM/DAB/
|
||||
// streamed program content sits close to 0 dBFS RMS routinely,
|
||||
// triggering continuous gain-reduction ("pumping" per ADI's own
|
||||
// docs) heard as exactly the hiss/unintelligible-speech symptom
|
||||
// under investigation. Pulling both thresholds down to -6 dBFS
|
||||
// gives real margin without being so conservative it can't be
|
||||
// heard whether this was the mechanism.
|
||||
{
|
||||
const unsigned char deviceAddr =
|
||||
static_cast<unsigned char>(pins_.i2cAddr7 << 1);
|
||||
ADI_REG_TYPE serialInFix = 0x08U;
|
||||
if (SIGMA_WRITE_REGISTER_BLOCK(deviceAddr, 0x081FU, 1U,
|
||||
&serialInFix) != 0)
|
||||
constexpr float kLimiterThresholdDb = -6.0F;
|
||||
constexpr float kLimiterThresholdLinear = 0.50118723F; // 10^(-6/20)
|
||||
const std::int32_t thresholdFixpoint =
|
||||
core::floatToFixpoint823(kLimiterThresholdLinear);
|
||||
if (auto lim1 = safeloadFixpoint(
|
||||
static_cast<unsigned>(ADDR_LIMITER1_THRESHOLD),
|
||||
thresholdFixpoint);
|
||||
!lim1)
|
||||
{
|
||||
ESP_LOGE(kTag, "SerialInputRegister override failed after retries");
|
||||
return std::unexpected(Adau1701Error::DownloadFailed);
|
||||
ESP_LOGW(kTag, "Limiter1 threshold override failed");
|
||||
}
|
||||
if (sigma_verify_block(0x081FU, &serialInFix, 1U) != 0)
|
||||
if (auto lim2 = safeloadFixpoint(
|
||||
static_cast<unsigned>(ADDR_LIMITER2_THRESHOLD),
|
||||
thresholdFixpoint);
|
||||
!lim2)
|
||||
{
|
||||
ESP_LOGW(kTag,
|
||||
"SerialInputRegister read-back mismatch -- ACKed "
|
||||
"but did not land as 0x08");
|
||||
ESP_LOGW(kTag, "Limiter2 threshold override failed");
|
||||
}
|
||||
ESP_LOGI(kTag, "Limiter1/2 threshold set to %.1f dBFS",
|
||||
static_cast<double>(kLimiterThresholdDb));
|
||||
}
|
||||
|
||||
booted_ = true;
|
||||
|
||||
@@ -37,11 +37,15 @@ namespace bt1035 {
|
||||
struct Bt1035Pins {
|
||||
int uartTx; ///< ESP32 TX -> module RX.
|
||||
int uartRx; ///< ESP32 RX <- module TX.
|
||||
int resetGpio; ///< Module RESET# (pin 8). Read-only: configured as a
|
||||
///< floating input (2026-08-22), never driven — relies
|
||||
///< entirely on the module's own internal pull-up.
|
||||
int sysCtlGpio; ///< SYS_CTL (pin 34). Driven HIGH once at boot, then
|
||||
///< never touched again for the process lifetime.
|
||||
int resetGpio; ///< Module RESET# (pin 8), active-low. Driven (not
|
||||
///< floating, since 2026-08-23): held LOW together with
|
||||
///< SYS_CTRL past the datasheet §4.8 Reset Protection
|
||||
///< timeout (~1.8 s) to force a genuine power-down on
|
||||
///< every resetAndInitOnce() attempt, then released HIGH
|
||||
///< before SYS_CTRL's power-up pulse (§4.7).
|
||||
int sysCtlGpio; ///< SYS_CTL (pin 34), active-high. Driven LOW/HIGH on
|
||||
///< every resetAndInitOnce() call, together with
|
||||
///< resetGpio, to force a real power-cycle each retry.
|
||||
int ctsGpio; ///< Host->module UART_CTS (module pin 15). Diagnostic
|
||||
///< only (2026-08-22): read-only floating input, never
|
||||
///< driven — this driver does not implement hardware
|
||||
@@ -249,6 +253,24 @@ public:
|
||||
*/
|
||||
[[nodiscard]] std::expected<std::uint8_t, Bt1035Error> queryAutoReconnect();
|
||||
|
||||
/**
|
||||
* @brief setA2dpCodecConfig — enable optional A2DP codecs (AT+A2DPCFG).
|
||||
*
|
||||
* @dname setA2dpCodecConfig
|
||||
* @param bitmask BIT0=AAC, BIT1=aptX, BIT2=aptX-LL, BIT3=aptX-HD,
|
||||
* BIT4=aptX-Adaptive, BIT5=LDAC (§5.3.4); 0 forces
|
||||
* the mandatory SBC-only baseline.
|
||||
* @return Ok on success, or Bt1035Error. Only affects the *next* A2DP
|
||||
* negotiation — an already-connected peer keeps its current
|
||||
* codec until it reconnects (see disconnectA2dp()).
|
||||
* @pubstate writes UART.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-08-24
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Bt1035Error> setA2dpCodecConfig(
|
||||
std::uint8_t bitmask);
|
||||
|
||||
/**
|
||||
* @brief queryPairedList — enumerate paired remotes (AT+PLIST).
|
||||
*
|
||||
|
||||
@@ -42,11 +42,11 @@ constexpr int kPostUartMs = 100;
|
||||
constexpr int kSysCtlAssertMs = 20;
|
||||
/** Datasheet §4.8: "Reset Protection timeout (typically greater than
|
||||
* ~1.8 s) causes the device to power down if VCHG is not present and
|
||||
* SYS_CTRL is low." Held comfortably longer than that before each
|
||||
* power-up assert, to guarantee a genuine full power-down rather than a
|
||||
* pulse too short for the module's own protection timer to act on — see
|
||||
* resetAndInitOnce()'s comment for why this now runs on every attempt,
|
||||
* not just the first. */
|
||||
* SYS_CTRL is low." RESET# and SYS_CTRL are held asserted/low together for
|
||||
* comfortably longer than that before each power-up, to guarantee a
|
||||
* genuine full power-down rather than a pulse too short for the module's
|
||||
* own protection timer to act on — see resetAndInitOnce()'s comment for
|
||||
* why this now runs on every attempt, not just the first. */
|
||||
constexpr int kSysCtlDeassertMs = 2500;
|
||||
/** Measured live (2026-08-20, power/wiring confirmed sound with a
|
||||
* multimeter — VBAT_IN/SYS_CTRL/1.8V_OUT/VDD_IO all correct, TX/RX pins
|
||||
@@ -798,6 +798,15 @@ std::expected<void, Bt1035Error> Bt1035Driver::setAutoReconnect(
|
||||
return transmitAndExpectOk(core::buildBt1035SetAutoConnLine(times));
|
||||
}
|
||||
|
||||
std::expected<void, Bt1035Error> Bt1035Driver::setA2dpCodecConfig(
|
||||
std::uint8_t bitmask)
|
||||
{
|
||||
if (auto ready = ensureBooted(); !ready) {
|
||||
return ready;
|
||||
}
|
||||
return transmitAndExpectOk(core::buildBt1035A2dpCodecConfigLine(bitmask));
|
||||
}
|
||||
|
||||
std::expected<std::uint8_t, Bt1035Error> Bt1035Driver::queryAutoReconnect()
|
||||
{
|
||||
if (auto ready = ensureBooted(); !ready) {
|
||||
@@ -924,6 +933,32 @@ bool Bt1035Driver::waitForA2dpStreaming(int timeoutMs)
|
||||
ESP_LOGI(kTag, "stream wait: A2DPSTAT=%u",
|
||||
static_cast<unsigned>(static_cast<std::uint8_t>(*state)));
|
||||
if (*state == core::Bt1035A2dpState::Streaming) {
|
||||
// Diagnostic, 2026-08-24: confirm which codec actually got
|
||||
// negotiated now that AT+A2DPCFG=1 (AAC) is sent at boot --
|
||||
// previously this was never queried, so every link was
|
||||
// silently SBC-only with no way to tell from the logs.
|
||||
if (auto codec = queryA2dpEncoder(); codec) {
|
||||
// Feasycom guide §5.3.5's AT+A2DPENC response table for
|
||||
// BT1035 only lists SBC/aptX/aptX-HD/aptX-LL/aptX-
|
||||
// Adaptive -- no AAC code, even though §5.3.4's
|
||||
// AT+A2DPCFG can enable it. Not inventing a mapping for
|
||||
// that gap: an AAC link (or anything else undocumented)
|
||||
// logs as "unknown" rather than a guessed label.
|
||||
const char* name = "unknown";
|
||||
switch (*codec) {
|
||||
case core::Bt1035A2dpCodec::Sbc: name = "SBC"; break;
|
||||
case core::Bt1035A2dpCodec::Aptx: name = "aptX"; break;
|
||||
case core::Bt1035A2dpCodec::AptxHd: name = "aptX-HD"; break;
|
||||
case core::Bt1035A2dpCodec::AptxLl: name = "aptX-LL"; break;
|
||||
case core::Bt1035A2dpCodec::AptxAdaptive:
|
||||
name = "aptX-Adaptive";
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(kTag, "A2DP streaming with codec: %s", name);
|
||||
} else {
|
||||
ESP_LOGW(kTag, "A2DPENC query failed (%d)",
|
||||
static_cast<int>(codec.error()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const TickType_t now = xTaskGetTickCount();
|
||||
@@ -974,38 +1009,35 @@ std::expected<void, Bt1035Error> Bt1035Driver::runInitSequence()
|
||||
|
||||
std::expected<void, Bt1035Error> Bt1035Driver::resetAndInitOnce()
|
||||
{
|
||||
// RESET# (pin 8) is deliberately never driven by this driver (see
|
||||
// boot()'s GPIO config below): it's left a floating input, relying
|
||||
// entirely on the BT1035's own "fixed strong pull-up to VDD_IO"
|
||||
// (datasheet §4.8), which the datasheet explicitly says means the pin
|
||||
// "can therefore be left unconnected". Diagnostic test (2026-08-22) to
|
||||
// check whether the previous external RESET# drive was contributing to
|
||||
// the intermittent boot failures.
|
||||
//
|
||||
// SYS_CTRL (pin 34) alternative usage (2026-08-22 experiment): drive a
|
||||
// genuine LOW-then-HIGH cycle every time this function runs, not just
|
||||
// once ever. With RESET# now Hi-Z, SYS_CTRL is the only pin this
|
||||
// driver can still use to force a real power-cycle — previously it
|
||||
// was asserted HIGH exactly once at first boot and never touched
|
||||
// again, which meant every later retry (hardware::bt1035RetryTask
|
||||
// calls boot() again on failure) just re-listened on an
|
||||
// already-asserted line without ever actually power-cycling the
|
||||
// module. Held LOW for kSysCtlDeassertMs first so the module's own
|
||||
// Reset Protection timeout actually elapses (a shorter pulse risks
|
||||
// the module staying "protected" on, per datasheet §4.8, so the
|
||||
// retry wouldn't be a real fresh power-on at all), then HIGH for the
|
||||
// datasheet's own >=20ms minimum.
|
||||
// 2026-08-23 fix: force a genuine power-down/restart on every retry by
|
||||
// driving RESET# together with SYS_CTRL, per datasheet §4.8: "Assertion
|
||||
// of RESET# beyond the Reset Protection timeout (typically >~1.8 s)
|
||||
// causes the device to power down if VCHG is not present and SYS_CTRL
|
||||
// is low. FSC-BT1035 then requires a SYS_CTRL assertion ... to
|
||||
// restart." The prior design (2026-08-22) only pulsed SYS_CTRL and left
|
||||
// RESET# floating; per §4.7, "when booted, software takes control of
|
||||
// the internal regulators and the state of SYS_CTRL is ignored" — so
|
||||
// once the module had booted once, that pulse alone could never force
|
||||
// a real power-cycle. Asserting RESET# is the documented way to do it.
|
||||
const auto sysCtlPin = static_cast<gpio_num_t>(pins_.sysCtlGpio);
|
||||
const auto resetPin = static_cast<gpio_num_t>(pins_.resetGpio);
|
||||
const auto ctsPin = static_cast<gpio_num_t>(pins_.ctsGpio);
|
||||
const auto rtsPin = static_cast<gpio_num_t>(pins_.rtsGpio);
|
||||
|
||||
// Assert RESET# (active-low) and deassert SYS_CTRL together, held past
|
||||
// the ~1.8s Reset Protection timeout so the module actually powers
|
||||
// down rather than staying "protected" on (§4.8).
|
||||
gpio_set_level(resetPin, 0);
|
||||
gpio_set_level(sysCtlPin, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(kSysCtlDeassertMs));
|
||||
|
||||
// Release RESET# before requesting power-up, then assert SYS_CTRL for
|
||||
// the datasheet's own >=20ms minimum to start the boot (§4.7).
|
||||
gpio_set_level(resetPin, 1);
|
||||
gpio_set_level(sysCtlPin, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(kSysCtlAssertMs));
|
||||
ESP_LOGI(kTag, "power-up: SYS_CTRL=%d (want 1, driven) RESET#=%d "
|
||||
"(want 1, Hi-Z + internal pull-up, not driven by us)",
|
||||
"(want 1, driven)",
|
||||
gpio_get_level(sysCtlPin), gpio_get_level(resetPin));
|
||||
ESP_LOGI(kTag, "before banner wait: CTS=%d (module's flow-control "
|
||||
"input, host side floating) RTS/PIO2=%d (module's "
|
||||
@@ -1027,21 +1059,25 @@ std::expected<void, Bt1035Error> Bt1035Driver::boot()
|
||||
return {};
|
||||
}
|
||||
|
||||
// RESET# (pin 8) is never driven by this driver (2026-08-22 diagnostic
|
||||
// change): configured as a pure floating input, pull-up/pull-down both
|
||||
// explicitly disabled, so nothing on the ESP32 side influences this
|
||||
// net electrically — the BT1035's own internal RESET# pull-up (§4.8)
|
||||
// is the only thing holding it high. GPIO_MODE_INPUT (not OUTPUT) still
|
||||
// lets gpio_get_level() read it back for the diagnostic log below,
|
||||
// without ever driving it.
|
||||
// RESET# (pin 8) is actively driven (2026-08-23 fix): datasheet §4.8
|
||||
// states the Reset Protection timeout that forces a genuine power-down
|
||||
// is triggered by *asserting RESET#* (while SYS_CTRL is low), not by
|
||||
// toggling SYS_CTRL alone. Leaving RESET# floating (the 2026-08-22
|
||||
// diagnostic change) meant that mechanism was never actually invoked —
|
||||
// every retry only pulsed SYS_CTRL, which the module ignores once
|
||||
// already booted (§4.7: "when booted, software takes control of the
|
||||
// internal regulators and the state of SYS_CTRL is ignored"). Driven
|
||||
// HIGH immediately below (deasserted) before anything else runs, so
|
||||
// configuring the pin never glitches it low.
|
||||
gpio_config_t resetCfg = {};
|
||||
resetCfg.pin_bit_mask = 1ULL << pins_.resetGpio;
|
||||
resetCfg.mode = GPIO_MODE_INPUT;
|
||||
resetCfg.mode = GPIO_MODE_INPUT_OUTPUT;
|
||||
resetCfg.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
resetCfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
if (gpio_config(&resetCfg) != ESP_OK) {
|
||||
return std::unexpected(Bt1035Error::ResetFailed);
|
||||
}
|
||||
gpio_set_level(static_cast<gpio_num_t>(pins_.resetGpio), 1);
|
||||
|
||||
// SYS_CTRL (pin 34) stays actively driven (GPIO_MODE_INPUT_OUTPUT:
|
||||
// the INPUT bit is what makes gpio_get_level() read the real driven
|
||||
@@ -1123,7 +1159,7 @@ std::expected<void, Bt1035Error> Bt1035Driver::boot()
|
||||
ESP_LOGI(kTag, "auto-link disabled (AT+LINKCFG=0,0)");
|
||||
|
||||
booted_ = true;
|
||||
ESP_LOGI(kTag, "I2S slave mode enabled (AT+AUXCFG=3, AT+I2SCFG=35)");
|
||||
ESP_LOGI(kTag, "I2S slave mode enabled (AT+AUXCFG=3, AT+I2SCFG=67)");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user