From 9b5f98ae7bcc97c329b329530acb6ab65ebdce5d Mon Sep 17 00:00:00 2001 From: matt_ed Date: Fri, 14 Aug 2026 12:07:26 -0700 Subject: [PATCH] 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 --- README.md | 12 +++++++++++- src/main.cpp | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a289bed..513b4e2 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,14 @@ itself — the header order below is only for orientation. | RX (UART) | **GPIO17** | P1 | | OUT | GPIO16 | P1, optional | -Header order, USB-C end at the top: +Header order. **Orientation matters: hold the board with the ESP32-S3-WROOM-1 +module and its antenna at the TOP, and both USB-C sockets at the BOTTOM.** +Pin 1 of each column is the end nearest the module, furthest from the USB +sockets. (Getting this upside down is an easy way to wire the sensor into the +PSRAM pins.) ``` + [ ESP32-S3-WROOM-1 module + antenna ] <- TOP P1 (left) P2 (right) 1 3V3 1 GND 2 3V3 2 GPIO43 U0TXD -> CH343 @@ -89,8 +94,13 @@ Header order, USB-C end at the top: 20 GPIO14 20 GPIO19 USB D- 21 5V 21 GND 22 GND 22 GND + [ USB-C: COM (CH343) | USB ] <- BOTTOM ``` +Reference pinout: https://mischianti.org/vcc-gnd-studio-yd-esp32-s3-devkitc-1-clone-high-resolution-pinout-and-specs/ +Note that board also has a plain built-in LED on **GPIO38**, separate from the +RGB WS2812 on GPIO48 that this firmware drives. + Do not use for I/O: **GPIO35/36/37** (octal PSRAM — the R8 in N16R8), **GPIO43/44** (UART0 to the CH343), **GPIO19/20** (native USB), GPIO0/45/46 (strapping), GPIO48 (RGB LED), GPIO26–32 (flash, not broken out). diff --git a/src/main.cpp b/src/main.cpp index 74184c6..03a4623 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -104,6 +104,24 @@ static bool i2cProbe(uint8_t addr) { // setup() before WiFi ever starts. Check the lines are actually high with // pull-ups on before handing them to Wire. static bool i2cBusIdle() { + // First, with the internal pull-ups OFF. A powered C4001 breakout has its + // own pull-ups, so the lines stay high. If they sag to 0 with nothing + // driving them, there is no powered module on these pins at all -- which + // separates "sensor wired but mute" from "sensor not actually connected". + pinMode(PIN_I2C_SDA, INPUT); + pinMode(PIN_I2C_SCL, INPUT); + delayMicroseconds(500); + int sdaHi = 0, sclHi = 0; + for (int i = 0; i < 8; i++) { + sdaHi += digitalRead(PIN_I2C_SDA); + sclHi += digitalRead(PIN_I2C_SCL); + delayMicroseconds(100); + } + sensor.detectLog += "I2C: external pull-ups sda=" + String(sdaHi) + "/8 scl=" + + String(sclHi) + "/8 " + + ((sdaHi == 8 && sclHi == 8) ? "(module present)" + : "(NO powered module on these pins)") + "\n"; + pinMode(PIN_I2C_SDA, INPUT_PULLUP); pinMode(PIN_I2C_SCL, INPUT_PULLUP); delayMicroseconds(200); @@ -135,7 +153,8 @@ static bool uartSensorPresent() { while (Serial1.available()) Serial1.read(); // flush boot noise // A running C4001 streams frames continuously; nudge it in case it's stopped. - Serial1.print("sensorStart\r\n"); + // No terminator -- DFRobot_C4001_UART::writeReg sends the bare string. + Serial1.print("sensorStart"); uint32_t t0 = millis(); while (millis() - t0 < 1500) { @@ -171,6 +190,18 @@ static void detectSensor() { sensor.detectLog += "I2C: no ACK at 0x" + String(a, HEX) + "\n"; } } + + // Neither expected address answered. Sweep the whole bus so the log can + // distinguish "sensor at an unexpected address" from "nothing on the bus + // at all" -- which usually means power, swapped SDA/SCL, or the module's + // mode switch is set to UART. + String found; + for (uint8_t a = 1; a < 127; a++) { + Wire.beginTransmission(a); + if (Wire.endTransmission() == 0) found += " 0x" + String(a, HEX); + } + sensor.detectLog += found.length() ? "I2C: bus scan ->" + found + "\n" + : "I2C: bus scan found no devices\n"; } // end if (i2cBusIdle()) Serial.println(" probing UART ...");