IMU#
The kite imu command reads the onboard LSM6DS3TR-C inertial measurement unit
on the XIAO BLE Sense. It provides 3-axis accelerometer and 3-axis gyroscope
readings through Zephyr's generic sensor API, so the code works the same way
regardless of which specific IMU chip is on the board.
Source: lib/kite/imu/kite_imu.c
Commands#
| Command | Description |
|---|---|
kite imu read |
Read both accelerometer and gyroscope |
kite imu accel |
Read accelerometer only (m/s^2) |
kite imu gyro |
Read gyroscope only (rad/s) |
Examples:
uart:~$ kite imu read
Accel (m/s^2):
X: 0.314733
Y: -0.194265
Z: 9.698172
Gyro (rad/s):
X: -0.012217
Y: 0.003054
Z: 0.001527
uart:~$ kite imu accel
X: 0.311679
Y: -0.197319
Z: 9.701226
Kconfig#
This selects both SENSOR (the generic sensor framework) and LSM6DSL (the
driver for the LSM6DS3TR-C, which uses the LSM6DSL driver family).
The sample prj.conf also configures the driver's output data rate:
- Disables the trigger/interrupt path -- we poll on demand from the shell, so there's no need for data-ready interrupts.
- Sets the output data rate. The value
4maps to 104 Hz in the LSM6DSL driver's ODR table. This is a reasonable default for interactive use.
Hardware Notes#
The LSM6DS3TR-C on the XIAO BLE Sense is connected to I2C0 (the internal
bus), not the XIAO connector's I2C1 bus that kite i2c uses. The device is
defined in the board's base device tree (xiao_ble_nrf52840_sense.dts), so no
overlay configuration is needed -- the driver is ready as soon as it's enabled.
Code Walkthrough#
Device Reference#
The IMU device is obtained via its device tree node label:
This is different from the other subsystems that use DT_ALIAS. The IMU has a
fixed node label in the board's device tree, so there's no need for an alias
in the overlay.
The Sensor API Pattern#
Zephyr's sensor API uses a two-step fetch-then-get pattern. The read command
demonstrates both steps:
int ret = sensor_sample_fetch(imu_dev); // (1)!
...
struct sensor_value accel[3];
struct sensor_value gyro[3];
ret = sensor_channel_get(imu_dev, SENSOR_CHAN_ACCEL_XYZ, accel); // (2)!
...
ret = sensor_channel_get(imu_dev, SENSOR_CHAN_GYRO_XYZ, gyro);
sensor_sample_fetchtriggers a full sample from the hardware. The driver reads all channels from the IMU over I2C and stores the results internally. This is a blocking call -- it waits for the I2C transfer to complete.sensor_channel_getretrieves the fetched data for a specific channel.SENSOR_CHAN_ACCEL_XYZpopulates a 3-element array with X, Y, Z acceleration. No I2C traffic happens here -- it just reads from the driver's internal buffer.
The accel and gyro commands use sensor_sample_fetch_chan instead, which
fetches only the requested channel:
- Only fetches accelerometer data, skipping the gyroscope. This is slightly more efficient if you only need one sensor.
sensor_value Format#
Zephyr represents sensor readings as a struct sensor_value with two integer
fields:
struct sensor_value {
int32_t val1; /* integer part */
int32_t val2; /* fractional part in millionths */
};
So a value of 9.698172 m/s^2 is stored as val1 = 9, val2 = 698172. The
helper function handles the sign of val2 for negative values:
static void print_sensor_val(const struct shell *sh, const char *label,
struct sensor_value *val)
{
bool neg = (val->val1 < 0) || (val->val2 < 0);
int32_t abs_val1 = val->val1 < 0 ? -val->val1 : val->val1;
int32_t abs_val2 = val->val2 < 0 ? -val->val2 : val->val2;
shell_print(sh, " %s: %s%d.%06d", label, neg ? "-" : "",
abs_val1, abs_val2); // (1)!
}
- For negative values like
-0.194265,val1 = 0andval2 = -194265. The sign is carried by whichever field is non-zero, so we check both and print a leading minus when either is negative.