xiao-imu-mpu-6050

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MPU-6050 / MPU-6500 motion sensing on the XIAO ESP32S3

基于XIAO ESP32S3的MPU-6050/MPU-6500运动检测

Bus pins are fixed by the board: SDA = D4 = GPIO5, SCL = D5 = GPIO6.
Wire.begin(D4, D5)
— pass the pins explicitly.
Read the
xiao-esp32s3
skill for the compile/upload workflow,
xiao-serial-monitor
for watching the output, and
xiao-i2c-sensors
for the general scan-first method. This skill is the MPU-60x0 instance of that method, plus the traps specific to it.
开发板的总线引脚是固定的:SDA = D4 = GPIO5SCL = D5 = GPIO6
Wire.begin(D4, D5)
— 需显式传入引脚参数。
编译/上传流程请参考
xiao-esp32s3
技能,查看输出请参考
xiao-serial-monitor
技能,通用的先扫描方法请参考
xiao-i2c-sensors
技能。本技能是该方法针对MPU-60x0模块的实例,同时包含该模块特有的陷阱。

The module is probably not the chip on the label

模块标注的芯片型号可能与实际不符

Modules sold as "MPU-6050" are frequently MPU-6500. Both answer at
0x68
and share the same data-register layout, so an I2C scan cannot tell them apart — the scan succeeds and the part still refuses to work with a 6050 driver.
WHO_AM_I
(register
0x75
) is the only thing that settles it:
ValueChip
0x68
MPU-6050
0x70
MPU-6500
0x71
MPU-9250
0x73
MPU-9255
Verified on the reference board:
WHO_AM_I = 0x70 -> MPU-6500
imu_motion
reads this at boot and adapts. Run
whoami_mpu
first if a driver is failing and you want the answer in isolation.
标注为“MPU-6050”的模块通常实际是MPU-6500。两款芯片的I2C地址均为
0x68
,且数据寄存器布局相同,因此I2C扫描无法区分它们——扫描成功但器件仍无法与6050驱动配合工作。
WHO_AM_I
寄存器(地址
0x75
)是唯一能确定芯片型号的依据:
数值芯片
0x68
MPU-6050
0x70
MPU-6500
0x71
MPU-9250
0x73
MPU-9255
在参考开发板上验证结果:
WHO_AM_I = 0x70 -> MPU-6500
imu_motion
会在开机时读取该寄存器并自动适配。若驱动无法正常工作,可先运行
whoami_mpu
单独获取芯片型号。

Wiring

接线方式

MPU moduleXIAO ESP32S3
VCC3V3
GNDGND
SDAD4 (GPIO5)
SCLD5 (GPIO6)
AD0 unconnected or low →
0x68
. AD0 high →
0x69
(change
IMU_ADDR
in the sketch).
MPU模块XIAO ESP32S3
VCC3V3
GNDGND
SDAD4 (GPIO5)
SCLD5 (GPIO6)
AD0引脚悬空或接低电平→地址为
0x68
。AD0引脚接高电平→地址为
0x69
(需修改代码中的
IMU_ADDR
)。

Sketches

代码示例

SketchWhat it does
assets/sketches/i2c_scan_mpu
scan the bus, flag
0x68
/
0x69
as a likely MPU
assets/sketches/whoami_mpu
read
WHO_AM_I
and name the chip
assets/sketches/imu_motion
accel + gyro + temperature, auto chip detection, calibration, moving/still
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 three were run on real hardware against an MPU-6500.
imu_motion
needs no library — it talks to registers directly. That is not purism: the obvious choice,
Adafruit_MPU6050
, cannot drive an MPU-6500 at all (pitfall 1).
Verified output:
WHO_AM_I = 0x70 -> MPU-6500
calibrating (200 samples) - keep still...
gyro bias (LSB): 215.9 111.1 -38.6
gravity baseline: 10.75 m/s^2 (theoretical 9.81)
ready
accel(m/s^2) x y z | gyro(deg/s) x y z | temp | state
A   0.85   0.32  10.69 | G     0.0     0.0     0.0 | 28.2C | still
Move the sensor →
MOVING
, and the onboard LED (GPIO21, active low) lights. Thresholds are at the top of the sketch:
ACCEL_THRESHOLD
0.5 m/s²,
GYRO_THRESHOLD
5 °/s. At rest the reference board held
still
across 70 consecutive samples with no false positives.
代码示例功能说明
assets/sketches/i2c_scan_mpu
扫描总线,标记
0x68
/
0x69
为疑似MPU模块
assets/sketches/whoami_mpu
读取
WHO_AM_I
寄存器并识别芯片型号
assets/sketches/imu_motion
读取加速度、陀螺仪、温度数据,自动检测芯片型号,校准,判断运动/静止状态
请将所需代码复制到用户工作区(文件夹名称必须与
.ino
文件名一致),而非从头编写驱动。这三个代码均已在搭载MPU-6500的真实硬件上运行验证。
imu_motion
无需任何库——它直接与寄存器通信。这并非刻意为之:常用的
Adafruit_MPU6050
库完全无法驱动MPU-6500(陷阱1)。
验证输出:
WHO_AM_I = 0x70 -> MPU-6500
calibrating (200 samples) - keep still...
gyro bias (LSB): 215.9 111.1 -38.6
gravity baseline: 10.75 m/s^2 (theoretical 9.81)
ready
accel(m/s^2) x y z | gyro(deg/s) x y z | temp | state
A   0.85   0.32  10.69 | G     0.0     0.0     0.0 | 28.2C | still
移动传感器→状态变为
MOVING
,板载LED(GPIO21,低电平有效)点亮。阈值设置在代码顶部:
ACCEL_THRESHOLD
为0.5 m/s²,
GYRO_THRESHOLD
为5 °/s。静止状态下,参考开发板连续70次采样均保持
still
状态,无误报。

