From: joeycastillo Date: Tue, 22 Oct 2024 11:49:41 +0000 (-0400) Subject: bring in Chirpy face X-Git-Url: https://git.earman.xyz/?a=commitdiff_plain;h=c0514ad39a0f76a087f33ded8f148dd4c094025d;p=sensor-watch.git bring in Chirpy face --- diff --git a/Makefile b/Makefile index 80e42b5..d37da38 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ INCLUDES += \ -I./filesystem \ -I./shell \ -I./movement/lib/sunriset \ + -I./movement/lib/chirpy_tx \ -I./watch-library/shared/watch \ -I./watch-library/shared/driver \ -I./watch-faces/clock \ @@ -51,6 +52,7 @@ INCLUDES += \ -I./watch-faces/demo \ -I./watch-faces/sensor \ -I./watch-faces/settings \ + -I./watch-faces/io \ # Add your source files here. SRCS += \ @@ -62,6 +64,7 @@ SRCS += \ ./shell/shell.c \ ./shell/shell_cmd_list.c \ ./movement/lib/sunriset/sunriset.c \ + ./movement/lib/chirpy_tx/chirpy_tx.c \ ./watch-library/shared/driver/thermistor_driver.c \ ./watch-library/shared/watch/watch_common_buzzer.c \ ./watch-library/shared/watch/watch_common_display.c \ diff --git a/movement/watch_faces/demo/chirpy_demo_face.c b/movement/watch_faces/demo/chirpy_demo_face.c deleted file mode 100644 index 84e1670..0000000 --- a/movement/watch_faces/demo/chirpy_demo_face.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2023 Gabor L Ugray - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include "chirpy_demo_face.h" -#include "chirpy_tx.h" -#include "filesystem.h" - -typedef enum { - CDM_CHOOSE = 0, - CDM_CHIRPING, -} chirpy_demo_mode_t; - -typedef enum { - CDP_SCALE = 0, - CDP_INFO_SHORT, - CDP_INFO_LONG, - CDP_INFO_NANOSEC, -} chirpy_demo_program_t; - -typedef struct { - // Current mode - chirpy_demo_mode_t mode; - - // Selected program - chirpy_demo_program_t program; - - // Helps us handle 1/64 ticks during transmission; including countdown timer - chirpy_tick_state_t tick_state; - - // Used by chirpy encoder during transmission - chirpy_encoder_state_t encoder_state; - -} chirpy_demo_state_t; - -static uint8_t long_data_str[] = - "There once was a ship that put to sea\n" - "The name of the ship was the Billy of Tea\n" - "The winds blew up, her bow dipped down\n" - "O blow, my bully boys, blow (huh)\n" - "\n" - "Soon may the Wellerman come\n" - "To bring us sugar and tea and rum\n" - "One day, when the tonguin' is done\n" - "We'll take our leave and go\n"; - -static uint16_t short_data_len = 20; - -static uint8_t short_data[] = { - 0x27, - 0x00, - 0x0c, - 0x42, - 0xa3, - 0xd4, - 0x06, - 0x54, - 0x00, - 0x00, - 0x02, - 0x0c, - 0x6b, - 0x05, - 0x5a, - 0x09, - 0xd8, - 0x00, - 0xf5, - 0x00, -}; - -#define NANOSEC_INI_FILE_NAME "nanosec.ini" - -static uint8_t *nanosec_buffer = 0; -static uint16_t nanosec_buffer_size = 0; - -void chirpy_demo_face_setup(uint8_t watch_face_index, void **context_ptr) { - (void)watch_face_index; - if (*context_ptr == NULL) { - *context_ptr = malloc(sizeof(chirpy_demo_state_t)); - memset(*context_ptr, 0, sizeof(chirpy_demo_state_t)); - // Do any one-time tasks in here; the inside of this conditional happens only at boot. - } - // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. -} - -void chirpy_demo_face_activate(void *context) { - chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; - - memset(context, 0, sizeof(chirpy_demo_state_t)); - state->mode = CDM_CHOOSE; - state->program = CDP_SCALE; - - // Do we have nanosec data? Load it. - int32_t sz = filesystem_get_file_size(NANOSEC_INI_FILE_NAME); - if (sz > 0) { - // We will free this in resign. - // I don't like any kind of dynamic allocation in long-running embedded software... - // But there's no way around it here; I don't want to hard-wire (and squat) any fixed size structure - // Nanosec data may change in the future too - nanosec_buffer_size = sz + 2; - nanosec_buffer = malloc(nanosec_buffer_size); - // First two bytes of prefix, so Chirpy RX can recognize this data type - nanosec_buffer[0] = 0xc0; - nanosec_buffer[1] = 0x00; - // Read file - filesystem_read_file(NANOSEC_INI_FILE_NAME, (char*)&nanosec_buffer[2], sz); - } -} - -// To create / check test file in emulator: -// echo TestData > nanosec.ini -// cat nanosec.ini - -static void _cdf_update_lcd(chirpy_demo_state_t *state) { - watch_display_string("CH", 0); - if (state->program == CDP_SCALE) - watch_display_string(" SCALE", 4); - else if (state->program == CDP_INFO_SHORT) - watch_display_string("SHORT ", 4); - else if (state->program == CDP_INFO_LONG) - watch_display_string(" LOng ", 4); - else if (state->program == CDP_INFO_NANOSEC) - watch_display_string("nAnO ", 4); - else - watch_display_string("---- ", 4); -} - -static void _cdf_quit_chirping(chirpy_demo_state_t *state) { - state->mode = CDM_CHOOSE; - watch_set_buzzer_off(); - watch_clear_indicator(WATCH_INDICATOR_BELL); - movement_request_tick_frequency(1); -} - -static void _cdf_scale_tick(void *context) { - chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; - chirpy_tick_state_t *tick_state = &state->tick_state; - - // Scale goes in 200Hz increments from 700 Hz to 12.3 kHz -> 58 steps - if (tick_state->seq_pos == 58) { - _cdf_quit_chirping(state); - return; - } - uint32_t freq = 700 + tick_state->seq_pos * 200; - uint32_t period = 1000000 / freq; - watch_set_buzzer_period_and_duty_cycle(period, 25); - watch_set_buzzer_on(); - ++tick_state->seq_pos; -} - -static void _cdf_data_tick(void *context) { - chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; - - uint8_t tone = chirpy_get_next_tone(&state->encoder_state); - // Transmission over? - if (tone == 255) { - _cdf_quit_chirping(state); - return; - } - uint16_t period = chirpy_get_tone_period(tone); - watch_set_buzzer_period_and_duty_cycle(period, 25); - watch_set_buzzer_on(); -} - -static uint8_t *curr_data_ptr; -static uint16_t curr_data_ix; -static uint16_t curr_data_len; - -static uint8_t _cdf_get_next_byte(uint8_t *next_byte) { - if (curr_data_ix == curr_data_len) - return 0; - *next_byte = curr_data_ptr[curr_data_ix]; - ++curr_data_ix; - return 1; -} - -static void _cdf_countdown_tick(void *context) { - chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; - chirpy_tick_state_t *tick_state = &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; - // We'll be chirping out a scale - if (state->program == CDP_SCALE) { - tick_state->tick_fun = _cdf_scale_tick; - } - // We'll be chirping out data - else { - // Set up the encoder - chirpy_init_encoder(&state->encoder_state, _cdf_get_next_byte); - tick_state->tick_fun = _cdf_data_tick; - // Set up the data - curr_data_ix = 0; - if (state->program == CDP_INFO_SHORT) { - curr_data_ptr = short_data; - curr_data_len = short_data_len; - } else if (state->program == CDP_INFO_LONG) { - curr_data_ptr = long_data_str; - curr_data_len = strlen((const char *)long_data_str); - } else if (state->program == CDP_INFO_NANOSEC) { - curr_data_ptr = nanosec_buffer; - curr_data_len = nanosec_buffer_size; - } - } - return; - } - // Sound or turn off buzzer - if ((tick_state->seq_pos % 8) == 0) { - watch_set_buzzer_period_and_duty_cycle(NotePeriods[BUZZER_NOTE_A5], 25); - watch_set_buzzer_on(); - } else if ((tick_state->seq_pos % 8) == 1) { - watch_set_buzzer_off(); - } - ++tick_state->seq_pos; -} - -static void _cdm_setup_chirp(chirpy_demo_state_t *state) { - // We want frequent callbacks from now on - movement_request_tick_frequency(64); - watch_set_indicator(WATCH_INDICATOR_BELL); - state->mode = CDM_CHIRPING; - // Set up tick state; start with countdown - state->tick_state.tick_count = -1; - state->tick_state.tick_compare = 8; - state->tick_state.seq_pos = 0; - state->tick_state.tick_fun = _cdf_countdown_tick; -} - -bool chirpy_demo_face_loop(movement_event_t event, void *context) { - chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; - - switch (event.event_type) { - case EVENT_ACTIVATE: - _cdf_update_lcd(state); - break; - case EVENT_MODE_BUTTON_UP: - // Do not exit face while we're chirping - if (state->mode != CDM_CHIRPING) { - movement_move_to_next_face(); - } - break; - case EVENT_LIGHT_BUTTON_UP: - // We don't do light. - break; - case EVENT_ALARM_BUTTON_UP: - // If in choose mode: select next program - if (state->mode == CDM_CHOOSE) { - if (state->program == CDP_SCALE) - state->program = CDP_INFO_SHORT; - else if (state->program == CDP_INFO_SHORT) - state->program = CDP_INFO_LONG; - else if (state->program == CDP_INFO_LONG) { - if (nanosec_buffer_size > 0) - state->program = CDP_INFO_NANOSEC; - else - state->program = CDP_SCALE; - } else if (state->program == CDP_INFO_NANOSEC) - state->program = CDP_SCALE; - _cdf_update_lcd(state); - } - // If chirping: stoppit - else if (state->mode == CDM_CHIRPING) { - _cdf_quit_chirping(state); - } - break; - case EVENT_ALARM_LONG_PRESS: - // If in choose mode: start chirping - if (state->mode == CDM_CHOOSE) { - _cdm_setup_chirp(state); - } - break; - case EVENT_TICK: - if (state->mode == CDM_CHIRPING) { - ++state->tick_state.tick_count; - if (state->tick_state.tick_count == state->tick_state.tick_compare) { - state->tick_state.tick_count = 0; - state->tick_state.tick_fun(context); - } - } - break; - case EVENT_TIMEOUT: - // Do not time out while we're chirping - if (state->mode != CDM_CHIRPING) { - movement_move_to_face(0); - } - default: - break; - } - - // Return true if the watch can enter standby mode. False needed when chirping. - if (state->mode == CDM_CHIRPING) - return false; - else - return true; -} - -void chirpy_demo_face_resign(void *context) { - (void)context; - - if (nanosec_buffer != 0) { - free(nanosec_buffer); - nanosec_buffer = 0; - nanosec_buffer_size = 0; - } -} diff --git a/movement/watch_faces/demo/chirpy_demo_face.h b/movement/watch_faces/demo/chirpy_demo_face.h deleted file mode 100644 index a3ada8d..0000000 --- a/movement/watch_faces/demo/chirpy_demo_face.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2023 <#author_name#> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIRPY_DEMO_FACE_H_ -#define CHIRPY_DEMO_FACE_H_ - -/* - * CHIRPY DEMO FACE - * - * This face demonstrates the chirpy-tx library. It is intended to help you - * include chirpy-tx in your own watch faces that need to transmit data out - * of the watch. - * - * The face's first screen lets you select from a few built-in transmissions: - * - * SCALE cycles through frequencies in fixed increments. This is intended to - * collect and analyze audio samples from different watches. With this info - * it may be possible to improve chirpy-tx's parameters like the frequencies - * it uses to make the method more robust. - * - * SHORT is a small transmission that contains data taked from the activity_face. - * - * LONG is a longer transmission that contains the first two strophes of a - * famous sea shanty. - * - * Select the transmission you want with ALARM, the press LONG ALARM to chirp. - * - * To record and decode a chirpy transmission on your computer, you can use the web app here: - * https://jealousmarkup.xyz/off/chirpy/rx/ - */ - -#include "movement.h" - -void chirpy_demo_face_setup(uint8_t watch_face_index, void ** context_ptr); -void chirpy_demo_face_activate(void *context); -bool chirpy_demo_face_loop(movement_event_t event, void *context); -void chirpy_demo_face_resign(void *context); - -#define chirpy_demo_face ((const watch_face_t){ \ - chirpy_demo_face_setup, \ - chirpy_demo_face_activate, \ - chirpy_demo_face_loop, \ - chirpy_demo_face_resign, \ - NULL, \ -}) - -#endif // CHIRPY_DEMO_FACE_H_ - diff --git a/movement_faces.h b/movement_faces.h index 2b72745..07896a1 100644 --- a/movement_faces.h +++ b/movement_faces.h @@ -44,4 +44,5 @@ #include "light_sensor_face.h" #include "accelerometer_sleep_state_face.h" #include "irda_demo_face.h" +#include "chirpy_demo_face.h" // New includes go above this line. diff --git a/watch-faces.mk b/watch-faces.mk index 3c7f1b4..24930d0 100644 --- a/watch-faces.mk +++ b/watch-faces.mk @@ -19,4 +19,5 @@ SRCS += \ ./watch-faces/demo/light_sensor_face.c \ ./watch-faces/demo/accelerometer_sleep_state_face.c \ ./watch-faces/demo/irda_demo_face.c \ + ./watch-faces/io/chirpy_demo_face.c \ # New watch faces go above this line. diff --git a/watch-faces/io/chirpy_demo_face.c b/watch-faces/io/chirpy_demo_face.c new file mode 100644 index 0000000..6530512 --- /dev/null +++ b/watch-faces/io/chirpy_demo_face.c @@ -0,0 +1,331 @@ +/* + * MIT License + * + * Copyright (c) 2023 Gabor L Ugray + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "chirpy_demo_face.h" +#include "chirpy_tx.h" +#include "filesystem.h" + +typedef enum { + CDM_CHOOSE = 0, + CDM_CHIRPING, +} chirpy_demo_mode_t; + +typedef enum { + CDP_SCALE = 0, + CDP_INFO_SHORT, + CDP_INFO_LONG, + CDP_INFO_NANOSEC, +} chirpy_demo_program_t; + +typedef struct { + // Current mode + chirpy_demo_mode_t mode; + + // Selected program + chirpy_demo_program_t program; + + // Helps us handle 1/64 ticks during transmission; including countdown timer + chirpy_tick_state_t tick_state; + + // Used by chirpy encoder during transmission + chirpy_encoder_state_t encoder_state; + +} chirpy_demo_state_t; + +static uint8_t long_data_str[] = + "There once was a ship that put to sea\n" + "The name of the ship was the Billy of Tea\n" + "The winds blew up, her bow dipped down\n" + "O blow, my bully boys, blow (huh)\n" + "\n" + "Soon may the Wellerman come\n" + "To bring us sugar and tea and rum\n" + "One day, when the tonguin' is done\n" + "We'll take our leave and go\n"; + +static uint16_t short_data_len = 20; + +static uint8_t short_data[] = { + 0x27, + 0x00, + 0x0c, + 0x42, + 0xa3, + 0xd4, + 0x06, + 0x54, + 0x00, + 0x00, + 0x02, + 0x0c, + 0x6b, + 0x05, + 0x5a, + 0x09, + 0xd8, + 0x00, + 0xf5, + 0x00, +}; + +#define ACTIVITY_DATA_FILE_NAME "activity.dat" + +static uint8_t *activity_buffer = 0; +static uint16_t activity_buffer_size = 0; + +void chirpy_demo_face_setup(uint8_t watch_face_index, void **context_ptr) { + (void)watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(chirpy_demo_state_t)); + memset(*context_ptr, 0, sizeof(chirpy_demo_state_t)); + // Do any one-time tasks in here; the inside of this conditional happens only at boot. + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void chirpy_demo_face_activate(void *context) { + chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; + + memset(context, 0, sizeof(chirpy_demo_state_t)); + state->mode = CDM_CHOOSE; + state->program = CDP_SCALE; + + // Do we have activity data? Load it. + int32_t sz = filesystem_get_file_size(ACTIVITY_DATA_FILE_NAME); + if (sz > 0) { + // We will free this in resign. + // I don't like any kind of dynamic allocation in long-running embedded software... + // But there's no way around it here; I don't want to hard-wire (and squat) any fixed size structure + // Nanosec data may change in the future too + activity_buffer_size = sz + 2; + activity_buffer = malloc(activity_buffer_size); + // First two bytes of prefix, so Chirpy RX can recognize this data type + activity_buffer[0] = 0xc0; + activity_buffer[1] = 0x00; + // Read file + filesystem_read_file(ACTIVITY_DATA_FILE_NAME, (char*)&activity_buffer[2], sz); + } +} + +// To create / check test file in emulator: +// echo TestData > activity.ini +// cat activity.ini + +static void _cdf_update_lcd(chirpy_demo_state_t *state) { + watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "CH", "Chirp"); + if (state->program == CDP_SCALE) + watch_display_text(WATCH_POSITION_BOTTOM, " SCALE"); + else if (state->program == CDP_INFO_SHORT) + watch_display_text(WATCH_POSITION_BOTTOM, "SHORT "); + else if (state->program == CDP_INFO_LONG) + watch_display_text(WATCH_POSITION_BOTTOM, " LOng "); + else if (state->program == CDP_INFO_NANOSEC) + watch_display_text(WATCH_POSITION_BOTTOM, " ACtIV"); + else + watch_display_text(WATCH_POSITION_BOTTOM, "---- "); +} + +static void _cdf_quit_chirping(chirpy_demo_state_t *state) { + state->mode = CDM_CHOOSE; + watch_set_buzzer_off(); + watch_clear_indicator(WATCH_INDICATOR_BELL); + movement_request_tick_frequency(1); +} + +static void _cdf_scale_tick(void *context) { + chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; + chirpy_tick_state_t *tick_state = &state->tick_state; + + // Scale goes in 200Hz increments from 700 Hz to 12.3 kHz -> 58 steps + if (tick_state->seq_pos == 58) { + _cdf_quit_chirping(state); + return; + } + uint32_t freq = 700 + tick_state->seq_pos * 200; + uint32_t period = 1000000 / freq; + watch_set_buzzer_period_and_duty_cycle(period, 25); + watch_set_buzzer_on(); + ++tick_state->seq_pos; +} + +static void _cdf_data_tick(void *context) { + chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; + + uint8_t tone = chirpy_get_next_tone(&state->encoder_state); + // Transmission over? + if (tone == 255) { + _cdf_quit_chirping(state); + return; + } + uint16_t period = chirpy_get_tone_period(tone); + watch_set_buzzer_period_and_duty_cycle(period, 25); + watch_set_buzzer_on(); +} + +static uint8_t *curr_data_ptr; +static uint16_t curr_data_ix; +static uint16_t curr_data_len; + +static uint8_t _cdf_get_next_byte(uint8_t *next_byte) { + if (curr_data_ix == curr_data_len) + return 0; + *next_byte = curr_data_ptr[curr_data_ix]; + ++curr_data_ix; + return 1; +} + +static void _cdf_countdown_tick(void *context) { + chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; + chirpy_tick_state_t *tick_state = &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; + // We'll be chirping out a scale + if (state->program == CDP_SCALE) { + tick_state->tick_fun = _cdf_scale_tick; + } + // We'll be chirping out data + else { + // Set up the encoder + chirpy_init_encoder(&state->encoder_state, _cdf_get_next_byte); + tick_state->tick_fun = _cdf_data_tick; + // Set up the data + curr_data_ix = 0; + if (state->program == CDP_INFO_SHORT) { + curr_data_ptr = short_data; + curr_data_len = short_data_len; + } else if (state->program == CDP_INFO_LONG) { + curr_data_ptr = long_data_str; + curr_data_len = strlen((const char *)long_data_str); + } else if (state->program == CDP_INFO_NANOSEC) { + curr_data_ptr = activity_buffer; + curr_data_len = activity_buffer_size; + } + } + return; + } + // Sound or turn off buzzer + if ((tick_state->seq_pos % 8) == 0) { + watch_set_buzzer_period_and_duty_cycle(NotePeriods[BUZZER_NOTE_A5], 25); + watch_set_buzzer_on(); + } else if ((tick_state->seq_pos % 8) == 1) { + watch_set_buzzer_off(); + } + ++tick_state->seq_pos; +} + +static void _cdm_setup_chirp(chirpy_demo_state_t *state) { + // We want frequent callbacks from now on + movement_request_tick_frequency(64); + watch_set_indicator(WATCH_INDICATOR_BELL); + state->mode = CDM_CHIRPING; + // Set up tick state; start with countdown + state->tick_state.tick_count = -1; + state->tick_state.tick_compare = 8; + state->tick_state.seq_pos = 0; + state->tick_state.tick_fun = _cdf_countdown_tick; +} + +bool chirpy_demo_face_loop(movement_event_t event, void *context) { + chirpy_demo_state_t *state = (chirpy_demo_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + _cdf_update_lcd(state); + break; + case EVENT_MODE_BUTTON_UP: + // Do not exit face while we're chirping + if (state->mode != CDM_CHIRPING) { + movement_move_to_next_face(); + } + break; + case EVENT_LIGHT_BUTTON_UP: + // We don't do light. + break; + case EVENT_ALARM_BUTTON_UP: + // If in choose mode: select next program + if (state->mode == CDM_CHOOSE) { + if (state->program == CDP_SCALE) + state->program = CDP_INFO_SHORT; + else if (state->program == CDP_INFO_SHORT) + state->program = CDP_INFO_LONG; + else if (state->program == CDP_INFO_LONG) { + if (activity_buffer_size > 0) + state->program = CDP_INFO_NANOSEC; + else + state->program = CDP_SCALE; + } else if (state->program == CDP_INFO_NANOSEC) + state->program = CDP_SCALE; + _cdf_update_lcd(state); + } + // If chirping: stoppit + else if (state->mode == CDM_CHIRPING) { + _cdf_quit_chirping(state); + } + break; + case EVENT_ALARM_LONG_PRESS: + // If in choose mode: start chirping + if (state->mode == CDM_CHOOSE) { + _cdm_setup_chirp(state); + } + break; + case EVENT_TICK: + if (state->mode == CDM_CHIRPING) { + ++state->tick_state.tick_count; + if (state->tick_state.tick_count == state->tick_state.tick_compare) { + state->tick_state.tick_count = 0; + state->tick_state.tick_fun(context); + } + } + break; + case EVENT_TIMEOUT: + // Do not time out while we're chirping + if (state->mode != CDM_CHIRPING) { + movement_move_to_face(0); + } + default: + break; + } + + // Return true if the watch can enter standby mode. False needed when chirping. + if (state->mode == CDM_CHIRPING) + return false; + else + return true; +} + +void chirpy_demo_face_resign(void *context) { + (void)context; + + if (activity_buffer != 0) { + free(activity_buffer); + activity_buffer = 0; + activity_buffer_size = 0; + } +} diff --git a/watch-faces/io/chirpy_demo_face.h b/watch-faces/io/chirpy_demo_face.h new file mode 100644 index 0000000..a3ada8d --- /dev/null +++ b/watch-faces/io/chirpy_demo_face.h @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2023 <#author_name#> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CHIRPY_DEMO_FACE_H_ +#define CHIRPY_DEMO_FACE_H_ + +/* + * CHIRPY DEMO FACE + * + * This face demonstrates the chirpy-tx library. It is intended to help you + * include chirpy-tx in your own watch faces that need to transmit data out + * of the watch. + * + * The face's first screen lets you select from a few built-in transmissions: + * + * SCALE cycles through frequencies in fixed increments. This is intended to + * collect and analyze audio samples from different watches. With this info + * it may be possible to improve chirpy-tx's parameters like the frequencies + * it uses to make the method more robust. + * + * SHORT is a small transmission that contains data taked from the activity_face. + * + * LONG is a longer transmission that contains the first two strophes of a + * famous sea shanty. + * + * Select the transmission you want with ALARM, the press LONG ALARM to chirp. + * + * To record and decode a chirpy transmission on your computer, you can use the web app here: + * https://jealousmarkup.xyz/off/chirpy/rx/ + */ + +#include "movement.h" + +void chirpy_demo_face_setup(uint8_t watch_face_index, void ** context_ptr); +void chirpy_demo_face_activate(void *context); +bool chirpy_demo_face_loop(movement_event_t event, void *context); +void chirpy_demo_face_resign(void *context); + +#define chirpy_demo_face ((const watch_face_t){ \ + chirpy_demo_face_setup, \ + chirpy_demo_face_activate, \ + chirpy_demo_face_loop, \ + chirpy_demo_face_resign, \ + NULL, \ +}) + +#endif // CHIRPY_DEMO_FACE_H_ +