diff --git a/Gui.h b/Gui.h index bd8fa90..e36ce00 100644 --- a/Gui.h +++ b/Gui.h @@ -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); diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino index 3a33007..7ae5f44 100644 --- a/RNode_Firmware.ino +++ b/RNode_Firmware.ino @@ -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 + #include #define BOSCH_BHI260_GPIO #include 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) {