Documentation Index
Fetch the complete documentation index at: https://docs.edgeimpulse.com/llms.txt
Use this file to discover all available pages before exploring further.
This project runs in two modes from the same codebase. In local sensor mode a board with an on-board IMU (Nesso N1, Arduino Nano 33 BLE Sense) reads sensor data, optionally runs Edge Impulse inference, and advertises results via BLE GATT to an Android phone. In relay mode a Nordic Thingy:53 connects to an
Reference code: https://github.com/edgeimpulse/ei-zephyr-ble-gatt-client
EI-Golioth peripheral and forwards its inference notifications to Android.Reference code: https://github.com/edgeimpulse/ei-zephyr-ble-gatt-client
Overview
Modes at a Glance
| Local Sensor mode | Relay mode | |
|---|---|---|
| Board | Nesso N1 (ESP32-C6) or Nano 33 BLE Sense | Nordic Thingy:53 |
| BLE role | Peripheral (server) | Central (client) + Peripheral (server) |
| Data source | On-board BMI270 / LSM9DS1 | EI-Golioth peripheral over BLE |
| Edge Impulse inference | Optional (drop in a model/ dir) | On the peripheral, not here |
| Kconfig | CONFIG_EI_SENSOR_LOCAL=y (auto via board conf) | default (CONFIG_EI_SENSOR_LOCAL=n) |
Hardware
Sensor-side boards (local sensor mode)
| Board | SoC | IMU | Notes |
|---|---|---|---|
| Arduino Nesso N1 | ESP32-C6 (RISC-V) | Bosch BMI270 (6-axis) | Requires west blobs fetch hal_espressif before building; needs Zephyr ≥ v4.1.0 |
| Arduino Nano 33 BLE Sense | nRF52840 | ST LSM9DS1 (6-axis) + HTS221 + LPS22HB + APDS9960 | All sensors on-board, no extra config needed |
| Any board with a supported IMU | — | LSM9DS1, BMI270, or FXOS8700 | Add a boards/<board>.overlay and a matching .conf |
Monitor board (relay mode)
| Board | SoC | Notes |
|---|---|---|
| Nordic Thingy:53 | nRF5340 | BLE 5.3, 1350 mAh Li-Po; no on-board sensors used |
Prerequisites
- Zephyr SDK 1.0+ and West 1.5.0+
- For Nesso N1 only: the
riscv64-zephyr-elftoolchain (install with./setup.sh -t riscv64-zephyr-elffrom the SDK directory) and the Espressif HAL blobs (see Step 1b) - For relay mode: a running
EI-Goliothperipheral (see example-edge-impulse)
1. Initialize the Repository
west update fetches Zephyr RTOS main (required for the Nesso N1 board), the Edge Impulse Zephyr SDK module, and all dependencies.
1b. Nesso N1: Fetch Espressif Blobs
ESP32 targets require closed-source binary blobs for the BLE and Wi-Fi stack. Fetch them once afterwest update:
The Arduino Nesso N1 board (
arduino_nesso_n1/esp32c6/hpcore) is provided by Zephyr main. Make sure west.yml pins zephyr to revision: main and that you have built the riscv64-zephyr-elf toolchain in your Zephyr SDK install. Export ZEPHYR_SDK_INSTALL_DIR so CMake picks up the right SDK:2. (Optional) Add an Edge Impulse Model
If you want the sensor board to run local inference and notify the result label over BLE:- In Edge Impulse Studio go to Deployment → Zephyr library, click Build, and download the
.zip - Extract the archive into a
model/directory next to the project root:
CMakeLists.txt auto-detects the model/ directory and links the SDK at build time. Without it the firmware streams raw sensor data only.
3. Build
Nesso N1 (local sensor mode)
The board qualifier
/esp32c6/hpcore selects the RISC-V HP application core. prj.conf enables CONFIG_EI_SENSOR_LOCAL=y, CONFIG_BMI270=y, CONFIG_I2C=y, and CONFIG_SENSOR=y so no manual Kconfig changes are needed.Arduino Nano 33 BLE Sense (local sensor mode)
Nordic Thingy:53 (relay mode)
.west/config:
west build --pristine.
4. Flash
west flash uses the built-in ESP-IDF flasher via the ESP32-C6’s native USB-Serial-JTAG port — no external programmer or BOOT/RESET dance required. Just plug in via USB-C and run west flash.
5. Monitor Serial Output
Local sensor mode (Nesso N1 / Nano 33 BLE Sense)
Boot log on a Nesso N1 (ESP32-C6) withwest espressif monitor:
model/ present at build time):
Relay mode (Thingy:53)
How It Works
Local sensor mode
- BLE stack + GATT server start advertising as
EI-Monitor ei_sensor_init()binds to the on-board IMU via the Zephyr Sensor API (device resolved at compile time from devicetree)ei_sensor_run_loop()samples the IMU everyCONFIG_EI_SENSOR_SAMPLE_INTERVAL_MSmilliseconds (default 10 ms = 100 Hz)- Each sample (
[accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z]in m/s² and rad/s) is notified to connected Android centrals viagatt_server_notify_sensor_data() - If a model is present and the feature buffer is full,
run_classifier()fires and the label/confidence is notified viagatt_server_notify_inference()
Relay mode
- BLE stack starts in both Central and Peripheral roles (
CONFIG_BT_MAX_CONN=2) - GATT client scans for
EI-Goliothby name, connects, discovers the EI service, and subscribes to the inference characteristic - GATT server simultaneously advertises as
EI-Monitorso Android can connect - Inference notifications from the peripheral are forwarded to Android via
gatt_server_notify_inference()
Sensor driver (ei_sensor.cpp)
The IMU is selected at compile time via DT_HAS_COMPAT_STATUS_OKAY():
| Devicetree compatible | Board | Channels |
|---|---|---|
st,lsm9ds1 | Nano 33 BLE Sense | ACCEL_XYZ, GYRO_XYZ |
bosch,bmi270 | Nesso N1 | ACCEL_XYZ, GYRO_XYZ |
nxp,fxos8700 | NXP boards | ACCEL_XYZ, GYRO_XYZ |
GATT service layout
| Characteristic | UUID suffix | Properties | Payload |
|---|---|---|---|
| Inference result | ...def1 | READ, NOTIFY | inference_result_t (label, confidence, timing) |
| Sensor data | ...def2 | READ, NOTIFY | float[] — raw IMU axes |
| Device state | ...def3 | READ, WRITE | Status flags |
Project Structure
Customising the Example
Adjust the sensor sampling rate
Inprj.conf or a board-specific .conf:
Port to a different board
- Add
boards/<your_board>.overlayandboards/<your_board>.conf - In
.conf, setCONFIG_EI_SENSOR_LOCAL=yand enable the IMU driver (e.g.CONFIG_ICM42688_P=y) - In
.overlay, enable the IMU node if it is not already on in the upstream board DTS - Add a
DT_HAS_COMPAT_STATUS_OKAY(your_compat)branch insrc/sensors/ei_sensor.cpp
Change the target peripheral name (relay mode)
Insrc/ble/gatt_client.cpp:
Increase memory for larger EI models
Troubleshooting
Nesso N1 build error: board not found
Nesso N1 build error: board not found
The Nesso N1 was added after Zephyr v4.0.0. Update
west.yml to revision: v4.1.0 (or main) and run west update again.Nesso N1 flash error or blank serial output
Nesso N1 flash error or blank serial output
ESP32-C6 requires binary blobs. Run
west blobs fetch hal_espressif once after west update. The Nesso N1 uses native USB-Serial-JTAG so no BOOT/RESET sequence is needed — flashing is fully automatic over USB-C. If the serial console is blank, use west espressif monitor instead of minicom to open the port.IMU not detected at runtime (local sensor mode)
IMU not detected at runtime (local sensor mode)
- Check the serial log for the
device not readyerror — it prints the compatible string it searched for - Verify the IMU node has
status = "okay"in the devicetree - Enable I²C and sensor debug logs:
Android cannot connect (local sensor mode)
Android cannot connect (local sensor mode)
- Confirm the board is advertising; a BLE scan on the phone should show
EI-Monitor - Check that
gatt_server_init()completed without error in the serial log - Only one Android central can connect at a time (
CONFIG_BT_MAX_CONN=2reserves one slot for the EI-Golioth peripheral in relay mode)
Relay mode: no EI-Golioth device found during scan
Relay mode: no EI-Golioth device found during scan
- Confirm the peripheral is powered on and advertising as
EI-Golioth - Enable BLE scan debug output:
- The scan filter matches by exact advertised name — verify the peripheral’s
CONFIG_BT_DEVICE_NAMEmatches exactly
No serial output on Thingy:53
No serial output on Thingy:53
Set your terminal to 115200 baud, 8N1. On macOS use
ls /dev/tty.usbmodem* to find the port. The boards/thingy53_nrf5340_cpuapp.overlay already routes zephyr,console to uart0.Next Steps
- Zephyr series overview
- IMU inference on Zephyr — build a standalone inference node
- ei-zephyr-golioth-integration — the relay-mode peripheral with Golioth cloud sync