Calibrate at boot, and measure the baseline rather than assuming it

开机时进行校准,测量基线而非采用理论值

Two corrections run in the first second. Both exist because the theoretical values are wrong on real parts by margins that swamp the measurement.
Gyro zero offset. These chips read several deg/s while sitting still — the reference unit showed 215.9 LSB on X, about 3.3 °/s. Integrate that for angle and it drifts ~200° per minute. Averaging a few hundred stationary samples and subtracting brings the resting reading to ±0.3 °/s.
Gravity baseline. At rest the accelerometer magnitude must equal 9.81 m/s² whatever the orientation — magnitude is rotation-invariant, so tilt cannot explain a deviation. The reference unit reads 10.75, about 9 % high, which is ordinary for clone parts (zero-g offset plus sensitivity tolerance). Subtracting the theoretical 9.81 leaves 0.94 of standing error, consuming most of a sensible motion threshold before the sensor has moved at all. So the sketch measures the resting magnitude at boot and subtracts that:
cpp
float accelDelta = fabs(accelMag - gravityBaseline);  // not - 9.80665
The cost is that the baseline is orientation-specific enough to matter if the module is remounted; reset the board to re-measure. Say so when handing the stream to the user — motion during calibration is silently baked in as the zero point, and opening a serial monitor resets the board, so calibration reruns every time.
真实器件的实际值与理论值存在偏差,该偏差会掩盖有效测量数据,因此开机后会进行两项修正。
陀螺仪零偏移:这些芯片在静止时仍会有若干度/秒的读数——参考器件的X轴读数为215.9 LSB,约3.3 °/s。若以此积分计算角度,每分钟会漂移约200°。通过对数百次静止采样取平均值并减去该偏移量,可将静止读数降至±0.3 °/s。
重力基线:静止时,加速度计的模长应等于9.81 m/s²,无论器件朝向如何——模长不受旋转影响,因此倾斜无法解释偏差。参考器件的读数为10.75,比理论值高约9%,这在仿冒器件中很常见(零重力偏移加上灵敏度公差)。若直接减去理论值9.81,会留下0.94的静止误差,在传感器未移动时就占用了大部分合理的运动阈值。因此代码会在开机时测量静止状态下的模长,并减去该实际测量值
cpp
float accelDelta = fabs(accelMag - gravityBaseline);  // 而非减去9.80665
代价是,若模块重新安装,基线会因朝向变化而产生显著差异;此时需重置开发板重新测量。交付给用户时需说明这一点——校准期间的运动会被默认为零点,且打开串口监视器会重置开发板,因此每次打开监视器都会重新进行校准。

Pitfalls

常见陷阱

These each cost a debugging session once.
  1. Adafruit_MPU6050
    cannot drive an MPU-6500.
    begin()
    checks
    WHO_AM_I
    , sees
    0x70
    instead of
    0x68
    , and returns false — so a correctly wired module reports
    MPU6050 not found - check wiring
    while an I2C scan shows
    0x68
    plainly. The wiring is fine; the library is simply refusing the part. Use the register-level sketch here, and read
    WHO_AM_I
    before blaming the bus.
  2. The temperature formula differs between the two chips. MPU-6050 is
    raw/340 + 36.53
    ; MPU-6500 is
    raw/333.87 + 21.0
    . Everything else about the data registers is identical, so using the wrong one produces plausible-looking temperature that is off by roughly 15 °C — the one channel with no obvious sanity check.
    imu_motion
    picks the formula from the detected chip.
  3. ACCEL_CONFIG2
    (
    0x1D
    ) exists only on the MPU-6500.
    It sets the accelerometer DLPF. On an MPU-6050 that address is not a documented register, so writing the accel filter config unconditionally pokes at something undefined. Gate it on the detected chip.
  4. Wake the part before reading anything. MPU-60x0 devices boot into sleep and return stale or zero data until
    PWR_MGMT_1
    (
    0x6B
    ) is cleared. The sketch resets (
    0x80
    ), waits, then selects the gyro X PLL as clock source (
    0x01
    ), which is more stable than the internal oscillator.
  5. Read all 14 data bytes in one burst.
    ACCEL_XOUT_H
    (
    0x3B
    ) through the gyro registers is one contiguous block. Reading axes in separate transactions samples them at different instants, which shows up as phantom rotation during fast motion.
  6. A message printed once in
    setup()
    is easy to lose.
    Draining the serial buffer before reading — the usual way to skip stale output — also discards the boot banner and any one-shot error line, so a sketch stuck in an error branch looks like a board producing no output at all. When debugging startup, read without discarding. This is how pitfall 1 first presented: zero bytes, no clue.
  7. Distinguish "stuck" from "crash-looping" with
    arduino-cli board list
    .
    Port still enumerating → the sketch is running and stuck or silent. Port gone or flickering → the board is resetting in a loop. Note that ROM bootloader messages never reach USB in
    hwcdc
    mode (they go to UART0 on D6/D7), so their absence is normal and not evidence of a boot problem.
  8. Keep serial output ASCII. The Windows console reads the port as the ANSI codepage, so Korean text in
    Serial.print
    comes back as
    ??????
    in captured logs. Explanations belong in comments; printed strings stay English.
这些陷阱都曾花费不少时间调试。
  1. Adafruit_MPU6050
    库无法驱动MPU-6500
    begin()
    函数会检查
    WHO_AM_I
    寄存器,若读取到
    0x70
    而非
    0x68
    ,则返回false——因此接线正确的模块会提示“MPU6050 not found - check wiring”,但I2C扫描明明显示
    0x68
    。接线没问题,只是库拒绝识别该器件。请使用本文提供的寄存器级代码,并在排查总线问题前先读取
    WHO_AM_I
    寄存器。
  2. 两款芯片的温度计算公式不同:MPU-6050的公式为
    raw/340 + 36.53
    ;MPU-6500的公式为
    raw/333.87 + 21.0
    。其他数据寄存器的布局完全相同,因此使用错误公式会得到看似合理但偏差约15°C的温度值——这是唯一没有明显校验方法的通道。
    imu_motion
    会根据检测到的芯片自动选择对应公式。
  3. ACCEL_CONFIG2
    0x1D
    )仅存在于MPU-6500
    :该寄存器用于设置加速度计数字低通滤波器(DLPF)。在MPU-6050中,该地址并非文档化寄存器,因此无条件写入加速度计滤波器配置会操作未定义的寄存器。需根据检测到的芯片进行条件判断。
  4. 读取数据前需唤醒器件:MPU-60x0器件开机后进入睡眠状态,直到
    PWR_MGMT_1
    0x6B
    )寄存器被清零,否则会返回陈旧或零数据。代码会先重置(
    0x80
    ),等待后选择陀螺仪X轴锁相环作为时钟源(
    0x01
    ),这比内部振荡器更稳定。
  5. 一次性读取全部14个数据字节:从
    ACCEL_XOUT_H
    0x3B
    )到陀螺仪寄存器是连续的块。若分多次读取各轴数据,会在不同时刻采样,导致快速运动时出现虚假旋转数据。
  6. setup()
    中仅打印一次的消息容易丢失
    :读取前清空串口缓冲区是跳过陈旧输出的常用方法,但这也会丢弃启动横幅和任何一次性错误信息,因此陷入错误分支的代码看起来像是开发板没有输出任何内容。调试启动过程时,请不要清空缓冲区。陷阱1最初就是这样呈现的:没有任何字节输出,毫无头绪。
  7. 使用
    arduino-cli board list
    区分“卡住”和“崩溃循环”
    :端口仍在枚举→代码正在运行但卡住或静默。端口消失或闪烁→开发板在循环重置。注意,在
    hwcdc
    模式下,ROM引导加载程序消息永远不会通过USB传输(它们会发送到D6/D7上的UART0),因此没有这些消息是正常的,并非引导问题的证据。
  8. 串口输出请使用ASCII编码:Windows控制台会将端口数据视为ANSI编码,因此
    Serial.print
    中的韩文文本在捕获的日志中会显示为
    ??????
    。说明内容应放在注释中;打印字符串请保持英文。

Handing the stream to the user

交付实时数据流给用户

Motion data is something the user watches while moving the board — a capture taken while it sat still on a desk shows nothing interesting. Verify with a bounded read yourself, then hand over the live command (see
xiao-serial-monitor
):
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. And remind them that an open monitor holds the port, so it has to be closed before the next upload.
运动数据需要用户在移动开发板时查看——静止放在桌面上捕获的数据毫无意义。您可以先进行有限时长的读取验证,再将实时命令交付给用户(参考
xiao-serial-monitor
技能):
powershell
.\scripts\mon.ps1 -Seconds 15    # 您的验证;执行后返回
mon                              # 用户的操作;运行直到按下Ctrl+C
切勿通过工具调用启动交互式监视器——它永远不会返回,会话会挂起。同时提醒用户,打开的监视器会占用端口,因此下次上传前必须关闭。

Adding another IMU

添加其他IMU模块

The method generalises: scan for the address, identify the part from its ID register before choosing a driver, wake it explicitly, read its data block in one burst, and calibrate against what the part actually reports at rest rather than what the datasheet says it should. The three sketches here are just instances of it.
该方法具有通用性:扫描地址,在选择驱动前通过ID寄存器识别器件,显式唤醒器件,一次性读取数据块,根据器件静止时的实际读数而非 datasheet 中的理论值进行校准。本文中的三个代码示例只是该方法的具体实现。