From e05abdbdb12c6d212d09b615c34d82242d00e319 Mon Sep 17 00:00:00 2001 From: Johan Oskarsson Date: Sat, 28 Sep 2024 08:30:15 -0700 Subject: [PATCH] Add Chirpy support to databank --- .../watch_faces/complication/databank_face.c | 105 +++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/databank_face.c b/movement/watch_faces/complication/databank_face.c index 8be54a66a..a6e91603d 100644 --- a/movement/watch_faces/complication/databank_face.c +++ b/movement/watch_faces/complication/databank_face.c @@ -27,6 +27,7 @@ #include "databank_face.h" #include "watch.h" #include "watch_private_display.h" +#include "chirpy_tx.h" const char *pi_data[] = { "PI", "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442", @@ -40,12 +41,99 @@ const char *pi_data[] = { const int databank_num_pages = (sizeof(pi_data) / sizeof(char*) / 2); +typedef enum { + DATABANK_DISPLAY = 0, + DATABANK_CHIRPING, +} databank_mode_t; + struct { uint8_t current_word; uint8_t databank_page; bool animating; + + // Current operating mode, used to know if we are chirping or not. + databank_mode_t mode; + + // Helps us handle 1/64 ticks during chirpy transmission; including countdown timer + chirpy_tick_state_t tick_state; + + // Used by chirpy encoder during transmission + chirpy_encoder_state_t encoder_state; } databank_state; +// Support for chirping out the data. See chipy_demo_face.* for more information +static void _databank_quit_chirping() { + databank_state.mode = DATABANK_DISPLAY; + watch_set_buzzer_off(); + watch_clear_indicator(WATCH_INDICATOR_BELL); + movement_request_tick_frequency(1); +} + +static void _databank_data_tick(void *context) { + uint8_t tone = chirpy_get_next_tone(&databank_state.encoder_state); + // Transmission over? + if (tone == 255) { + _databank_quit_chirping(); + return; + } + uint16_t period = chirpy_get_tone_period(tone); + watch_set_buzzer_period(period); + watch_set_buzzer_on(); +} + +// Which byte are we currently processing? +static uint16_t curr_data_ix; + +static uint8_t _databank_get_next_byte(uint8_t *next_byte) { + // We always send out the currently displayed dataset + int curr_pos = databank_state.databank_page * 2 + 1; + if (curr_data_ix == strlen(pi_data[curr_pos])) { + return 0; + } + *next_byte = pi_data[curr_pos][curr_data_ix]; + ++curr_data_ix; + return 1; +} + +static void _databank_countdown_tick(void *context) { + chirpy_tick_state_t *tick_state = &databank_state.tick_state; + + // Countdown over: start actual broadcast + if (tick_state->seq_pos == 8 * 3) { + tick_state->tick_compare = 3; + tick_state->tick_count = -1; + tick_state->seq_pos = 0; + + // Set up the encoder + chirpy_init_encoder(&databank_state.encoder_state, _databank_get_next_byte); + tick_state->tick_fun = _databank_data_tick; + // Set up the data + curr_data_ix = 0; + return; + } + + // Sound or turn off buzzer + if ((tick_state->seq_pos % 8) == 0) { + watch_set_buzzer_period(NotePeriods[BUZZER_NOTE_A5]); + watch_set_buzzer_on(); + } else if ((tick_state->seq_pos % 8) == 1) { + watch_set_buzzer_off(); + } + ++tick_state->seq_pos; +} + +static void _databank_setup_chirp() { + // We want frequent callbacks from now on + movement_request_tick_frequency(64); + watch_set_indicator(WATCH_INDICATOR_BELL); + databank_state.mode = DATABANK_CHIRPING; + // Set up tick state; start with countdown + databank_state.tick_state.tick_count = -1; + databank_state.tick_state.tick_compare = 8; + databank_state.tick_state.seq_pos = 0; + databank_state.tick_state.tick_fun = _databank_countdown_tick; +} + void databank_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { // These next two lines just silence the compiler warnings associated with unused parameters. // We have no use for the settings or the watch_face_index, so we make that explicit here. @@ -92,11 +180,20 @@ bool databank_face_loop(movement_event_t event, movement_settings_t *settings, v switch (event.event_type) { case EVENT_ACTIVATE: - display(); + display(); case EVENT_TICK: + if (databank_state.mode == DATABANK_CHIRPING) { + ++databank_state.tick_state.tick_count; + if (databank_state.tick_state.tick_count == databank_state.tick_state.tick_compare) { + databank_state.tick_state.tick_count = 0; + databank_state.tick_state.tick_fun(context); + } + } break; case EVENT_LIGHT_BUTTON_UP: databank_state.current_word = (databank_state.current_word + max_words - 1) % max_words; + // TODO ideally change this to chirp if both buttons are pressed at the same time? + _databank_setup_chirp(); display(); break; case EVENT_LIGHT_LONG_PRESS: @@ -126,7 +223,11 @@ bool databank_face_loop(movement_event_t event, movement_settings_t *settings, v // This function will return the watch to the first screen (usually a simple clock), // and it will do it long before the watch enters low energy mode. This ensures we // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above. - movement_move_to_face(0); + + // Do not time out while we're chirping + if (databank_state.mode != DATABANK_CHIRPING) { + movement_move_to_face(0); + } break; case EVENT_LIGHT_BUTTON_DOWN: // don't light up every time light is hit