Kode Dot
Magnetometer
The compass in a Kode Dot: a QMC5883P on the shared I2C bus, what it measures, why the board itself is the first thing it sees, and the code to read it.
What it is
The magnetometer measures the magnetic field where the Kode Dot is, on three axes, in gauss. Away from magnets and iron, that field is the Earth's: about half a gauss, pointing roughly north and, in Europe, steeply down. The direction of the field across the two horizontal axes is a compass heading.
The part is a QMC5883P from QST: a magneto-resistive bridge on each axis, a 16-bit converter, and temperature compensation on the chip, so what you read out is already corrected. It is a sensor of field, not of north: anything magnetic near it adds to what it reads, and on the Kode Dot several things are.
How it is connected
flowchart LR p4["ESP32-P4"] -- "I2C, 0x2C" --> mag["QMC5883P"]
The QMC5883P sits on the P4's shared I2C bus at 0x2C, brought up by
kode_bsp_init and reached through kode_bsp_i2c(). It has no interrupt
line and needs none: you configure it, then read six bytes whenever you want
the field. Whether its data-ready pin reaches the P4 is TBD; no Kode
example uses it.
Specifications
| Thing | Value |
|---|---|
| Address | 0x2C. Register 0x00 reads 0x80, the part's identity |
| Range | ±30, ±12, ±8 or ±2 gauss, bits 3:2 of CTRL2 (0x0B): 1000, 2500, 3750 or 15000 counts per gauss |
| Rate | 10, 50, 100 or 200 Hz, bits 3:2 of CTRL1 (0x0A). Mode in bits 1:0: suspend, normal, single, continuous. Suspended out of reset |
| Output | 0x01–0x06: x y z, int16_t, low byte first, temperature-compensated on the chip. Bit 0 of 0x09 says a sample is ready |
| Setup | The datasheet's sequence: 0x29 ← 0x06, then CTRL2, then CTRL1 |
| Resolution | 2 mG on X and Y, 3 mG on Z, at ±30 gauss |
| Supply | 2.5–3.6 V, 35 µA typical at 10 Hz. Bit 7 of CTRL2 is a soft reset |
| Kode driver | None yet. TBD: a QMC5883P driver in kode-sdk |
| Validated | TBD: not yet run on a V1 board. Register map from QST's datasheet, rev. E |
Gotchas
Example code
Put the compass in normal mode and log the field ten times a second. Turn the Kode Dot a full circle on the table and each axis swings by hundreds of milligauss.
/*
* compass.c - 2026-09-17 - from Kode with love
*
* Reads the Kode Dot's QMC5883P over the shared I2C bus. Puts the part in
* normal mode at 200 Hz on the ±8 gauss range and logs the field in
* milligauss ten times a second. Turn the Kode Dot on the table and the
* three numbers swing by hundreds of milligauss.
*/
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "kode.h"
#define MAG_ADDR 0x2C
#define REG_XOUT_L 0x01 /* six bytes: x y z, low byte first */
#define REG_CTRL1 0x0A /* mode, rate, oversampling */
#define REG_CTRL2 0x0B /* range, set/reset, soft reset */
#define REG_SIGN 0x29 /* axis sign; the datasheet's setup writes 0x06 */
static i2c_master_dev_handle_t s_mag;
/* One register write: address byte, then the value. */
static void mag_write(uint8_t reg, uint8_t value)
{
uint8_t frame[2] = { reg, value };
ESP_ERROR_CHECK(i2c_master_transmit(s_mag, frame, 2, 100));
}
/* Attach the compass 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 = MAG_ADDR, .scl_speed_hz = KODE_I2C_FREQ_HZ };
ESP_ERROR_CHECK(i2c_master_bus_add_device(kode_bsp_i2c(), &cfg, &s_mag));
mag_write(REG_SIGN, 0x06); /* the datasheet's setup sequence, in its order */
mag_write(REG_CTRL2, 0x08); /* ±8 gauss, set/reset on */
mag_write(REG_CTRL1, 0xCD); /* normal mode, 200 Hz, oversampling 8 */
while (1) {
uint8_t reg = REG_XOUT_L, raw[6];
ESP_ERROR_CHECK(i2c_master_transmit_receive(s_mag, ®, 1, raw, 6, 100));
int16_t x = (int16_t)(raw[0] | raw[1] << 8);
int16_t y = (int16_t)(raw[2] | raw[3] << 8);
int16_t z = (int16_t)(raw[4] | raw[5] << 8);
ESP_LOGI("mag", "field %6ld %6ld %6ld mG", x * 1000L / 3750, y * 1000L / 3750, z * 1000L / 3750);
vTaskDelay(pdMS_TO_TICKS(100)); /* 3750 counts per gauss on the ±8 G range */
}
}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 QMC5883P driver; today the folder holds the prototype'slis2mdl.