blk_box_lib/drivers/char_lcd.cpp
2026-04-04 11:38:16 -05:00

318 lines
9.7 KiB
C++

#include "blk_box_drivers/char_lcd.hpp"
#include "lcd2004.hpp"
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <esp_log.h>
// mutex is for all these vars
SemaphoreHandle_t lcd_mutex;
LCD2004I2C lcd;
static CursorMode cursor_resting_mode = CursorMode::Hide;
static CursorMode cursor_print_mode = CursorMode::Hide;
static uint8_t resting_cursor_row = 0;
static uint8_t resting_cursor_col = 0;
static bool is_header_enabled = false;
static const char *TAG = "char_lcd";
static const char* EMPTY_ROW = " ";
// TODO: move this to power.cpp
// 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) {
// if (strcmp(event, "LCD_CLEAR") == 0) {
// lcd_clear();
// }
// else if (strcmp(event, "LCD_SET_DISPLAY") == 0) {
// lcd_set_display(strcmp(arg, "true") == 0);
// }
// else if (strcmp(event, "LCD_CURSOR_VIS") == 0) {
// lcd_set_cursor_vis(strcmp(arg, "true") == 0);
// }
// else if (strcmp(event, "LCD_CURSOR_BLINK") == 0) {
// lcd_set_cursor_blink(strcmp(arg, "true") == 0);
// }
// else if (strcmp(event, "LCD_SCROLL_DISPLAY_LEFT") == 0) {
// lcd_scroll_display_left();
// }
// else if (strcmp(event, "LCD_SCROLL_DISPLAY_RIGHT") == 0) {
// lcd_scroll_display_right();
// }
// else if (strcmp(event, "LCD_LEFT_TO_RIGHT") == 0) {
// lcd_left_to_right();
// }
// else if (strcmp(event, "LCD_RIGHT_TO_LEFT") == 0) {
// lcd_right_to_left();
// }
// else if (strcmp(event, "LCD_AUTOSCROLL") == 0) {
// lcd_set_autoscroll(strcmp(arg, "true") == 0);
// }
// else if (strcmp(event, "LCD_BACKLIGHT") == 0) {
// lcd_set_backlight(strcmp(arg, "true") == 0);
// }
// else 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);
// }
// else if (strcmp(event, "LCD_PRINT") == 0) {
// char* str = strtok(arg, ",");
// uint8_t row = atoi(str);
// str = strtok(NULL, ",");
// uint8_t col = atoi(str);
// // get remaining part of string.
// str = strtok(NULL, "");
// // TODO: handle \r and \n
// lcd_print(row, col, str);
// } else {
// return false;
// }
// return true;
// }
void init_lcd() {
ESP_LOGI(TAG, "Initializing LCD...");
lcd_mutex = xSemaphoreCreateRecursiveMutex();
assert(lcd_mutex != NULL);
lcd = LCD2004I2C();
lcd.init(LCD_ADDR);
// register_replay_fn(replay_handler);
// xTaskCreate(monitor_battery_task, "bat_monitor", 1024*2, nullptr, 0, nullptr);
ESP_LOGI(TAG, "LCD initialized!");
}
void LCDController::clear() {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
if (!is_header_enabled) {
lcd.clear();
// if (is_state_tracking()) {
// event_occured("LCD_CLEAR", NULL);
// }
} else {
print(1, 0, EMPTY_ROW);
print(2, 0, EMPTY_ROW);
print(3, 0, EMPTY_ROW);
}
xSemaphoreGiveRecursive(lcd_mutex);
}
bool LCDController::get_backlight() {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
bool backlight = lcd.get_backlight();
xSemaphoreGiveRecursive(lcd_mutex);
return backlight;
}
void LCDController::set_backlight(bool backlight) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
lcd.set_backlight(backlight);
xSemaphoreGiveRecursive(lcd_mutex);
// if (is_state_tracking()) {
// sprintf(buf, "%d", backlight);
// event_occured("LCD_BACKLIGHT", backlight ? "true" : "false");
// }
}
void LCDController::set_display_show(bool show_display) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
lcd.show_hide(show_display);
xSemaphoreGiveRecursive(lcd_mutex);
}
bool LCDController::get_display_show() {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
bool show_display = lcd.get_show_hide();
xSemaphoreGiveRecursive(lcd_mutex);
return show_display;
}
/// Changes the cursor display mode to the given mode.
static void change_cursor_display(CursorMode cursor_display_mode) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
lcd.show_blink_cursor(
cursor_display_mode != CursorMode::Hide,
cursor_display_mode == CursorMode::Blink
);
xSemaphoreGiveRecursive(lcd_mutex);
}
/// Moves the position of the resting cursor.
///
/// If the resting cursor mode is `Hide`, then this position has no effect on the display, but is still stored
/// for when it is put into resting cursor mode.
void LCDController::set_resting_cursor_pos(uint8_t row, uint8_t col) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
// update pos if we're not hiding the resting cursor
if (
(resting_cursor_row != row || resting_cursor_col != col) &&
cursor_resting_mode != CursorMode::Hide
) {
lcd.move_cursor(row, col);
}
resting_cursor_row = row;
resting_cursor_col = col;
xSemaphoreGiveRecursive(lcd_mutex);
}
/// Gets the position of the resting cursor.
///
/// This will return the value of the resting cursor position even if the resting cursor mode is `Hide`
/// even though the values are not meaningful during that time.
void LCDController::get_cursor_resting_position(uint8_t* row, uint8_t* col) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
if (row) *row = resting_cursor_row;
if (col) *col = resting_cursor_col;
xSemaphoreGiveRecursive(lcd_mutex);
}
/// This puts the display in and out of resting cursor mode.
///
/// If the resting mode is not `Hide`, then the cursor will be displayed in the resting position when not printing.
///
/// The cursor mode will change to the "cursor print mode"
/// during prints, then return to it's resting location and
/// switch back to the "cursor resting mode".
void LCDController::set_resting_cursor_mode(CursorMode new_mode) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
if (cursor_resting_mode != new_mode) {
cursor_resting_mode = new_mode;
change_cursor_display(cursor_resting_mode);
if (cursor_resting_mode != CursorMode::Hide) {
lcd.move_cursor(resting_cursor_row, resting_cursor_col);
}
}
xSemaphoreGiveRecursive(lcd_mutex);
}
/// Gets the display mode of the cursor when it is resting.
CursorMode LCDController::get_resting_cursor_mode() {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
CursorMode mode = cursor_resting_mode;
xSemaphoreGiveRecursive(lcd_mutex);
return mode;
}
/// Sets the display mode of the cursor during printing.
void LCDController::set_cursor_print_mode(CursorMode new_mode) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
cursor_print_mode = new_mode;
xSemaphoreGiveRecursive(lcd_mutex);
}
/// Gets the display mode of the cursor during printing.
CursorMode LCDController::get_cursor_print_mode() {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
CursorMode mode = cursor_print_mode;
xSemaphoreGiveRecursive(lcd_mutex);
return mode;
}
void LCDController::create_custom_char(uint8_t location, const uint8_t charmap[]) {
if (location == 8) location = 0;
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
lcd.create_custom_char(location, charmap);
xSemaphoreGiveRecursive(lcd_mutex);
// if (is_state_tracking()) {
// snprintf(buf, sizeof(buf),
// "%d,%d,%d,%d,%d,%d,%d,%d,%d", location,
// charmap[0], charmap[1], charmap[2], charmap[3], charmap[4], charmap[5], charmap[6], charmap[7]
// );
// event_occured("LCD_CREATE_CHAR", buf);
// }
}
/// Prints a string to the given row and column.
///
/// Do not print across lines, as that leads to goofy behavior.
void LCDController::print(uint8_t row, uint8_t col, const char* str) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
lcd.move_cursor(row, col);
change_cursor_display(cursor_print_mode);
lcd.write_str(str);
if (cursor_resting_mode != CursorMode::Hide) {
lcd.move_cursor(resting_cursor_row, resting_cursor_col);
}
if (cursor_resting_mode != cursor_print_mode) {
change_cursor_display(cursor_resting_mode);
}
xSemaphoreGiveRecursive(lcd_mutex);
// if (is_state_tracking()) {
// // TODO: handle \r and \n and others
// snprintf(buf, sizeof(buf), "%d,%d,%s", row, col, str);
// event_occured("LCD_PRINT", buf);
// }
}
void LCDController::set_lcd_header_enabled(bool enable) {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
bool old_header_enabled = is_header_enabled;
is_header_enabled = enable;
// print the header in response to enabling/disabling it
if (enable && !old_header_enabled) {
print_header();
} else if (!enable && old_header_enabled) {
print(0, 0, EMPTY_ROW);
}
xSemaphoreGiveRecursive(lcd_mutex);
}
bool LCDController::header_enabled() {
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
bool enabled = is_header_enabled;
xSemaphoreGiveRecursive(lcd_mutex);
return enabled;
}
bool LCDController::lock(uint32_t ticks_to_wait) {
return xSemaphoreTakeRecursive(lcd_mutex, ticks_to_wait);
}
void LCDController::unlock() {
xSemaphoreGiveRecursive(lcd_mutex);
}
void LCDController::print_header() {
// TODO:
// lcd_print_header_star_code();
// lcd_print_header_step();
// lcd_print_header_bat();
}