Skip to content

Latest commit

 

History

History
75 lines (60 loc) · 3.96 KB

File metadata and controls

75 lines (60 loc) · 3.96 KB

Collar firmware (nRF52840, Zephyr)

The cat collar. BLE-only , it senses on the cat and runs one connectable advertisement carrying an 8-byte telemetry packet plus a custom audio service. When the station connects and subscribes, the collar streams paired audio + IMU in 100 ms frames for training capture. It never touches WiFi/internet, which keeps it tiny and low-power, and it always runs on its own battery.

Modules (src/)

File Responsibility
main.c boot + the telemetry loop (orchestration only)
ble.c advertising, identity, the audio GATT service, notify
audio.c PDM mic capture -> 8 kHz µ-law
streaming.c the decoupled reader/sender threads that assemble + send frames
battery.c 1S LiPo charge via the onboard divider
imu.c LSM6DS3TR-C continuous sampler
activity.c rules-based activity gate (cascade tier 0: low-power rest / wake)
production.c rolling IMU window: runs inference and writes real telemetry
classifier.c the confidence-gated action cascade (IMU first, audio confirms when unsure)
tflm_classifier.cpp TensorFlow Lite Micro backend for the cascade (runtime-loaded models)
model_loader.h runtime model-install API (clf_set_imu_model / clf_set_audio_model)
ota.c OTA receive state machine: stages models to flash, defers the swap
audio_codec.h µ-law + the model-input audio representation

Telemetry packet (manufacturer AD data, 8 bytes)

bytes field
0-1 company id (0xFFFF, test)
2 version
3 state (0 sleep, 1 rest, 2 active, 3 walk, 4 play, 5 groom)
4 activity (0-100)
5-6 steps (uint16, little-endian)
7 battery (0-100)

The collar's BLE address identifies which collar it is, so no id goes in the payload.

On-device AI

The inference engine is TensorFlow Lite Micro (tflm_classifier.cpp). It runs a confidence-gated cascade: the IMU model classifies first, and the audio model confirms only when the IMU is unsure. No model is compiled into the firmware , each stage loads its .tflite at runtime (clf_set_imu_model / clf_set_audio_model, the future OTA path). Until a model lands the engine runs model-free: clf_*_model_present() is false and the cascade returns UNKNOWN, so the firmware is safe to run with no model at all. The input pre-processing (per-window normalization, int8 quantization from the model's own scale/zero-point) mirrors the training pipeline so on-device inference matches what was trained.

Two things still pending: the telemetry state/activity/steps are simulated for now (a dwell-based behaviour state machine; real battery is wired) and switch to real classifier output once a model is loaded; and the OTA delivery that pushes a trained .tflite to the collar is Phase 3. See the root README for the model + OTA plan. The cascade itself already runs at capture time in streaming.c on the IMU window paired with each clip.

Build & flash

./build.ps1

build.ps1 finds the local NCS toolchain itself, runs west build -b xiao_ble/nrf52840/sense, and copies the UF2. The XIAO nRF52840 has no debug COM port for flashing , flashing is always UF2 drive-copy: double-tap RESET so the board mounts as a USB drive, then re-run (the script copies the UF2 across). Artifact: build/collar/zephyr/zephyr.uf2. Verified with NCS v3.3.1.

Console note: the firmware uses the legacy USB device stack (CONFIG_USB_DEVICE_STACK + CONFIG_USB_CDC_ACM) and calls usb_enable(), because the new (NEXT) stack's CDC console silently drops output on this board. Keep exactly one zephyr,cdc-acm-uart node.

Watching it run

Open the CDC serial port (any baud) , MEOW> collar id=cat_xxxxxx and state= steps= batt= print every 2 s. The port only appears once the app is running (not in the UF2 bootloader). That MEOW> collar id= line is what the dashboard reads over Web Serial to register the collar.