From 27e9b11400a68a227ab5cabd8f65f328a5e7e99a Mon Sep 17 00:00:00 2001 From: Mitchell Marino Date: Fri, 28 Mar 2025 15:29:01 -0500 Subject: [PATCH] playback for char lcd --- main/drivers/bottom_half.cpp | 6 +++ main/drivers/char_lcd.cpp | 85 ++++++++++++++++++++++++++++++++- main/drivers/leds.cpp | 6 +++ main/drivers/speaker.cpp | 6 +++ main/drivers/sseg.cpp | 6 +++ main/drivers/state_tracking.cpp | 54 ++++++++++++++++++--- main/drivers/state_tracking.h | 2 +- main/drivers/tft.cpp | 6 +-- main/main.cpp | 3 +- main/steps/setup_wires.cpp | 2 +- main/steps/step0.cpp | 17 +++++++ 11 files changed, 177 insertions(+), 16 deletions(-) diff --git a/main/drivers/bottom_half.cpp b/main/drivers/bottom_half.cpp index f2a0b4b..defd928 100644 --- a/main/drivers/bottom_half.cpp +++ b/main/drivers/bottom_half.cpp @@ -30,6 +30,10 @@ static void receive_keypad(); static void receive_button_switch(); static void receive_touch(); +static bool replay_handler(const char* event, char* arg) { + return false; +} + // TODO: add intrupt on bottom half delta pin // static void IRAM_ATTR gpio_isr_handler(void* arg) // { @@ -57,6 +61,8 @@ void init_bottom_half() { xTaskCreate(poll_bottom_task, "poll_bottom", 4096, NULL, 10, NULL); + register_replay_fn(replay_handler); + ESP_LOGI(TAG, "Bottom half initialized!"); } diff --git a/main/drivers/char_lcd.cpp b/main/drivers/char_lcd.cpp index d427bae..cc4bca6 100644 --- a/main/drivers/char_lcd.cpp +++ b/main/drivers/char_lcd.cpp @@ -3,6 +3,7 @@ #include "./i2c_lcd_pcf8574.h" #include #include "state_tracking.h" +#include i2c_lcd_pcf8574_handle_t lcd; @@ -10,6 +11,83 @@ static const char *TAG = "char_lcd"; static char buf[65]; +static bool replay_handler(const char* event, char* arg) { + if (strcmp(event, "LCD_CLEAR") == 0) { + lcd_clear(); + return true; + } + if (strcmp(event, "LCD_CURSOR") == 0) { + char* col_str = strtok(arg, ","); + char* row_str = strtok(NULL, ","); + uint32_t col = atoi(col_str); + uint32_t row = atoi(row_str); + lcd_set_cursor_pos(col, row); + return true; + } + if (strcmp(event, "LCD_SET_DISPLAY") == 0) { + lcd_set_display(strcmp(arg, "true") == 0); + return true; + } + if (strcmp(event, "LCD_CURSOR_VIS") == 0) { + lcd_set_cursor_vis(strcmp(arg, "true") == 0); + return true; + } + if (strcmp(event, "LCD_CURSOR_BLINK") == 0) { + lcd_set_cursor_blink(strcmp(arg, "true") == 0); + return true; + } + if (strcmp(event, "LCD_SCROLL_DISPLAY_LEFT") == 0) { + lcd_scroll_display_left(); + return true; + } + if (strcmp(event, "LCD_SCROLL_DISPLAY_RIGHT") == 0) { + lcd_scroll_display_right(); + return true; + } + if (strcmp(event, "LCD_LEFT_TO_RIGHT") == 0) { + lcd_left_to_right(); + return true; + } + if (strcmp(event, "LCD_RIGHT_TO_LEFT") == 0) { + lcd_right_to_left(); + return true; + } + if (strcmp(event, "LCD_AUTOSCROLL") == 0) { + lcd_set_autoscroll(strcmp(arg, "true") == 0); + return true; + } + if (strcmp(event, "LCD_BACKLIGHT") == 0) { + uint32_t brightness = atoi(arg); + lcd_set_backlight(brightness); + return true; + } + if (strcmp(event, "LCD_CREATE_CHAR") == 0) { + char* location_str = strtok(arg, ","); + uint8_t location = atoi(location_str); + + uint8_t charmap[8]; + for (int i = 0; i < 8; i++) { + char* str = strtok(NULL, ","); + charmap[i] = atoi(str); + } + + lcd_create_char(location, charmap); + return true; + } + if (strcmp(event, "LCD_WRITE") == 0) { + uint8_t value = atoi(arg); + lcd_write(value); + return true; + } + if (strcmp(event, "LCD_PRINT") == 0) { + // TODO: handle \r and \n + lcd_print(arg); + return true; + } + + return false; +} + void init_lcd() { ESP_LOGI(TAG, "Initializing LCD..."); @@ -18,6 +96,8 @@ void init_lcd() { lcd_set_backlight(&lcd, 255); + register_replay_fn(replay_handler); + ESP_LOGI(TAG, "LCD initialized!"); } @@ -101,14 +181,14 @@ void lcd_left_to_right() { lcd_left_to_right(&lcd); if (is_state_tracking()) { - event_occured("LCD_SCROLL_LEFT_TO_RIGHT", NULL); + event_occured("LCD_LEFT_TO_RIGHT", NULL); } } void lcd_right_to_left() { lcd_right_to_left(&lcd); if (is_state_tracking()) { - event_occured("LCD_SCROLL_RIGHT_TO_LEFT", NULL); + event_occured("LCD_RIGHT_TO_LEFT", NULL); } } @@ -158,6 +238,7 @@ void lcd_print(const char* str) { lcd_print(&lcd, str); if (is_state_tracking()) { + // TODO: handle \r and \n event_occured("LCD_PRINT", str); } } diff --git a/main/drivers/leds.cpp b/main/drivers/leds.cpp index bbad0de..c792ae9 100644 --- a/main/drivers/leds.cpp +++ b/main/drivers/leds.cpp @@ -7,6 +7,10 @@ static const char* TAG = "leds"; static led_strip_handle_t leds; +static bool replay_handler(const char* event, char* arg) { + return false; +} + void init_leds() { ESP_LOGI(TAG, "Initializing LEDs..."); @@ -25,6 +29,8 @@ void init_leds() { ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &leds)); + register_replay_fn(replay_handler); + ESP_LOGI(TAG, "LEDs initialized!"); } diff --git a/main/drivers/speaker.cpp b/main/drivers/speaker.cpp index 29ca618..b54a213 100644 --- a/main/drivers/speaker.cpp +++ b/main/drivers/speaker.cpp @@ -30,6 +30,10 @@ QueueHandle_t play_clip_queue; /// The clips that are currently playing std::vector playing_clips; +static bool replay_handler(const char* event, char* arg) { + return false; +} + static bool push_clip(audio_clip_t clip) { ESP_LOGD(TAG, "playing %s", clip.file_name); FILE* fh = fopen(clip.file_name, "rb"); @@ -233,6 +237,8 @@ void init_speaker(void) { xTaskCreate(speaker_task, "play_audio", 4096, NULL, 5, NULL); play_clip_wav(MOUNT_POINT "/startup.wav", true, false, 4, 0); + + register_replay_fn(replay_handler); ESP_LOGI(TAG, "Speaker initialized!"); } diff --git a/main/drivers/sseg.cpp b/main/drivers/sseg.cpp index a7dc162..3e023ca 100644 --- a/main/drivers/sseg.cpp +++ b/main/drivers/sseg.cpp @@ -6,11 +6,17 @@ TM1640* sseg = nullptr; static const char *TAG = "sseg"; +static bool replay_handler(const char* event, char* arg) { + return false; +} + void init_sseg() { ESP_LOGI(TAG, "Initializing sseg..."); sseg = new TM1640(SSEG_PIN_DATA, SSEG_PIN_CLK, 8); + register_replay_fn(replay_handler); + ESP_LOGI(TAG, "Sseg initialized!"); } diff --git a/main/drivers/state_tracking.cpp b/main/drivers/state_tracking.cpp index 50e2228..f191d55 100644 --- a/main/drivers/state_tracking.cpp +++ b/main/drivers/state_tracking.cpp @@ -8,7 +8,7 @@ enum state_t { STATE_PLAYBACK = 2, }; -static std::vector replay_fns; +static std::vector replay_fns; static bool should_close_recording_stream; static FILE* recording_stream; @@ -43,11 +43,29 @@ static void flush_file_task(void* arg) { static void playback_task(void* arg) { const size_t size = 16*1024; char* buf = (char*) malloc(size*sizeof(char)); + if (buf == nullptr) { + ESP_LOGE("playback", "buf alloc failure"); + vTaskDelete(NULL); + } while (state == STATE_PLAYBACK && playback_stream != nullptr) { fgets(buf, size, playback_stream); + ESP_LOGI("playback", "handling: %s", buf); + if (buf[0] == '\0') { + ESP_LOGI("playback", "playback done"); + break; + } + // get rid of the '\n' and possibly '\r' in the string + for (int i = 0; i < size; i++) { + if (buf[i] == '\0') break; + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } // look for a comma, indicating the end of the "ticks" part + // TODO: replace with strtok size_t comma_pos = 0; for (int i = 0; i < 11; i++) { if (buf[i] == ',') { @@ -71,6 +89,7 @@ static void playback_task(void* arg) { colon_pos = i; break; } + i++; } if (colon_pos == 0) { ESP_LOGE("playback", "Failed to find colon in playback line"); @@ -81,6 +100,7 @@ static void playback_task(void* arg) { char* event_name = buf + (comma_pos + 1); char* arg = buf + (colon_pos + 1); + ESP_LOGI("playback", "going to wait until %ld", tick); int32_t ticks_to_wait = ((int32_t) tick) - (int32_t)(xTaskGetTickCount() - playback_start_time); if (ticks_to_wait < 0) { @@ -90,14 +110,22 @@ static void playback_task(void* arg) { vTaskDelay(ticks_to_wait); } + + ESP_LOGI("playback", "matching..."); + bool matched = false; for (const auto& fn : replay_fns) { - (fn)(event_name, arg); + matched = (fn)(event_name, arg); + if (matched) break; } + ESP_LOGI("playback", "matched? %d", matched); } + + free(buf); + vTaskDelete(NULL); } -void register_event_cb(void (*replay_callback)(const char*, const char*)) { - replay_fns.push_back(replay_callback); +void register_replay_fn(bool (*replay_fn)(const char*, char*)) { + replay_fns.push_back(replay_fn); } void event_occured(const char* name, const char* arg) { @@ -162,13 +190,25 @@ bool start_playback() { state = STATE_PLAYBACK; playback_start_time = xTaskGetTickCount(); - xTaskCreate(playback_task, "playback", 2048, NULL, 2, &playback_task_handle); + xTaskCreate(playback_task, "playback", 4096, NULL, 2, &playback_task_handle); return true; } bool stop_playback() { - // TODO: impl - return false; + if (state != STATE_PLAYBACK) return false; + + state = STATE_IDLE; + if (should_close_playback_stream) { + fclose(playback_stream); + playback_stream = nullptr; + } + + if (playback_task_handle != nullptr) { + // TODO: NO!! This leaks the malloc. Instead, use a queue to request a graceful stop + vTaskDelete(playback_task_handle); + playback_task_handle = NULL; + } + return true; } bool is_state_tracking() { diff --git a/main/drivers/state_tracking.h b/main/drivers/state_tracking.h index a2e8d72..406117f 100644 --- a/main/drivers/state_tracking.h +++ b/main/drivers/state_tracking.h @@ -6,7 +6,7 @@ /// @brief Registers function to be called on replay. /// @param replay_callback A function to call to playback the event. -void register_replay_fn(void (*replay_fn)(const char*, const char*)); +void register_replay_fn(bool (*replay_fn)(const char*, char*)); // TODO: add one for generically responding to all events. diff --git a/main/drivers/tft.cpp b/main/drivers/tft.cpp index fc9ff9b..023fa97 100644 --- a/main/drivers/tft.cpp +++ b/main/drivers/tft.cpp @@ -17,8 +17,8 @@ static lv_style_t style_screen; SemaphoreHandle_t xGuiSemaphore; -static void replay_handler(const char* event, const char* arg) { - +static bool replay_handler(const char* event, char* arg) { + return false; } static bool notify_lvgl_flush_ready( @@ -50,7 +50,7 @@ static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); // TODO: change this to be a kconfig value - #if true + #if false if (is_state_tracking()) { size_t size = (offsetx2 + 1 - offsetx1) * (offsety2 + 1 - offsety1) + 1; diff --git a/main/main.cpp b/main/main.cpp index 5836ee3..625e877 100755 --- a/main/main.cpp +++ b/main/main.cpp @@ -36,10 +36,9 @@ extern "C" void app_main(void) { init_wires(); vTaskDelay(pdMS_TO_TICKS(1000)); - clean_bomb(); step0(); - + // set_recording_source(stdout, false); FILE* record_file = fopen(MOUNT_POINT "/record.txt", "w"); if (record_file == nullptr) { diff --git a/main/steps/setup_wires.cpp b/main/steps/setup_wires.cpp index 09d19dc..732a43f 100644 --- a/main/steps/setup_wires.cpp +++ b/main/steps/setup_wires.cpp @@ -30,7 +30,7 @@ void setup_wires(void) { clear_all_pressed_released(); get_cut_wires(); lcd_clear(); - lcd_set_cursor_vis(false); + lcd_set_cursor_vis(true); WireColor wires[NUM_WIRES]; load_wires_from_sd_card(wires); diff --git a/main/steps/step0.cpp b/main/steps/step0.cpp index cc909a3..415fa4d 100644 --- a/main/steps/step0.cpp +++ b/main/steps/step0.cpp @@ -1,4 +1,5 @@ #include "step0.h" +#include "drivers/state_tracking.h" static const char* TAG = "step0"; @@ -34,6 +35,16 @@ static void battery_stats() { } } +// TODO: remove. This is temperary +static void replay_last() { + FILE* record_file = fopen(MOUNT_POINT "/record.txt", "r"); + if (record_file == nullptr) { + ESP_LOGE("main", "failed to open record.txt"); + } + set_playback_source(record_file, true); + start_playback(); +} + /// Wait for "*9819" void step0() { led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE); @@ -154,6 +165,12 @@ void step0() { .should_exit = false, .callback = flashbang, }, + { + .code = "*1113", + .display_text = "replay_last", + .should_exit = false, + .callback = replay_last, + }, }; int len = sizeof(star_codes)/sizeof(StarCodeHandler); do_star_codes(star_codes, len);