From b6a14c6b14928fbbbb7c54147a864b525d6fd78a Mon Sep 17 00:00:00 2001 From: Hector van der Aa Date: Mon, 23 Mar 2026 16:51:01 +0100 Subject: [PATCH] Added render loop function and print overloads with cleared flag to avoid blocking --- src/modules/lcd/lcd.h | 215 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 205 insertions(+), 10 deletions(-) diff --git a/src/modules/lcd/lcd.h b/src/modules/lcd/lcd.h index baf3e82..30a4fd5 100644 --- a/src/modules/lcd/lcd.h +++ b/src/modules/lcd/lcd.h @@ -1,26 +1,100 @@ #pragma once -#define MOD "modules/lcd/lcd.h" #include "flags.h" #include "modules/logger/system_logger.h" +#include "modules/gps/gps.h" #include +#define MOD "modules/lcd/lcd.h" + +namespace screen +{ + +typedef enum lcd_screen { + blank, + gps_debug, +}; +} // namespace screen + class lcd { private: LiquidCrystal_I2C *_display; + bool _dispaly_cleared; system_logger *_logger = nullptr; - + screen::lcd_screen _screen; + unsigned long _last_render; + unsigned long _frame_duration; + gps *_gps; + void clear(); + void print(const String& msg); + void print(char); + void print(const char []); + void print(double val, int digits); + void print(unsigned long val, int base); + void print(long val, int base); + void print(unsigned int val, int base); + void print(int val, int base); public: lcd(); lcd(system_logger *logger); ~lcd(); int init(); + int set_gps(gps *gps); int print_message(String message); + int switch_screen(screen::lcd_screen new_screen); + int render_task(); }; -lcd::lcd() { _display = new LiquidCrystal_I2C(0x27, 20, 4); } +void lcd::clear() { + if (!_dispaly_cleared) { + _display->clear(); + _dispaly_cleared = true; + } +} -lcd::lcd(system_logger *logger) { +void lcd::print(const String& msg) { + _display->print(msg); + _dispaly_cleared = false; +} + +void lcd::print(char c) { + _display->print(c); + _dispaly_cleared = false; +} + +void lcd::print(const char c[]) { + _display->print(c); + _dispaly_cleared = false; +} + +void lcd::print(double d, int digits=2) { + _display->print(d, digits); + _dispaly_cleared = false; +} + +void lcd::print(unsigned long l, int base=10) { + _display->print(l, base); + _dispaly_cleared = false; +} + +void lcd::print(long l, int base=10) { + _display->print(l, base); + _dispaly_cleared = false; +} + +void lcd::print(unsigned int i, int base=10) { + _display->print(i, base); + _dispaly_cleared = false; +} + +void lcd::print(int i, int base=10) { + _display->print(i, base); + _dispaly_cleared = false; +} + +lcd::lcd(): _logger(nullptr), _screen(screen::blank), _last_render(0), _frame_duration(500), _dispaly_cleared(false), _gps(nullptr) { _display = new LiquidCrystal_I2C(0x27, 20, 4); } + +lcd::lcd(system_logger *logger): _logger(logger), _screen(screen::blank), _last_render(0), _frame_duration(500), _dispaly_cleared(false), _gps(nullptr) { _display = new LiquidCrystal_I2C(0x27, 20, 4); - _logger = logger; + } lcd::~lcd() {} @@ -32,8 +106,9 @@ int lcd::init() { } #endif _display->init(); + Wire.setClock(400000); _display->backlight(); - _display->clear(); + this->clear(); _display->setCursor(0, 0); #ifdef DEEP_DEBUG if (_logger != nullptr) { @@ -43,26 +118,146 @@ int lcd::init() { return 0; } +int lcd::set_gps(gps *gps) { + _gps = gps; +} + int lcd::print_message(String message) { #ifdef DEEP_DEBUG if (_logger != nullptr) { _logger->deep_debug(String(MOD) + ": print_message: Begin"); } #endif - _display->clear(); - _display->setCursor(0, 0); - _display->print(message); + + String original = message; + this->clear(); + + if (message.length() > 80) { + message = message.substring(0, 80); + } + + String lines[4] = {"", "", "", ""}; + int lineIndex = 0; + + while (message.length() > 0 && lineIndex < 4) { + if (message.length() <= 20) { + lines[lineIndex++] = message; + break; + } + + int splitIndex = message.lastIndexOf(' ', 20); + + if (splitIndex == -1 || splitIndex == 0) { + splitIndex = 20; + } + + lines[lineIndex++] = message.substring(0, splitIndex); + + if (splitIndex < message.length() && message.charAt(splitIndex) == ' ') { + message = message.substring(splitIndex + 1); + } else { + message = message.substring(splitIndex); + } + } + + int usedLines = 0; + for (int i = 0; i < 4; i++) { + if (lines[i].length() > 0) usedLines++; + } + + int startRow = 0; + if (usedLines == 1) startRow = 1; + else if (usedLines == 2) startRow = 1; + else startRow = 0; + + int currentRow = startRow; + for (int i = 0; i < 4; i++) { + if (lines[i].length() == 0) continue; + + int col = (20 - lines[i].length()) / 2; + if (col < 0) col = 0; + + _display->setCursor(col, currentRow++); + this->print(lines[i]); + } + #ifdef INFO if (_logger != nullptr) { - _logger->info(message); + _logger->info(original); } #endif + #ifdef DEEP_DEBUG if (_logger != nullptr) { _logger->deep_debug(String(MOD) + ": print_message: End"); } #endif + return 0; } +int lcd::switch_screen(screen::lcd_screen new_screen) { + _screen = new_screen; + return 0; +} + +int lcd::render_task() { + unsigned long now = millis(); + if (now < _last_render + _frame_duration) { + return 1; + } + switch (_screen) + { + case screen::blank: + this->clear(); + break; + + case screen::gps_debug: + this->clear(); + if (_gps == nullptr) { + this->print_message("No GPS Found"); + break; + } + gps_data data = _gps->get_data(); + + _display->setCursor(0,0); + this->print("Alt: "); + if (data.altitude.valid) { + this->print(data.altitude.value, 5); + } else { + this->print("not valid"); + } + + _display->setCursor(0,1); + this->print("Lat: "); + if (data.lat.valid) { + this->print(data.lat.value, 5); + } else { + this->print("not valid"); + } + + _display->setCursor(0,2); + this->print("Lng: "); + if (data.lng.valid) { + this->print(data.lng.value, 5); + } else { + this->print("not valid"); + } + + _display->setCursor(0,3); + this->print("Spd: "); + if (data.speed.valid) { + this->print(data.speed.value, 5); + } else { + this->print("not valid"); + } + break; + + default: + break; + } + _last_render = millis(); + return 1; +} + #undef MOD \ No newline at end of file