playback for char lcd

This commit is contained in:
Mitchell Marino 2025-03-28 15:29:01 -05:00 committed by Mitchell M
parent 361a14aa09
commit 6508358ddf
11 changed files with 177 additions and 16 deletions

View File

@ -30,6 +30,10 @@ static void receive_keypad();
static void receive_button_switch(); static void receive_button_switch();
static void receive_touch(); static void receive_touch();
static bool replay_handler(const char* event, char* arg) {
return false;
}
// TODO: add intrupt on bottom half delta pin // TODO: add intrupt on bottom half delta pin
// static void IRAM_ATTR gpio_isr_handler(void* arg) // 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); xTaskCreate(poll_bottom_task, "poll_bottom", 4096, NULL, 10, NULL);
register_replay_fn(replay_handler);
ESP_LOGI(TAG, "Bottom half initialized!"); ESP_LOGI(TAG, "Bottom half initialized!");
} }

View File

@ -3,6 +3,7 @@
#include "./i2c_lcd_pcf8574.h" #include "./i2c_lcd_pcf8574.h"
#include <esp_log.h> #include <esp_log.h>
#include "state_tracking.h" #include "state_tracking.h"
#include <cstring>
i2c_lcd_pcf8574_handle_t lcd; i2c_lcd_pcf8574_handle_t lcd;
@ -10,6 +11,83 @@ static const char *TAG = "char_lcd";
static char buf[65]; 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() { void init_lcd() {
ESP_LOGI(TAG, "Initializing LCD..."); ESP_LOGI(TAG, "Initializing LCD...");
@ -18,6 +96,8 @@ void init_lcd() {
lcd_set_backlight(&lcd, 255); lcd_set_backlight(&lcd, 255);
register_replay_fn(replay_handler);
ESP_LOGI(TAG, "LCD initialized!"); ESP_LOGI(TAG, "LCD initialized!");
} }
@ -101,14 +181,14 @@ void lcd_left_to_right() {
lcd_left_to_right(&lcd); lcd_left_to_right(&lcd);
if (is_state_tracking()) { if (is_state_tracking()) {
event_occured("LCD_SCROLL_LEFT_TO_RIGHT", NULL); event_occured("LCD_LEFT_TO_RIGHT", NULL);
} }
} }
void lcd_right_to_left() { void lcd_right_to_left() {
lcd_right_to_left(&lcd); lcd_right_to_left(&lcd);
if (is_state_tracking()) { 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); lcd_print(&lcd, str);
if (is_state_tracking()) { if (is_state_tracking()) {
// TODO: handle \r and \n
event_occured("LCD_PRINT", str); event_occured("LCD_PRINT", str);
} }
} }

View File

@ -7,6 +7,10 @@ static const char* TAG = "leds";
static led_strip_handle_t leds; static led_strip_handle_t leds;
static bool replay_handler(const char* event, char* arg) {
return false;
}
void init_leds() { void init_leds() {
ESP_LOGI(TAG, "Initializing 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)); ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &leds));
register_replay_fn(replay_handler);
ESP_LOGI(TAG, "LEDs initialized!"); ESP_LOGI(TAG, "LEDs initialized!");
} }

View File

@ -30,6 +30,10 @@ QueueHandle_t play_clip_queue;
/// The clips that are currently playing /// The clips that are currently playing
std::vector<playing_audio_clip_t> playing_clips; std::vector<playing_audio_clip_t> playing_clips;
static bool replay_handler(const char* event, char* arg) {
return false;
}
static bool push_clip(audio_clip_t clip) { static bool push_clip(audio_clip_t clip) {
ESP_LOGD(TAG, "playing %s", clip.file_name); ESP_LOGD(TAG, "playing %s", clip.file_name);
FILE* fh = fopen(clip.file_name, "rb"); FILE* fh = fopen(clip.file_name, "rb");
@ -233,6 +237,8 @@ void init_speaker(void) {
xTaskCreate(speaker_task, "play_audio", 4096, NULL, 5, NULL); xTaskCreate(speaker_task, "play_audio", 4096, NULL, 5, NULL);
play_clip_wav(MOUNT_POINT "/startup.wav", true, false, 4, 0); play_clip_wav(MOUNT_POINT "/startup.wav", true, false, 4, 0);
register_replay_fn(replay_handler);
ESP_LOGI(TAG, "Speaker initialized!"); ESP_LOGI(TAG, "Speaker initialized!");
} }

View File

@ -6,11 +6,17 @@ TM1640* sseg = nullptr;
static const char *TAG = "sseg"; static const char *TAG = "sseg";
static bool replay_handler(const char* event, char* arg) {
return false;
}
void init_sseg() { void init_sseg() {
ESP_LOGI(TAG, "Initializing sseg..."); ESP_LOGI(TAG, "Initializing sseg...");
sseg = new TM1640(SSEG_PIN_DATA, SSEG_PIN_CLK, 8); sseg = new TM1640(SSEG_PIN_DATA, SSEG_PIN_CLK, 8);
register_replay_fn(replay_handler);
ESP_LOGI(TAG, "Sseg initialized!"); ESP_LOGI(TAG, "Sseg initialized!");
} }

View File

@ -8,7 +8,7 @@ enum state_t {
STATE_PLAYBACK = 2, STATE_PLAYBACK = 2,
}; };
static std::vector<void(*)(const char*, const char*)> replay_fns; static std::vector<bool(*)(const char*, char*)> replay_fns;
static bool should_close_recording_stream; static bool should_close_recording_stream;
static FILE* recording_stream; static FILE* recording_stream;
@ -43,11 +43,29 @@ static void flush_file_task(void* arg) {
static void playback_task(void* arg) { static void playback_task(void* arg) {
const size_t size = 16*1024; const size_t size = 16*1024;
char* buf = (char*) malloc(size*sizeof(char)); 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) { while (state == STATE_PLAYBACK && playback_stream != nullptr) {
fgets(buf, size, playback_stream); 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 // look for a comma, indicating the end of the "ticks" part
// TODO: replace with strtok
size_t comma_pos = 0; size_t comma_pos = 0;
for (int i = 0; i < 11; i++) { for (int i = 0; i < 11; i++) {
if (buf[i] == ',') { if (buf[i] == ',') {
@ -71,6 +89,7 @@ static void playback_task(void* arg) {
colon_pos = i; colon_pos = i;
break; break;
} }
i++;
} }
if (colon_pos == 0) { if (colon_pos == 0) {
ESP_LOGE("playback", "Failed to find colon in playback line"); 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* event_name = buf + (comma_pos + 1);
char* arg = buf + (colon_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); int32_t ticks_to_wait = ((int32_t) tick) - (int32_t)(xTaskGetTickCount() - playback_start_time);
if (ticks_to_wait < 0) { if (ticks_to_wait < 0) {
@ -90,14 +110,22 @@ static void playback_task(void* arg) {
vTaskDelay(ticks_to_wait); vTaskDelay(ticks_to_wait);
} }
ESP_LOGI("playback", "matching...");
bool matched = false;
for (const auto& fn : replay_fns) { 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*)) { void register_replay_fn(bool (*replay_fn)(const char*, char*)) {
replay_fns.push_back(replay_callback); replay_fns.push_back(replay_fn);
} }
void event_occured(const char* name, const char* arg) { void event_occured(const char* name, const char* arg) {
@ -162,13 +190,25 @@ bool start_playback() {
state = STATE_PLAYBACK; state = STATE_PLAYBACK;
playback_start_time = xTaskGetTickCount(); 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; return true;
} }
bool stop_playback() { bool stop_playback() {
// TODO: impl if (state != STATE_PLAYBACK) return false;
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() { bool is_state_tracking() {

View File

@ -6,7 +6,7 @@
/// @brief Registers function to be called on replay. /// @brief Registers function to be called on replay.
/// @param replay_callback A function to call to playback the event. /// @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. // TODO: add one for generically responding to all events.

View File

@ -17,8 +17,8 @@ static lv_style_t style_screen;
SemaphoreHandle_t xGuiSemaphore; 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( 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); esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
// TODO: change this to be a kconfig value // TODO: change this to be a kconfig value
#if true #if false
if (is_state_tracking()) { if (is_state_tracking()) {
size_t size = (offsetx2 + 1 - offsetx1) * (offsety2 + 1 - offsety1) + 1; size_t size = (offsetx2 + 1 - offsetx1) * (offsety2 + 1 - offsety1) + 1;

View File

@ -36,10 +36,9 @@ extern "C" void app_main(void) {
init_wires(); init_wires();
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
clean_bomb(); clean_bomb();
step0(); step0();
// set_recording_source(stdout, false); // set_recording_source(stdout, false);
FILE* record_file = fopen(MOUNT_POINT "/record.txt", "w"); FILE* record_file = fopen(MOUNT_POINT "/record.txt", "w");
if (record_file == nullptr) { if (record_file == nullptr) {

View File

@ -30,7 +30,7 @@ void setup_wires(void) {
clear_all_pressed_released(); clear_all_pressed_released();
get_cut_wires(); get_cut_wires();
lcd_clear(); lcd_clear();
lcd_set_cursor_vis(false); lcd_set_cursor_vis(true);
WireColor wires[NUM_WIRES]; WireColor wires[NUM_WIRES];
load_wires_from_sd_card(wires); load_wires_from_sd_card(wires);

View File

@ -1,4 +1,5 @@
#include "step0.h" #include "step0.h"
#include "drivers/state_tracking.h"
static const char* TAG = "step0"; 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" /// Wait for "*9819"
void step0() { void step0() {
led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE); led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE);
@ -154,6 +165,12 @@ void step0() {
.should_exit = false, .should_exit = false,
.callback = flashbang, .callback = flashbang,
}, },
{
.code = "*1113",
.display_text = "replay_last",
.should_exit = false,
.callback = replay_last,
},
}; };
int len = sizeof(star_codes)/sizeof(StarCodeHandler); int len = sizeof(star_codes)/sizeof(StarCodeHandler);
do_star_codes(star_codes, len); do_star_codes(star_codes, len);