Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding really long press for each button #457

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b8e1a0a
adding really long press for each button
mcguirepr89 Aug 31, 2024
3ea8700
Made the T and Y characters look more unique on the 4 and 6 position
voloved Aug 23, 2024
56362d5
Update TOTP Face Documentation
nlordell Aug 22, 2024
8443504
make sure we don't miss our scheduled tasks
TenMinJoe Aug 18, 2024
5fbe314
add the ability to set location and birthdate in movement config
madhogs Mar 18, 2024
5d6163b
Added debouncing
voloved Jul 29, 2024
ea7de4f
Leaving sleep with alarm button up doesn't trigger alarm button
voloved Jul 29, 2024
77c0b3d
debouince now uses cb_fast_tick and holding the button now also gets …
voloved Jul 29, 2024
e2c3ce9
Removed some dead code
voloved Jul 29, 2024
b229f68
Made the debounce register rising edges rather than falling edges
voloved Jul 30, 2024
f1dda58
Revert "Leaving sleep with alarm button up doesn't trigger alarm button"
voloved Jul 30, 2024
e6defaa
using cb_fast_tick again
voloved Jul 30, 2024
4f5c28b
Cleaned up code
voloved Jul 30, 2024
6a9c8bb
Fixed stuck fast_tick
voloved Jul 30, 2024
f03d177
Delay for starting the debounce no loonger happens
voloved Jul 30, 2024
1686c58
A little bit of clean-up
voloved Jul 30, 2024
a2343c3
Using debounce that triggers when there's no change for Xms rather th…
voloved Jul 31, 2024
e36543f
Moved a few lines around to match main
voloved Jul 31, 2024
f8a13d8
Brought debounce time to 8ms rather than 15
voloved Jul 31, 2024
c3b0bc9
Changed debounce to ignore button presses after a press down or up ra…
voloved Aug 11, 2024
606da3d
Set the debounce tick variables to 0 to make the face work the same a…
voloved Aug 17, 2024
49702a2
Stops Running cb_fast_tick when the watch debounce timer is defined as 0
voloved Aug 17, 2024
3fb795b
Code review edits
voloved Aug 23, 2024
b9ac649
Additional code review change
voloved Aug 24, 2024
2af59b2
Update SIGNAL_TUNE_KIM_POSSIBLE notes
metehan-arslan Aug 27, 2024
d394795
style: remove extra whitespace
metehan-arslan Aug 28, 2024
6ca759a
avoid delta overflow in countdown draw
TenMinJoe Aug 18, 2024
b0c2652
don't change the bell indicator from within background task
TenMinJoe Aug 18, 2024
71e2f23
Swapped the bell and alarm icon on the clock face to match Casio's doc
voloved Aug 10, 2024
a32fb3e
fix: july has 31 days
xtruan Aug 8, 2024
10afd41
Fix all days in a month
Kistelini Aug 3, 2024
5c72105
Leap Years Now Handled Dynamically
voloved Aug 3, 2024
38fe949
Made the days_in_month its own function
voloved Aug 10, 2024
90102f6
Fix to remove compiler complaint
voloved Aug 3, 2024
0348d17
Made it so the code works with a completely empty preset list
voloved Aug 3, 2024
0fa45e1
add temp input to simulator
FreeMasen Aug 2, 2024
ba505c4
CLOCK_FACE_24H_ONLY hides the preference to change the setting and de…
voloved Jul 8, 2024
7400897
Typo fix on PREFERENCES_FACE_NUM_PREFERENCES
voloved Jul 8, 2024
7acc937
isolating this bit of complexity in movement function; Add ifdefs in …
voloved Jul 10, 2024
b10fa82
wait for RTC SYNCBUSY in watch_register_extwake_callback
TenMinJoe Aug 30, 2024
8a12a9d
Comment change
voloved Aug 27, 2024
9876778
add power rangers tune
metehan-arslan Sep 1, 2024
b344b19
add layla tune
metehan-arslan Aug 31, 2024
fe4a6a3
update layla tune
metehan-arslan Sep 1, 2024
d818a2e
bump to the newer upload-artifact github action
Sep 4, 2024
9c60842
adding really long press for each button
mcguirepr89 Aug 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
env:
COLOR: BLUE

