Kode Dot
IMU
The accelerometer and gyroscope in a Kode Dot: a QMI8658A on the shared I2C bus, what each one measures, and the code to read them.
What it is
An IMU, an inertial measurement unit, is an accelerometer and a gyroscope in one package. Together they tell the Kode Dot how it is moving.
- The accelerometer measures acceleration on three axes, gravity included. At rest it reads 1 g straight down, so tilting the Kode Dot moves that 1 g from one axis to another: it is how you know which way is down. Shake it and the reading spikes; that is how a step counter counts.
- The gyroscope measures rotation rate on three axes, in degrees per second. It says nothing about where you are pointing, only how fast that is changing. Integrate it over time and you get an angle; leave it long enough and the angle drifts.
The part is a QMI8658A from QST: both sensors on one die, 16-bit outputs, a small motion co-processor for things like pedometer and tap detection, and a thermometer for the die itself.
How it is connected
flowchart LR p4["ESP32-P4"] -- "I2C, 0x6A" --> imu["QMI8658A"]
The QMI8658A sits on the P4's shared I2C bus at 0x6A, brought up by
kode_bsp_init and reached through kode_bsp_i2c(). Whether its two
interrupt pins reach the P4 is TBD; no Kode example uses them, and the
examples poll instead.
Specifications
| Thing | Value |
|---|---|
| Address | 0x6A. Register 0x00 reads 0x05, the part's identity |
| Accelerometer | ±2, ±4, ±8 or ±16 g, bits 6:4 of CTRL2 (0x03). At ±2 g one count is 2000/32768 mg |
| Gyroscope | ±16 to ±1024 dps in seven steps, bits 6:4 of CTRL3 (0x04). At ±256 dps one count is 256000/32768 mdps |
| Rate | Bits 3:0 of CTRL2 and CTRL3: 3 = 1000 Hz, 4 = 500, 5 = 250, 6 = 125, 7 = 62.5, 8 = 31.25 |
| On and off | CTRL7 (0x08): bit 0 accelerometer, bit 1 gyroscope. Both off out of reset |
| Output | 0x35–0x40: ax ay az gx gy gz, int16_t, low byte first. Set bit 6 of CTRL1 (0x02) and one read returns all twelve bytes |
| Temperature | 0x33–0x34, the die's own |
| Kode driver | None yet: kode_imu in the kode component targets the prototype's part and returns ESP_ERR_NOT_FOUND here. TBD: a QMI8658A driver |
| Validated | TBD: not yet run on a V1 board. Register map from QST's datasheet, cross-checked against a public driver |
Gotchas
Apps that use it
On the marketplace, apps that put this part to work. Install one from
start/first-app and see it before you write a line.
Example code
Start the accelerometer and the gyroscope, then log a sample ten times a second. Set the Kode Dot flat and one axis reads about 1000 mg with the other two near zero; tilt it and the 1000 mg walks from one axis to another.
/*
* imu.c - 2026-09-17 - from Kode with love
*
* Reads the Kode Dot's QMI8658A over the shared I2C bus. Brings the board
* up, wakes the accelerometer at ±2 g and the gyroscope at ±256 dps, both at
* 125 Hz, and logs acceleration in mg and rotation in mdps ten times a
* second. Flat on a table, the Z axis reads about 1000 mg.
*/
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "kode.h"
#define IMU_ADDR 0x6A
#define REG_CTRL1 0x02 /* interface: bit 6 = address auto-increment */
#define REG_CTRL2 0x03 /* accelerometer: range in bits 6:4, rate in 3:0 */
#define REG_CTRL3 0x04 /* gyroscope: same layout */
#define REG_CTRL7 0x08 /* bit 0 = accel on, bit 1 = gyro on */
#define REG_AX_L 0x35 /* twelve bytes: ax ay az gx gy gz, low byte first */
static i2c_master_dev_handle_t s_imu;
/* One register write: address byte, then the value. */
static void imu_write(uint8_t reg, uint8_t value)
{
uint8_t frame[2] = { reg, value };
ESP_ERROR_CHECK(i2c_master_transmit(s_imu, frame, 2, 100));
}
/* Attach the IMU to the bus kode_bsp owns, configure it, then log forever. */
void app_main(void)
{
ESP_ERROR_CHECK(kode_bsp_init());
i2c_device_config_t cfg = { .device_address = IMU_ADDR, .scl_speed_hz = KODE_I2C_FREQ_HZ };
ESP_ERROR_CHECK(i2c_master_bus_add_device(kode_bsp_i2c(), &cfg, &s_imu));
imu_write(REG_CTRL1, 0x40); /* auto-increment, so one read returns all six axes */
imu_write(REG_CTRL2, 0x06); /* ±2 g, 125 Hz */
imu_write(REG_CTRL3, 0x46); /* ±256 dps, 125 Hz */
imu_write(REG_CTRL7, 0x03); /* accelerometer and gyroscope on */
while (1) {
uint8_t reg = REG_AX_L, raw[12];
ESP_ERROR_CHECK(i2c_master_transmit_receive(s_imu, ®, 1, raw, 12, 100));
int16_t v[6];
for (int i = 0; i < 6; i++) v[i] = (int16_t)(raw[2 * i] | raw[2 * i + 1] << 8);
ESP_LOGI("imu", "acc %5ld %5ld %5ld mg gyro %6ld %6ld %6ld mdps",
v[0] * 2000L / 32768, v[1] * 2000L / 32768, v[2] * 2000L / 32768,
v[3] * 256000L / 32768, v[4] * 256000L / 32768, v[5] * 256000L / 32768);
vTaskDelay(pdMS_TO_TICKS(100));
}
}Compiled on the date in the header against the kode component with
ESP-IDF v6.1. TBD: a run on a V1 board.
In the SDK
kdrv_motion: the motion drivers inkode-sdk. TBD: a QMI8658A driver; today the folder holds the prototype'slsm6dsv.