Enable BHI260AP wrist tilt wake and step counter

Wrist tilt gesture (sensor ID 67): waking the display by raising
the wrist toward you, with a light haptic click. Replaces the need
to touch the screen or press a button for the most common interaction.

Step counter (sensor ID 52): always-on pedometer displayed below
the complications on the watch face. Shows count once walking starts.

Both sensors configured after the deferred BHI260AP firmware upload
(~5s after boot). IMU events processed in the main loop via
bhi260->update(). Wrist tilt triggers display_unblank() directly.

Also added BoschSensorDataHelper.hpp for typed sensor data parsing
and imu_step_count/imu_wrist_tilt globals for cross-module access.
This commit is contained in:
GlassOnTin 2026-03-28 16:54:08 +00:00
commit cb367eff5c
2 changed files with 56 additions and 2 deletions

20
Gui.h
View file

@ -85,6 +85,7 @@ static lv_obj_t *gui_gps_value = NULL;
static lv_obj_t *gui_gps_label = NULL;
static lv_obj_t *gui_batt_value = NULL; // battery detail in complications
static lv_obj_t *gui_batt_detail = NULL;
static lv_obj_t *gui_step_label = NULL; // step count below complications
// Radio status widgets
static lv_obj_t *gui_radio_freq = NULL;
@ -140,9 +141,10 @@ static uint32_t gui_inject_until = 0; // millis() deadline for injected touch
// Shadow framebuffer for screenshots (RGB565 swapped / big-endian — same as display)
uint16_t *gui_screenshot_buf = NULL;
// Forward declarations — defined in Display.h / Power.h after Gui.h is included
// Forward declarations — defined in Display.h / Power.h / .ino after Gui.h is included
void display_unblank();
extern float pmu_temperature;
extern volatile uint32_t imu_step_count;
#ifndef PMU_TEMP_MIN
#define PMU_TEMP_MIN -30
#endif
@ -305,6 +307,12 @@ static void gui_create_watchface(lv_obj_t *parent) {
// Rule 2
gui_create_rule(parent, GUI_RULE2_Y);
// Step counter below complications
gui_step_label = gui_label(parent, &lv_font_montserrat_20, GUI_COL_DIM, "");
lv_obj_set_width(gui_step_label, GUI_W);
lv_obj_set_style_text_align(gui_step_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_pos(gui_step_label, 0, GUI_RULE2_Y + 15);
}
// ---------------------------------------------------------------------------
@ -532,6 +540,16 @@ static void gui_update_data() {
}
#endif
// Step counter
if (gui_step_label) {
if (imu_step_count > 0) {
lv_label_set_text_fmt(gui_step_label, "%lu steps", imu_step_count);
lv_obj_set_style_text_color(gui_step_label, lv_color_hex(GUI_COL_MID), 0);
} else {
lv_label_set_text(gui_step_label, "");
}
}
// Battery complication — voltage and state
if (battery_state == BATTERY_STATE_CHARGING) {
lv_label_set_text_fmt(gui_batt_value, "%.2fV", battery_voltage);

View file

@ -22,12 +22,26 @@
#include "CO5300.h"
#include "DRV2605.h"
// BHI260AP sensor hub — IMU + GPIO expansion
// BHI260AP sensor hub — IMU + step counter + wrist wake
#include <SensorBHI260AP.hpp>
#include <bosch/BoschSensorDataHelper.hpp>
#define BOSCH_BHI260_GPIO
#include <BoschFirmware.h>
SensorBHI260AP *bhi260 = NULL;
bool bhi260_ready = false;
volatile uint32_t imu_step_count = 0;
volatile bool imu_wrist_tilt = false;
// IMU sensor callbacks
void imu_step_cb(uint8_t sensor_id, uint8_t *data, uint32_t size, uint64_t *timestamp, void *user_data) {
if (size >= 4) {
imu_step_count = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}
}
void imu_wrist_tilt_cb(uint8_t sensor_id, uint8_t *data, uint32_t size, uint64_t *timestamp, void *user_data) {
imu_wrist_tilt = true;
}
// MAX98357A I2S speaker + SPM1423 PDM microphone
#include "Speaker.h"
@ -2086,9 +2100,31 @@ void loop() {
if (bhi260->begin(Wire, 0x28, I2C_SDA, I2C_SCL)) {
bhi260_ready = true;
pinMode(SENSOR_INT, INPUT);
// Enable wrist tilt gesture for display wake
bhi260->configure(SensorBHI260AP::WRIST_TILT_GESTURE, 1.0, 0);
bhi260->onResultEvent(SensorBHI260AP::WRIST_TILT_GESTURE, imu_wrist_tilt_cb);
// Enable step counter (low power, always-on)
bhi260->configure(SensorBHI260AP::STEP_COUNTER, 1.0, 0);
bhi260->onResultEvent(SensorBHI260AP::STEP_COUNTER, imu_step_cb);
}
Wire.setClock(400000UL);
}
// Process IMU events and handle wrist wake
if (bhi260_ready) {
bhi260->update();
if (imu_wrist_tilt) {
imu_wrist_tilt = false;
#if HAS_DISPLAY
if (display_blanked) {
display_unblank();
if (drv2605_ready) drv2605_play(HAPTIC_LIGHT_CLICK);
}
#endif
}
}
#endif
if (memory_low) {