xiao-i2c-sensors

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

I2C sensors on the XIAO ESP32S3

XIAO ESP32S3上的I2C传感器

Bus pins are fixed by the board: SDA = D4 = GPIO5, SCL = D5 = GPIO6.
Wire.begin(D4, D5)
— the no-argument form picks the wrong pins.
Read the
xiao-esp32s3
skill for the compile/upload workflow, and
xiao-serial-monitor
for watching the output.
开发板的总线引脚是固定的:SDA = D4 = GPIO5SCL = D5 = GPIO6
Wire.begin(D4, D5)
—— 无参数形式会选择错误的引脚。
编译/上传流程请参考
xiao-esp32s3
技能,查看输出请参考
xiao-serial-monitor
技能。

Always scan before writing driver code

编写驱动代码前务必先扫描总线

Module silkscreens lie and address straps vary between clones. One scan settles both, and everything downstream depends on being right about it:
powershell
arduino-cli compile --fqbn esp32:esp32:XIAO_ESP32S3 -u -p <PORT> assets\sketches\i2c_scan
i2c_scan
prints every responding address, guesses the part, and then reads the ID register of anything at 0x76 and 0x29 so the guess becomes a fact. Verified output on the reference board:
  0x23  BH1750 (ADDR=L)
  0x29  BNO055 (ADDR=H)
  0x76  BME280/BMP280 (SDO=L)
  0x76 chip id = 0x60  -> BME280
  0x29 chip id = 0xA0  -> BNO055
模块丝印可能有误,不同克隆版的地址跳线也可能不同。一次扫描即可解决这两个问题,后续所有操作都依赖正确的地址:
powershell
arduino-cli compile --fqbn esp32:esp32:XIAO_ESP32S3 -u -p <PORT> assets\sketches\i2c_scan
i2c_scan
会打印所有响应的地址,猜测器件型号,然后读取地址0x76和0x29处器件的ID寄存器,将猜测转化为事实。参考开发板上的验证输出:
  0x23  BH1750 (ADDR=L)
  0x29  BNO055 (ADDR=H)
  0x76  BME280/BMP280 (SDO=L)
  0x76 chip id = 0x60  -> BME280
  0x29 chip id = 0xA0  -> BNO055

Sketches

代码示例

SketchWhat it does
assets/sketches/i2c_scan
scan + chip-ID identification
assets/sketches/test_bh1750
BH1750 unit test, 5 automated checks
assets/sketches/test_bme280
BME280 unit test, 8 automated checks
assets/sketches/test_bno055
BNO055 unit test, 10 automated checks
assets/sketches/sensors_all
all three streaming together, 1 Hz
Copy the one you need into the user's workspace (folder name must match the
.ino
name) rather than writing a driver from scratch. All five were run on real hardware; the unit tests all pass at 5/5, 8/8, 10/10.
Libraries:
powershell
arduino-cli lib install "BH1750"
arduino-cli lib install "Adafruit BME280 Library"
arduino-cli lib install "Adafruit BNO055"
sensors_all
output, one line per second:
lux     48.3 | 25.86C 1006.31hPa  42.7%RH | H  359.9 R    7.9 P   -5.2 | cal 1300
cal
is the BNO055's four calibration bytes (sys/gyro/accel/mag), each 0–3.
代码示例功能
assets/sketches/i2c_scan
总线扫描 + 芯片ID识别
assets/sketches/test_bh1750
BH1750单元测试,包含5项自动化检查
assets/sketches/test_bme280
BME280单元测试,包含8项自动化检查
assets/sketches/test_bno055
BNO055单元测试,包含10项自动化检查
assets/sketches/sensors_all
同时流式传输三个传感器的数据,频率1Hz
无需从零编写驱动,只需将所需的代码示例复制到用户工作区(文件夹名称必须与
.ino
文件名一致)。这五个示例均在真实硬件上运行过,单元测试分别达到5/5、8/8、10/10的通过率。
所需库:
powershell
arduino-cli lib install "BH1750"
arduino-cli lib install "Adafruit BME280 Library"
arduino-cli lib install "Adafruit BNO055"
sensors_all
的输出,每秒一行:
lux     48.3 | 25.86C 1006.31hPa  42.7%RH | H  359.9 R    7.9 P   -5.2 | cal 1300
cal
是BNO055的四个校准字节(系统/陀螺仪/加速度计/磁力计),每个字节取值范围为0–3。

