Skip to content

drivers/sensors: add LSM6DS3TR-C uORB driver for the XIAO ESP32-S3 - #19997

Merged
acassis merged 2 commits into
apache:masterfrom
FelipeMdeO:feature/lsm6ds3trc-driver
Aug 29, 2026
Merged

drivers/sensors: add LSM6DS3TR-C uORB driver for the XIAO ESP32-S3#19997
acassis merged 2 commits into
apache:masterfrom
FelipeMdeO:feature/lsm6ds3trc-driver

Conversation

@FelipeMdeO

@FelipeMdeO FelipeMdeO commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

No driver exists for the LSM6DS3TR-C. lsm6dsl.c is the closest
register-compatible match (same WHO_AM_I, same CTRL1_XL/CTRL2_G layout)
but is the deprecated legacy char-device style; lsm6dso32_uorb.c is the
closest uORB-style match but is for a different chip variant. The new
driver borrows lsm6dso32_uorb.c's structure (dual sensor_lowerhalf_s,
raw I2C_TRANSFER helpers) and lsm6dsl.h's register map -- fixing a bug
in the header it was ported from along the way: LSM6DSL_FIFO_CTRL2_SHIFT
is defined as 255 instead of 0.

Delivery mode is chosen the same way mpu6050 does: kthread polling by
default, or interrupt-driven if the board supplies attach().

