├── .gitignore ├── LICENSE ├── README.md ├── example ├── .gitignore ├── README.md ├── lib │ └── LiquidCrystal_I2C │ │ ├── LiquidCrystal_I2C.cpp │ │ ├── LiquidCrystal_I2C.h │ │ ├── LiquidCrystal_I2C.o │ │ ├── README.md │ │ ├── keywords.txt │ │ ├── library.json │ │ └── library.properties ├── platformio.ini └── src │ └── example.cpp ├── library.properties └── src ├── lcdHelper.h └── lcdHelper.tpp /.gitignore: -------------------------------------------------------------------------------- 1 | arduino-lcdHelper-library.code-workspace 2 | .pio 3 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 3urobeat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arduino-lcdHelper-library 2 | This Arduino library aims to make your life easier when using LCD (HD44780) displays and improves support for UTF-8 chars (like ä, ö, ü, ß etc.). 3 | 4 | It inherits all functions from your LCD library and provides additional functions, especially with UTF-8 fixes, like `clearLine()`, `centerPrint()` or `movingPrint()`. 5 | All functions are documented below. 6 | 7 |   8 | 9 | The library is made for [LiquidCrystal_I2C](https://github.com/johnrickman/LiquidCrystal_I2C) but was mainly developed using a fork called [NoiascaLiquidCrystal](https://werner.rothschopf.net/202009_arduino_liquid_crystal_intro.htm) to support German UTF-8 chars. 10 | 11 | The library only supports C char arrays, no Arduino or C++ Strings, to be more efficient on these low memory devices. 12 | You can still pass Strings to functions provided by `LiquidCrystal` as they are inherited. 13 | 14 |   15 | 16 | ## Usage 17 | Simply clone this repository, add the folder to your project (for example into a lib folder), include the library in your code (`#include `) and include it when compiling (depends on what dev env you are using, see below for a PIO example). 18 | 19 | Init the library by calling `lcdHelper lcd(addr, cols, rows)` (replace LiquidCrystal_I2C with the lcd lib you are using). 20 | Remove any initialization of `LiquidCrystal_I2C` itself if you have one. lcdHelper does this for you. 21 | You can now access all LiquidCrystal and lcdHelper functions from `lcd`. 22 | 23 |   24 | 25 | **Example when using PlatformIO:** 26 | [Download](https://github.com/3urobeat/arduino-lcdHelper-library/archive/refs/heads/main.zip), extract and then put the `arduino-lcdHelper-library` folder into the `lib` folder of your project, which was generated by PlatformIO. 27 | Include the library in your main code file by adding `#include `. 28 | PlatformIO will automatically compile included libraries that are in the `lib` folder. 29 | You can now initialize the lib just like shown above by calling the `lcdHelper` constructor with the address, columns and rows of your display. 30 | 31 |   32 | 33 | ## Functions added by lcdHelper 34 | Check out the example sketch showcasing every function [here](https://github.com/3urobeat/arduino-lcdHelper-library/blob/main/example/src/example.cpp)! 35 | I included an example PIO config and a copy of `LiquidCrystal_I2C` in the `examples/lib` folder so you can quickly compile & upload the example inside an PIO environment to try it out. 36 | 37 |   38 | 39 | ### lcdHelper objectName(uint8_t addr, uint8_t cols, uint8_t rows) 40 | - `addr` - Address of your display, usually `0x27` I think 41 | - `cols` - The amount of columns your display has. When you have a 4x20 display for example, provide 20 here 42 | - `rows` - The amount of rows your display has. When you have a 4x20 display for example, provide 4 here 43 | 44 | Constructor which creates a lcdHelper and YOUR_LCD_LIBRARY object and inherits all functions from YOUR_LCD_LIBRARY. 45 | 46 | Example: 47 | ``` 48 | #include 49 | 50 | lcdHelper lcd(0x27, 20, 4); 51 | ``` 52 | 53 | ### void clearLine(uint8_t row) 54 | - `row` - The row to clear (counted from 0) 55 | 56 | Clears a specific line on your display. 57 | 58 | ### int calculateCenterOffset(const char *str) 59 | - `str` - The char array to get the offset for 60 | 61 | Returns column offset from the beginning of a row. 62 | Gets the offset from the beginning of a row to display a char array centered. If the char array is longer than the display width, 0 will be returned. 63 | 64 | ### void centerPrint(const char *str, uint8_t row, bool callClearLine = false) 65 | - `str` - The char array to print 66 | - `row` - The row to print the char array in 67 | - `callClearLine` - **Optional:** Set to true if line should be cleared before printing 68 | 69 | Print a char array centered in a row on your display. 70 | 71 | ### void movingPrint(const char *str, uint8_t *moveOffset, uint8_t width) 72 | - `str` - The char array to print 73 | - `moveOffset` - Pointer to int tracking offset 74 | - `width` - Width of the space on screen the char array will be moved across 75 | 76 | Prints a char array that will be moved across a row by one char each time the function is called. 77 | 78 | Example: 79 | ``` 80 | uint8_t moveOffset = 0; // Define a var tracking the current offset somewhere at the top scope 81 | 82 | // Call movingPrint() each time you want to move your char array, for example in your arduino loop 83 | void loop() 84 | { 85 | // Message shall appear on row 2 86 | lcd.setCursor(0, 1); 87 | 88 | // Pass char array, pointer to moveOffset and the width to move across 89 | lcd.movingPrint("This is a test message which will move across the screen", *moveOffset, 20); 90 | 91 | // Example delay so you can actually read what is happening on your display 92 | delay(250); 93 | } 94 | ``` 95 | 96 | ### fadeInPrint(const char *str, uint8_t fadeInDelay, bool rightToLeft, uint8_t currentCol, uint8_t currentRow) 97 | - `str` - The char array to print 98 | - `fadeInDelay` - **Optional:** Set a different delay between characters than the default of 50ms 99 | - `rightToLeft` - **Optional:** Set to true to print from right to left instead of left to right 100 | - `currentCol` - **Semi-Optional:** If rightToLeft is true, you must provide the current cursor column here, as the function has to control the cursor now 101 | - `currentRow` - **Semi-Optional:** If rightToLeft is true, you must provide the current cursor row here, as the function has to control the cursor now 102 | 103 | Prints a char array that will fade in left to right, or right to left, from the current cursor position. 104 | 105 | Example: 106 | ``` 107 | // Very simple left to right fade in, from position col 1, row 1 108 | lcd.setCursor(1, 1); 109 | lcd.fadeInPrint("lcdHelper Example!"); 110 | 111 | // More complex right to left fade in, from position col 5, row 0: The function will handle an underflow with a line break 112 | const char fadeInMsg[] = "Underflowing rightToLeft"; 113 | lcd.setCursor(5, 0); // Will cause a col and row underflow which fadeInPrint will handle 114 | lcd.fadeInPrint(fadeInMsg, 25, true, 5, 0); // A little faster and right to left 115 | ``` 116 | 117 | ### void animationPrint(const char **animationArray, uint8_t animationSize, uint8_t *animationFrame, uint8_t col, uint8_t row) 118 | - `animationArray` - Pointer to an array containing char arrays for each animation frame 119 | - `animationSize` - Amount of frames your animation has, counting from 1 120 | - `animationFrame` - Pointer to int tracking animation progress 121 | - `col` - The column to print the animation at 122 | - `row` - The row to print the animation in 123 | 124 | Print an animation frame by frame each time the function is called. 125 | I provided a few default animations (loading, waiting, bounce, progress, arrows, bouncearrow), accessible like so: 126 | ``` 127 | uint8_t animationFrame = 0; // Define a var tracking the current frame somewhere at the top scope 128 | 129 | void loop() 130 | { 131 | // Print loading animation in row 2 at column 10 132 | lcd.animationPrint(lcd.animations.loading, 8, &animationFrame, 10, 1); 133 | 134 | // Example delay 135 | delay(250); 136 | } 137 | ``` 138 | The size of each default animation is written as a comment behind each definition, your IntelliSense should display it for you. 139 | Feel free to check out [lcdHelper.h](https://github.com/3urobeat/arduino-lcdHelper-library/blob/main/src/lcdHelper.h) to take a look at all default animations which are defined in a struct. 140 | 141 | ### void alignedPrint(const char *align, const char *str, uint8_t width) 142 | - `align` - "left", "center" or "right" 143 | - `str` - The char array to print 144 | - `width` - The fixed width of the resulting char array, which str will be aligned to 145 | 146 | Print a char array aligned left, center or right to a fixed width. 147 | 148 | ### void limitedPrint(const char *str, uint8_t length) 149 | - `str` - The char array to print 150 | - `length` - The length to limit str to 151 | 152 | Prints a char array to the display with a limited length (UTF-8 aware) without making a copy. 153 | 154 | ### size_t utf8_strlen(const char *str) 155 | - `str` - The char array to get the length of 156 | 157 | Returns the length of str. 158 | Custom strlen function to correctly count chars that are two bytes long (like ä ö or ü). 159 | 160 | ### char* toFixedLengthNumber(char *dest, int num, uint8_t len) 161 | - `dest` - Pointer to destination char array 162 | - `num` - The number to convert 163 | - `len` - The length the resulting char array will have. Zeroes will be added infront of num until it matches this length 164 | 165 | Returns pointer to `dest` so that you can use it directly inside another function. 166 | Converts num to char array and precedes it with zeroes to match length. 167 | Make sure buf is at least len bytes long! 168 | 169 | Example, showing uptime in minutes, with values <10 having a preceding 0: 170 | ``` 171 | unsigned long uptime = millis() / 1000; // Current uptime in seconds 172 | char temp[3] = ""; // Buffer char array 173 | 174 | // Uptime in minutes, 5 will be written as 05 into temp to match len 2 175 | lcd.toFixedLengthNumber(temp, (uptime % 3600) / 60, 2); 176 | lcd.print(temp); // Print "05" to display 177 | 178 | // ...or make use of return to print directly 179 | lcd.print(lcd.toFixedLengthNumber(temp, (uptime % 3600) / 60, 2)); 180 | ``` 181 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # lcdHelper platform.io example 2 | This is an example of how to use all lcdHelper functions, packaged inside an PIO environment so you can try it out as easily as possible (if you are using PIO (which you should, it's amazing)). 3 | 4 | The example is made for an 4x20 HD44780 LCD display so if you are using a smaller version this won't work out of the box (or you just won't see everything). 5 | Customize the `platformio.ini` config if you are using a different board than an ESP8266 devkit. 6 | 7 | I included a copy of `LiquidCrystal_I2C` in the `lib` folder, the include path of `arduino-lcdHelper-library` however is local and points to the `project_root/src` folder, so you can only use this example in the given folder structure. 8 | To use the lib like in the example in your own project, put the library into the `lib` folder like explained in the README. 9 | 10 |   11 | 12 | Open the example folder inside VsCode with PlatformIO installed. 13 | If your board and upload_port in `platformio.ini` are correct and your 4x20 display is connected to D1 and D2 (aka the same as mine) then you should be able to just press the PlatformIO Upload button. 14 | 15 |   16 | 17 | And if you don't have a PlatformIO setup or don't want to bother with configuring if you don't have the same setup then you can ofc just look at the implementations of each function inside `example.cpp` and use them in your own project. 18 | 19 |   20 |   21 | 22 | This text is a mess. -------------------------------------------------------------------------------- /example/lib/LiquidCrystal_I2C/LiquidCrystal_I2C.cpp: -------------------------------------------------------------------------------- 1 | // Based on the work by DFRobot 2 | 3 | #include "LiquidCrystal_I2C.h" 4 | #include 5 | #if defined(ARDUINO) && ARDUINO >= 100 6 | 7 | #include "Arduino.h" 8 | 9 | #define printIIC(args) Wire.write(args) 10 | inline size_t LiquidCrystal_I2C::write(uint8_t value) { 11 | send(value, Rs); 12 | return 1; 13 | } 14 | 15 | #else 16 | #include "WProgram.h" 17 | 18 | #define printIIC(args) Wire.send(args) 19 | inline void LiquidCrystal_I2C::write(uint8_t value) { 20 | send(value, Rs); 21 | } 22 | 23 | #endif 24 | #include "Wire.h" 25 | 26 | 27 | 28 | // When the display powers up, it is configured as follows: 29 | // 30 | // 1. Display clear 31 | // 2. Function set: 32 | // DL = 1; 8-bit interface data 33 | // N = 0; 1-line display 34 | // F = 0; 5x8 dot character font 35 | // 3. Display on/off control: 36 | // D = 0; Display off 37 | // C = 0; Cursor off 38 | // B = 0; Blinking off 39 | // 4. Entry mode set: 40 | // I/D = 1; Increment by 1 41 | // S = 0; No shift 42 | // 43 | // Note, however, that resetting the Arduino doesn't reset the LCD, so we 44 | // can't assume that its in that state when a sketch starts (and the 45 | // LiquidCrystal constructor is called). 46 | 47 | LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows) 48 | { 49 | _Addr = lcd_Addr; 50 | _cols = lcd_cols; 51 | _rows = lcd_rows; 52 | _backlightval = LCD_NOBACKLIGHT; 53 | } 54 | 55 | void LiquidCrystal_I2C::init(){ 56 | init_priv(); 57 | } 58 | 59 | void LiquidCrystal_I2C::init_priv() 60 | { 61 | Wire.begin(); 62 | _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; 63 | begin(_cols, _rows); 64 | } 65 | 66 | void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { 67 | if (lines > 1) { 68 | _displayfunction |= LCD_2LINE; 69 | } 70 | _numlines = lines; 71 | 72 | // for some 1 line displays you can select a 10 pixel high font 73 | if ((dotsize != 0) && (lines == 1)) { 74 | _displayfunction |= LCD_5x10DOTS; 75 | } 76 | 77 | // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! 78 | // according to datasheet, we need at least 40ms after power rises above 2.7V 79 | // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 80 | delay(50); 81 | 82 | // Now we pull both RS and R/W low to begin commands 83 | expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1) 84 | delay(1000); 85 | 86 | //put the LCD into 4 bit mode 87 | // this is according to the hitachi HD44780 datasheet 88 | // figure 24, pg 46 89 | 90 | // we start in 8bit mode, try to set 4 bit mode 91 | write4bits(0x03 << 4); 92 | delayMicroseconds(4500); // wait min 4.1ms 93 | 94 | // second try 95 | write4bits(0x03 << 4); 96 | delayMicroseconds(4500); // wait min 4.1ms 97 | 98 | // third go! 99 | write4bits(0x03 << 4); 100 | delayMicroseconds(150); 101 | 102 | // finally, set to 4-bit interface 103 | write4bits(0x02 << 4); 104 | 105 | 106 | // set # lines, font size, etc. 107 | command(LCD_FUNCTIONSET | _displayfunction); 108 | 109 | // turn the display on with no cursor or blinking default 110 | _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; 111 | display(); 112 | 113 | // clear it off 114 | clear(); 115 | 116 | // Initialize to default text direction (for roman languages) 117 | _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; 118 | 119 | // set the entry mode 120 | command(LCD_ENTRYMODESET | _displaymode); 121 | 122 | home(); 123 | 124 | } 125 | 126 | /********** high level commands, for the user! */ 127 | void LiquidCrystal_I2C::clear(){ 128 | command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero 129 | delayMicroseconds(2000); // this command takes a long time! 130 | } 131 | 132 | void LiquidCrystal_I2C::home(){ 133 | command(LCD_RETURNHOME); // set cursor position to zero 134 | delayMicroseconds(2000); // this command takes a long time! 135 | } 136 | 137 | void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row){ 138 | int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; 139 | if ( row > _numlines ) { 140 | row = _numlines-1; // we count rows starting w/0 141 | } 142 | command(LCD_SETDDRAMADDR | (col + row_offsets[row])); 143 | } 144 | 145 | // Turn the display on/off (quickly) 146 | void LiquidCrystal_I2C::noDisplay() { 147 | _displaycontrol &= ~LCD_DISPLAYON; 148 | command(LCD_DISPLAYCONTROL | _displaycontrol); 149 | } 150 | void LiquidCrystal_I2C::display() { 151 | _displaycontrol |= LCD_DISPLAYON; 152 | command(LCD_DISPLAYCONTROL | _displaycontrol); 153 | } 154 | 155 | // Turns the underline cursor on/off 156 | void LiquidCrystal_I2C::noCursor() { 157 | _displaycontrol &= ~LCD_CURSORON; 158 | command(LCD_DISPLAYCONTROL | _displaycontrol); 159 | } 160 | void LiquidCrystal_I2C::cursor() { 161 | _displaycontrol |= LCD_CURSORON; 162 | command(LCD_DISPLAYCONTROL | _displaycontrol); 163 | } 164 | 165 | // Turn on and off the blinking cursor 166 | void LiquidCrystal_I2C::noBlink() { 167 | _displaycontrol &= ~LCD_BLINKON; 168 | command(LCD_DISPLAYCONTROL | _displaycontrol); 169 | } 170 | void LiquidCrystal_I2C::blink() { 171 | _displaycontrol |= LCD_BLINKON; 172 | command(LCD_DISPLAYCONTROL | _displaycontrol); 173 | } 174 | 175 | // These commands scroll the display without changing the RAM 176 | void LiquidCrystal_I2C::scrollDisplayLeft(void) { 177 | command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); 178 | } 179 | void LiquidCrystal_I2C::scrollDisplayRight(void) { 180 | command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); 181 | } 182 | 183 | // This is for text that flows Left to Right 184 | void LiquidCrystal_I2C::leftToRight(void) { 185 | _displaymode |= LCD_ENTRYLEFT; 186 | command(LCD_ENTRYMODESET | _displaymode); 187 | } 188 | 189 | // This is for text that flows Right to Left 190 | void LiquidCrystal_I2C::rightToLeft(void) { 191 | _displaymode &= ~LCD_ENTRYLEFT; 192 | command(LCD_ENTRYMODESET | _displaymode); 193 | } 194 | 195 | // This will 'right justify' text from the cursor 196 | void LiquidCrystal_I2C::autoscroll(void) { 197 | _displaymode |= LCD_ENTRYSHIFTINCREMENT; 198 | command(LCD_ENTRYMODESET | _displaymode); 199 | } 200 | 201 | // This will 'left justify' text from the cursor 202 | void LiquidCrystal_I2C::noAutoscroll(void) { 203 | _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; 204 | command(LCD_ENTRYMODESET | _displaymode); 205 | } 206 | 207 | // Allows us to fill the first 8 CGRAM locations 208 | // with custom characters 209 | void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]) { 210 | location &= 0x7; // we only have 8 locations 0-7 211 | command(LCD_SETCGRAMADDR | (location << 3)); 212 | for (int i=0; i<8; i++) { 213 | write(charmap[i]); 214 | } 215 | } 216 | 217 | // Turn the (optional) backlight off/on 218 | void LiquidCrystal_I2C::noBacklight(void) { 219 | _backlightval=LCD_NOBACKLIGHT; 220 | expanderWrite(0); 221 | } 222 | 223 | void LiquidCrystal_I2C::backlight(void) { 224 | _backlightval=LCD_BACKLIGHT; 225 | expanderWrite(0); 226 | } 227 | 228 | 229 | 230 | /*********** mid level commands, for sending data/cmds */ 231 | 232 | inline void LiquidCrystal_I2C::command(uint8_t value) { 233 | send(value, 0); 234 | } 235 | 236 | 237 | /************ low level data pushing commands **********/ 238 | 239 | // write either command or data 240 | void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) { 241 | uint8_t highnib=value&0xf0; 242 | uint8_t lownib=(value<<4)&0xf0; 243 | write4bits((highnib)|mode); 244 | write4bits((lownib)|mode); 245 | } 246 | 247 | void LiquidCrystal_I2C::write4bits(uint8_t value) { 248 | expanderWrite(value); 249 | pulseEnable(value); 250 | } 251 | 252 | void LiquidCrystal_I2C::expanderWrite(uint8_t _data){ 253 | Wire.beginTransmission(_Addr); 254 | printIIC((int)(_data) | _backlightval); 255 | Wire.endTransmission(); 256 | } 257 | 258 | void LiquidCrystal_I2C::pulseEnable(uint8_t _data){ 259 | expanderWrite(_data | En); // En high 260 | delayMicroseconds(1); // enable pulse must be >450ns 261 | 262 | expanderWrite(_data & ~En); // En low 263 | delayMicroseconds(50); // commands need > 37us to settle 264 | } 265 | 266 | 267 | // Alias functions 268 | 269 | void LiquidCrystal_I2C::cursor_on(){ 270 | cursor(); 271 | } 272 | 273 | void LiquidCrystal_I2C::cursor_off(){ 274 | noCursor(); 275 | } 276 | 277 | void LiquidCrystal_I2C::blink_on(){ 278 | blink(); 279 | } 280 | 281 | void LiquidCrystal_I2C::blink_off(){ 282 | noBlink(); 283 | } 284 | 285 | void LiquidCrystal_I2C::load_custom_character(uint8_t char_num, uint8_t *rows){ 286 | createChar(char_num, rows); 287 | } 288 | 289 | void LiquidCrystal_I2C::setBacklight(uint8_t new_val){ 290 | if(new_val){ 291 | backlight(); // turn backlight on 292 | }else{ 293 | noBacklight(); // turn backlight off 294 | } 295 | } 296 | 297 | void LiquidCrystal_I2C::printstr(const char c[]){ 298 | //This function is not identical to the function used for "real" I2C displays 299 | //it's here so the user sketch doesn't have to be changed 300 | print(c); 301 | } 302 | 303 | 304 | // unsupported API functions 305 | void LiquidCrystal_I2C::off(){} 306 | void LiquidCrystal_I2C::on(){} 307 | void LiquidCrystal_I2C::setDelay (int cmdDelay,int charDelay) {} 308 | uint8_t LiquidCrystal_I2C::status(){return 0;} 309 | uint8_t LiquidCrystal_I2C::keypad (){return 0;} 310 | uint8_t LiquidCrystal_I2C::init_bargraph(uint8_t graphtype){return 0;} 311 | void LiquidCrystal_I2C::draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end){} 312 | void LiquidCrystal_I2C::draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_row_end){} 313 | void LiquidCrystal_I2C::setContrast(uint8_t new_val){} 314 | 315 | 316 | -------------------------------------------------------------------------------- /example/lib/LiquidCrystal_I2C/LiquidCrystal_I2C.h: -------------------------------------------------------------------------------- 1 | //YWROBOT 2 | #ifndef LiquidCrystal_I2C_h 3 | #define LiquidCrystal_I2C_h 4 | 5 | #include 6 | #include "Print.h" 7 | #include 8 | 9 | // commands 10 | #define LCD_CLEARDISPLAY 0x01 11 | #define LCD_RETURNHOME 0x02 12 | #define LCD_ENTRYMODESET 0x04 13 | #define LCD_DISPLAYCONTROL 0x08 14 | #define LCD_CURSORSHIFT 0x10 15 | #define LCD_FUNCTIONSET 0x20 16 | #define LCD_SETCGRAMADDR 0x40 17 | #define LCD_SETDDRAMADDR 0x80 18 | 19 | // flags for display entry mode 20 | #define LCD_ENTRYRIGHT 0x00 21 | #define LCD_ENTRYLEFT 0x02 22 | #define LCD_ENTRYSHIFTINCREMENT 0x01 23 | #define LCD_ENTRYSHIFTDECREMENT 0x00 24 | 25 | // flags for display on/off control 26 | #define LCD_DISPLAYON 0x04 27 | #define LCD_DISPLAYOFF 0x00 28 | #define LCD_CURSORON 0x02 29 | #define LCD_CURSOROFF 0x00 30 | #define LCD_BLINKON 0x01 31 | #define LCD_BLINKOFF 0x00 32 | 33 | // flags for display/cursor shift 34 | #define LCD_DISPLAYMOVE 0x08 35 | #define LCD_CURSORMOVE 0x00 36 | #define LCD_MOVERIGHT 0x04 37 | #define LCD_MOVELEFT 0x00 38 | 39 | // flags for function set 40 | #define LCD_8BITMODE 0x10 41 | #define LCD_4BITMODE 0x00 42 | #define LCD_2LINE 0x08 43 | #define LCD_1LINE 0x00 44 | #define LCD_5x10DOTS 0x04 45 | #define LCD_5x8DOTS 0x00 46 | 47 | // flags for backlight control 48 | #define LCD_BACKLIGHT 0x08 49 | #define LCD_NOBACKLIGHT 0x00 50 | 51 | #define En B00000100 // Enable bit 52 | #define Rw B00000010 // Read/Write bit 53 | #define Rs B00000001 // Register select bit 54 | 55 | class LiquidCrystal_I2C : public Print { 56 | public: 57 | LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows); 58 | void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS ); 59 | void clear(); 60 | void home(); 61 | void noDisplay(); 62 | void display(); 63 | void noBlink(); 64 | void blink(); 65 | void noCursor(); 66 | void cursor(); 67 | void scrollDisplayLeft(); 68 | void scrollDisplayRight(); 69 | void printLeft(); 70 | void printRight(); 71 | void leftToRight(); 72 | void rightToLeft(); 73 | void shiftIncrement(); 74 | void shiftDecrement(); 75 | void noBacklight(); 76 | void backlight(); 77 | void autoscroll(); 78 | void noAutoscroll(); 79 | void createChar(uint8_t, uint8_t[]); 80 | void setCursor(uint8_t, uint8_t); 81 | #if defined(ARDUINO) && ARDUINO >= 100 82 | virtual size_t write(uint8_t); 83 | #else 84 | virtual void write(uint8_t); 85 | #endif 86 | void command(uint8_t); 87 | void init(); 88 | 89 | ////compatibility API function aliases 90 | void blink_on(); // alias for blink() 91 | void blink_off(); // alias for noBlink() 92 | void cursor_on(); // alias for cursor() 93 | void cursor_off(); // alias for noCursor() 94 | void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight() 95 | void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar() 96 | void printstr(const char[]); 97 | 98 | ////Unsupported API functions (not implemented in this library) 99 | uint8_t status(); 100 | void setContrast(uint8_t new_val); 101 | uint8_t keypad(); 102 | void setDelay(int,int); 103 | void on(); 104 | void off(); 105 | uint8_t init_bargraph(uint8_t graphtype); 106 | void draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); 107 | void draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); 108 | 109 | 110 | private: 111 | void init_priv(); 112 | void send(uint8_t, uint8_t); 113 | void write4bits(uint8_t); 114 | void expanderWrite(uint8_t); 115 | void pulseEnable(uint8_t); 116 | uint8_t _Addr; 117 | uint8_t _displayfunction; 118 | uint8_t _displaycontrol; 119 | uint8_t _displaymode; 120 | uint8_t _numlines; 121 | uint8_t _cols; 122 | uint8_t _rows; 123 | uint8_t _backlightval; 124 | }; 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /example/lib/LiquidCrystal_I2C/LiquidCrystal_I2C.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3urobeat/arduino-lcdHelper-library/b159fe0e422efe9858476696c8e23b6cc0b76a8d/example/lib/LiquidCrystal_I2C/LiquidCrystal_I2C.o -------------------------------------------------------------------------------- /example/lib/LiquidCrystal_I2C/README.md: -------------------------------------------------------------------------------- 1 | # LiquidCrystal_I2C 2 | LiquidCrystal Arduino library for the DFRobot I2C LCD displays 3 | -------------------------------------------------------------------------------- /example/lib/LiquidCrystal_I2C/keywords.txt: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Syntax Coloring Map For LiquidCrystal_I2C 3 | ########################################### 4 | 5 | ########################################### 6 | # Datatypes (KEYWORD1) 7 | ########################################### 8 | 9 | LiquidCrystal_I2C KEYWORD1 10 | 11 | ########################################### 12 | # Methods and Functions (KEYWORD2) 13 | ########################################### 14 | init KEYWORD2 15 | begin KEYWORD2 16 | clear KEYWORD2 17 | home KEYWORD2 18 | noDisplay KEYWORD2 19 | display KEYWORD2 20 | noBlink KEYWORD2 21 | blink KEYWORD2 22 | noCursor KEYWORD2 23 | cursor KEYWORD2 24 | scrollDisplayLeft KEYWORD2 25 | scrollDisplayRight KEYWORD2 26 | leftToRight KEYWORD2 27 | rightToLeft KEYWORD2 28 | shiftIncrement KEYWORD2 29 | shiftDecrement KEYWORD2 30 | noBacklight KEYWORD2 31 | backlight KEYWORD2 32 | autoscroll KEYWORD2 33 | noAutoscroll KEYWORD2 34 | createChar KEYWORD2 35 | setCursor KEYWORD2 36 | print KEYWORD2 37 | blink_on KEYWORD2 38 | blink_off KEYWORD2 39 | cursor_on KEYWORD2 40 | cursor_off KEYWORD2 41 | setBacklight KEYWORD2 42 | load_custom_character KEYWORD2 43 | printstr KEYWORD2 44 | ########################################### 45 | # Constants (LITERAL1) 46 | ########################################### 47 | -------------------------------------------------------------------------------- /example/lib/LiquidCrystal_I2C/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LiquidCrystal_I2C", 3 | "keywords": "LCD, liquidcrystal, I2C", 4 | "description": "A library for DFRobot I2C LCD displays", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/marcoschwartz/LiquidCrystal_I2C.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": 12 | [ 13 | "atmelavr" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/lib/LiquidCrystal_I2C/library.properties: -------------------------------------------------------------------------------- 1 | name=LiquidCrystal I2C 2 | version=1.1.2 3 | author=Frank de Brabander 4 | maintainer=Marco Schwartz 5 | sentence=A library for I2C LCD displays. 6 | paragraph= The library allows to control I2C displays with functions extremely similar to LiquidCrystal library. THIS LIBRARY MIGHT NOT BE COMPATIBLE WITH EXISTING SKETCHES. 7 | category=Display 8 | url=https://github.com/marcoschwartz/LiquidCrystal_I2C 9 | architectures=avr 10 | -------------------------------------------------------------------------------- /example/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | ; Example PIO config. Customize to whatever board you are using, this is currently configured to work with an ESP8266 devkit board, connected to ttyUSB0. 13 | 14 | [env:nodemcuv2] 15 | platform = espressif8266 16 | board = nodemcuv2 17 | framework = arduino 18 | upload_port = /dev/ttyUSB0 -------------------------------------------------------------------------------- /example/src/example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: example.cpp 3 | * Project: arduino-lcdHelper-library 4 | * Created Date: 2022-11-22 16:50:28 5 | * Author: 3urobeat 6 | * 7 | * Last Modified: 2025-08-30 15:43:55 8 | * Modified By: 3urobeat 9 | * 10 | * Copyright (c) 2022 - 2025 3urobeat 11 | * 12 | * Licensed under the MIT license: https://opensource.org/licenses/MIT 13 | * Permission is granted to use, copy, modify, and redistribute the work. 14 | * Full license information available in the project LICENSE file. 15 | */ 16 | 17 | 18 | /* 19 | lcdHelper v1.2.0 usage example 20 | 21 | ------ 22 | 23 | This example shows you how to use all new functions added by lcdHelper by displaying random stuff on the display. 24 | A copy of the LiquidCrystal_I2C library is included in the lib folder, lcdHelper is referenced from the project_root/src folder. 25 | */ 26 | 27 | 28 | // Include libraries 29 | #include // Ignore this import, I'm using an ESP8266 for testing, this import is needed for setup() and loop() to be defined 30 | #include 31 | //#include // Normal import when lcdHelper is inside your lib folder - use this one in your project 32 | #include "../../src/lcdHelper.h" // "Custom" import just for this example as lcdHelper.h is inside src at the project root and not inside lib of this example folder 33 | 34 | 35 | // Init lcdHelper, which inits LiquidCrystal, so we can use our display 36 | lcdHelper lcd(0x27, 20, 4); // My display is a 4x20 HD44780 with address 0x27 37 | 38 | // Define two persistent animationFrame vars for animationPrint() 39 | uint8_t animFrameTracker1 = 0; // uint8_t is an Arduino datatype - an unsigned 8 bit number 40 | uint8_t animFrameTracker2 = 0; 41 | 42 | // Define a persistent moveOffset var for movingPrint() 43 | uint8_t moveOffset = 0; 44 | uint8_t dots = 0; // also save the dots progress of our little animation for later 45 | 46 | 47 | // Setup display, enable backlight and print stuff which won't change and therefore doesn't need to be updated each 500ms 48 | void setup() 49 | { 50 | lcd.init(); 51 | lcd.backlight(); 52 | 53 | 54 | /* 55 | fadeInPrint() 56 | 57 | Let a message fade in left to right or right to left 58 | */ 59 | // Left to right fade in from the middle of row 1 60 | lcd.setCursor(1, 1); 61 | lcd.fadeInPrint("lcdHelper Example!"); 62 | 63 | // ...or a fade in from right to left with optional options used. In this case the column & row will underflow and the library will handle it with a line break 64 | const char fadeInMsg[] = "Underflowing rightToLeft"; 65 | lcd.setCursor(5, 0); // Will cause a col and row underflow which fadeInPrint will handle 66 | lcd.fadeInPrint(fadeInMsg, 25, true, 5, 0); // A little faster and right to left 67 | 68 | // Clear that mess again 69 | delay(2500); 70 | lcd.clear(); 71 | 72 | 73 | /* 74 | centerPrint() 75 | 76 | Print a centered Hello! which will stay for the whole runtime 77 | */ 78 | lcd.centerPrint("Hello!", 0); // We don't need to clear the row beforehand so we don't provide callClearLine as the default value is false 79 | 80 | 81 | /* 82 | clearLine() 83 | 84 | Print something in line 2 and clear line after 2.5 seconds 85 | */ 86 | lcd.centerPrint("Center Message", 2); 87 | delay(2500); 88 | lcd.clearLine(2); // This will remove "Center message" but keep "Hello!" as we only clear one row 89 | 90 | 91 | // End of "startup welcome screen" 92 | 93 | 94 | /* 95 | utf8_strlen() 96 | 97 | Count length of a char array which contains UTF-8 chars (two byte long), convert size_t to char array and print to line 1 98 | Print strlen() result infront to see the difference 99 | 100 | You can also see some basic examples on how to work with char arrays here 101 | */ 102 | const char utf8Message[] = "Here are UTF-8 chars: ä ö ü ß ° $"; // 39 bytes + 1 for null byte 103 | char tempLen[21] = "UTF8: "; // Temp char array to construct message showing length 104 | 105 | char tempUTF8[3] = ""; // Temp char arrs to convert ints returned by strlen() to char arrs 106 | char tempByte[3] = ""; 107 | 108 | itoa(lcd.utf8_strlen(utf8Message), tempUTF8, 10); // Get result from lcdHelper's utf8_strlen() and onvert int to char arr 109 | itoa(strlen(utf8Message), tempByte, 10); // Get result from C's string.h strlen() and onvert int to char arr 110 | 111 | strncat(tempLen, tempUTF8, 2); // Construct tempLen message which will be printed by adding the 2 results to the end of tempLen 112 | strcat(tempLen, " Normal: "); // No need for strncat when we cat a fixed length char arr 113 | strncat(tempLen, tempByte, 2); 114 | 115 | lcd.setCursor(0, 1); 116 | lcd.print(tempLen); // Print it! 117 | 118 | 119 | /* 120 | limitedPrint() 121 | 122 | Print a cut off message without needing to do modify char array. 123 | This function was made to work especially well with UTF-8 chars but LiquidCrystal_I2C doesn't really support them so you have to trust me. 124 | This is why I'm using NoiascaLiquidCrystal: https://werner.rothschopf.net/202009_arduino_liquid_crystal_intro.htm 125 | */ 126 | lcd.setCursor(0, 2); 127 | lcd.limitedPrint("Cut off msg - Secret: You won't see this", 10); 128 | } 129 | 130 | 131 | // Now print stuff that should update every 250ms 132 | void loop() 133 | { 134 | /* 135 | animationPrint() 136 | 137 | Print a loading and progress animation in the first row beside the centered "Hello!" 138 | */ 139 | lcd.animationPrint(lcd.animations.loading, 8, &animFrameTracker1, 0, 0); 140 | lcd.animationPrint(lcd.animations.progress, 6, &animFrameTracker2, 15, 0); // progress is 5 chars wide 141 | 142 | 143 | /* 144 | alignedPrint() 145 | 146 | Print some moving dots aligned to the right of row 2 147 | */ 148 | dots++; // Add another dot 149 | if (dots > 4) dots = 0; // Reset when 4 dots are present 150 | 151 | char tempDots[5] = ""; // Temp char arr to construct dots 152 | for (uint8_t i = 0; i < dots; i++) strcat(tempDots, "."); // Add dots amount of dots to temp 153 | 154 | lcd.setCursor(11, 2); 155 | lcd.print("Dots:"); // Show something infront to showcase that temp is aligned to the right 156 | lcd.alignedPrint("right", tempDots, 4); // Print temp and overwrite any previous dots 157 | 158 | 159 | /* 160 | movingPrint() 161 | 162 | Let a long message run across 2/3 of the last row 163 | */ 164 | lcd.setCursor(0, 3); 165 | lcd.movingPrint(" This is a long message moving every 500ms ", &moveOffset, 14); // Pass address of moveOffset and limit to a width of 14 chars 166 | 167 | 168 | /* 169 | toFixedLengthNumber() 170 | 171 | Show seconds of uptime with fixed width at the end of the last row 172 | */ 173 | unsigned long uptime = millis() / 1000; // Current uptime in seconds 174 | char tempSeconds[3] = ""; // Buffer char array with space for two digits and null byte 175 | 176 | lcd.toFixedLengthNumber(tempSeconds, uptime % 60, 2); // Add preceding 0 to seconds if <10 177 | 178 | lcd.setCursor(18, 3); 179 | lcd.print(tempSeconds); // Print using the normal print function inherited from LiquidCrystal 180 | 181 | 182 | 183 | // Delay next iteration 184 | delay(500); 185 | } 186 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=arduino-lcdHelper-library 2 | version=1.2.0 3 | author=3urobeat 4 | maintainer=3urobeat 5 | sentence=Make working with LCD displays easier and improve UTF-8 support 6 | paragraph=This Arduino library aims to make your life easier when using LCD (HD44780) displays and improves support for UTF-8 chars (like ä, ö, ü, ß etc.).

It inherits all functions from your LCD library and provides additional functions, especially with UTF-8 fixes, like clearLine(), centerPrint() or movingPrint().
The library is made for LiquidCrystal_I2C but was mainly developed using a fork called NoiascaLiquidCrystal to support German UTF-8 chars. 7 | category=Display 8 | url=https://github.com/3urobeat/arduino-lcdHelper-library 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/lcdHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: lcdHelper.h 3 | * Project: arduino-lcdHelper-library 4 | * Created Date: 2022-08-26 12:04:51 5 | * Author: 3urobeat 6 | * 7 | * Last Modified: 2025-08-30 15:44:04 8 | * Modified By: 3urobeat 9 | * 10 | * Copyright (c) 2022 - 2025 3urobeat 11 | * 12 | * Licensed under the MIT license: https://opensource.org/licenses/MIT 13 | * Permission is granted to use, copy, modify, and redistribute the work. 14 | * Full license information available in the project LICENSE file. 15 | */ 16 | 17 | // Version 1.2.0 18 | 19 | #ifndef LCDHELPER_H 20 | #define LCDHELPER_H 21 | 22 | 23 | template 24 | class lcdHelper : public lcd { //use template as base to inherit from 25 | public: 26 | 27 | /** 28 | * Constructor 29 | * @param addr The address of your display 30 | * @param cols The amount of columns your display has 31 | * @param rows The amount of rows your display has 32 | */ 33 | lcdHelper(uint8_t addr, uint8_t cols, uint8_t rows) : lcd(addr, cols, rows) { 34 | _lcdCols = cols; 35 | _lcdRows = rows; 36 | }; 37 | 38 | /** 39 | * Clears a specific line on your display 40 | * @param row The row to clear (counted from 0) 41 | */ 42 | void clearLine(uint8_t row); 43 | 44 | /** 45 | * Gets the offset from the beginning of a row to display a char array centered. If the char array is longer than the display width, 0 will be returned. 46 | * @param str The char array to get the offset for 47 | * @return Column offset from the beginning of a row 48 | */ 49 | int calculateCenterOffset(const char *str); 50 | 51 | /** 52 | * Print a char array centered in a row on your display. 53 | * @param str The char array to print 54 | * @param row The row to print the char array in 55 | * @param callClearLine Optional: Set to true if line should be cleared before printing 56 | */ 57 | void centerPrint(const char *str, uint8_t row, bool callClearLine = false); 58 | 59 | /** 60 | * Prints a char array that will be moved across a row by one char each time the function is called 61 | * @param str The char array to print 62 | * @param moveOffset Pointer to int tracking offset 63 | * @param width Width of the space on screen the char array will be moved across 64 | */ 65 | void movingPrint(const char *str, uint8_t *moveOffset, uint8_t width); 66 | 67 | /** 68 | * Prints a char array that will fade in left to right, or right to left, from the current cursor position 69 | * @param str The char array to print 70 | * @param fadeInDelay Optional: Set a different delay between characters than the default of 50ms 71 | * @param rightToLeft Optional: Set to true to print from right to left instead of left to right 72 | * @param currentCol Semi-Optional: If rightToLeft is true, you must provide the current cursor column here, as the function has to control the cursor now 73 | * @param currentRow Semi-Optional: If rightToLeft is true, you must provide the current cursor row here, as the function has to control the cursor now 74 | */ 75 | void fadeInPrint(const char *str, uint8_t fadeInDelay = 50, bool rightToLeft = false, uint8_t currentCol = 0, uint8_t currentRow = 0); 76 | 77 | /** 78 | * Print an animation frame by frame each time the function is called 79 | * @param animationArray Pointer to an array containing char arrays for each animation frame 80 | * @param animationSize Amount of frames your animation has, counting from 1 81 | * @param animationFrame Pointer to int tracking animation progress 82 | * @param col The column to print the animation at 83 | * @param row The row to print the animation in 84 | */ 85 | void animationPrint(const char **animationArray, uint8_t animationSize, uint8_t *animationFrame, uint8_t col, uint8_t row); 86 | 87 | // Provide a few default animation (see chars that are referenced as hex here: https://werner.rothschopf.net/2020/lcd_charset_a00.gif) 88 | struct { 89 | const char *loading[8] = { "|", "/", "-", "\x60", "|", "/", "-", "\x60" }; // animationSize: 8 90 | const char *waiting[5] = { " ", ". ", ".. ", "... ", "...." }; // animationSize: 5 91 | const char *bounce[10] = { "= ", " = ", " = ", " = ", " = ", " =", " = ", " = ", " = ", " = " }; // animationSize: 10 92 | const char *progress[6] = { " ", "\xFF ", "\xFF\xFF ", "\xFF\xFF\xFF ", "\xFF\xFF\xFF\xFF ", "\xFF\xFF\xFF\xFF\xFF" }; // animationSize: 6 93 | const char *arrows[5] = { " ", "> ", ">> ", ">>> ", ">>>>" }; // animationSize: 5 94 | const char *bouncearrow[10] = { "> ", " > ", " > ", " > ", " >", " <", " < ", " < ", " < ", "< " }; // animationSize: 10 95 | } animations; 96 | 97 | /** 98 | * Print a char array aligned left, center or right to a fixed width. 99 | * @param align "left", "center" or "right" 100 | * @param str The char array to print 101 | * @param width The fixed width of the resulting char array, which str will be aligned to 102 | */ 103 | void alignedPrint(const char *align, const char *str, uint8_t width); 104 | 105 | /** 106 | * Prints a char array to the display with a limited length (UTF-8 aware) without making a copy. 107 | * @param str The char array to print 108 | * @param length The length to limit str to 109 | */ 110 | void limitedPrint(const char *str, uint8_t length); 111 | 112 | /** 113 | * Custom strlen function to correctly count chars that are two bytes long (like ä ö or ü) 114 | * @param str The char array to get the length of 115 | * @return Length of str 116 | */ 117 | size_t utf8_strlen(const char *str); 118 | 119 | /** 120 | * Converts num to char array and precedes it with zeroes to match length. 121 | * Make sure buf is at least len bytes long! 122 | * @param dest Destination char array 123 | * @param num The number to convert 124 | * @param len The length the resulting char array will have. Zeroes will be added infront of num until it matches this length 125 | */ 126 | char* toFixedLengthNumber(char *dest, int num, uint8_t len); 127 | 128 | private: 129 | 130 | uint8_t _lcdCols; 131 | uint8_t _lcdRows; 132 | 133 | }; 134 | 135 | 136 | // Include template implementation file 137 | #include "lcdHelper.tpp" 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /src/lcdHelper.tpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: lcdHelper.tpp 3 | * Project: arduino-lcdHelper-library 4 | * Created Date: 2022-08-28 22:55:04 5 | * Author: 3urobeat 6 | * 7 | * Last Modified: 2025-08-30 14:01:56 8 | * Modified By: 3urobeat 9 | * 10 | * Copyright (c) 2022 - 2025 3urobeat 11 | * 12 | * Licensed under the MIT license: https://opensource.org/licenses/MIT 13 | * Permission is granted to use, copy, modify, and redistribute the work. 14 | * Full license information available in the project LICENSE file. 15 | */ 16 | 17 | // Template implementation file 18 | // https://stackoverflow.com/a/495056 19 | 20 | 21 | template 22 | void lcdHelper::clearLine(uint8_t row) 23 | { 24 | // Print _lcdCols amount of spaces to effectively clear the line 25 | this->setCursor(0, row); 26 | 27 | for (uint8_t i = 0; i < this->_lcdCols; i++) { 28 | this->print(" "); 29 | } 30 | } 31 | 32 | template 33 | int lcdHelper::calculateCenterOffset(const char *str) 34 | { 35 | int offset = this->_lcdCols - this->utf8_strlen(str); 36 | if (offset < 0) offset = 0; // Set offset to 0 if it would be negative 37 | return offset / 2; 38 | } 39 | 40 | template 41 | void lcdHelper::centerPrint(const char *str, uint8_t row, bool callClearLine) 42 | { 43 | // clear the line first to avoid old characters corrupting the text when content is not the same 44 | if (callClearLine) this->clearLine(row); 45 | 46 | // Calculate column 47 | int offset = this->calculateCenterOffset(str); 48 | 49 | this->setCursor(offset, row); //center char array 50 | this->print(str); 51 | } 52 | 53 | template 54 | void lcdHelper::movingPrint(const char *str, uint8_t *moveOffset, uint8_t width) 55 | { 56 | // Check if we actually have to move something 57 | if (utf8_strlen(str) > width) { 58 | // Fix for UTF-8 chars: Increase moveOffset if a two byte long char will now leave the display to move it off completely and display the next char 59 | uint8_t char0Len = (*(str + *moveOffset) & 0xc0) != 0x80; 60 | if (char0Len == 0) (*moveOffset)++; 61 | 62 | // Reset if char array was fully displayed by checking if what is left over of the char array would not fill the whole width anymore 63 | if (utf8_strlen(str + *moveOffset) < width) *moveOffset = 0; 64 | 65 | // Print width amount of chars, starting from current moveOffset using our fancy limitedPrint function to correctly display two byte long chars (UTF-8) 66 | this->limitedPrint(str + *moveOffset, width); 67 | 68 | // Increase offset 69 | (*moveOffset)++; 70 | } else { 71 | this->print(str); 72 | } 73 | } 74 | 75 | template 76 | void lcdHelper::fadeInPrint(const char *str, uint8_t fadeInDelay, bool rightToLeft, uint8_t currentCol, uint8_t currentRow) 77 | { 78 | // Right to left? Cursor Management time. 79 | if (rightToLeft) 80 | { 81 | int length = this->utf8_strlen(str); 82 | 83 | for (uint8_t i = 0; i < length; i++) 84 | { 85 | this->print(*(str + length - i - 1)); 86 | 87 | // Check for col & row underflow 88 | if (currentCol == 0) 89 | { 90 | if (currentRow == 0) currentRow = this->_lcdRows; 91 | currentRow--; 92 | currentCol = this->_lcdCols; 93 | } 94 | 95 | currentCol--; 96 | this->setCursor(currentCol, currentRow); 97 | delay(fadeInDelay); 98 | } 99 | } 100 | else // Left to right? Chill time 101 | { 102 | // Print next char until reaching null byte 103 | while (*str) 104 | { 105 | this->print(*str++); 106 | delay(fadeInDelay); 107 | } 108 | } 109 | 110 | // TODO: Math function parameter to create dynamic fadeInDelay 111 | } 112 | 113 | template 114 | void lcdHelper::animationPrint(const char **animationArray, uint8_t animationSize, uint8_t *animationFrame, uint8_t col, uint8_t row) 115 | { 116 | // Print current frame and overwrite previous one 117 | this->setCursor(col, row); 118 | this->print(animationArray[*animationFrame]); 119 | 120 | // Increment index or reset if all frames were displayed 121 | (*animationFrame)++; 122 | 123 | if (*animationFrame > animationSize - 1) *animationFrame = 0; 124 | } 125 | 126 | template 127 | void lcdHelper::alignedPrint(const char *align, const char *str, uint8_t width) 128 | { 129 | // Workarounds to correctly display Umlaute 130 | size_t len = utf8_strlen(str); // length on the display 131 | size_t blen = strlen(str); // actual length in mem 132 | 133 | char temp[width + (blen - len) + 1] = ""; // blen - len to correct deviation caused by 2 byte long chars (Umlaute) 134 | 135 | // check if we even have to do something 136 | if (len == width) { 137 | this->print(str); 138 | return; 139 | } 140 | 141 | // check if char array is too long, cut it and display it as is 142 | if (len > width) { 143 | strncpy(temp, str, width); 144 | this->print(temp); 145 | 146 | } else { //if shorter, align text as requested 147 | 148 | // switch case doesn't work with char arrays so here we go 149 | if (strcmp(align, "left") == 0) { 150 | strcpy(temp, str); 151 | memset(temp + blen, ' ', width - len); // fill remaining space with spaces and keep existing null byte at the end 152 | 153 | } else if (strcmp(align, "center") == 0) { 154 | int offset = (width - len) / 2; // calculate offset to the left 155 | 156 | memset(temp, ' ', offset); // offset str with spaces 157 | strcat(temp, str); // put str into the middle 158 | memset(temp + offset + blen, ' ', width - offset - len); // fill remaining space with spaces 159 | 160 | } else if (strcmp(align, "right") == 0) { 161 | memset(temp, ' ', width - len); // offset char array 162 | strcpy(temp + width - len, str); 163 | } 164 | 165 | this->print(temp); 166 | } 167 | } 168 | 169 | template 170 | void lcdHelper::limitedPrint(const char *str, uint8_t length) 171 | { 172 | // Check if we actually have to do something 173 | if (this->utf8_strlen(str) > length) { 174 | uint8_t currentLen = 0; // UTF-8 175 | uint8_t index = 0; // Actual location in the char arr 176 | 177 | // Print all chars until utf8_strlen() == length is reached 178 | while (currentLen < length) { // Check above guarantees we can't exceed 179 | uint8_t thisCharLen = (*(str + index + 1) & 0xc0) != 0x80; // Count only one byte of a 2 byte long char (from utf8_strlen()) 180 | 181 | // If we've encountered a 2 byte long char, we need to copy this and the next byte and print as a whole 182 | if (thisCharLen == 0) { 183 | char temp[3] = ""; // Create 2 byte + null char long temp char arr // TODO: Can this be optimized to work without temp? 184 | 185 | strncpy(temp, str + index, 2); // Copy both bytes, then print 186 | this->print(temp); 187 | 188 | index += 2; // Count both bytes 189 | } else { 190 | this->print(*(str + index)); // Simply print this char 191 | 192 | index++; // Count this one char 193 | } 194 | 195 | currentLen++; 196 | } 197 | } else { 198 | this->print(str); // Print as is if str is shorter than length 199 | } 200 | } 201 | 202 | template 203 | size_t lcdHelper::utf8_strlen(const char *str) 204 | { 205 | size_t len = 0; 206 | while (*str) len += (*str++ & 0xc0) != 0x80; 207 | return len; 208 | } 209 | 210 | template 211 | char* lcdHelper::toFixedLengthNumber(char *dest, int num, uint8_t len) 212 | { 213 | char numStr[len + 1] = ""; // no real way to check how long num will be so let's just use len as it will always be >= 214 | 215 | itoa(num, numStr, 10); // convert int and save into numStr 216 | 217 | memset(dest, '0', len - strlen(numStr)); // set all chars except the ones used by numStr to 0 (not \0 kek) 218 | strcpy(dest + (len - strlen(numStr)), numStr); // finally add the number itself to the end (use strcpy instead of strcat to make sure numStr ends up at the correct place) 219 | 220 | return dest; // return pointer to dest again to make using the function inside another call easier 221 | } 222 | --------------------------------------------------------------------------------