Handing the stream to the user

将数据流交付给用户

A sensor stream is something the user watches, not something you screenshot once. Verify it yourself with a bounded read, then tell them how to open it themselves — the
xiao-serial-monitor
skill covers both halves:
powershell
.\scripts\mon.ps1 -Seconds 15    # your check; returns
mon                              # theirs; runs until Ctrl+C
Never launch the interactive monitor from a tool call — it never returns and the session hangs. Close instead with a handoff that names the command, how to run it, and how to stop it:
센서 값이 1초마다 흐르고 있습니다. 직접 보시려면:
! D:\path\to\mon.ps1
Claude Code에서
!
를 붙이면 이 세션에서 바로 실행됩니다. Ctrl+C로 종료. 프로필 단축키를 넣으셨다면 새 터미널에서
mon
만 쳐도 됩니다.
This matters more for sensors than for most things: the interesting part is what happens when the user covers the light sensor, breathes on the humidity sensor, or rotates the IMU. None of that shows up in a capture you took while the board sat still on a desk.
传感器数据流需要用户实时查看,而不是仅截取一次截图。你可以先通过有限时长读取来验证,然后告诉用户如何自行打开数据流——
xiao-serial-monitor
技能涵盖了这两部分:
powershell
.\scripts\mon.ps1 -Seconds 15    # 你的验证操作;执行后返回
mon                              # 用户的操作;运行直到按下Ctrl+C
切勿通过工具调用启动交互式监视器——它不会返回,会话会挂起。应改为告知用户命令名称、运行方式和停止方式:
传感器数据正在每秒流式传输。如需查看,请执行:
! D:\path\to\mon.ps1
在Claude Code中添加
!
即可在当前会话中直接运行。按下Ctrl+C可终止。 若已设置配置文件快捷方式,在新终端中输入
mon
即可运行。
这一点对传感器而言尤为重要:有趣的部分是用户遮挡光传感器、对着湿度传感器呼气或旋转IMU时的变化。这些内容在开发板静置时的截图中是无法体现的。

How these unit tests are built

单元测试的构建规则

Two rules make the difference between a test that proves something and a test that just prints numbers:
Bounds-check, don't eyeball. Every reading is asserted against the datasheet range, because the failure that actually happens is a dead bus returning
0
,
NaN
, or a pegged rail — not a part that is 2 % out of calibration. The strongest single check in the set is the BNO055's gravity magnitude: however the board is lying,
|g|
must come out near 9.8 m/s², so one assertion covers all three accelerometer axes and their scaling at once.
Never assert something that needs a human hand. An early version of the BH1750 test asserted that consecutive readings differ, reasoning that a frozen bus repeats itself. Under steady room light a healthy sensor returns exactly 46.7 lx every time, so the test failed on working hardware whenever nobody happened to be waving at it. Checks that need physical stimulus — covering the light sensor, breathing on the humidity sensor, moving the IMU to calibrate — belong in a printed "manual check" section that is not scored. Keep the automated count honest and it stays worth reading.
以下两条规则区分了真正能验证功能的测试和仅打印数值的测试:
进行边界检查,而非目视判断。 每次读数都需根据数据手册的范围进行断言,因为实际出现的故障通常是总线失效返回
0
NaN
或固定值——而不是器件校准误差超出2%。这套测试中最有效的检查是BNO055的重力幅值:无论开发板如何放置,
|g|
必须接近9.8 m/s²,因此一个断言即可同时覆盖三个加速度计轴及其缩放比例。
切勿断言需要人工操作的内容。 BH1750测试的早期版本断言连续读数必须不同,理由是冻结的总线会重复返回相同值。但在稳定的室内光线条件下,正常的传感器每次都会返回精确的46.7 lx,因此当无人晃动传感器时,正常硬件上的测试会失败。需要物理刺激的检查——比如遮挡光传感器、对着湿度传感器呼气、移动IMU进行校准——应放在未计分的“手动检查”打印部分。保持自动化计数的准确性,才能让测试结果有参考价值。

Pitfalls

常见陷阱

