diff --git a/src/fw/applib/accel_service.c b/src/fw/applib/accel_service.c index 5ed8679d0..6fb951a35 100644 --- a/src/fw/applib/accel_service.c +++ b/src/fw/applib/accel_service.c @@ -69,7 +69,19 @@ static void prv_do_shake_handle(PebbleEvent *e, void *context) { if (task == PebbleTask_Worker || task == PebbleTask_App) { sys_analytics_inc(ANALYTICS_APP_METRIC_ACCEL_SHAKE_COUNT, AnalyticsClient_CurrentTask); } - state->shake_handler(e->accel_tap.axis, e->accel_tap.direction); + state->shake_handler(); +} + + +// ---------------------------------------------------------------------------------------------- +static void prv_do_tap_handle(PebbleEvent *e, void *context) { + PebbleTask task = pebble_task_get_current(); + AccelServiceState *state = (AccelServiceState *)accel_service_private_get_session(task); + PBL_ASSERTN(state->tap_handler != NULL); + // only kernel clients can subscribe to tap right now, so just increment tap count + // device analytic here + analytics_inc(ANALYTICS_DEVICE_METRIC_ACCEL_TAP_COUNT, AnalyticsClient_System); + state->tap_handler(e->accel_tap.axis, e->accel_tap.direction); } @@ -225,18 +237,30 @@ void accel_data_service_unsubscribe(void) { accel_session_data_unsubscribe(session); } +// ---------------------------------------------------------------------------------------------- +void accel_shake_service_subscribe(AccelShakeHandler handler) { + AccelServiceState * session = accel_service_private_get_session(PebbleTask_Unknown); + accel_session_shake_subscribe(session, handler); +} + + +// ---------------------------------------------------------------------------------------------- +void accel_shake_service_unsubscribe(void) { + AccelServiceState * session = accel_service_private_get_session(PebbleTask_Unknown); + accel_session_shake_unsubscribe(session); +} // ---------------------------------------------------------------------------------------------- void accel_tap_service_subscribe(AccelTapHandler handler) { AccelServiceState * session = accel_service_private_get_session(PebbleTask_Unknown); - accel_session_shake_subscribe(session, handler); + accel_session_tap_subscribe(session, handler); } // ---------------------------------------------------------------------------------------------- void accel_tap_service_unsubscribe(void) { AccelServiceState * session = accel_service_private_get_session(PebbleTask_Unknown); - accel_session_shake_unsubscribe(session); + accel_session_tap_unsubscribe(session); } @@ -279,6 +303,10 @@ void accel_service_state_init(AccelServiceState *state) { .type = PEBBLE_ACCEL_SHAKE_EVENT, .handler = &prv_do_shake_handle, }, + .accel_tap_info = { + .type = PEBBLE_ACCEL_TAP_EVENT, + .handler = &prv_do_tap_handle, + }, .accel_double_tap_info = { .type = PEBBLE_ACCEL_DOUBLE_TAP_EVENT, .handler = &prv_do_double_tap_handle, @@ -292,7 +320,17 @@ void accel_service_state_init(AccelServiceState *state) { static void prv_session_do_shake_handle(PebbleEvent *e, void *context) { AccelServiceState *state = context; if (state->shake_handler != NULL) { - state->shake_handler(e->accel_tap.axis, e->accel_tap.direction); + state->shake_handler(); + } +} + + +// ---------------------------------------------------------------------------------------------- +// Event service handler for tap events +static void prv_session_do_tap_handle(PebbleEvent *e, void *context) { + AccelServiceState *state = context; + if (state->tap_handler != NULL) { + state->tap_handler(e->accel_tap.axis, e->accel_tap.direction); } } @@ -319,6 +357,11 @@ AccelServiceState * accel_session_create(void) { .handler = &prv_session_do_shake_handle, .context = state, }, + .accel_tap_info = { + .type = PEBBLE_ACCEL_TAP_EVENT, + .handler = &prv_session_do_tap_handle, + .context = state, + }, .accel_double_tap_info = { .type = PEBBLE_ACCEL_DOUBLE_TAP_EVENT, .handler = &prv_session_do_double_tap_handle, @@ -345,7 +388,7 @@ void accel_session_delete(AccelServiceState * session) { // ---------------------------------------------------------------------------------------------- -void accel_session_shake_subscribe(AccelServiceState * session, AccelTapHandler handler) { +void accel_session_shake_subscribe(AccelServiceState * session, AccelShakeHandler handler) { AccelServiceState *state = (AccelServiceState *)session; state->shake_handler = handler; event_service_client_subscribe(&state->accel_shake_info); @@ -359,6 +402,21 @@ void accel_session_shake_unsubscribe(AccelServiceState *state) { } +// ---------------------------------------------------------------------------------------------- +void accel_session_tap_subscribe(AccelServiceState * session, AccelTapHandler handler) { + AccelServiceState *state = (AccelServiceState *)session; + state->tap_handler = handler; + event_service_client_subscribe(&state->accel_tap_info); +} + + +// ---------------------------------------------------------------------------------------------- +void accel_session_tap_unsubscribe(AccelServiceState *state) { + event_service_client_unsubscribe(&state->accel_tap_info); + state->tap_handler = NULL; +} + + // ----------------------------------------------------------------------------------------------- void accel_session_double_tap_subscribe(AccelServiceState *state, AccelTapHandler handler) { state->double_tap_handler = handler; diff --git a/src/fw/applib/accel_service.h b/src/fw/applib/accel_service.h index 1a276801d..4de799602 100644 --- a/src/fw/applib/accel_service.h +++ b/src/fw/applib/accel_service.h @@ -52,6 +52,9 @@ _Static_assert(ACCEL_AXIS_Z == (int)AXIS_Z, #define ACCEL_DEFAULT_SAMPLING_RATE ACCEL_SAMPLING_25HZ #define ACCEL_MINIMUM_SAMPLING_RATE ACCEL_SAMPLING_10HZ +//! Callback type for accelerometer shake events +typedef void (*AccelShakeHandler)(void); + //! Callback type for accelerometer tap events //! @param axis the axis on which a tap was registered (x, y, or z) //! @param direction the direction (-1 or +1) of the tap @@ -68,6 +71,15 @@ typedef void (*AccelDataHandler)(AccelData *data, uint32_t num_samples); //! @param timestamp the timestamp, in ms, of the first sample. typedef void (*AccelRawDataHandler)(AccelRawData *data, uint32_t num_samples, uint64_t timestamp); +//! Subscribe to the accelerometer shake event service. Once subscribed, the handler +//! gets called on every shake event emitted by the accelerometer. +//! @param handler A callback to be executed on shake event +void accel_shake_service_subscribe(AccelShakeHandler handler); + +//! Unsubscribe from the accelerometer shake event service. Once unsubscribed, +//! the previously registered handler will no longer be called. +void accel_shake_service_unsubscribe(void); + //! Subscribe to the accelerometer tap event service. Once subscribed, the handler //! gets called on every tap event emitted by the accelerometer. //! @param handler A callback to be executed on tap event diff --git a/src/fw/applib/accel_service_private.h b/src/fw/applib/accel_service_private.h index 8d7c9cc16..d518dbd6d 100644 --- a/src/fw/applib/accel_service_private.h +++ b/src/fw/applib/accel_service_private.h @@ -18,13 +18,15 @@ typedef struct AccelServiceState { // User-provided callbacks for various events AccelDataHandler data_handler; - AccelTapHandler shake_handler; + AccelShakeHandler shake_handler; + AccelTapHandler tap_handler; AccelTapHandler double_tap_handler; AccelRawDataHandler raw_data_handler; AccelRawDataHandler__deprecated raw_data_handler_deprecated; // Configuration for our other types of events EventServiceInfo accel_shake_info; + EventServiceInfo accel_tap_info; EventServiceInfo accel_double_tap_info; #if LOG_DOMAIN_ACCEL @@ -56,13 +58,24 @@ void accel_session_delete(AccelServiceState *session); //! only. //! @param session An Accel session created by accel_session_create() //! @param handler A callback to be executed on shake event -void accel_session_shake_subscribe(AccelServiceState *session, AccelTapHandler handler); +void accel_session_shake_subscribe(AccelServiceState *session, AccelShakeHandler handler); //! Unsubscribe from the accelerometer shake event service by session ref. Used by kernel clients //! only. //! @param session An Accel session created by accel_session_create() void accel_session_shake_unsubscribe(AccelServiceState *session); +//! Subscribe to the accelerometer tap event service by session ref. Used by kernel clients +//! only. +//! @param session An Accel session created by accel_session_create() +//! @param handler A callback to be executed on tap event +void accel_session_tap_subscribe(AccelServiceState *session, AccelTapHandler handler); + +//! Unsubscribe from the accelerometer tap event service by session ref. Used by kernel +//! clients only. +//! @param session An Accel session created by accel_session_create() +void accel_session_tap_unsubscribe(AccelServiceState *session); + //! Subscribe to the accelerometer double tap event service by session ref. Used by kernel clients //! only. //! @param session An Accel session created by accel_session_create() diff --git a/src/fw/drivers/accel.h b/src/fw/drivers/accel.h index 30ab573cb..15aa156e8 100644 --- a/src/fw/drivers/accel.h +++ b/src/fw/drivers/accel.h @@ -168,13 +168,16 @@ extern void accel_cb_new_sample(AccelDriverSample const *data); //! Function called by driver whenever shake is detected. //! -//! @param axis Axis which the shake was detected on -//! @param direction The sign indicates whether the shake was on the positive or -//! negative axis -//! //! @note It is up to the implementer to filter out shake events triggered by the //! vibrate motor. -extern void accel_cb_shake_detected(IMUCoordinateAxis axis, int32_t direction); +extern void accel_cb_shake_detected(void); + +//! Function called by driver whenever a tap is detected. +//! +//! @param axis Axis which the tap was detected on +//! @param direction The sign indicates whether the tap was on the positive or +//! negative axis +extern void accel_cb_tap_detected(IMUCoordinateAxis axis, int32_t direction); //! Function called by driver whenever a double tap is detected. //! diff --git a/src/fw/drivers/imu/bma255/bma255.c b/src/fw/drivers/imu/bma255/bma255.c index eec4c547f..c9cdfb9bb 100644 --- a/src/fw/drivers/imu/bma255/bma255.c +++ b/src/fw/drivers/imu/bma255/bma255.c @@ -277,26 +277,7 @@ static void prv_handle_motion_interrupts(void) { bool anymotion = (int0_status & BMA255_INT_STATUS_0_SLOPE_MASK); if (anymotion) { - const AccelConfig *cfg = &BOARD_CONFIG_ACCEL.accel_config; - IMUCoordinateAxis axis = AXIS_X; - bool invert = false; - - if (int2_status & BMA255_INT_STATUS_2_SLOPE_FIRST_X) { - axis = AXIS_X; - invert = cfg->axes_inverts[AXIS_X]; - } else if (int2_status & BMA255_INT_STATUS_2_SLOPE_FIRST_Y) { - axis = AXIS_Y; - invert = cfg->axes_inverts[AXIS_Y]; - } else if (int2_status & BMA255_INT_STATUS_2_SLOPE_FIRST_Z) { - axis = AXIS_Z; - invert = cfg->axes_inverts[AXIS_Z]; - } else { - BMA255_DBG("No Axis?: 0x%"PRIx8" 0x%"PRIx8, int0_status, int2_status); - } - int32_t direction = ((int2_status & BMA255_INT_STATUS_2_SLOPE_SIGN) == 0) ? 1 : -1; - direction *= (invert ? -1 : 1); - - accel_cb_shake_detected(axis, direction); + accel_cb_shake_detected(); } } diff --git a/src/fw/drivers/imu/bmi160/bmi160.c b/src/fw/drivers/imu/bmi160/bmi160.c index 85e01cedf..749ddd76b 100644 --- a/src/fw/drivers/imu/bmi160/bmi160.c +++ b/src/fw/drivers/imu/bmi160/bmi160.c @@ -326,12 +326,7 @@ static void prv_handle_motion_interrupts(void) { // debug bool anymotion = ((int0_status & BMI160_INT_STATUS_0_ANYM_MASK) != 0); if (anymotion) { - int32_t direction; - IMUCoordinateAxis axis = prv_get_axis_direction(int0_status, int2_status, - BMI160_INT_STATUS_2_ANYM_SIGN, - BMI160_INT_STATUS_2_ANYM_FIRST_X, &direction); - BMI160_DBG("Anymotion on axis %"PRIu8" in direction %"PRId32, axis, direction); - accel_cb_shake_detected(axis, direction); + accel_cb_shake_detected(); } bool double_tap = ((int0_status & BMI160_INT_STATUS_0_D_TAP_MASK) != 0); if (double_tap) { diff --git a/src/fw/drivers/imu/lis2dw12/lis2dw12.c b/src/fw/drivers/imu/lis2dw12/lis2dw12.c index e5f84b1b2..2cd3f889e 100644 --- a/src/fw/drivers/imu/lis2dw12/lis2dw12.c +++ b/src/fw/drivers/imu/lis2dw12/lis2dw12.c @@ -377,30 +377,8 @@ static void prv_lis2dw12_process_interrupts(void) { (unsigned long)(now_ms - s_last_wake_event_ms)); } else { s_last_wake_event_ms = now_ms; - - // Use wake_up_src already read by all_sources_get - don't re-read the register - // as that can clear flags and cause race conditions with pulsed interrupts - IMUCoordinateAxis axis = AXIS_X; - int32_t direction = 1; - - // Determine which axis triggered from already-read wake_up_src - const AccelConfig *cfg = &BOARD_CONFIG_ACCEL.accel_config; - if (all_sources.wake_up_src.x_wu) { - axis = AXIS_X; - } else if (all_sources.wake_up_src.y_wu) { - axis = AXIS_Y; - } else if (all_sources.wake_up_src.z_wu) { - axis = AXIS_Z; - } - - // Use last known sample for direction instead of reading new data during interrupt - // Reading acceleration data here can interfere with FIFO operation - int16_t val = s_last_sample_mg[cfg->axes_offsets[axis]]; - bool invert = cfg->axes_inverts[axis]; - direction = (val >= 0 ? 1 : -1) * (invert ? -1 : 1); - - PBL_LOG(LOG_LEVEL_DEBUG, "LIS2DW12: Shake detected; axis=%d, direction=%lu", axis, direction); - accel_cb_shake_detected(axis, direction); + PBL_LOG(LOG_LEVEL_DEBUG, "LIS2DW12: Shake detected"); + accel_cb_shake_detected(); } // The wake-up interrupt is latched - if the wake condition is still true (device still diff --git a/src/fw/drivers/imu/lsm6dso/lsm6dso.c b/src/fw/drivers/imu/lsm6dso/lsm6dso.c index 352bc623a..85284c1fe 100644 --- a/src/fw/drivers/imu/lsm6dso/lsm6dso.c +++ b/src/fw/drivers/imu/lsm6dso/lsm6dso.c @@ -848,31 +848,8 @@ static void prv_lsm6dso_process_interrupts(void) { s_last_wake_event_ms = now_ms; lsm6dso_wake_up_src_t wake_src; if (lsm6dso_read_reg(&lsm6dso_ctx, LSM6DSO_WAKE_UP_SRC, (uint8_t *)&wake_src, 1) == 0) { - IMUCoordinateAxis axis = AXIS_X; - int32_t direction = 1; // LSM6DSO does not give sign directly for wake-up; approximate via - // sign of latest sample on axis - // Determine which axis triggered: order X,Y,Z - const AccelConfig *cfg = &BOARD_CONFIG_ACCEL.accel_config; - if (wake_src.x_wu) { - axis = AXIS_X; - } else if (wake_src.y_wu) { - axis = AXIS_Y; - } else if (wake_src.z_wu) { - axis = AXIS_Z; - } - // Read current sample to infer direction - int16_t accel_raw[3]; - if (lsm6dso_acceleration_raw_get(&lsm6dso_ctx, accel_raw) == 0) { - int16_t val = accel_raw[cfg->axes_offsets[axis]]; - bool invert = cfg->axes_inverts[axis]; - direction = (val >= 0 ? 1 : -1) * (invert ? -1 : 1); - int16_t mg_x = prv_get_axis_projection_mg(X_AXIS, accel_raw); - int16_t mg_y = prv_get_axis_projection_mg(Y_AXIS, accel_raw); - int16_t mg_z = prv_get_axis_projection_mg(Z_AXIS, accel_raw); - prv_note_new_sample_mg(mg_x, mg_y, mg_z); - } - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Shake detected; axis=%d, direction=%lu", axis, direction); - accel_cb_shake_detected(axis, direction); + PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Shake detected"); + accel_cb_shake_detected(); } } } diff --git a/src/fw/kernel/events.h b/src/fw/kernel/events.h index 25b6f614d..20eb23187 100644 --- a/src/fw/kernel/events.h +++ b/src/fw/kernel/events.h @@ -51,6 +51,7 @@ typedef struct PebblePhoneCaller PebblePhoneCaller; typedef enum { PEBBLE_NULL_EVENT = 0, PEBBLE_ACCEL_SHAKE_EVENT, + PEBBLE_ACCEL_TAP_EVENT, PEBBLE_ACCEL_DOUBLE_TAP_EVENT, PEBBLE_BT_CONNECTION_EVENT, PEBBLE_BT_CONNECTION_DEBOUNCED_EVENT, diff --git a/src/fw/services/common/accel_manager.c b/src/fw/services/common/accel_manager.c index 386be7143..160bbfa30 100644 --- a/src/fw/services/common/accel_manager.c +++ b/src/fw/services/common/accel_manager.c @@ -725,9 +725,17 @@ void accel_cb_new_sample(AccelDriverSample const *data) { prv_dispatch_data(true /* post_event */); } -void accel_cb_shake_detected(IMUCoordinateAxis axis, int32_t direction) { +void accel_cb_shake_detected(void) { PebbleEvent e = { .type = PEBBLE_ACCEL_SHAKE_EVENT, + }; + + event_put(&e); +} + +void accel_cb_tap_detected(IMUCoordinateAxis axis, int32_t direction) { + PebbleEvent e = { + .type = PEBBLE_ACCEL_TAP_EVENT, .accel_tap = { .axis = axis, .direction = direction, diff --git a/src/fw/services/common/analytics/analytics_metric_table.h b/src/fw/services/common/analytics/analytics_metric_table.h index 53d413eb2..2f740b866 100644 --- a/src/fw/services/common/analytics/analytics_metric_table.h +++ b/src/fw/services/common/analytics/analytics_metric_table.h @@ -133,6 +133,7 @@ DEVICE(ANALYTICS_DEVICE_METRIC_I2C_ERROR_COUNT, UINT8) \ DEVICE(ANALYTICS_DEVICE_METRIC_BUTTON_PRESSED_COUNT, UINT16) \ DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_SHAKE_COUNT, UINT16) \ + DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_TAP_COUNT, UINT16) \ DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_DOUBLE_TAP_COUNT, UINT16) \ DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_PEEK_COUNT, UINT32) \ DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_SAMPLE_COUNT, UINT32) \ diff --git a/src/fw/services/normal/stationary.c b/src/fw/services/normal/stationary.c index 60146708d..bdf25e2c7 100644 --- a/src/fw/services/normal/stationary.c +++ b/src/fw/services/normal/stationary.c @@ -111,7 +111,7 @@ void stationary_handle_battery_connection_change_event(void) { //! A movement of the watch will make the watch wake up. In this state, the watch //! will have a low tap threshold, so it will be sensitive to motion -static void prv_accel_tap_handler(AccelAxisType axis, int32_t direction) { +static void prv_accel_shake_handler(void) { prv_handle_action(StationaryActionWakeUp); } @@ -249,7 +249,7 @@ static void prv_exit_disabled_state(void) { s_stationary_count_down = STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS; PBL_ASSERTN(s_accel_session == NULL); s_accel_session = accel_session_create(); - accel_session_shake_subscribe(s_accel_session, prv_accel_tap_handler); + accel_session_shake_subscribe(s_accel_session, prv_accel_shake_handler); event_service_client_subscribe(&s_button_event_info); #if DEBUG_STATIONARY