Commit Graph
3 Commits
Author SHA1 Message Date
mattandClaude 9b5f98ae7b Add I2C bus scan and pull-up presence test; fix S3 board orientation in README
Detection previously reported only "no ACK at 0x2a/0x2b", which cannot tell a
sensor at an unexpected address from one that is not wired at all.

- Sweep the full 1..126 address range and log any responders.
- Before enabling the internal pull-ups, sample SDA/SCL as plain inputs. A
  powered C4001 breakout holds them high through its own pull-ups, so an
  all-low reading means nothing powered is on those pins -- a wiring answer
  rather than a protocol one. This immediately identified a miswire that the
  ACK-only log had left ambiguous.
- Drop the \r\n from the raw UART probe. DFRobot_C4001_UART::writeReg sends the
  bare command string with no terminator, so the probe should match it.

README had the YD-ESP32-S3 header order right but the orientation wrong: it
said USB-C at the top when the module is at the top and both USB-C sockets are
at the bottom. Read upside down that walks the sensor into the PSRAM pins.
Also note the separate plain LED on GPIO38 vs the RGB on GPIO48.

Verified on hardware: C4001 detected at I2C 0x2a, init ok, streaming range,
velocity and energy in speed mode.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 12:07:26 -07:00
mattandClaude 9c1c3fe1e8 Add YD-ESP32-S3 support, boot diagnostics, and hang guards
Board is now selected by `default_envs` in platformio.ini rather than being
hardcoded. Both boards stay supported and both targets build.

- src/board_pins.h holds one block per board plus an ledSet() abstraction, so
  main.cpp has no board-specific code. The C6's active-low LED and the S3's
  WS2812 RGB go through the same call, and the C6 antenna-switch lines are
  behind #if HAS_RF_SWITCH.
- YD-ESP32-S3 env targets esp32-s3-devkitc1-n16r8 (verified against the actual
  part: ESP32-S3 rev v0.2, 16MB quad flash, 8MB octal PSRAM, CH343 bridge).
  Serial is UART0 -> CH343, not USB CDC.

Fixes for hangs that left the board alive on USB but dead on the network:

- The vendor library's UART path has loops with no timeout: sensorStop() is
  while(1) that only exits on a "sensorStop" reply, and the UART branch of
  getStatus() is while(len == 0). Calling either with no sensor attached hung
  setup() permanently. UART is now probed with a raw 1.5s read on Serial1, and
  the library only gets the port once bytes actually arrive. The I2C path is
  bounded, so prefer I2C.
- Floating SDA/SCL can stall Wire.endTransmission() forever. Check both lines
  read high with pull-ups before touching Wire.
- Serial.setTxTimeoutMs(0) is a trap: HWCDC::write seeds a uint32_t from it and
  does a bare tries--, so 0 underflows to 4.29e9 and spins for ~49 days. Use 10,
  and only on USB CDC boards.

Diagnostics, so "did it crash?" is answerable without a serial cable:

- Boot count in RTC memory, esp_reset_reason(), free/min heap and WiFi drop
  count exposed via /api/live and shown as header pills in the UI.
- WiFi auto-reconnect, and retry of station mode from the AP fallback. Without
  it a single dropout left the board unreachable forever, which looks exactly
  like a crash.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 11:55:03 -07:00
mattandClaude b9162db707 XIAO ESP32-C6 + DFRobot C4001 mmWave presence sensor
Firmware for a Seeed XIAO ESP32-C6 driving a C4001 24GHz radar, with a
web UI for live data and calibration.

- Auto-detects whether the C4001 is on I2C (0x2A/0x2B) or UART, so the
  board's mode switch position doesn't matter. Serves the probe log over
  HTTP when nothing is found.
- Single-page UI inlined into flash (no CDN, so it works in AP mode):
  presence state, target count, range, speed, energy, rolling 60s chart,
  and mode-aware calibration controls.
- Joins WiFi from a gitignored include/secrets.h, falls back to a
  "xiao-mmwave" AP, registers mmwave.local.
- Poll rate adapts to the interface: UART at 9600 costs ~60ms per register
  round-trip, so polling it at the I2C rate starved the HTTP server.

Two hardware gotchas documented in the README:

- The sensor UART must not use D6/D7. Those are GPIO16/17, the C6's
  hard-wired U0TXD/U0RXD. Pointing Serial1 at them deadlocks the UART
  driver and trips the interrupt watchdog, boot-looping the board. UART1
  has no fixed pins, so it routes through the GPIO matrix to D10/D9.
- DFRobot's own wiring table pairs the sensor's RX with the MCU's RX, and
  their library swaps the rxpin/txpin parameter names between the header
  and the .cpp.

Verified on ESP32-C6FH4 rev v0.1, 4MB flash: builds at 57.6% flash /
11.9% RAM, flashes over the native USB Serial/JTAG port, and reads a live
C4001 on I2C @ 0x2A.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 10:05:27 -07:00