On the XIAO ESP32-S3 with Seeed's IMU Breakout Board, INT1/INT2 route to
GPIO3/GPIO4 (confirmed from the breakout board's schematic, not guessed).
Only INT1/GPIO3 is wired up, since one pin is now enough; GPIO4/INT2 is
documented as available but unused.

Also: CTRL1_XL's FS_XL bits were never actually written to match the
driver's own software default (4g) -- registration set the in-memory
value but the chip stayed at its 2g reset default until a caller issued
an explicit SNIOC_SETFULLSCALE. register() now writes it.

Impact

New optional driver (CONFIG_SENSORS_LSM6DS3TRC, default n), no impact
on boards that don't enable it.

Testing

Host: Ubuntu 24.04.4 LTS. xtensa-esp-elf-gcc (crosstool-NG
esp-14.2.0_20241119) 14.2.0. checkpatch.sh (style + -m commit message)
clean.

Compiles clean for the XIAO ESP32-S3 target, rebased onto current
master with no conflicts.

On hardware -- Seeed XIAO ESP32-S3 Sense + IMU Breakout Board
(LSM6DS3TR-C @ 0x6A, I2C0 SDA=GPIO5/SCL=GPIO6, INT1=GPIO3), native USB
Serial/JTAG console:

i2c dev 0 0x7f finds the IMU at 0x6a and the board's OLED at 0x3c,
matching the breakout board schematic.

i2c get -a 0x6a -r 0x0f reads WHO_AM_I = 0x6a.

Polling mode: sensor_accel0/sensor_gyro0 stream continuously for
minutes with no read errors, reproduced across multiple reboots.

Interrupt mode: reproduced across multiple fresh reboots, ~300 samples
of each topic per 6s window (matching the configured 52 Hz ODR), with
shared timestamps down to the microsecond between the two topics
per event -- confirming both come from the same burst read.

Console results:

nsh> i2c dev 0 0x7f
NOTE: Some devices may not appear with this scan.
You may also try a scan with the -z flag to discover more devices using a zero-byte write request.
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- 6a -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

nsh> i2c get -a 0x6a -r 0x0f
READ Bus: 0 Addr: 6a Subaddr: 0f Value: 6a

nsh> uorb_listener sensor_accel0,sensor_gyro0

Monitor objects num:2
object_name:sensor_gyro, object_instance:0
object_name:sensor_accel, object_instance:0
sensor_gyro(now:21370000):0x3fc96ab4B
sensor_accel(now:21370000):0x3fc96ab4B
sensor_gyro(now:21380000):0x3fc96ab4B
sensor_accel(now:21380000):0x3fc96ab4B
sensor_gyro(now:21400000):0x3fc96ab4B
sensor_accel(now:21400000):0x3fc96ab4B
sensor_gyro(now:21420000):0x3fc96ab4B
sensor_accel(now:21420000):0x3fc96ab4B
sensor_gyro(now:21440000):0x3fc96ab4B
sensor_accel(now:21440000):0x3fc96ab4B

@FelipeMdeO
FelipeMdeO force-pushed the feature/lsm6ds3trc-driver branch from ad640a0 to 3bd75e6 Compare August 28, 2026 18:26
@github-actions github-actions Bot added Arch: xtensa Issues related to the Xtensa architecture Area: Sensors Sensors issues Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. Board: xtensa labels Aug 28, 2026
No driver exists for this exact chip. lsm6dsl.c is the closest
register-compatible match but is the deprecated legacy char-device
style; lsm6dso32_uorb.c is the closest uORB-style match but is for a
different chip variant. The new driver borrows lsm6dso32_uorb.c's
structure (dual sensor_lowerhalf_s, raw I2C_TRANSFER helpers) and
lsm6dsl.h's register map -- fixing a bug in the header it was ported
from along the way: LSM6DSL_FIFO_CTRL2_SHIFT is defined as 255 instead
of 0.

Delivery mode is chosen the same way mpu6050 does: kthread polling by
default, or interrupt-driven if the board supplies attach(). Unlike
the earlier lsm6dso32-style design this went through first -- one INT
pin and one activate()/interrupt path per sub-sensor -- the shipped
version uses a single shared INT pin for both, mirroring mpu6050's own
one-handler-one-worker design (apache#19601) instead. The two-independent-
paths version worked for accel alone but was intermittently broken for
gyro: activate() sometimes never actually turned CTRL2_G on even
though the interrupt-enable bit was written correctly, and other times
the whole console hung -- a real race, never conclusively root-caused
on a serial console with no JTAG available. The LSM6DS3TR-C supports
OR'ing both DRDY_XL and DRDY_G onto one pin via independent enable
bits in that pin's INTn_CTRL register, so there was no need for two
paths in the first place: one ISR times the burst, one HPWORK worker
reads OUT_TEMP_L..OUTZ_H_A (14 contiguous bytes covering temp, gyro
and accel in one I2C transaction) and pushes whichever topic(s) are
currently subscribed. activate() now just flips each sub-sensor's own
bit in the shared register instead of running its own attach.

On the XIAO ESP32-S3 with Seeed's IMU Breakout Board, INT1/INT2 route
to GPIO3/GPIO4 (confirmed from the breakout board's schematic, not
guessed). Only INT1/GPIO3 is wired up, since one pin is now enough;
GPIO4/INT2 is documented as available but unused.

Also: CTRL1_XL's FS_XL bits were never actually written to match the
driver's own software default (4g) -- registration set the in-memory
value but the chip stayed at its 2g reset default until a caller
issued an explicit SNIOC_SETFULLSCALE. register() now writes it.

Validated on the bench, both modes, reproduced across multiple fresh
reboots: WHO_AM_I reads 0x6a, sensor_accel0/sensor_gyro0 stream
continuously. Interrupt mode delivers ~300 samples of each per 6s
window with shared timestamps down to the microsecond between the two
topics per event, confirming both come from the same burst read.

Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>

@acassis acassis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@github-actions github-actions Bot removed the Arch: xtensa Issues related to the Xtensa architecture label Aug 28, 2026
@FelipeMdeO
FelipeMdeO requested a review from Donny9 as a code owner August 28, 2026 19:36
@FelipeMdeO
FelipeMdeO force-pushed the feature/lsm6ds3trc-driver branch from 1595736 to 6812081 Compare August 28, 2026 19:36
@github-actions github-actions Bot added Area: Documentation Improvements or additions to documentation and removed Area: Sensors Sensors issues labels Aug 28, 2026
Adds a dedicated page (matching the lsm6dso32 one, the closest
register-compatible driver already documented) instead of a plain list
entry: chip description, registration examples for both polling and
interrupt-driven mode -- including the single-shared-INT-pin
config_s/attach() shape this driver actually uses, unlike sensors that
need one interrupt pin per sub-sensor -- and the two custom ioctls
(SNIOC_WHO_AM_I, SNIOC_SETFULLSCALE) with their argument units.

Requested in review on this PR.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
@FelipeMdeO
FelipeMdeO force-pushed the feature/lsm6ds3trc-driver branch from 6812081 to 384afb5 Compare August 28, 2026 19:58
@FelipeMdeO

FelipeMdeO commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Documentation added

@acassis
acassis merged commit 4b0a206 into apache:master Aug 29, 2026
28 checks passed
FelipeMdeO added a commit to FelipeMdeO/nuttx that referenced this pull request Aug 29, 2026
…bringup.c

Merging master (which now carries the LSM6DS3TR-C driver, PR apache#19997)
into this branch left the CONFIG_MMCSD_SPI block's closing brace
missing and a trailing-whitespace-only line behind, breaking the
build. Restore the missing "}" and drop the stray whitespace.

Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: Felipe Moura <mouraf@fiteclabs.org.br>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Documentation Improvements or additions to documentation Board: xtensa Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants