3 & 4-line Big Font Numerals
As an extension to the large numeral fonts for 4-line LCD displays, I have added a set of 3-line LCD numerals that can be combined at will. Both the 4-line and 3-line numbers 0 to 9 use combinations of the the same set of 8 glyphs. The 3- and 4-line height font design make the numerals both easy to differentiate and easy to read at a distance and ideal for displays that benefit from highlighting some numbers.
To illustrate the combination of 3-line and 4-line high fonts, large numbers are used to display the time from a software-derived clock. The hours and minutes are displayed in 3-line height numbers while the seconds are displayed using 4-line height numerals.

The software is written so that the custom character glyphs and the numeral character arrays are stored entirely in program memory so do not take up valuable SRAM. Compared to the code to display 4-line high numerals, this combination code takes up no additional SRAM yet provides the user more flexibility.
[codesyntax lang=”php” title=”3-line and 4-line high numerals” blockstate=”collapsed”]
//**************************************************************************************************
// 3-line and 4-line LCD NUMBERS
// Adrian Jones, March 2015
//
//**************************************************************************************************
// Build 1
// r1 150306 - initial build
//********************************************************************************************
#define build 1
#define revision 1
//********************************************************************************************
#include <avr/pgmspace.h> // for memory storage in program space
#include <Wire.h>
#include <LCD.h> // Standard lcd library
#include <LiquidCrystal_I2C.h> // library for I@C interface
#define I2C_ADDR 0x27 // address found from I2C scanner
#define RS_pin 0 // pin configuration for LCM1602 interface module
#define RW_pin 1
#define EN_pin 2
#define BACK_pin 3
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(I2C_ADDR, EN_pin, RW_pin, RS_pin, D4_pin, D5_pin, D6_pin, D7_pin, BACK_pin, POSITIVE);
//Pins for the LCD are SCL A5, SDA A4
const char custom[][8] PROGMEM = {
{0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}, // char 1: bottom right triangle
{0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F}, // char 2: bottom block
{0x10, 0x1C, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}, // char 3: bottom left triangle
{0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00}, // char 4: top right triangle
{0x1F, 0x1E, 0x1C, 0x10, 0x00, 0x00, 0x00, 0x00}, // char 5: top left triangle
{0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00}, // char 6: upper block
{0x1F, 0x1F, 0x1E, 0x1C, 0x18, 0x18, 0x10, 0x10}, // char 7: full top right triangle
{0x01, 0x07, 0x0F, 0x1F, 0x00, 0x00, 0x00, 0x00} // char 8: top right triangle
};
const char bn3[][30] PROGMEM = { // 3-line numbers
// 0 1 2 3 4 5 6 7 8 9
{0x01,0x06,0x03, 0x08,0xFF,0xFE, 0x08,0x06,0x03, 0x08,0x06,0x03, 0xFF,0xFE,0xFF, 0xFF,0x06,0x06, 0x01,0x06,0xFE, 0x06,0x06,0xFF, 0x01,0x06,0x03, 0x01,0x06,0x03},
{0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0x01,0x06,0x06, 0xFE,0x06,0xFF, 0x06,0x06,0xFF, 0x06,0x06,0xFF, 0xFF,0x06,0x03, 0xFE,0x01,0x07, 0xFF,0x06,0xFF, 0x04,0x06,0xFF},
{0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x06,0x06,0x06, 0x04,0x06,0x05, 0xFE,0xFE,0x06, 0x04,0x06,0x05, 0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x04,0x06,0x05, 0xFE,0xFE,0x06}
};
const char bn4[][30] PROGMEM = { // 4-line numbers
// 0 1 2 3 4 5 6 7 8 9
{0x01,0x06,0x03, 0x08,0xFF,0xFE, 0x08,0x06,0x03, 0x08,0x06,0x03, 0xFF,0xFE,0xFF, 0xFF,0x06,0x06, 0x01,0x06,0x03, 0x06,0x06,0xFF, 0x01,0x06,0x03, 0x01,0x06,0x03},
{0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0x02,0x02,0xFF, 0xFE,0x02,0xFF, 0xFF,0x02,0xFF, 0xFF,0x02,0x02, 0xFF,0x02,0x02, 0xFE,0x01,0x07, 0xFF,0x02,0xFF, 0xFF,0x02,0xFF},
{0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0xFF,0xFE,0xFE, 0xFE,0xFE,0xFF, 0xFE,0xFE,0xFF, 0xFE,0xFE,0xFF, 0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0xFF,0xFE,0xFF, 0xFE,0xFE,0xFF},
{0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x06,0x06,0x06, 0x04,0x06,0x05, 0xFE,0xFE,0x06, 0x04,0x06,0x05, 0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x04,0x06,0x05, 0x04,0x06,0x05}
};
byte col,row,nb=0,bc=0; // general0xFE,0x01,0x07
byte bb[8]; // byte buffer for reading from PROGMEM
#include <Wire.h>
#include "RTClib.h"
RTC_Millis RTC;
byte hr, mn, se, osec;
//*****************************************************************************************//
// Initial Setup
//*****************************************************************************************//
void setup() {
randomSeed(analogRead(0));
RTC.begin(DateTime(__DATE__, __TIME__));
lcd.begin(20, 4);
for (nb=0; nb<8; nb++ ) { // create 8 custom characters
for (bc=0; bc<8; bc++) bb[bc]= pgm_read_byte( &custom[nb][bc] );
lcd.createChar ( nb+1, bb );
}
lcd.clear();
lcd.setCursor(4, 0);
lcd.print(F("3&4 Line BIG"));
lcd.setCursor(4, 1);
lcd.print(F("NUMERALS"));
lcd.setCursor(5, 3);
lcd.print(F("V"));
lcd.print(build);
lcd.print(F("."));
lcd.print(revision);
lcd.print(F(" "));
lcd.print(freeRam());
lcd.print(F("B"));
printNum4(random(0,10), 0);
printNum4(random(0,10),17);
delay(5000);
lcd.clear();
lcd.print(F("SoftClock (AJ)"));
}
//*****************************************************************************************//
// MAIN LOOP
//*****************************************************************************************//
void loop() {
DateTime now = RTC.now();
hr = now.hour(); if(hr==0) hr=24;
mn = now.minute();
se = now.second();
if(se != osec) {
printNum3(hr/10, 0, 1);
printNum3(hr%10, 3, 1);
printColon(6, 1);
printNum3(mn/10, 7, 1);
printNum3(mn%10,10, 1);
printColon(13, 1);
printNum4(se/10, 14);
printNum4(se%10, 17);
osec = se;
}
delay(50);
}
// ********************************************************************************** //
// SUBROUTINES
// ********************************************************************************** //
void printNum3(byte digit, byte leftAdjust, byte topAdjust) {
for(row=0; row<3; row++) {
lcd.setCursor(leftAdjust,row+topAdjust);
for(byte num=digit*3; num <digit*3+3; num++) {
lcd.write(pgm_read_byte( &bn3[row][num]) );
}
}
}
void printNum4(byte digit, byte leftAdjust) {
for(row=0; row<4; row++) {
lcd.setCursor(leftAdjust,row);
for(byte num=digit*3; num <digit*3+3; num++) {
lcd.write(pgm_read_byte( &bn4[row][num]) );
}
}
}
void printColon(byte leftAdjust, byte topAdjust) {
for(row=0; row<(4-topAdjust); row++) {
lcd.setCursor(leftAdjust,row+topAdjust);
if(topAdjust == 1) {
if(row == 0 || row == 1) lcd.print(F(".")); else lcd.print(F(" "));
} else {
if(row == 1 || row == 2) lcd.print(F(".")); else lcd.print(F(" "));
}
}
}
// ********************************************************************************** //
// OPERATION ROUTINES
// ********************************************************************************** //
// FREERAM: Returns the number of bytes currently free in RAM
int freeRam(void) {
extern int __bss_end, *__brkval;
int free_memory;
if((int)__brkval == 0) {
free_memory = ((int)&free_memory) - ((int)&__bss_end);
}
else {
free_memory = ((int)&free_memory) - ((int)__brkval);
}
return free_memory;
}
[/codesyntax]
The more, the merrier!
2 thoughts on “3 & 4-line Big Font Numerals”
This will upload but goes nuts when it comes to the part that it shoul display the temp. ????
//**************************************************************************************************
// 3-line and 4-line LCD NUMBERS
// Adrian Jones, March 2015
//
//**************************************************************************************************
// Build 1
// r1 150306 – initial build
//********************************************************************************************
#define build 1
#define revision 1
//********************************************************************************************
#include
#include // for memory storage in program space
#include
#include // Standard lcd library
#include // library for I@C interface
#define DHTPIN 4 // what pin we’re connected to
#define DHTTYPE DHT22 // DHT 22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);
#define I2C_ADDR 0x27 // address found from I2C scanner
#define RS_pin 0 // pin configuration for LCM1602 interface module
#define RW_pin 1
#define EN_pin 2
#define BACK_pin 3
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(0x3F, EN_pin, RW_pin, RS_pin, D4_pin, D5_pin, D6_pin, D7_pin, BACK_pin, POSITIVE);
//Pins for the LCD are SCL A5, SDA A4
const char custom[][8] PROGMEM = {
{0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}, // char 1: bottom right triangle
{0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F}, // char 2: bottom block
{0x10, 0x1C, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}, // char 3: bottom left triangle
{0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00}, // char 4: top right triangle
{0x1F, 0x1E, 0x1C, 0x10, 0x00, 0x00, 0x00, 0x00}, // char 5: top left triangle
{0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00}, // char 6: upper block
{0x1F, 0x1F, 0x1E, 0x1C, 0x18, 0x18, 0x10, 0x10}, // char 7: full top right triangle
{0x01, 0x07, 0x0F, 0x1F, 0x00, 0x00, 0x00, 0x00} // char 8: top right triangle
};
const char bn3[][30] PROGMEM = { // 3-line numbers
// 0 1 2 3 4 5 6 7 8 9
{0x01,0x06,0x03, 0x08,0xFF,0xFE, 0x08,0x06,0x03, 0x08,0x06,0x03, 0xFF,0xFE,0xFF, 0xFF,0x06,0x06, 0x01,0x06,0xFE, 0x06,0x06,0xFF, 0x01,0x06,0x03, 0x01,0x06,0x03},
{0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0x01,0x06,0x06, 0xFE,0x06,0xFF, 0x06,0x06,0xFF, 0x06,0x06,0xFF, 0xFF,0x06,0x03, 0xFE,0x01,0x07, 0xFF,0x06,0xFF, 0x04,0x06,0xFF},
{0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x06,0x06,0x06, 0x04,0x06,0x05, 0xFE,0xFE,0x06, 0x04,0x06,0x05, 0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x04,0x06,0x05, 0xFE,0xFE,0x06}
};
const char bn4[][30] PROGMEM = { // 4-line numbers
// 0 1 2 3 4 5 6 7 8 9
{0x01,0x06,0x03, 0x08,0xFF,0xFE, 0x08,0x06,0x03, 0x08,0x06,0x03, 0xFF,0xFE,0xFF, 0xFF,0x06,0x06, 0x01,0x06,0x03, 0x06,0x06,0xFF, 0x01,0x06,0x03, 0x01,0x06,0x03},
{0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0x02,0x02,0xFF, 0xFE,0x02,0xFF, 0xFF,0x02,0xFF, 0xFF,0x02,0x02, 0xFF,0x02,0x02, 0xFE,0x01,0x07, 0xFF,0x02,0xFF, 0xFF,0x02,0xFF},
{0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0xFF,0xFE,0xFE, 0xFE,0xFE,0xFF, 0xFE,0xFE,0xFF, 0xFE,0xFE,0xFF, 0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0xFF,0xFE,0xFF, 0xFE,0xFE,0xFF},
{0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x06,0x06,0x06, 0x04,0x06,0x05, 0xFE,0xFE,0x06, 0x04,0x06,0x05, 0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x04,0x06,0x05, 0x04,0x06,0x05}
};
byte col,row,nb=0,bc=0; // general0xFE,0x01,0x07
byte bb[8]; // byte buffer for reading from PROGMEM
int chk;
float hum; //Stores humidity value
float temp; //Stores temperature value
// float hi = dht.computeHeatIndex(temp, hum);
// float hiDegC = dht.convertFtoC(hi);
#include
#include “RTClib.h”
RTC_Millis RTC;
byte hr, mn, se, osec;
//*****************************************************************************************//
// Initial Setup
//*****************************************************************************************//
void setup() {
dht.begin();
randomSeed(analogRead(0));
RTC.begin(DateTime(__DATE__, __TIME__));
lcd.begin(20, 4);
for (nb=0; nb<8; nb++ ) { // create 8 custom characters
for (bc=0; bc<8; bc++) bb[bc]= pgm_read_byte( &custom[nb][bc] );
lcd.createChar ( nb+1, bb );
}
lcd.clear();
lcd.setCursor(4, 0);
lcd.print(F("3&4 Line BIG"));
lcd.setCursor(4, 1);
lcd.print(F("NUMERALS"));
lcd.setCursor(5, 3);
lcd.print(F("V"));
lcd.print(build);
lcd.print(F("."));
lcd.print(revision);
lcd.print(F(" "));
lcd.print(freeRam());
lcd.print(F("B"));
printNum4(random(0,10), 0);
printNum4((7),17);
delay(5000);
lcd.clear();
lcd.print(F("SoftClock (AJ)"));
hum = dht.readHumidity()*.95;
temp = dht.readTemperature()* 1.8 + 32 *.989;
}
//*****************************************************************************************//
// MAIN LOOP
//*****************************************************************************************//
void loop() {
DateTime now = RTC.now();
hr = now.hour(); if(hr==0) hr=24;
mn = now.minute();
se = now.second();
if(se != osec) {
printNum3(hr/10, 0, 1);
printNum3(hr%10, 3, 1);
printColon(6, 1);
printNum3(mn/10, 7, 1);
printNum3(mn%10,10, 1);
printColon(13, 1);
printNum4(se/10, 14);
printNum4(se%10, 17);
osec = se;
}
// delay(500);
// lcd.clear();
lcd.clear();
lcd.print(freeRam());
char thisTemp[5];
//dtostrf(temp, 0 ,1,temp);
printNum4(thisTemp,0);
//lcd.clear();
//delay(5000);
//printNum4((temp), 9);
//delay(5000);
//lcd.clear();
//lcd.print(freeRam());
}
// ********************************************************************************** //
// SUBROUTINES
// ********************************************************************************** //
void printNum3(byte digit, byte leftAdjust, byte topAdjust) {
for(row=0; row<3; row++) {
lcd.setCursor(leftAdjust,row+topAdjust);
for(byte num=digit*3; num <digit*3+3; num++) {
lcd.write(pgm_read_byte( &bn3[row][num]) );
}
}
}
void printNum4(byte digit, byte leftAdjust) {
for(row=0; row<4; row++) {
lcd.setCursor(leftAdjust,row);
for(byte num=digit*3; num <digit*3+3; num++) {
lcd.write(pgm_read_byte( &bn4[row][num]) );
}
}
}
void printColon(byte leftAdjust, byte topAdjust) {
for(row=0; row<(4-topAdjust); row++) {
lcd.setCursor(leftAdjust,row+topAdjust);
if(topAdjust == 1) {
if(row == 0 || row == 1) lcd.print(F(".")); else lcd.print(F(" "));
} else {
if(row == 1 || row == 2) lcd.print(F(".")); else lcd.print(F(" "));
}
}
}
// ********************************************************************************** //
// OPERATION ROUTINES
// ********************************************************************************** //
// FREERAM: Returns the number of bytes currently free in RAM
int freeRam(void) {
extern int __bss_end, *__brkval;
int free_memory;
if((int)__brkval == 0) {
free_memory = ((int)&free_memory) – ((int)&__bss_end);
}
else {
free_memory = ((int)&free_memory) – ((int)__brkval);
}
return free_memory;
}
Hi, I was trying to make your 4 line font work with my weather smurfer. It seems that it will flicker all sorts of characters when I upload it to my uno. I saw the random command and can change it to print only single number, If I try two, it doesn’t like that. If I try char thisTemp[5];
dtostrf(temp, 0, 1,thisTemp);
writeBigString(thisTemp, 0, 2); which worked with the two line font, it still doesn’t like it. Any suggestions??