diff --git a/blk_box.cpp b/blk_box.cpp index d0ac5ba..c7564e2 100644 --- a/blk_box.cpp +++ b/blk_box.cpp @@ -16,7 +16,7 @@ void init_blk_box(BlkBoxInitConfig cfg) { init_main_i2c(); init_expander(); init_leds(); - init_lcd(); + init_lcd(cfg.lcd_backlight); init_ssegs(); init_tft(); diff --git a/drivers/char_lcd.cpp b/drivers/char_lcd.cpp index 2115f25..7446fa3 100644 --- a/drivers/char_lcd.cpp +++ b/drivers/char_lcd.cpp @@ -5,6 +5,7 @@ #include #include #include +#include // mutex is for all these vars SemaphoreHandle_t lcd_mutex; @@ -21,6 +22,12 @@ static bool is_header_enabled = false; static const char *TAG = "char_lcd"; static const char* EMPTY_ROW = " "; +static void log_lcd_error(const char* operation, esp_err_t err) { + if (err != ESP_OK) { + ESP_LOGE(TAG, "LCD %s failed: %s", operation, esp_err_to_name(err)); + } +} + // TODO: move this to power.cpp // static void monitor_battery_task(void* _arg) { // (void) _arg; @@ -91,14 +98,17 @@ static const char* EMPTY_ROW = " "; // return true; // } -void init_lcd() { +void init_lcd(bool initial_backlight) { ESP_LOGI(TAG, "Initializing LCD..."); lcd_mutex = xSemaphoreCreateRecursiveMutex(); assert(lcd_mutex != NULL); lcd = LCD2004I2C(); - lcd.init(LCD_ADDR); + esp_err_t err = lcd.init(LCD_ADDR, initial_backlight); + if (err != ESP_OK) { + ESP_LOGE(TAG, "LCD initialization failed: %s", esp_err_to_name(err)); + } // register_replay_fn(replay_handler); @@ -111,7 +121,7 @@ void LCDController::clear() { xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY); if (!is_header_enabled) { - lcd.clear(); + log_lcd_error("clear", lcd.clear()); // if (is_state_tracking()) { // event_occured("LCD_CLEAR", NULL); @@ -134,7 +144,7 @@ bool LCDController::get_backlight() { void LCDController::set_backlight(bool backlight) { xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY); - lcd.set_backlight(backlight); + log_lcd_error("set backlight", lcd.set_backlight(backlight)); xSemaphoreGiveRecursive(lcd_mutex); // if (is_state_tracking()) { @@ -145,7 +155,7 @@ void LCDController::set_backlight(bool backlight) { void LCDController::set_display_show(bool show_display) { xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY); - lcd.show_hide(show_display); + log_lcd_error("set display", lcd.show_hide(show_display)); xSemaphoreGiveRecursive(lcd_mutex); } @@ -159,10 +169,10 @@ bool LCDController::get_display_show() { /// 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( + log_lcd_error("set cursor display", lcd.show_blink_cursor( cursor_display_mode != CursorMode::Hide, cursor_display_mode == CursorMode::Blink - ); + )); xSemaphoreGiveRecursive(lcd_mutex); } @@ -178,7 +188,7 @@ void LCDController::set_resting_cursor_pos(uint8_t row, uint8_t col) { (resting_cursor_row != row || resting_cursor_col != col) && cursor_resting_mode != CursorMode::Hide ) { - lcd.move_cursor(row, col); + log_lcd_error("move cursor", lcd.move_cursor(row, col)); } resting_cursor_row = row; @@ -243,7 +253,7 @@ 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); + log_lcd_error("create custom character", lcd.create_custom_char(location, charmap)); xSemaphoreGiveRecursive(lcd_mutex); // if (is_state_tracking()) { @@ -260,12 +270,12 @@ void LCDController::create_custom_char(uint8_t location, const uint8_t charmap[] /// 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); + log_lcd_error("move cursor", lcd.move_cursor(row, col)); change_cursor_display(cursor_print_mode); - lcd.write_str(str); + log_lcd_error("write string", lcd.write_str(str)); if (cursor_resting_mode != CursorMode::Hide) { - lcd.move_cursor(resting_cursor_row, resting_cursor_col); + log_lcd_error("restore cursor", lcd.move_cursor(resting_cursor_row, resting_cursor_col)); } if (cursor_resting_mode != cursor_print_mode) { change_cursor_display(cursor_resting_mode); diff --git a/drivers/lcd2004.cpp b/drivers/lcd2004.cpp index a2d4a57..00459cd 100644 --- a/drivers/lcd2004.cpp +++ b/drivers/lcd2004.cpp @@ -49,7 +49,8 @@ LCD2004I2C::LCD2004I2C() { entry_mode = 0; } -void LCD2004I2C::init(uint8_t addr) { +esp_err_t LCD2004I2C::init(uint8_t addr, bool initial_backlight) { + if (initialized) return ESP_ERR_INVALID_STATE; i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, @@ -60,42 +61,52 @@ void LCD2004I2C::init(uint8_t addr) { } }; - // TODO: replace all these ESP_ERROR_CHECK with proper error handling that doesn't just crash the program - ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_main_bus_handle, &dev_config, &lcd_device_handle)); + esp_err_t err = i2c_master_bus_add_device(i2c_main_bus_handle, &dev_config, &lcd_device_handle); + if (err != ESP_OK) return err; ESP_LOGD(TAG, "LCD2004 device added to bus"); + backlight = initial_backlight ? BACKLIGHT_MASK : 0; // Initialize the LCD // set to 8-bit mode to bring it to a known state for (int i = 0; i < 3; i++) { - send4_cmd(CMD_FUNCTION_SET | FUNCTION_SET_DL); + err = send4_cmd(CMD_FUNCTION_SET | FUNCTION_SET_DL); + if (err != ESP_OK) return err; vTaskDelay(pdMS_TO_TICKS(5)); } // set to 4-bit mode, while in 8 bit mode - send4_cmd(CMD_FUNCTION_SET); + err = send4_cmd(CMD_FUNCTION_SET); + if (err != ESP_OK) return err; // use all 4 rows - send_cmd(CMD_FUNCTION_SET | FUNCTION_SET_N); + err = send_cmd(CMD_FUNCTION_SET | FUNCTION_SET_N); + if (err != ESP_OK) return err; // display, set cursor visible - send_cmd(CMD_DISPLAY_CONTROL | DISPLAY_CONTROL_SHOW); + err = send_cmd(CMD_DISPLAY_CONTROL | DISPLAY_CONTROL_SHOW); + if (err != ESP_OK) return err; // clear display - send_cmd(CMD_CLEAR_DISPLAY); + err = send_cmd(CMD_CLEAR_DISPLAY); + if (err != ESP_OK) return err; + esp_rom_delay_us(1600); // set cursor to increment to the right - send_cmd(CMD_ENTRY_MODE_SET | ENTRY_MODE_INC_DEC); + err = send_cmd(CMD_ENTRY_MODE_SET | ENTRY_MODE_INC_DEC); + if (err != ESP_OK) return err; display_control = DISPLAY_CONTROL_SHOW; entry_mode = ENTRY_MODE_INC_DEC; + initialized = true; ESP_LOGI(TAG, "LCD2004 initialized!"); + return ESP_OK; } // i2c wrappers /// Sends `cmd` to the device as a command. -void LCD2004I2C::send_cmd(uint8_t cmd) { +esp_err_t LCD2004I2C::send_cmd(uint8_t cmd) { uint8_t control_bits = backlight; uint8_t high = control_bits | (cmd & 0xF0); @@ -110,10 +121,10 @@ void LCD2004I2C::send_cmd(uint8_t cmd) { low }; - ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS)); + return i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS); } -void LCD2004I2C::send_data(uint8_t data) { +esp_err_t LCD2004I2C::send_data(uint8_t data) { uint8_t control_bits = DATA_MASK | backlight; uint8_t high = control_bits | (data & 0xF0); @@ -129,11 +140,11 @@ void LCD2004I2C::send_data(uint8_t data) { low }; - ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS)); + return i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS); } /// Sends the ***HIGH*** 4 bits of `cmd` as a command. -void LCD2004I2C::send4_cmd(uint8_t cmd) { +esp_err_t LCD2004I2C::send4_cmd(uint8_t cmd) { uint8_t control_bits = backlight; uint8_t high = control_bits | (cmd & 0xF0); @@ -145,7 +156,7 @@ void LCD2004I2C::send4_cmd(uint8_t cmd) { high }; - ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS)); + return i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS); } // LCD2004 class implementations @@ -154,35 +165,41 @@ bool LCD2004I2C::get_backlight() { return backlight != 0; } -void LCD2004I2C::set_backlight(bool backlight) { +esp_err_t LCD2004I2C::set_backlight(bool backlight) { if (get_backlight() == backlight) { - return; + return ESP_OK; } - this->backlight = backlight ? BACKLIGHT_MASK : 0; - uint8_t data = this->backlight; - ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, &data, 1, I2C_MASTER_TIMEOUT_MS)); + uint8_t data = backlight ? BACKLIGHT_MASK : 0; + esp_err_t err = i2c_master_transmit(lcd_device_handle, &data, 1, I2C_MASTER_TIMEOUT_MS); + if (err == ESP_OK) { + this->backlight = data; + } + return err; } -void LCD2004I2C::clear() { - send_cmd(CMD_CLEAR_DISPLAY); +esp_err_t LCD2004I2C::clear() { + esp_err_t err = send_cmd(CMD_CLEAR_DISPLAY); + if (err != ESP_OK) return err; esp_rom_delay_us(1600); entry_mode |= ENTRY_MODE_INC_DEC; + return ESP_OK; } -void LCD2004I2C::move_cursor(uint8_t row, uint8_t col) { +esp_err_t LCD2004I2C::move_cursor(uint8_t row, uint8_t col) { const uint8_t ROW_OFFSETS[4] = {0x00, 0x40, 0x14, 0x54}; + if (row >= LCD2004_ROWS || col >= LCD2004_COLS) return ESP_ERR_INVALID_ARG; uint8_t addr = ROW_OFFSETS[row] + col; - send_cmd(CMD_SET_DDRAM_ADDRESS | addr); + return send_cmd(CMD_SET_DDRAM_ADDRESS | addr); } bool LCD2004I2C::get_show_hide() { return (display_control & DISPLAY_CONTROL_SHOW) != 0; } -void LCD2004I2C::show_hide(bool show) { +esp_err_t LCD2004I2C::show_hide(bool show) { if (get_show_hide() == show) { - return; + return ESP_OK; } if (show) { @@ -190,16 +207,16 @@ void LCD2004I2C::show_hide(bool show) { } else { display_control &= ~DISPLAY_CONTROL_SHOW; } - send_cmd(CMD_DISPLAY_CONTROL | display_control); + return send_cmd(CMD_DISPLAY_CONTROL | display_control); } bool LCD2004I2C::get_show_cursor() { return (display_control & DISPLAY_CONTROL_CURSOR) != 0; } -void LCD2004I2C::show_cursor(bool show_cursor) { +esp_err_t LCD2004I2C::show_cursor(bool show_cursor) { if (get_show_cursor() == show_cursor) { - return; + return ESP_OK; } if (show_cursor) { @@ -207,16 +224,16 @@ void LCD2004I2C::show_cursor(bool show_cursor) { } else { display_control &= ~DISPLAY_CONTROL_CURSOR; } - send_cmd(CMD_DISPLAY_CONTROL | display_control); + return send_cmd(CMD_DISPLAY_CONTROL | display_control); } bool LCD2004I2C::get_blink_cursor() { return (display_control & DISPLAY_CONTROL_BLINK) != 0; } -void LCD2004I2C::blink_cursor(bool blink_cursor) { +esp_err_t LCD2004I2C::blink_cursor(bool blink_cursor) { if (get_blink_cursor() == blink_cursor) { - return; + return ESP_OK; } if (blink_cursor) { @@ -224,12 +241,12 @@ void LCD2004I2C::blink_cursor(bool blink_cursor) { } else { display_control &= ~DISPLAY_CONTROL_BLINK; } - send_cmd(CMD_DISPLAY_CONTROL | display_control); + return send_cmd(CMD_DISPLAY_CONTROL | display_control); } -void LCD2004I2C::show_blink_cursor(bool show_cursor, bool blink_cursor) { +esp_err_t LCD2004I2C::show_blink_cursor(bool show_cursor, bool blink_cursor) { if (get_show_cursor() == show_cursor && get_blink_cursor() == blink_cursor) { - return; + return ESP_OK; } if (blink_cursor) { @@ -242,16 +259,16 @@ void LCD2004I2C::show_blink_cursor(bool show_cursor, bool blink_cursor) { } else { display_control &= ~DISPLAY_CONTROL_CURSOR; } - send_cmd(CMD_DISPLAY_CONTROL | display_control); + return send_cmd(CMD_DISPLAY_CONTROL | display_control); } bool LCD2004I2C::get_scroll_direction() { return (entry_mode & ENTRY_MODE_INC_DEC) != 0; } -void LCD2004I2C::scroll_direction(bool left_to_right) { +esp_err_t LCD2004I2C::scroll_direction(bool left_to_right) { if (get_scroll_direction() == left_to_right) { - return; + return ESP_OK; } if (left_to_right) { @@ -259,16 +276,16 @@ void LCD2004I2C::scroll_direction(bool left_to_right) { } else { entry_mode &= ~ENTRY_MODE_INC_DEC; } - send_cmd(CMD_ENTRY_MODE_SET | entry_mode); + return send_cmd(CMD_ENTRY_MODE_SET | entry_mode); } bool LCD2004I2C::get_display_shift() { return (entry_mode & ENTRY_MODE_SHIFT) != 0; } -void LCD2004I2C::shift_display(bool shift_display) { +esp_err_t LCD2004I2C::shift_display(bool shift_display) { if (get_display_shift() == shift_display) { - return; + return ESP_OK; } if (shift_display) { @@ -276,53 +293,60 @@ void LCD2004I2C::shift_display(bool shift_display) { } else { entry_mode &= ~ENTRY_MODE_SHIFT; } - send_cmd(CMD_ENTRY_MODE_SET | entry_mode); + return send_cmd(CMD_ENTRY_MODE_SET | entry_mode); } -void LCD2004I2C::shift_cursor_left() { - send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT); +esp_err_t LCD2004I2C::shift_cursor_left() { + return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT); } -void LCD2004I2C::shift_cursor_right() { - send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_RL); +esp_err_t LCD2004I2C::shift_cursor_right() { + return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_RL); } -void LCD2004I2C::shift_display_left() { - send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC); +esp_err_t LCD2004I2C::shift_display_left() { + return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC); } -void LCD2004I2C::shift_display_right() { - send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC | CURSOR_OR_DISPLAY_SHIFT_RL); +esp_err_t LCD2004I2C::shift_display_right() { + return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC | CURSOR_OR_DISPLAY_SHIFT_RL); } -void LCD2004I2C::create_custom_char(uint8_t location, const uint8_t char_map[8]) { +esp_err_t LCD2004I2C::create_custom_char(uint8_t location, const uint8_t char_map[8]) { + if (char_map == nullptr) return ESP_ERR_INVALID_ARG; uint8_t loc = location % 8; - send_cmd(CMD_SET_CGRAM_ADDRESS | (loc << 3)); + esp_err_t err = send_cmd(CMD_SET_CGRAM_ADDRESS | (loc << 3)); + if (err != ESP_OK) return err; for (int i = 0; i < 8; i++) { - send_data(char_map[i]); + err = send_data(char_map[i]); + if (err != ESP_OK) return err; } + return ESP_OK; } -void LCD2004I2C::print_char(char c) { +esp_err_t LCD2004I2C::print_char(char c) { if (c == '\x08') { // custom char 0 is represented by \x08 since \x00 is a null terminator c = 0; } - send_data(static_cast(c)); + return send_data(static_cast(c)); } -void LCD2004I2C::print_u8(uint8_t b) { +esp_err_t LCD2004I2C::print_u8(uint8_t b) { if (b == 0x08) { // custom char 0 is represented by \x08 since \x00 is a null terminator b = 0; } - send_data(b); + return send_data(b); } -void LCD2004I2C::write_str(const char* s) { +esp_err_t LCD2004I2C::write_str(const char* s) { // TODO: replace this with a single I2C transmission. + if (s == nullptr) return ESP_ERR_INVALID_ARG; while (*s) { - print_char(*s); + esp_err_t err = print_char(*s); + if (err != ESP_OK) return err; s++; } + return ESP_OK; } diff --git a/drivers/lcd2004.hpp b/drivers/lcd2004.hpp index 6e06a4c..f1b9814 100644 --- a/drivers/lcd2004.hpp +++ b/drivers/lcd2004.hpp @@ -2,15 +2,19 @@ #define LCD2004_HPP #include "driver/i2c_master.h" +#include "esp_err.h" #include #define LCD2004_CLK_SPEED_HZ 100000 #define I2C_MASTER_TIMEOUT_MS 1000 +#define LCD2004_ROWS 4 +#define LCD2004_COLS 20 class LCD2004I2C { private: - i2c_master_dev_handle_t lcd_device_handle; + i2c_master_dev_handle_t lcd_device_handle = nullptr; + bool initialized = false; /// Weather or not the backlight is on. /// `BACKLIGHT_MASK` if backlight is on, /// `0` if backlight is off. @@ -19,9 +23,9 @@ private: uint8_t display_control; uint8_t entry_mode; - void send_cmd(uint8_t cmd); - void send_data(uint8_t data); - void send4_cmd(uint8_t cmd); + esp_err_t send_cmd(uint8_t cmd); + esp_err_t send_data(uint8_t data); + esp_err_t send4_cmd(uint8_t cmd); public: /// Constructs a new LCD2004 device. @@ -30,31 +34,31 @@ public: /// Initializes the LCD2004 device. Must be called before any other methods. /// /// The address is usually `0x27`. - void init(uint8_t addr); + esp_err_t init(uint8_t addr, bool initial_backlight); /// Gets the state of the backlight of the module. bool get_backlight(); /// Sets the backlight of the module to be on or off. - void set_backlight(bool backlight); + esp_err_t set_backlight(bool backlight); /// Clears the display, and resets the cursor to - void clear(); + esp_err_t clear(); /// Moves the cursor to `row` and `col`. - void move_cursor(uint8_t row, uint8_t col); + esp_err_t move_cursor(uint8_t row, uint8_t col); /// Returns `true` iff the display is showing. bool get_show_hide(); /// Shows or hides the display. - void show_hide(bool show); + esp_err_t show_hide(bool show); /// Returns `true` iff the cursor display is showing. bool get_show_cursor(); /// Shows or hides the cursor. - void show_cursor(bool show_cursor); + esp_err_t show_cursor(bool show_cursor); /// Returns `true` iff the cursor is blinking. /// @@ -62,16 +66,16 @@ public: bool get_blink_cursor(); /// Blinks or stops blinking the cursor. - void blink_cursor(bool blink_cursor); + esp_err_t blink_cursor(bool blink_cursor); /// Sets the cursor show state and blink state. - void show_blink_cursor(bool show_cursor, bool blink_cursor); + esp_err_t show_blink_cursor(bool show_cursor, bool blink_cursor); /// Returns `true` iff the display scrolls left to right. bool get_scroll_direction(); /// Sets the scroll direction for the display. - void scroll_direction(bool left_to_right); + esp_err_t scroll_direction(bool left_to_right); /// Returns `true` iff the display is shifting with the cursor. bool get_display_shift(); @@ -79,34 +83,34 @@ public: /// Sets the display to shift with the cursor. /// /// This is like autoscrolling. - void shift_display(bool shift_display); + esp_err_t shift_display(bool shift_display); /// Shifts the cursor left 1 - void shift_cursor_left(); + esp_err_t shift_cursor_left(); /// Shifts the cursor right 1 - void shift_cursor_right(); + esp_err_t shift_cursor_right(); /// Shifts the display left 1 - void shift_display_left(); + esp_err_t shift_display_left(); /// Shifts the display right 1 - void shift_display_right(); + esp_err_t shift_display_right(); /// Sets the character map for a custom character. /// /// There are 8 locations (0..=7), for 8 characters. /// You can print these characters by sending '\x00' - '\x07'. - void create_custom_char(uint8_t location, const uint8_t char_map[8]); + esp_err_t create_custom_char(uint8_t location, const uint8_t char_map[8]); /// Prints a single character to the display. - void print_char(char c); + esp_err_t print_char(char c); /// Prints a single byte to the display. - void print_u8(uint8_t b); + esp_err_t print_u8(uint8_t b); /// Writes a string to the display. - void write_str(const char* s); + esp_err_t write_str(const char* s); }; #endif // LCD2004_HPP \ No newline at end of file diff --git a/include/blk_box.h b/include/blk_box.h index 9fa7ce7..f41f427 100644 --- a/include/blk_box.h +++ b/include/blk_box.h @@ -2,11 +2,10 @@ #define BLK_BOX_H struct BlkBoxInitConfig { - // nothing for now... + /// Initial character LCD backlight state. + bool lcd_backlight = true; }; - - void init_blk_box(BlkBoxInitConfig cfg); #endif // BLK_BOX_H diff --git a/include/blk_box_drivers/char_lcd.hpp b/include/blk_box_drivers/char_lcd.hpp index f9cebae..ed97cc0 100644 --- a/include/blk_box_drivers/char_lcd.hpp +++ b/include/blk_box_drivers/char_lcd.hpp @@ -18,7 +18,7 @@ enum class CursorMode: uint8_t { }; /// Initializes the 2004 Character LCD -void init_lcd(); +void init_lcd(bool initial_backlight); class LCDController { public: