more LCD Header work

This commit is contained in:
Mitchell Marino 2025-07-17 17:02:31 -05:00
parent 15e8630a88
commit a9e44145f0
14 changed files with 123 additions and 72 deletions

View File

@ -14,4 +14,6 @@ void init_drivers() {
init_tft(); init_tft();
init_leds(); init_leds();
init_power_board(); init_power_board();
set_lcd_header_enabled(true);
} }

View File

@ -8,14 +8,22 @@
i2c_lcd_pcf8574_handle_t lcd; i2c_lcd_pcf8574_handle_t lcd;
static volatile bool header_enabled; static volatile bool header_enabled = false;
static const char *TAG = "char_lcd"; static const char *TAG = "char_lcd";
static const char* EMPTY_ROW = " "; static const char* EMPTY_ROW = " ";
static const char* EMPTY_ROW3 = " ";
static char buf[65]; static char buf[65];
static void monitor_battery_task(void* _arg) {
(void) _arg;
while (true) {
vTaskDelay(pdMS_TO_TICKS(1'000));
lcd_print_header_bat();
}
}
static bool replay_handler(const char* event, char* arg) { static bool replay_handler(const char* event, char* arg) {
if (strcmp(event, "LCD_CLEAR") == 0) { if (strcmp(event, "LCD_CLEAR") == 0) {
lcd_clear(); lcd_clear();
@ -97,6 +105,8 @@ void init_lcd() {
register_replay_fn(replay_handler); register_replay_fn(replay_handler);
xTaskCreate(monitor_battery_task, "bat_monitor", 1024*2, nullptr, 0, nullptr);
ESP_LOGI(TAG, "LCD initialized!"); ESP_LOGI(TAG, "LCD initialized!");
} }
@ -108,14 +118,18 @@ void lcd_clear() {
event_occured("LCD_CLEAR", NULL); event_occured("LCD_CLEAR", NULL);
} }
} else { } else {
lcd_print(1, 2, EMPTY_ROW3); lcd_print(0, 1, EMPTY_ROW);
lcd_print(0, 2, EMPTY_ROW);
lcd_print(0, 3, EMPTY_ROW);
} }
} }
// TODO: rm
void lcd_cursor_home() { void lcd_cursor_home() {
lcd_set_cursor_pos(0, 0); lcd_set_cursor_pos(0, 0);
} }
// TODO: with print requiring you to set a pos every time, this function is not helpful
void lcd_set_cursor_pos(uint8_t col, uint8_t row) { void lcd_set_cursor_pos(uint8_t col, uint8_t row) {
lcd_set_cursor(&lcd, col, row); lcd_set_cursor(&lcd, col, row);
@ -212,7 +226,7 @@ void lcd_set_backlight(bool backlight) {
} }
} }
void lcd_create_char(uint8_t location, uint8_t* charmap) { void lcd_create_char(uint8_t location, const uint8_t charmap[]) {
lcd_create_char(&lcd, location, charmap); lcd_create_char(&lcd, location, charmap);
if (is_state_tracking()) { if (is_state_tracking()) {
@ -236,14 +250,15 @@ void lcd_print(uint8_t col, uint8_t row, const char* str) {
} }
void set_lcd_header_enabled(bool enable) { void set_lcd_header_enabled(bool enable) {
if (!header_enabled && enable) { bool old_header_enabled = header_enabled;
// enabling header
lcd_print_header();
} else if (header_enabled && !enable) {
// disabling header
lcd_print(1, 1, EMPTY_ROW);
}
header_enabled = enable; header_enabled = enable;
// update header in response to enabling/disabling the header
if (enable && !old_header_enabled) {
lcd_print_header();
} else if (!enable && old_header_enabled) {
lcd_print(0, 0, EMPTY_ROW);
}
} }
bool lcd_header_enabled() { bool lcd_header_enabled() {
@ -256,4 +271,24 @@ void lcd_print_header() {
lcd_print_header_bat(); lcd_print_header_bat();
} }
// TODO: add task to continoususly print the battery header. void lcd_do_splash() {
const uint8_t custom_char[6][8] = {
{ 0x01, 0x01, 0x02, 0x02, 0x07, 0x07, 0x0F, 0x0D },
{ 0x10, 0x10, 0x18, 0x18, 0x1C, 0x0C, 0x0E, 0x06 },
{ 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x07 },
{ 0x19, 0x1B, 0x13, 0x17, 0x07, 0x0F, 0x0F, 0x1F },
{ 0x13, 0x1B, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x1F },
{ 0x00, 0x00, 0x10, 0x10, 0x00, 0x18, 0x1C, 0x1C },
};
// TODO: make the lcd_lib somehow support the custom character 0 which would otherwise be a null terminator
lcd_create_char(1, custom_char[0]);
lcd_create_char(2, custom_char[1]);
lcd_create_char(3, custom_char[2]);
lcd_create_char(4, custom_char[3]);
lcd_create_char(5, custom_char[4]);
lcd_create_char(6, custom_char[5]);
lcd_print(6, 1, "\x01\x02Marino");
lcd_print(5, 2, "\x03\x04\x05\x06""DEV");
}

View File

@ -47,7 +47,7 @@ void lcd_set_autoscroll(bool autoscroll);
void lcd_set_backlight(bool backlight); void lcd_set_backlight(bool backlight);
// Create a custom character // Create a custom character
void lcd_create_char(uint8_t location, uint8_t charmap[]); void lcd_create_char(uint8_t location, const uint8_t charmap[]);
/// @brief Print a string to the LCD at a given pos. /// @brief Print a string to the LCD at a given pos.
/// @param col the column to print the string at. /// @param col the column to print the string at.
@ -75,4 +75,7 @@ void lcd_print_header_step();
/// @brief Prints the battery section of the LCD header. /// @brief Prints the battery section of the LCD header.
void lcd_print_header_bat(); void lcd_print_header_bat();
/// @brief Prints the splash screen for the BLK_BOX.
void lcd_do_splash();
#endif /* CHAR_LCD_H */ #endif /* CHAR_LCD_H */

View File

@ -2,7 +2,8 @@
#include <stdio.h> #include <stdio.h>
#include "char_lcd.h" #include "char_lcd.h"
static char game_state[GAME_STATE_MAX_LEN+1] = " "; // static char game_state[GAME_STATE_MAX_LEN+1] = " ";
static char game_state[GAME_STATE_MAX_LEN+1] = "MENU ";
void set_game_state(char* new_state) { void set_game_state(char* new_state) {
snprintf(game_state, sizeof(game_state), "%-5s", new_state); snprintf(game_state, sizeof(game_state), "%-5s", new_state);
@ -18,5 +19,5 @@ void reset_game_state() {
void lcd_print_header_step() { void lcd_print_header_step() {
if (!lcd_header_enabled()) return; if (!lcd_header_enabled()) return;
lcd_print(11, 1, game_state); lcd_print(10, 0, game_state);
} }

View File

@ -11,7 +11,7 @@ void set_game_state(char* new_state);
/// @brief Resets the game state to be blank. /// @brief Resets the game state to be blank.
void reset_game_state(); void reset_game_state();
/// @brief Prints the game state section of the header to the char_lcd. (row 1, columns 11-15) /// @brief Prints the game state section of the header to the char_lcd. (row 0, columns 10-14)
void lcd_print_header_step(); void lcd_print_header_step();
#endif /* GAME_INFO_H */ #endif /* GAME_INFO_H */

View File

@ -241,7 +241,7 @@ void lcd_set_backlight(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness) {
} // lcd_set_backlight() } // lcd_set_backlight()
// Custom character creation: allows us to create up to 8 custom characters in the CGRAM locations // Custom character creation: allows us to create up to 8 custom characters in the CGRAM locations
void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, uint8_t charmap[]) { void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, const uint8_t charmap[]) {
location &= 0x7; // Only 8 locations are available location &= 0x7; // Only 8 locations are available
// Set the CGRAM address // Set the CGRAM address
lcd_send(lcd, 0x40 | (location << 3), false); lcd_send(lcd, 0x40 | (location << 3), false);

View File

@ -94,7 +94,7 @@ void lcd_no_autoscroll(i2c_lcd_pcf8574_handle_t* lcd);
void lcd_set_backlight(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness); void lcd_set_backlight(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness);
// Create a custom character // Create a custom character
void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, uint8_t charmap[]); void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, const uint8_t charmap[]);
// Write a character to the LCD // Write a character to the LCD
void lcd_write(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value); void lcd_write(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value);

View File

@ -65,9 +65,9 @@ void lcd_print_header_bat() {
snprintf(buf, sizeof(buf), "%3d%%", soc); snprintf(buf, sizeof(buf), "%3d%%", soc);
} }
if (lipo.chgFlag()) { if (lipo.current() > 0) {
buf[4] = '+'; buf[3] = '+';
} }
lcd_print(17, 1, buf); lcd_print(16, 0, buf);
} }

View File

@ -14,7 +14,7 @@ void init_power_board();
/// @return battery voltage in mV. /// @return battery voltage in mV.
uint16_t get_bat_voltage(); uint16_t get_bat_voltage();
/// @brief Prints the battery section of the header to the char_lcd. (row 1, columns 17-20) /// @brief Prints the battery section of the header to the char_lcd. (row 0, columns 16-19)
void lcd_print_header_bat(); void lcd_print_header_bat();
#endif /* POWER_H */ #endif /* POWER_H */

View File

@ -20,7 +20,8 @@ static std::vector<StarCodeEntry> star_codes;
static const char EMPTY_STAR_CODE_HEADER[] = " "; static const char EMPTY_STAR_CODE_HEADER[] = " ";
esp_timer_handle_t starcode_delay_timer; esp_timer_handle_t starcode_delay_timer;
static volatile StarCodeEntry* processing_starcode = nullptr; static volatile bool processing_starcode;
static volatile StarCodeEntry* current_starcode = nullptr;
static volatile bool doing_starcode = false; static volatile bool doing_starcode = false;
static uint16_t starcode_waiting_on_release; static uint16_t starcode_waiting_on_release;
static char current[STARCODE_MAX_LEN + 1]; static char current[STARCODE_MAX_LEN + 1];
@ -29,19 +30,24 @@ static size_t current_idx;
static void starcode_trigger_cb(void* arg) { static void starcode_trigger_cb(void* arg) {
(void) arg; (void) arg;
if (processing_starcode->triggered_sem != nullptr) processing_starcode = false;
xSemaphoreGive(processing_starcode->triggered_sem);
if (processing_starcode->callback != nullptr) if (current_starcode != nullptr) {
(processing_starcode->callback)(); if (current_starcode->triggered_sem != nullptr)
xSemaphoreGive(current_starcode->triggered_sem);
if (current_starcode->callback != nullptr)
(current_starcode->callback)();
current_starcode = nullptr;
}
processing_starcode = nullptr;
// TODO: rename star code everywhere to starcode // TODO: rename star code everywhere to starcode
lcd_print_header_star_code(); lcd_print_header_star_code();
} }
void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released) { void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released) {
if ((processing_starcode == nullptr) && handling_new_starcodes && (*just_pressed | (1 << KeypadKey::star))) { if ((!processing_starcode) && handling_new_starcodes && (*just_pressed & (1 << KeypadKey::star))) {
current_idx = 0; current_idx = 0;
current[current_idx] = '\0'; current[current_idx] = '\0';
doing_starcode = true; doing_starcode = true;
@ -57,7 +63,6 @@ void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released) {
current[current_idx] = '\0'; current[current_idx] = '\0';
} else if (key == KeypadKey::pound) { } else if (key == KeypadKey::pound) {
doing_starcode = false; doing_starcode = false;
lcd_print_header_star_code();
if (current_idx != 0) { if (current_idx != 0) {
trigger_star_code(current); trigger_star_code(current);
} }
@ -72,8 +77,8 @@ void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released) {
// append the character // append the character
current[current_idx++] = char_of_keypad_key(key); current[current_idx++] = char_of_keypad_key(key);
current[current_idx] = '\0'; current[current_idx] = '\0';
lcd_print_header_star_code();
} }
lcd_print_header_star_code();
} }
} }
@ -95,7 +100,7 @@ void init_star_code_system() {
.skip_unhandled_events = false, .skip_unhandled_events = false,
}; };
esp_timer_create(&timer_args, &starcode_delay_timer); ESP_ERROR_CHECK(esp_timer_create(&timer_args, &starcode_delay_timer));
handling_new_starcodes = true; handling_new_starcodes = true;
system_initialized = true; system_initialized = true;
@ -191,24 +196,26 @@ void clear_star_codes() {
} }
bool trigger_star_code(const char* code) { bool trigger_star_code(const char* code) {
ESP_LOGI(TAG, "Star codes to checK:");
for (const auto& entry : star_codes) {
ESP_LOGI(TAG, "%s", entry.code);
}
auto it = std::find_if(star_codes.begin(), star_codes.end(), [&](const StarCodeEntry& other) { auto it = std::find_if(star_codes.begin(), star_codes.end(), [&](const StarCodeEntry& other) {
return check_code_match(code, other.code); return check_code_match(code, other.code);
}); });
if (it == star_codes.end()) { uint64_t delay_us = 2'000'000;
return false; processing_starcode = true;
if (it != star_codes.end()) {
current_starcode = &*it;
delay_us = current_starcode->delay_us;
} }
processing_starcode = &*it; ESP_ERROR_CHECK(esp_timer_start_once(starcode_delay_timer, delay_us));
// print display text return current_starcode != nullptr;
if (processing_starcode->display_text != nullptr) {
lcd_print_header_star_code();
}
esp_timer_start_once(starcode_delay_timer, processing_starcode->delay_us);
return true;
} }
void pause_star_code_system() { void pause_star_code_system() {
@ -223,19 +230,23 @@ void resume_star_code_system() {
void lcd_print_header_star_code() { void lcd_print_header_star_code() {
if (!lcd_header_enabled()) return; if (!lcd_header_enabled()) return;
if (processing_starcode != nullptr) { // TODO: consider upping the display text size to be able to overwrite the game_state area.
if (processing_starcode->display_text != nullptr) { if (processing_starcode) {
if (current_starcode == nullptr) {
lcd_print(0, 0, "Invalid ");
} else if (current_starcode->display_text != nullptr) {
char buf[STARCODE_MAX_LEN + 2]; char buf[STARCODE_MAX_LEN + 2];
snprintf(buf, sizeof(buf), "%s", processing_starcode->display_text); snprintf(buf, sizeof(buf), "%s", current_starcode->display_text);
lcd_print(1, 1, processing_starcode->display_text); lcd_print(0, 0, buf);
} else { } else {
lcd_print(1, 1, EMPTY_STAR_CODE_HEADER); lcd_print(0, 0, EMPTY_STAR_CODE_HEADER);
} }
} else if (doing_starcode) { } else if (doing_starcode) {
char buf[STARCODE_MAX_LEN + 2]; char buf[STARCODE_MAX_LEN + 2];
snprintf(buf, sizeof(buf), "*%-8s", current); snprintf(buf, sizeof(buf), "*%-8s", current);
lcd_print(1, 1, buf); lcd_print(0, 0, buf);
} else { } else {
lcd_print(1, 1, EMPTY_STAR_CODE_HEADER); lcd_print(0, 0, EMPTY_STAR_CODE_HEADER);
} }
} }

View File

@ -81,7 +81,7 @@ void set_star_code_sys_enabled(bool enable);
/// @return `true` if the star code system is handling star codes. /// @return `true` if the star code system is handling star codes.
bool star_code_sys_enabled(); bool star_code_sys_enabled();
/// @brief Prints the star code section of the header to the char_lcd. (row 1, columns 1-10) /// @brief Prints the star code section of the header to the char_lcd. (row 0, columns 0-9)
void lcd_print_header_star_code(); void lcd_print_header_star_code();
#endif /* STAR_CODE_H */ #endif /* STAR_CODE_H */

View File

@ -17,9 +17,8 @@ void clean_bomb(void) {
set_module_sseg_raw(clear); set_module_sseg_raw(clear);
// clear char lcd // clear char lcd
lcd_clear();
lcd_set_cursor_vis(false); lcd_set_cursor_vis(false);
lcd_cursor_home(); lcd_clear();
// TODO: add stuff for starcode system // TODO: add stuff for starcode system
} }

View File

@ -37,6 +37,7 @@ extern "C" void app_main(void) {
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
clean_bomb(); clean_bomb();
lcd_do_splash();
step0(); step0();
// set_recording_source(stdout, false); // set_recording_source(stdout, false);

View File

@ -47,18 +47,27 @@ static void replay_last() {
void step0() { void step0() {
led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE);
leds_flush();
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
if (continue_sem == nullptr) {
ESP_LOGE(TAG, "could not create semaphore");
return;
}
StarCodeEntry star_codes[] = { StarCodeEntry star_codes[] = {
{ {
.code = "9819", .code = "9819",
.display_text = "Defusal Initiated", .display_text = "Defusal Initiated",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = nullptr, .callback = nullptr,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
{ {
.code = "59862", .code = "59862",
.display_text = "Set Up Wires", .display_text = "Set Up Wires",
.delay_us = 2'000'000, .delay_us = 10'000'000,
.callback = setup_wires, .callback = setup_wires,
.triggered_sem = nullptr, .triggered_sem = nullptr,
}, },
@ -130,42 +139,42 @@ void step0() {
.display_text = "Skip To Step 1", .display_text = "Skip To Step 1",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = skip_to_step1, .callback = skip_to_step1,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
{ {
.code = "59882", .code = "59882",
.display_text = "Skip To Step 2", .display_text = "Skip To Step 2",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = skip_to_step2, .callback = skip_to_step2,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
{ {
.code = "59883", .code = "59883",
.display_text = "Skip To Step 3", .display_text = "Skip To Step 3",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = skip_to_step3, .callback = skip_to_step3,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
{ {
.code = "59884", .code = "59884",
.display_text = "Skip To Step 4", .display_text = "Skip To Step 4",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = skip_to_step4, .callback = skip_to_step4,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
{ {
.code = "59885", .code = "59885",
.display_text = "Skip To Step 5", .display_text = "Skip To Step 5",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = skip_to_step5, .callback = skip_to_step5,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
{ {
.code = "59886", .code = "59886",
.display_text = "Skip To Step 6", .display_text = "Skip To Step 6",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = skip_to_step6, .callback = skip_to_step6,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
{ {
.code = "1111", .code = "1111",
@ -186,21 +195,11 @@ void step0() {
.display_text = "replay_last", .display_text = "replay_last",
.delay_us = 2'000'000, .delay_us = 2'000'000,
.callback = replay_last, .callback = replay_last,
.triggered_sem = nullptr, .triggered_sem = continue_sem,
}, },
}; };
size_t len = sizeof(star_codes)/sizeof(star_codes[0]); size_t len = sizeof(star_codes)/sizeof(star_codes[0]);
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
if (continue_sem == nullptr) {
ESP_LOGE(TAG, "could not create semaphore");
return;
}
led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE);
leds_flush();
add_star_codes(star_codes, len); add_star_codes(star_codes, len);
xSemaphoreTake(continue_sem, portMAX_DELAY); xSemaphoreTake(continue_sem, portMAX_DELAY);
rm_star_codes(star_codes, len); rm_star_codes(star_codes, len);