Hi,
I have an I2C LCD in a project. For now, I use the cursor controls to insert text into the display. It works OK.
What I would like to do is to have an 80-char buffer which is sent in a block to the LCD, either every 50ms, or when there's a change.
I have tried memmove to move a string into the buffer, but the buffer stays blank. The copy to serial monitor is also blank.
TIA for any help.
Dick-san
p o s t y o u r c o d e
-
The LCD only needs to be updated about once per second, your brain/eyes are slow.
-
In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.
Use the < CODE / > icon from the ‘posting menu’ to attach the copied sketch. -
The process of updating the LCD is blocking too.
You only need to update the LCD when the characters being displayed change. As long as the LCD has power, it will display whatever was last sent to it.
Using a buffer, and sending when there is a change, would likely take longer than positioning the cursor and inserting text as needed, unless you want to make a lot of little changes that cover a large percentage of the display, and only write the buffer out to the LCD with the combined changes. Certainly sending the full 80 byte buffer for every small change will be highly inefficient.
/*
241124 redoing LCD screens (block write)
*/
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20 column and 4 rows
#include <EEPROM.h>
#include <SPI.h> //SD functions for auto mode
int C, c, x, y, z, st, target;
unsigned char lcdX, lcdY, digit, SD_char;
char lcd_text[81];
char temp_local[80];
void blinkBL(char blinx) {
for (c = 0; c < blinx; c++) {
lcd.noBacklight();
delay(300);
lcd.backlight();
delay(300);
}
}
void show_lcd(void) { //lcd_text is logical (4rows, chars 0-79)
lcd.setCursor(0, 0); // move cursor to row0
for (c = 0; c < 20; c++) lcd.write(lcd_text[c]); //row0
lcd.setCursor(0, 2); // move cursor to row1
for (c = 40; c < 60; c++) lcd.write(lcd_text[c]); //row1
lcd.setCursor(0, 1); // move cursor to row2
for (c = 20; c < 40; c++) lcd.write(lcd_text[c]); //row2
lcd.setCursor(0, 3); // move cursor to row3
for (c = 60; c < 80; c++) lcd.write(lcd_text[c]); //row3
}
void setup() {
Serial.begin(9600);
lcd_text[80] = "\0";
Serial.println(F("241124 re-doing screens (block write)"));
lcd.init(); // initialize the lcd
lcd.backlight();
blinkBL(1);
for (c = 0; c < 80; c++) lcd_text[c] = ' '; //clear buffer
lcd.clear(); // Clear the screen
delay(1000);
temp_local[19] = "241124 XYZ, screens"; // print message at the first row
memmove(lcd_text[0], temp_local[0], strlen(temp_local));
Serial.println(lcd_text);
show_lcd();
Serial.println(strlen(lcd_text));
delay(2000);
temp_local[17] = "check if Sim mode";
memmove(lcd_text[20], temp_local[0], strlen(temp_local));
Serial.println(temp_local);
show_lcd();
Serial.println(lcd_text);
Serial.println(strlen(lcd_text));
delay(5000);
}
void loop() {
}
Obviously, this is not the full sketch. The other 2k lines are not relevant
Dick-san
P.S This would be a piece of cake in assembler!
-
What if these change every 1ms ?
-
Your brain is the decider . . .
Once per second if(age >= 70)
Once every 500ms if (age <= 50)
Well, here's an idea. Write a test code that simply exercises your buffer and transfers it's content to the LCD. When you have that working, then peek into your 'real' code, and see why it doesn't do what your test code does.
If it would be a piece of cake in assembler, it's a piece of cake in C too, if you use the right techniques. Your test code would illuminate for us, or you, exactly what it is you're misusing.
lcd_text[80] = "\0";
temp_local[19] = "241124 XYZ, screens"; // print message at the first row
You can't set a single character to a string of text.
memmove(lcd_text[0], temp_local[0], strlen(temp_local));
Either put an ampersand & in front of each array element, or drop the brackets, you want the entire array, not one element of the array.
strlen(temp_local) will be zero because the array is initialized to all zeros, and you have not put any characters into the first 19 elements of the array.
Display "pages" of full text.
- Put your text in an array.
- Position zero is the LEFT of the display
- Display (display width) number characters
- If position plus display width is greater than size of array, exit (to get another page of text)
- Add 1 to Position.
- go to 3
This code scrolls the text on an LCD starting with a blank display, scrolls the text, then ends with a blank screen (note: "blank screen" = "<-- 16 blank spaces -->"
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
const char msg[] = "<- 16 spaces ->Never gonna give you up!<- 16 spaces ->"; // 16 blank spaces to scroll the text off the line
const int screenWidth = 16, msgSize = sizeof(msg); // chagracters in array
char page[screenWidth]; // a row of characters for the LCD
void setup() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
}
void loop() {
for (int i = 0; i < msgSize - screenWidth; i++) { // stop one "row" before end of message
for (int j = 0; j < screenWidth; j++) { // 16 characters
page[j] = msg[i + j]; // create a new page (LCD line) to print
}
lcd.setCursor(0, 0); // cursor home
lcd.print(page); // print new page
delay(100);
}
}
diagram.json for wokwi.com
{
"version": 1,
"author": "Anonymous maker",
"editor": "wokwi",
"parts": [
{
"type": "wokwi-arduino-nano",
"id": "nano",
"top": -89.2,
"left": -94.7,
"rotate": 270,
"attrs": {}
},
{
"type": "wokwi-lcd1602",
"id": "lcd1",
"top": -147.2,
"left": 63.2,
"attrs": { "pins": "i2c" }
}
],
"connections": [
[ "lcd1:GND", "nano:GND.1", "black", [ "h0" ] ],
[ "lcd1:VCC", "nano:5V", "red", [ "h-38.4", "v9.7" ] ],
[ "lcd1:SDA", "nano:A4", "green", [ "h-28.8", "v19.4" ] ],
[ "lcd1:SCL", "nano:A5", "green", [ "h-19.2", "v9.9" ] ]
],
"dependencies": {}
}
Thanks, guys! You've given me ideas to work on.
Luckily, I have a day off tomorrow: no-one wants me to fix something,
so I can do this.
I'm amused by the 1s refresh rate! Not fast enough for me, despite my advanced age!
Dick-san
How so? What do you think you can do with assembly language that can't be done or would be so much harder in C/C++?
Just curious. If this was 1974 I might agree.
Unless you are saying you don't know C very well.
a7
Hi,
Set the destination, source & a count
LXI H,DEST
LXI D,SRC
MVI C,15
- a couple of lines for the loop.
I've been working with C for about 20 years, but never had to mess with inserting strings into other strings.
It was all about getting the hardware to do its thing.
Never too old to learn!
Assembler? That was 1976 & on
Regards,
Dick-san
Hi,
My LCD is 4x20, & if you just send it 80 characters, it will fill 0-19, 40-59, 20-39 & 60 to 79 (rows 0, 2, 1 & 3)..
My show_lcd() procedure handles that.
I just want to have a static display, where I can insert text & numbers from time to time, then refresh.
For the moment, it works OK, setting the cursor position every time, but I was curious to try another way.
I know! If it ain't broke, don't fix it!
Regards,
Dick-san
For scrolling... You need to send 20 starting at 0, then 20 starting at 1, then 20 starting at 2... 20 starting at 61. Then, either stop, or send 19 more blanks.
For static, send 20 starting at 0 to row 0. Then send 20 starting at 20, to row 1, then row 2 and row 3.
I remember the odd addressing of the 4 row LCD. and I get wanting to have an 80 character frame you just write to and hit refresh.
Here it is just a waste of memory, and by refreshing 80 each time a waste of time. You may have plenty of both.
You mean these?
temp_local[19] = "241124 XYZ, screens"; // print message at the first row
memmove(lcd_text[0], temp_local[0], strlen(temp_local));
temp_local[17] = "check if Sim mode";
memmove(lcd_text[20], temp_local[0], strlen(temp_local));
OIC @david_2018 has aready pointed you at those.
a7
I don't really see the need for the double buffer (lcd_text and temp_local), just write your text directly to lcd_text.
strlen(temp_local) is going to return a length of zero until you overwrite the default zero value in the first element of the array, so nothing will get copied.
This is likely to write past the end of the lcd_text array, although the way it is written the compiler just throws out a bunch of warnings.
As for the line wrap order for a 20 x 4 LCD display, there are libraries such as hd44780 that will handle the line wrap for you, so you can simply print an 80-character line of text and it will display in the correct order.
-
How fast is reasonable for you
?
-
To fill a 20X4 LCD takes 46.5ms (Arduino UNO) of sketch blocking time.
PULSE62D12;
lcd.setCursor(0, 0);
// 1111111111
// 01234567890123456789
lcd.print("ABCDEFGHIJKLMNOPQRST");
lcd.setCursor(0, 1);
// 1111111111
// 01234567890123456789
lcd.print("UVWXYZ0123456789****");
lcd.setCursor(0, 2);
// 1111111111
// 01234567890123456789
lcd.print("abcdefghijklmnopqrst");
lcd.setCursor(0, 3);
// 1111111111
// 01234567890123456789
lcd.print("uvwxyz0123456789****");
PULSE62D12;
I don't think that does what you think it does!
You can't assign string literals to C-style strings. This will do something weird like setting the 19th character in temp_local
to the low byte of the pointer to the string literal. Use strcpy() or memmove() to put the string into there as well.
Hi,
Thanks for your input.
I understand that my code is a mess (often the case, until it works!).
All I would like to do, based on an input (1 or 0), is put the words "Active" or "Inactive" starting at position 30 in the 80-char array (without the quotes). This will put the text half-way along the 2nd line of the display. My procedure show_led() handles the interlaced lines.
Once that works, I will understand the idea, & the rest should follow.