jobs:
jobs:
build:
container:
image: ghcr.io/armmbed/mbed-os-env:latest
Expand All @@ -29,7 +29,7 @@ jobs:
run: make
working-directory: 'movement/make'
- name: Upload UF2
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: watch.uf2
path: movement/make/build/watch.uf2
Expand All @@ -52,7 +52,7 @@ jobs:
cp watch.html index.html
tar -czf simulator.tar.gz index.html watch.wasm watch.js
- name: Upload simulator build
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: simulator.tar.gz
path: movement/make/build-sim/simulator.tar.gz
9 changes: 3 additions & 6 deletions apps/beats-time/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <string.h>
#include <math.h>
#include "watch.h"
#include "watch_utility.h"

const int8_t UTC_OFFSET = 4; // set to your current UTC offset to see correct beats time
const uint8_t BEAT_REFRESH_FREQUENCY = 8;
Expand Down Expand Up @@ -203,7 +204,6 @@ void set_time_mode_handle_primary_button(void) {

void set_time_mode_handle_secondary_button(void) {
watch_date_time date_time = watch_rtc_get_date_time();
const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};

switch (application_state.page) {
case 0: // hour
Expand All @@ -224,13 +224,10 @@ void set_time_mode_handle_secondary_button(void) {
break;
case 5: // day
date_time.unit.day = date_time.unit.day + 1;
// can't set to the 29th on a leap year. if it's february 29, set to 11:59 on the 28th.
// and it should roll over.
if (date_time.unit.day > days_in_month[date_time.unit.month - 1]) {
date_time.unit.day = 1;
}
break;
}
if (date_time.unit.day > days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR))
date_time.unit.day = 1;
watch_rtc_set_date_time(date_time);
}

Expand Down
163 changes: 140 additions & 23 deletions movement/movement.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Joey Castillo
* Copyright (c) 2022 Joey Castillo (edits by Patrick McGuire)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -23,6 +23,17 @@
*/

#define MOVEMENT_LONG_PRESS_TICKS 64
#define MOVEMENT_REALLY_LONG_PRESS_TICKS 384
#define DEBOUNCE_TICKS_DOWN 0
#define DEBOUNCE_TICKS_UP 0
/*
DEBOUNCE_TICKS_DOWN and DEBOUNCE_TICKS_UP are in terms of fast_cb ticks after a button is pressed.
The logic is that pressed of a button are ignored until the cb_fast_tick function runs this variable amount of times.
Without modifying the code, the cb_fast_tick frequency is 128Hz, or 7.8125ms.
It is not suggested to set this value to one for debouncing, as the callback occurs asynchronously of the button's press,
meaning that if a button was pressed and 7ms passed since th elast time cb_fast_tick was called, then there will be only 812.5us
of debounce time.
*/

#include <stdio.h>
#include <string.h>
Expand Down Expand Up @@ -95,6 +106,31 @@
#define MOVEMENT_DEFAULT_LED_DURATION 1
#endif

// Default to no set location latitude
#ifndef MOVEMENT_DEFAULT_LATITUDE
#define MOVEMENT_DEFAULT_LATITUDE 0
#endif

// Default to no set location longitude
#ifndef MOVEMENT_DEFAULT_LONGITUDE
#define MOVEMENT_DEFAULT_LONGITUDE 0
#endif

// Default to no set birthdate year
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_YEAR
#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0
#endif

// Default to no set birthdate month
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_MONTH
#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
#endif

// Default to no set birthdate day
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_DAY
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
#endif

#if __EMSCRIPTEN__
#include <emscripten.h>
#endif
Expand Down Expand Up @@ -169,6 +205,9 @@ static inline void _movement_reset_inactivity_countdown(void) {
static inline void _movement_enable_fast_tick_if_needed(void) {
if (!movement_state.fast_tick_enabled) {
movement_state.fast_ticks = 0;
movement_state.debounce_ticks_light = 0;
movement_state.debounce_ticks_alarm = 0;
movement_state.debounce_ticks_mode = 0;
watch_rtc_register_periodic_callback(cb_fast_tick, 128);
movement_state.fast_tick_enabled = true;
}
Expand All @@ -177,6 +216,7 @@ static inline void _movement_enable_fast_tick_if_needed(void) {
static inline void _movement_disable_fast_tick_if_possible(void) {
if ((movement_state.light_ticks == -1) &&
(movement_state.alarm_ticks == -1) &&
((movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm) == 0) &&
((movement_state.light_down_timestamp + movement_state.mode_down_timestamp + movement_state.alarm_down_timestamp) == 0)) {
movement_state.fast_tick_enabled = false;
watch_rtc_disable_periodic_callback(128);
Expand All @@ -201,7 +241,7 @@ static void _movement_handle_scheduled_tasks(void) {

for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
if (scheduled_tasks[i].reg) {
if (scheduled_tasks[i].reg == date_time.reg) {
if (scheduled_tasks[i].reg <= date_time.reg) {
scheduled_tasks[i].reg = 0;
movement_event_t background_event = { EVENT_BACKGROUND_TASK, 0 };
watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]);
Expand Down Expand Up @@ -328,6 +368,14 @@ static void end_buzzing_and_disable_buzzer(void) {
watch_disable_buzzer();
}

static void set_initial_clock_mode(void) {
#ifdef CLOCK_FACE_24H_ONLY
movement_state.settings.bit.clock_mode_24h = true;
#else
movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
#endif
}

void movement_play_signal(void) {
void *maybe_disable_buzzer = end_buzzing_and_disable_buzzer;
if (watch_is_buzzer_or_led_enabled()) {
Expand Down Expand Up @@ -376,14 +424,18 @@ void app_init(void) {
#endif

memset(&movement_state, 0, sizeof(movement_state));

movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
set_initial_clock_mode();
movement_state.settings.bit.led_red_color = MOVEMENT_DEFAULT_RED_COLOR;
movement_state.settings.bit.led_green_color = MOVEMENT_DEFAULT_GREEN_COLOR;
movement_state.settings.bit.button_should_sound = MOVEMENT_DEFAULT_BUTTON_SOUND;
movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL;
movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL;
movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION;
movement_state.location.bit.latitude = MOVEMENT_DEFAULT_LATITUDE;
movement_state.location.bit.longitude = MOVEMENT_DEFAULT_LONGITUDE;
movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR;
movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH;
movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY;
movement_state.light_ticks = -1;
movement_state.alarm_ticks = -1;
movement_state.next_available_backup_register = 4;
Expand All @@ -406,10 +458,14 @@ void app_init(void) {

void app_wake_from_backup(void) {
movement_state.settings.reg = watch_get_backup_data(0);
movement_state.location.reg = watch_get_backup_data(1);
movement_state.birthdate.reg = watch_get_backup_data(2);
}

void app_setup(void) {
watch_store_backup_data(movement_state.settings.reg, 0);
watch_store_backup_data(movement_state.location.reg, 1);
watch_store_backup_data(movement_state.birthdate.reg, 2);

static bool is_first_launch = true;

Expand Down Expand Up @@ -462,6 +518,7 @@ void app_wake_from_standby(void) {

static void _sleep_mode_app_loop(void) {
movement_state.needs_wake = false;
movement_state.ignore_alarm_btn_after_sleep = true;
// as long as le_mode_ticks is -1 (i.e. we are in low energy mode), we wake up here, update the screen, and go right back to sleep.
while (movement_state.le_mode_ticks == -1) {
// we also have to handle background tasks here in the mini-runloop
Expand Down Expand Up @@ -627,29 +684,69 @@ static movement_event_type_t _figure_out_button_event(bool pin_level, movement_e
// now that that's out of the way, handle falling edge
uint16_t diff = movement_state.fast_ticks - *down_timestamp;
*down_timestamp = 0;
_movement_disable_fast_tick_if_possible();
// any press over a half second is considered a long press. Fire the long-up event
if (diff > MOVEMENT_LONG_PRESS_TICKS) return button_down_event_type + 3;
else return button_down_event_type + 1;
if (diff < MOVEMENT_REALLY_LONG_PRESS_TICKS && diff > MOVEMENT_LONG_PRESS_TICKS) {
return button_down_event_type + 3;
} else if (diff > MOVEMENT_REALLY_LONG_PRESS_TICKS) {
return button_down_event_type + 5;
} else return button_down_event_type + 1;
}
}

static movement_event_type_t btn_action(bool pin_level, int code, uint16_t *timestamp) {
_movement_reset_inactivity_countdown();
return _figure_out_button_event(pin_level, code, timestamp);
}

static void light_btn_action(bool pin_level) {
event.event_type = btn_action(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp);
}

static void mode_btn_action(bool pin_level) {
event.event_type = btn_action(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp);
}

static void alarm_btn_action(bool pin_level) {
uint8_t event_type = btn_action(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp);
if (movement_state.ignore_alarm_btn_after_sleep){
if (event_type == EVENT_ALARM_BUTTON_UP || event_type == EVENT_ALARM_LONG_UP) movement_state.ignore_alarm_btn_after_sleep = false;
return;
}
event.event_type = event_type;
}

static void debounce_btn_press(uint8_t pin, uint8_t *debounce_ticks, uint16_t *down_timestamp, void (*function)(bool)) {
if (*debounce_ticks == 0) {
bool pin_level = watch_get_pin_level(pin);
function(pin_level);
*debounce_ticks = pin_level ? DEBOUNCE_TICKS_DOWN : DEBOUNCE_TICKS_UP;
if (*debounce_ticks != 0) _movement_enable_fast_tick_if_needed();
}
else
*down_timestamp = 0;
}

static void disable_if_needed(uint8_t *ticks) {
if (*ticks > 0 && --*ticks == 0)
_movement_disable_fast_tick_if_possible();
}

static void movement_disable_if_debounce_complete(void) {
disable_if_needed(&movement_state.debounce_ticks_light);
disable_if_needed(&movement_state.debounce_ticks_alarm);
disable_if_needed(&movement_state.debounce_ticks_mode);
}

void cb_light_btn_interrupt(void) {
bool pin_level = watch_get_pin_level(BTN_LIGHT);
_movement_reset_inactivity_countdown();
event.event_type = _figure_out_button_event(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp);
debounce_btn_press(BTN_LIGHT, &movement_state.debounce_ticks_light, &movement_state.light_down_timestamp, light_btn_action);
}

void cb_mode_btn_interrupt(void) {
bool pin_level = watch_get_pin_level(BTN_MODE);
_movement_reset_inactivity_countdown();
event.event_type = _figure_out_button_event(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp);
debounce_btn_press(BTN_MODE, &movement_state.debounce_ticks_mode, &movement_state.mode_down_timestamp, mode_btn_action);
}

void cb_alarm_btn_interrupt(void) {
bool pin_level = watch_get_pin_level(BTN_ALARM);
_movement_reset_inactivity_countdown();
event.event_type = _figure_out_button_event(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp);
debounce_btn_press(BTN_ALARM, &movement_state.debounce_ticks_alarm, &movement_state.alarm_down_timestamp, alarm_btn_action);
}

void cb_alarm_btn_extwake(void) {
Expand All @@ -662,21 +759,41 @@ void cb_alarm_fired(void) {
}

void cb_fast_tick(void) {
movement_state.fast_ticks++;
movement_disable_if_debounce_complete();
if (movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm == 0)
movement_state.fast_ticks++;
if (movement_state.light_ticks > 0) movement_state.light_ticks--;
if (movement_state.alarm_ticks > 0) movement_state.alarm_ticks--;
// check timestamps and auto-fire the long-press events
// Notice: is it possible that two or more buttons have an identical timestamp? In this case
// only one of these buttons would receive the long press event. Don't bother for now...
if (movement_state.light_down_timestamp > 0)
if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1)
//if (movement_state.light_down_timestamp > 0)
// if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1)
// event.event_type = EVENT_LIGHT_LONG_PRESS;
//if (movement_state.mode_down_timestamp > 0)
// if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1)
// event.event_type = EVENT_MODE_LONG_PRESS;
if (movement_state.light_down_timestamp > 0) {
if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_REALLY_LONG_PRESS_TICKS + 1) {
event.event_type = EVENT_LIGHT_REALLY_LONG_PRESS;
} else if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) {
event.event_type = EVENT_LIGHT_LONG_PRESS;
if (movement_state.mode_down_timestamp > 0)
if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1)
}
}
if (movement_state.mode_down_timestamp > 0) {
if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_REALLY_LONG_PRESS_TICKS + 1) {
event.event_type = EVENT_MODE_REALLY_LONG_PRESS;
} else if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) {
event.event_type = EVENT_MODE_LONG_PRESS;
if (movement_state.alarm_down_timestamp > 0)
if (movement_state.fast_ticks - movement_state.alarm_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1)
}
}
if (movement_state.alarm_down_timestamp > 0) {
if (movement_state.fast_ticks - movement_state.alarm_down_timestamp == MOVEMENT_REALLY_LONG_PRESS_TICKS + 1) {
event.event_type = EVENT_ALARM_REALLY_LONG_PRESS;
} else if (movement_state.fast_ticks - movement_state.alarm_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) {
event.event_type = EVENT_ALARM_LONG_PRESS;
}
}
// this is just a fail-safe; fast tick should be disabled as soon as the button is up, the LED times out, and/or the alarm finishes.
// but if for whatever reason it isn't, this forces the fast tick off after 20 seconds.
if (movement_state.fast_ticks >= 128 * 20) {
Expand Down
14 changes: 13 additions & 1 deletion movement/movement.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Joey Castillo
* Copyright (c) 2022 Joey Castillo (edits by Patrick McGuire)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -112,14 +112,20 @@ typedef enum {
EVENT_LIGHT_BUTTON_UP, // The light button was pressed for less than half a second, and released.
EVENT_LIGHT_LONG_PRESS, // The light button was held for over half a second, but not yet released.
EVENT_LIGHT_LONG_UP, // The light button was held for over half a second, and released.
EVENT_LIGHT_REALLY_LONG_PRESS, // The light button was held for over 3 seconds, but not yet released.
EVENT_LIGHT_REALLY_LONG_UP, // The light button was held for over 3 second, and released.
EVENT_MODE_BUTTON_DOWN, // The mode button has been pressed, but not yet released.
EVENT_MODE_BUTTON_UP, // The mode button was pressed for less than half a second, and released.
EVENT_MODE_LONG_PRESS, // The mode button was held for over half a second, but not yet released.
EVENT_MODE_LONG_UP, // The mode button was held for over half a second, and released. NOTE: your watch face will resign immediately after receiving this event.
EVENT_MODE_REALLY_LONG_PRESS, // The mode button was held for over 3 seconds, but not yet released.
EVENT_MODE_REALLY_LONG_UP, // The mode button was held for over 3 seconds, and released.
EVENT_ALARM_BUTTON_DOWN, // The alarm button has been pressed, but not yet released.
EVENT_ALARM_BUTTON_UP, // The alarm button was pressed for less than half a second, and released.
EVENT_ALARM_LONG_PRESS, // The alarm button was held for over half a second, but not yet released.
EVENT_ALARM_LONG_UP, // The alarm button was held for over half a second, and released.
EVENT_ALARM_REALLY_LONG_PRESS, // The alarm button was held for over 3 seconds, but not yet released.
EVENT_ALARM_REALLY_LONG_UP, // The alarm button was held for over 3 seconds, and released.
} movement_event_type_t;

typedef struct {
Expand Down Expand Up @@ -242,6 +248,8 @@ typedef struct {
typedef struct {
// properties stored in BACKUP register
movement_settings_t settings;
movement_location_t location;
movement_birthdate_t birthdate;

// transient properties
int16_t current_face_idx;
Expand Down Expand Up @@ -270,6 +278,10 @@ typedef struct {

// low energy mode countdown
int32_t le_mode_ticks;
uint8_t debounce_ticks_light;
uint8_t debounce_ticks_alarm;
uint8_t debounce_ticks_mode;
bool ignore_alarm_btn_after_sleep;

// app resignation countdown (TODO: consolidate with LE countdown?)
int16_t timeout_ticks;
Expand Down
Loading