These each cost a debugging session once.
  1. A BNO055 at 0x29 will not talk to the default driver. The Adafruit library constructor defaults to 0x28; a module with ADDR/COM3 strapped high sits at 0x29 and
    begin()
    fails while the scan shows the device plainly. Pass it explicitly:
    Adafruit_BNO055 bno(55, 0x29, &Wire);
  2. setExtCrystalUse(true)
    silently zeroes the BNO055.
    Many clone modules have no 32 kHz crystal fitted, and selecting an absent clock source parks the chip in idle with every output stuck at
    0.00
    — temperature, gravity, euler angles, all of it. The trap is that it still looks healthy: the power-on self test reports
    0x0F
    and
    system_error
    stays
    0
    . Leave it on the internal oscillator unless you have confirmed the crystal exists.
    Guard against it by checking the mode rather than the self test:
    cpp
    uint8_t sysStat, selfTest, sysErr;
    bno.getSystemStatus(&sysStat, &selfTest, &sysErr);
    // 5 = sensor fusion algorithm running. Anything else and the
    // orientation numbers are not real, whatever self_test says.
  3. 0x76 does not mean BME280. BMP280 shares the address and has no humidity channel, so a humidity read returns garbage rather than an error. Read chip-ID register
    0xD0
    :
    0x60
    = BME280,
    0x58
    = BMP280,
    0x61
    = BME680,
    0x55
    = BMP180. Decide the driver from that, not the module's label.
  4. BH1750 needs ~120 ms after
    begin()
    before the first conversion.
    Reading immediately returns a stale or zero value that looks like a wiring fault. The tests wait 200 ms.
  5. One missing module should not silence the others. In
    sensors_all
    each sensor is probed independently and a missing one is reported once at startup, then skipped. A single loose wire otherwise takes the whole stream down and sends you looking for a software bug.
  6. Keep serial output ASCII. The Windows console reads the port as the ANSI codepage, so Korean or accented text in
    Serial.print
    comes back as mojibake in captured logs. Put the explanation in comments, keep the printed strings English.
这些陷阱都曾花费过调试时间:
  1. 地址0x29的BNO055无法与默认驱动通信。 Adafruit库的构造函数默认地址为0x28;若模块的ADDR/COM3引脚接高电平,器件地址为0x29,此时
    begin()
    会失败,但扫描结果会显示该器件存在。需显式传入地址:
    Adafruit_BNO055 bno(55, 0x29, &Wire);
  2. setExtCrystalUse(true)
    会使BNO055静默返回0。
    许多克隆模块未安装32 kHz晶体,选择不存在的时钟源会使芯片进入空闲状态,所有输出(温度、重力、欧拉角等)都固定为
    0.00
    。陷阱在于芯片看起来仍正常:上电自检报告
    0x0F
    system_error
    始终为
    0
    。除非确认晶体存在,否则应使用内部振荡器。
    可通过检查模式而非自检结果来避免此问题:
    cpp
    uint8_t sysStat, selfTest, sysErr;
    bno.getSystemStatus(&sysStat, &selfTest, &sysErr);
    // 5 = 传感器融合算法正在运行。任何其他值都意味着姿态数据无效,无论自检结果如何。
  3. 地址0x76并不代表是BME280。 BMP280共享该地址且无湿度通道,因此读取湿度会返回垃圾数据而非错误。读取芯片ID寄存器
    0xD0
    0x60
    = BME280,
    0x58
    = BMP280,
    0x61
    = BME680,
    0x55
    = BMP180。应根据该值选择驱动,而非模块标签。
  4. BH1750在
    begin()
    后需要约120 ms才能完成首次转换。
    立即读取会返回过时或0值,看起来像是接线故障。测试中会等待200 ms。
  5. 单个模块缺失不应导致其他模块无输出。
    sensors_all
    中,每个传感器都会被独立探测,缺失的模块会在启动时报告一次,之后被跳过。否则,一根松动的线会导致整个数据流中断,让你误以为是软件bug。
  6. 串口输出请使用ASCII编码。 Windows控制台以ANSI代码页读取端口,因此
    Serial.print
    中的韩文或带重音的文本会在捕获的日志中显示为乱码。说明文字可放在注释中,打印字符串请使用英文。

Adding another I2C sensor

添加其他I2C传感器

Scan first and confirm the address; identify the part from its ID register where one exists; bounds-check every reading against the datasheet; and keep any check that needs a human hand out of the automated score. That is the whole method — the five sketches here are just instances of it.
首先扫描并确认地址;若有ID寄存器,通过其识别器件;根据数据手册对每次读数进行边界检查;将需要人工操作的检查排除在自动化计分之外。这就是完整的方法——此处的五个代码示例只是该方法的具体实现。