├── .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 | ### void centerPrint(const char *str, uint8_t row, bool callClearLine = false) 59 | - `str` - The char array to print 60 | - `row` - The row to print the char array in 61 | - `callClearLine` - Optional: Set to true if line should be cleared before printing 62 | 63 | Print a char array centered in a row on your display. 64 | 65 | ### void movingPrint(const char *str, uint8_t *moveOffset, uint8_t width) 66 | - `str` - The char array to print 67 | - `moveOffset` - Pointer to int tracking offset 68 | - `width` - Width of the space on screen the char array will be moved across 69 | 70 | Prints a char array that will be moved across a row by one char each time the function is called. 71 | 72 | Example: 73 | ``` 74 | uint8_t moveOffset = 0; // Define a var tracking the current offset somewhere at the top scope 75 | 76 | // Call movingPrint() each time you want to move your char array, for example in your arduino loop 77 | void loop() 78 | { 79 | // Message shall appear on row 2 80 | lcd.setCursor(0, 1); 81 | 82 | // Pass char array, pointer to moveOffset and the width to move across 83 | lcd.movingPrint("This is a test message which will move across the screen", *moveOffset, 20); 84 | 85 | // Example delay so you can actually read what is happening on your display 86 | delay(250); 87 | } 88 | ``` 89 | 90 | ### void animationPrint(const char **animationArray, uint8_t animationSize, uint8_t *animationFrame, uint8_t col, uint8_t row) 91 | - `animationArray` - Pointer to an array containing char arrays for each animation frame 92 | - `animationSize` - Amount of frames your animation has, counting from 1 93 | - `animationFrame` - Pointer to int tracking animation progress 94 | - `col` - The column to print the animation at 95 | - `row` - The row to print the animation in 96 | 97 | Print an animation frame by frame each time the function is called. 98 | I provided a few default animations (loading, waiting, bounce, progress, arrows, bouncearrow), accessible like so: 99 | ``` 100 | uint8_t animationFrame = 0; // Define a var tracking the current frame somewhere at the top scope 101 | 102 | void loop() 103 | { 104 | // Print loading animation in row 2 at column 10 105 | lcd.animationPrint(lcd.animations.loading, 8, &animationFrame, 10, 1); 106 | 107 | // Example delay 108 | delay(250); 109 | } 110 | ``` 111 | The size of each default animation is written as a comment behind each definition, your IntelliSense should display it for you. 112 | 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. 113 | 114 | ### void alignedPrint(const char *align, const char *str, uint8_t width) 115 | - `align` - "left", "center" or "right" 116 | - `str` - The char array to print 117 | - `width` - The fixed width of the resulting char array, which str will be aligned to 118 | 119 | Print a char array aligned left, center or right to a fixed width. 120 | 121 | ### void limitedPrint(const char *str, uint8_t length) 122 | - `str` - The char array to print 123 | - `length` - The length to limit str to 124 | 125 | Prints a char array to the display with a limited length (UTF-8 aware) without making a copy. 126 | 127 | ### size_t utf8_strlen(const char *str) 128 | - `str` - The char array to get the length of 129 | 130 | Returns the length of str. 131 | Custom strlen function to correctly count chars that are two bytes long (like ä ö or ü). 132 | 133 | ### char* toFixedLengthNumber(char *dest, int num, uint8_t len) 134 | - `dest` - Pointer to destination char array 135 | - `num` - The number to convert 136 | - `len` - The length the resulting char array will have. Zeroes will be added infront of num until it matches this length 137 | 138 | Returns pointer to `dest` so that you can use it directly inside another function. 139 | Converts num to char array and precedes it with zeroes to match length. 140 | Make sure buf is at least len bytes long! 141 | 142 | Example, showing uptime in minutes, with values <10 having a preceding 0: 143 | ``` 144 | unsigned long uptime = millis() / 1000; // Current uptime in seconds 145 | char temp[3] = ""; // Buffer char array 146 | 147 | // Uptime in minutes, 5 will be written as 05 into temp to match len 2 148 | lcd.toFixedLengthNumber(temp, (uptime % 3600) / 60, 2); 149 | lcd.print(temp); // Print "05" to display 150 | 151 | // ...or make use of return to print directly 152 | lcd.print(lcd.toFixedLengthNumber(temp, (uptime % 3600) / 60, 2)); 153 | ``` -------------------------------------------------------------------------------- /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/8651ef3d21baaf50fd28ee025e06fe5d83d50759/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: 22.11.2022 16:50:28 5 | * Author: 3urobeat 6 | * 7 | * Last Modified: 30.06.2023 10:01:00 8 | * Modified By: 3urobeat 9 | * 10 | * Copyright (c) 2022 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.1.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 | centerPrint() 56 | 57 | Print a centered Hello! which will stay for the whole runtime 58 | */ 59 | 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 60 | 61 | 62 | /* 63 | clearLine() 64 | 65 | Print something in line 2 and clear line after 2.5 seconds 66 | */ 67 | lcd.centerPrint("lcdHelper Example", 2); 68 | delay(2500); 69 | lcd.clearLine(2); // This will remove "lcdHelper Example" but keep "Hello!" as we only clear one row 70 | 71 | 72 | // End of "startup welcome screen" 73 | 74 | 75 | /* 76 | utf8_strlen() 77 | 78 | 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 79 | Print strlen() result infront to see the difference 80 | 81 | You can also see some basic examples on how to work with char arrays here 82 | */ 83 | const char utf8Message[] = "Here are UTF-8 chars: ä ö ü ß ° $"; // 39 bytes + 1 for null byte 84 | char tempLen[21] = "UTF8: "; // Temp char array to construct message showing length 85 | 86 | char tempUTF8[3] = ""; // Temp char arrs to convert ints returned by strlen() to char arrs 87 | char tempByte[3] = ""; 88 | 89 | itoa(lcd.utf8_strlen(utf8Message), tempUTF8, 10); // Get result from lcdHelper's utf8_strlen() and onvert int to char arr 90 | itoa(strlen(utf8Message), tempByte, 10); // Get result from C's string.h strlen() and onvert int to char arr 91 | 92 | strncat(tempLen, tempUTF8, 2); // Construct tempLen message which will be printed by adding the 2 results to the end of tempLen 93 | strcat(tempLen, " Normal: "); // No need for strncat when we cat a fixed length char arr 94 | strncat(tempLen, tempByte, 2); 95 | 96 | lcd.setCursor(0, 1); 97 | lcd.print(tempLen); // Print it! 98 | 99 | 100 | /* 101 | limitedPrint() 102 | 103 | Print a cut off message without needing to do modify char array. 104 | 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. 105 | This is why I'm using NoiascaLiquidCrystal: https://werner.rothschopf.net/202009_arduino_liquid_crystal_intro.htm 106 | */ 107 | lcd.setCursor(0, 2); 108 | lcd.limitedPrint("Cut off msg - Secret: You won't see this", 10); 109 | } 110 | 111 | 112 | // Now print stuff that should update every 250ms 113 | void loop() 114 | { 115 | /* 116 | animationPrint() 117 | 118 | Print a loading and progress animation in the first row beside the centered "Hello!" 119 | */ 120 | lcd.animationPrint(lcd.animations.loading, 8, &animFrameTracker1, 0, 0); 121 | lcd.animationPrint(lcd.animations.progress, 6, &animFrameTracker2, 15, 0); // progress is 5 chars wide 122 | 123 | 124 | /* 125 | alignedPrint() 126 | 127 | Print some moving dots aligned to the right of row 2 128 | */ 129 | dots++; // Add another dot 130 | if (dots > 4) dots = 0; // Reset when 4 dots are present 131 | 132 | char tempDots[5] = ""; // Temp char arr to construct dots 133 | for (uint8_t i = 0; i < dots; i++) strcat(tempDots, "."); // Add dots amount of dots to temp 134 | 135 | lcd.setCursor(11, 2); 136 | lcd.print("Dots:"); // Show something infront to showcase that temp is aligned to the right 137 | lcd.alignedPrint("right", tempDots, 4); // Print temp and overwrite any previous dots 138 | 139 | 140 | /* 141 | movingPrint() 142 | 143 | Let a long message run across 2/3 of the last row 144 | */ 145 | lcd.setCursor(0, 3); 146 | lcd.movingPrint(" This is a long message moving every 500ms ", &moveOffset, 14); // Pass address of moveOffset and limit to a width of 14 chars 147 | 148 | 149 | /* 150 | toFixedLengthNumber() 151 | 152 | Show seconds of uptime with fixed width at the end of the last row 153 | */ 154 | unsigned long uptime = millis() / 1000; // Current uptime in seconds 155 | char tempSeconds[3] = ""; // Buffer char array with space for two digits and null byte 156 | 157 | lcd.toFixedLengthNumber(tempSeconds, uptime % 60, 2); // Add preceding 0 to seconds if <10 158 | 159 | lcd.setCursor(18, 3); 160 | lcd.print(tempSeconds); // Print using the normal print function inherited from LiquidCrystal 161 | 162 | 163 | 164 | // Delay next iteration 165 | delay(500); 166 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=arduino-lcdHelper-library 2 | version=1.1.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: 26.08.2022 12:04:51 5 | * Author: 3urobeat 6 | * 7 | * Last Modified: 30.06.2023 10:01:00 8 | * Modified By: 3urobeat 9 | * 10 | * Copyright (c) 2022 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.1.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 | * Print a char array centered in a row on your display. 46 | * @param str The char array to print 47 | * @param row The row to print the char array in 48 | * @param callClearLine Optional: Set to true if line should be cleared before printing 49 | */ 50 | void centerPrint(const char *str, uint8_t row, bool callClearLine = false); 51 | 52 | /** 53 | * Prints a char array that will be moved across a row by one char each time the function is called 54 | * @param str The char array to print 55 | * @param moveOffset Pointer to int tracking offset 56 | * @param width Width of the space on screen the char array will be moved across 57 | */ 58 | void movingPrint(const char *str, uint8_t *moveOffset, uint8_t width); 59 | 60 | /** 61 | * Print an animation frame by frame each time the function is called 62 | * @param animationArray Pointer to an array containing char arrays for each animation frame 63 | * @param animationSize Amount of frames your animation has, counting from 1 64 | * @param animationFrame Pointer to int tracking animation progress 65 | * @param col The column to print the animation at 66 | * @param row The row to print the animation in 67 | */ 68 | void animationPrint(const char **animationArray, uint8_t animationSize, uint8_t *animationFrame, uint8_t col, uint8_t row); 69 | 70 | // Provide a few default animation (see chars that are referenced as hex here: https://werner.rothschopf.net/2020/lcd_charset_a00.gif) 71 | struct { 72 | const char *loading[8] = { "|", "/", "-", "\x60", "|", "/", "-", "\x60" }; // animationSize: 8 73 | const char *waiting[5] = { " ", ". ", ".. ", "... ", "...." }; // animationSize: 5 74 | const char *bounce[10] = { "= ", " = ", " = ", " = ", " = ", " =", " = ", " = ", " = ", " = " }; // animationSize: 10 75 | const char *progress[6] = { " ", "\xFF ", "\xFF\xFF ", "\xFF\xFF\xFF ", "\xFF\xFF\xFF\xFF ", "\xFF\xFF\xFF\xFF\xFF" }; // animationSize: 6 76 | const char *arrows[5] = { " ", "> ", ">> ", ">>> ", ">>>>" }; // animationSize: 5 77 | const char *bouncearrow[10] = { "> ", " > ", " > ", " > ", " >", " <", " < ", " < ", " < ", "< " }; // animationSize: 10 78 | } animations; 79 | 80 | /** 81 | * Print a char array aligned left, center or right to a fixed width. 82 | * @param align "left", "center" or "right" 83 | * @param str The char array to print 84 | * @param width The fixed width of the resulting char array, which str will be aligned to 85 | */ 86 | void alignedPrint(const char *align, const char *str, uint8_t width); 87 | 88 | /** 89 | * Prints a char array to the display with a limited length (UTF-8 aware) without making a copy. 90 | * @param str The char array to print 91 | * @param length The length to limit str to 92 | */ 93 | void limitedPrint(const char *str, uint8_t length); 94 | 95 | /** 96 | * Custom strlen function to correctly count chars that are two bytes long (like ä ö or ü) 97 | * @param str The char array to get the length of 98 | * @return Length of str 99 | */ 100 | size_t utf8_strlen(const char *str); 101 | 102 | /** 103 | * Converts num to char array and precedes it with zeroes to match length. 104 | * Make sure buf is at least len bytes long! 105 | * @param dest Destination char array 106 | * @param num The number to convert 107 | * @param len The length the resulting char array will have. Zeroes will be added infront of num until it matches this length 108 | */ 109 | char* toFixedLengthNumber(char *dest, int num, uint8_t len); 110 | 111 | private: 112 | 113 | uint8_t _lcdCols; 114 | uint8_t _lcdRows; 115 | 116 | }; 117 | 118 | 119 | // Include template implementation file 120 | #include "lcdHelper.tpp" 121 | 122 | #endif -------------------------------------------------------------------------------- /src/lcdHelper.tpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: lcdHelper.tpp 3 | * Project: arduino-lcdHelper-library 4 | * Created Date: 28.08.2022 22:55:04 5 | * Author: 3urobeat 6 | * 7 | * Last Modified: 30.06.2023 10:01:00 8 | * Modified By: 3urobeat 9 | * 10 | * Copyright (c) 2022 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 | void lcdHelper::centerPrint(const char *str, uint8_t row, bool callClearLine) 34 | { 35 | // clear the line first to avoid old characters corrupting the text when content is not the same 36 | if (callClearLine) this->clearLine(row); 37 | 38 | // Calculate column 39 | int offset = this->_lcdCols - this->utf8_strlen(str); 40 | if (offset < 0) offset = 0; //set offset to 0 if it would be negative 41 | 42 | this->setCursor(offset / 2, row); //center char array 43 | this->print(str); 44 | } 45 | 46 | template 47 | void lcdHelper::movingPrint(const char *str, uint8_t *moveOffset, uint8_t width) 48 | { 49 | // Check if we actually have to move something 50 | if (utf8_strlen(str) > width) { 51 | // 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 52 | uint8_t char0Len = (*(str + *moveOffset) & 0xc0) != 0x80; 53 | if (char0Len == 0) (*moveOffset)++; 54 | 55 | // 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 56 | if (utf8_strlen(str + *moveOffset) < width) *moveOffset = 0; 57 | 58 | // Print width amount of chars, starting from current moveOffset using our fancy limitedPrint function to correctly display two byte long chars (UTF-8) 59 | this->limitedPrint(str + *moveOffset, width); 60 | 61 | // Increase offset 62 | (*moveOffset)++; 63 | } else { 64 | this->print(str); 65 | } 66 | } 67 | 68 | template 69 | void lcdHelper::animationPrint(const char **animationArray, uint8_t animationSize, uint8_t *animationFrame, uint8_t col, uint8_t row) 70 | { 71 | // Print current frame and overwrite previous one 72 | this->setCursor(col, row); 73 | this->print(animationArray[*animationFrame]); 74 | 75 | // Increment index or reset if all frames were displayed 76 | (*animationFrame)++; 77 | 78 | if (*animationFrame > animationSize - 1) *animationFrame = 0; 79 | } 80 | 81 | template 82 | void lcdHelper::alignedPrint(const char *align, const char *str, uint8_t width) 83 | { 84 | // Workarounds to correctly display Umlaute 85 | size_t len = utf8_strlen(str); // length on the display 86 | size_t blen = strlen(str); // actual length in mem 87 | 88 | char temp[width + (blen - len) + 1] = ""; // blen - len to correct deviation caused by 2 byte long chars (Umlaute) 89 | 90 | // check if we even have to do something 91 | if (len == width) { 92 | this->print(str); 93 | return; 94 | } 95 | 96 | // check if char array is too long, cut it and display it as is 97 | if (len > width) { 98 | strncpy(temp, str, width); 99 | this->print(temp); 100 | 101 | } else { //if shorter, align text as requested 102 | 103 | // switch case doesn't work with char arrays so here we go 104 | if (strcmp(align, "left") == 0) { 105 | strcpy(temp, str); 106 | memset(temp + blen, ' ', width - len); // fill remaining space with spaces and keep existing null byte at the end 107 | 108 | } else if (strcmp(align, "center") == 0) { 109 | int offset = (width - len) / 2; // calculate offset to the left 110 | 111 | memset(temp, ' ', offset); // offset str with spaces 112 | strcat(temp, str); // put str into the middle 113 | memset(temp + offset + blen, ' ', width - offset - len); // fill remaining space with spaces 114 | 115 | } else if (strcmp(align, "right") == 0) { 116 | memset(temp, ' ', width - len); // offset char array 117 | strcpy(temp + width - len, str); 118 | } 119 | 120 | this->print(temp); 121 | } 122 | } 123 | 124 | template 125 | void lcdHelper::limitedPrint(const char *str, uint8_t length) 126 | { 127 | // Check if we actually have to do something 128 | if (this->utf8_strlen(str) > length) { 129 | uint8_t currentLen = 0; // UTF-8 130 | uint8_t index = 0; // Actual location in the char arr 131 | 132 | // Print all chars until utf8_strlen() == length is reached 133 | while (currentLen < length) { // Check above guarantees we can't exceed 134 | uint8_t thisCharLen = (*(str + index + 1) & 0xc0) != 0x80; // Count only one byte of a 2 byte long char (from utf8_strlen()) 135 | 136 | // If we've encountered a 2 byte long char, we need to copy this and the next byte and print as a whole 137 | if (thisCharLen == 0) { 138 | char temp[3] = ""; // Create 2 byte + null char long temp char arr // TODO: Can this be optimized to work without temp? 139 | 140 | strncpy(temp, str + index, 2); // Copy both bytes, then print 141 | this->print(temp); 142 | 143 | index += 2; // Count both bytes 144 | } else { 145 | this->print(*(str + index)); // Simply print this char 146 | 147 | index++; // Count this one char 148 | } 149 | 150 | currentLen++; 151 | } 152 | } else { 153 | this->print(str); // Print as is if str is shorter than length 154 | } 155 | } 156 | 157 | template 158 | size_t lcdHelper::utf8_strlen(const char *str) 159 | { 160 | size_t len = 0; 161 | while (*str) len += (*str++ & 0xc0) != 0x80; 162 | return len; 163 | } 164 | 165 | template 166 | char* lcdHelper::toFixedLengthNumber(char *dest, int num, uint8_t len) 167 | { 168 | 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 >= 169 | 170 | itoa(num, numStr, 10); // convert int and save into numStr 171 | 172 | memset(dest, '0', len - strlen(numStr)); // set all chars except the ones used by numStr to 0 (not \0 kek) 173 | 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) 174 | 175 | return dest; // return pointer to dest again to make using the function inside another call easier 176 | } --------------------------------------------------------------------------------