1602 LCD/Making it useful/09. Numbers that change
Making it useful · 09 of 11

Numbers that change

Print 100, then print 99, and the screen says 990. A cell holds what was last written to it, and a shorter number does not erase a longer one — which is the first bug everybody writes on one of these.

What a print actually does

It writes characters into as many cells as the string is long, starting where the cursor is. That is all. There is no background, no erase and no concept of a field — the cell beyond the end of your string keeps whatever was last put there, for as long as the display has power.

What is left behind when 100 becomes 99
not started
Count
Top row shows
Cells a print touches
A print overwrites what it covers, and nothing else. Two characters written over three leaves the third one lit, and the display has no way to know it is now part of a different number. There is no erase on a 1602, and the fix is not to clear the screen — 32 cells is a visible repaint. Pad the number to a fixed width so it always covers its own old digits, with snprintf(buf, sizeof buf, "%3d", n) or by printing a trailing space yourself.

Count down past a hundred and the third cell still holds the 0 from 100. The screen says 990, which is neither the old number nor the new one, and it will keep saying it until something writes over that cell.

The fix is a fixed width

Make every update the same length, so it always covers its own leftovers:

char line[17];
snprintf(line, sizeof(line), "FIXED  %3d", count);
lcd.setCursor(0, 1);
lcd.print(line);

%3d is always three characters — 105, 99, 9 — so the digits that shrink are replaced by spaces rather than left behind. Printing a couple of trailing spaces after the number does the same job with less ceremony:

lcd.print(count);
lcd.print("  ");

Not clear()

clear() would fix it too, and it is the wrong tool. It rewrites all eighty cells and blocks for about 1.5 ms on top of that, to change two characters — roughly forty milliseconds of work per update, which is the flicker people blame on the display itself.

Clear when the layout changes. Overwrite when the value changes.

And not delay()

delay(400) in the loop means four hundred milliseconds where the sketch does nothing at all — no button, no sensor, no serial. Comparing millis() costs nothing and leaves the rest of the loop running.

The code

counter_1602.ino

The same countdown printed twice. The top row prints the number as it comes; the bottom row pads it to three characters first. Watch the two rows as the count crosses 100.

// Wiring (Arduino Uno):
//
//   LCD GND -> GND
//   LCD VCC -> 5V
//   LCD SDA -> A4
//   LCD SCK -> A5
//
// Arduino IDE: Tools > Board "Arduino Uno", and
//   Sketch > Include Library > Manage Libraries... > "LiquidCrystal I2C"
//   by Frank de Brabander.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

int count = 105;
unsigned long last = 0;
char line[17];              // 16 cells plus the terminator

void setup() {
  lcd.init();
  lcd.backlight();
}

void loop() {
  // millis() rather than delay(), so the loop stays free to do other work.
  if (millis() - last < 400) return;
  last = millis();

  // The bug: two digits written over three leave the third behind.
  lcd.setCursor(0, 0);
  lcd.print("RAW    ");
  lcd.print(count);

  // The fix: %3d is always three characters, so it covers its own leftovers.
  snprintf(line, sizeof(line), "FIXED  %3d", count);
  lcd.setCursor(0, 1);
  lcd.print(line);

  if (--count < 0) {
    count = 105;
    lcd.clear();            // once per cycle, not once per update
  }
}

The fix is the padding, not clear(). Calling clear() every update would also work and would repaint all eighty cells to change two of them — about 40 ms of blocking I²C, once every update, which is the flicker people blame on the display.

When it does not work

A digit from the old number is stuck on screen

That is the whole of this page. Pad the number to a fixed width — snprintf with %3d, or print trailing spaces yourself — so that every update covers exactly the cells the last one lit.

The whole screen flickers on every update

You are calling clear() each time round. It rewrites all eighty cells and blocks for about 1.5 ms on top of the writes, to change two characters. Overwrite the cells you need instead and leave the rest alone.

The number updates but the rest of the sketch has gone sluggish

I²C is blocking, and a redraw of a full row is roughly 20 ms of it. If the loop also reads a sensor or watches a button, update the screen on a millis() interval rather than every pass.

Negative numbers leave a stray minus sign

Same cause, one character further left. %3d does not reserve room for the sign; use %4d if the value can go negative, and check the total still fits in sixteen columns.

snprintf prints a question mark or nothing on an ESP32

Check the buffer is big enough for the string plus its terminator — seventeen bytes for a full row. snprintf truncates rather than overrunning, so a short buffer shows up as a missing tail rather than a crash.

Where this goes next

Five by eight dots, eight slots, and the progress bar they buy you.

Eight characters of your own

Edit this page — content/books/lcd1602/numbers-that-change.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

3-Pack 1602 LCD Display Module, I2C 16x2 Blue

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum