Retaining custom characters on an LCD display

xenington

Well-known member
Hi Everyone

Hopefully this will be a quick yes or no question:

I am trying to draw a graph across 18 characters on a 20x4 character display. The 'line' is made by drawing a pixel representing the Y value in each column of a custom character. When the 5 columns of the character are full, the character is displayed on the screen. The graph then moves forward 1 character and repeats the procedure. Unfortunately, when a new character is created, the characters already on the screen also change and become the new character.

I was hoping that once something is printed on the display, it would remain there until either written over or cleared. Is this incorrect? Perhaps there is a way to achieve it? Or am I really at the mercy of the 8 characters limit?

Thank you
 
Here is some code to illustrate what I am trying to do.

Five random numbers are generated and used to create a character which is displayed in cursor position 1. After a further 5 random numbers, the character is re-created and displayed in cursor position 2 and so on. The problem is that as new characters are created, they replace all of the previous characters already being displayed on the lcd. It seems as if the entire screen is updated with each lcd.write(7), when only 1 character block needs to be updated. Is there a way to write to just 1 character block?

Code:
#include "Arduino.h"
#include <LiquidCrystal_I2C.h>
#define blockWidth 5
#define blockHeight 8
LiquidCrystal_I2C lcd(0x27, 20, 4);
uint8_t sensorNumber = 4;

void setup()
{
    Serial.begin(115200);
    lcd.init();
}
int freqDiffGraph;
uint8_t screenScanGraphX = 1;               // position on screen
uint8_t scanGraphColumn = 4;                // column within character block
uint8_t scanGraphCharacter[8] = {};         // array to hold character
uint8_t scanGraphCharacterColumn[8] = {};   // array to hold character column
uint8_t scanGraphCharacterCount = 1;        // number of character

void loop()
{
    freqDiffGraph = random(-10000, 10000);
    screenScanGraph();
    if (scanGraphColumn == 0) {                     // character has 5 columns of pixels
        lcd.createChar(7, scanGraphCharacter);      // create new character
        lcd.setCursor(screenScanGraphX, 0);         // position on screen
        lcd.write(7);                               // print new character on screen
        lcd.print(" ");                             // erase next block
        scanGraphColumn = 4;                        // reset column counter
        screenScanGraphX ++;              // increment position on screen
        if (screenScanGraphX > 18) {      // reach end of screen
            screenScanGraphX = 1;         // start from beginning again
        }
        delay(500);
    }
}

void screenScanGraph()      // create custom characters - use log2, logn or log10?
{
    freqDiffGraph = abs(freqDiffGraph);                     // only use positive values
    freqDiffGraph = constrain(freqDiffGraph, 1, 32768);     // constrain between log(1) = 0 and log(32768) = 15, log(0) is not possible
    float freqDiffGraphLog = log2(freqDiffGraph);           // log of freqDiffGraph
    uint8_t scanGraphPixelRow = freqDiffGraphLog / 2;       // remap values to block height = 8 pixels
    for (uint8_t i = 0; i < 8; i ++) {
        scanGraphCharacterColumn[i] = 0;                    // clear column
    }
    scanGraphCharacterColumn[scanGraphPixelRow] = 1;        // add pixel to column
    for (uint8_t j = 0; j < 8; j ++) {                      // cycle through columns
        bitWrite(scanGraphCharacter[j], scanGraphColumn, scanGraphCharacterColumn[j]);
    }
    scanGraphColumn --;                                     // increment column counter
}
 
OK, so I couldn't figure out how to do horizontal graphs on a character lcd in the way I wanted. The 8 custom character limitation prevents it.

So, I came up with an acceptable alternative - vertical scrolling graphs. It only displays 4 previous values instead of 19, but it does allow for extra characters. If anyone would like to use it, here is the code:

Code:
/*
 *      VERTICAL SCROLLING GRAPHS ON LCD DISPLAY
 *
 */
#include "Arduino.h"
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);   // 20 x 4 lcd
uint8_t scanGraphNumber = 2;    // 1, 2 or max 4
uint8_t scanGraphWidth = 20 / scanGraphNumber;
int freqDiffGraph = 0;
uint8_t scanGraphArray[4][4] = {};      // array to remember old graphs for scrolling
uint8_t scanGraphArrayPos = 0;          // index within array
int scanGraphScreenRow = 0;             // screen row

uint8_t scanGraphCharacter[8][8] PROGMEM = {
    {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000}, // char 0x00: 1 bar
    {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000}, // char 0x01: 2 bar
    {B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100}, // char 0x02: 3 bar
    {B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110}, // char 0x03: 4 bar
    {B11111, B11011, B10011, B11011, B11011, B11011, B10001, B11111}, // char 0x04: reverse 1 (white on black)
    {B11111, B10001, B10101, B11101, B11011, B10111, B10001, B11111}, // char 0x05: reverse 2
    {B11111, B10001, B11101, B11001, B11101, B10101, B10001, B11111}, // char 0x06: reverse 3
    {B11111, B10101, B10101, B10101, B10001, B11101, B11101, B11111}  // char 0x07: reverse 4
};
void setup()
{
    Serial.begin(115200);
    lcd.init();
    createScanGraphCharacter();
    screenScanSetup();
}
void createScanGraphCharacter()
{
    for (uint8_t i = 0; i < 8; i ++) {
        lcd.createChar(i, scanGraphCharacter[i]);   // create characters 0 to 7
    }
}
void screenScanSetup()
{
    for (uint8_t i = 0; i < scanGraphNumber; i ++) {
        for (uint8_t j = 0; j < 4; j ++) {
            lcd.setCursor(i * scanGraphWidth, j);
            lcd.write(0xFF);                            // fill first columns with blocks
            lcd.setCursor(i * scanGraphWidth, 0);
            lcd.write(i + 4);                           // add reverse numbers
        }
    }
}
void loop()
{
    delay(1000);
    freqDiffGraph = random(1, 10);
    screenScanGraph();
}
void screenScanGraph()      // create custom characters - use log2, logn or log10?
{
//  log2(freqDiff): 1 = 0, 2 = 1, 4 = 2, 8 = 3, 16 = 4, 32 = 5, 64 = 6, 128 = 7, 256 = 8, 512 = 9, 1024 = 10, 2048 = 11, 4096 = 12, 8192 = 13, 16384 = 14, 32768 = 15, etc
//  logn(freqDiff): 1 = 0, 10 = 1, 100 = 2, 1000 = 3, 10000 = 4, 100000 = 5, 1000000 = 6, 10000000 = 7, etc
//  log10(freqDiff): 1 = 1, 10 = 2, 100 = 3, 1000 = 4, 10000 = 5, 100000 = 6, 1000000 = 7, etc
    for (uint8_t i = 0; i < scanGraphNumber; i ++) {                      // erase previous graphs
        for (uint8_t j = 0; j < 4; j ++) {
            lcd.setCursor((i * scanGraphWidth) + 1, j);
            for (uint8_t k = 0; k < (scanGraphWidth - 1); k ++) {
                lcd.write(0xFE);
            }
        }
    }
    freqDiffGraph = abs(freqDiffGraph);                     // only use positive values
    freqDiffGraph = constrain(freqDiffGraph, 1, 10000);     // constrain between log(1) = 0 and log(10000) = 4, log(0) is not possible
    uint16_t freqDiffGraphLog = (log2(freqDiffGraph)) * 10;     // logn of freqDiffGraph, * 10 to preserve decimal point during map()
    uint16_t scanGraphXColumn = (scanGraphWidth - 1) * 5;       // number of pixel columns available to draw each graph
    uint16_t scanGraphValue = map(freqDiffGraphLog, 0, 120, 0, scanGraphXColumn);
    scanGraphArray[0][scanGraphArrayPos]= scanGraphValue;
    for (uint8_t i = 0; i < scanGraphNumber; i ++) {
        scanGraphScreenRow = scanGraphArrayPos;                 // latest value
        for (uint8_t j = 0; j < 4; j ++) {
            uint16_t scanGraphXBlock = scanGraphArray[0][scanGraphScreenRow] / 5;       // calculate number of block to fill
            uint16_t scanGraphXPixel = scanGraphArray[0][scanGraphScreenRow] % 5;       // calculate extra pixels
            lcd.setCursor((i * scanGraphWidth) + 1, j);
            for (uint8_t i = 0; i < scanGraphXBlock; i ++) {
                lcd.write(0xFF);
            }
            if (scanGraphXPixel == 0) {
                lcd.write(0xFE);
            } else {
                lcd.write(scanGraphXPixel - 1);
            }
            scanGraphScreenRow --;
            if (scanGraphScreenRow == -1) {
                scanGraphScreenRow = 3;
            }
        }
    }
    scanGraphArrayPos ++;
    if (scanGraphArrayPos > 3) {
        scanGraphArrayPos = 0;
    }
}
 
Back
Top