Newbie having trouble displaying variable on lcd.

So I've read multiple tutorials, and the arduino lcd reference, but have not seen this seemingly simple problem addressed anywhere.

This is my first time using an LCD, I'm using one of the common 16x2 lcd's. I'm wanting to display angle of a servo, which is controlled by a potentiometer (right now I have the servo left out). The problem is the way it is displaying the values. Originally using lcd.print(val), when displaying something like 51 for example, it would display 51515151515151515151, anything over 2 digits would end up a garbled mess.

I tried almost every type of code from the arduino reference, and found that adding lcd.home(), did help a little, but not completely. Once I get up to a triple digit number, the 3rd digit never goes away, so something like 150 would be correct, but 67 becomes 670, 1 becomes 100, etc.

How can I get this value to display correctly? Here is the code I'm using.

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int potpin = 0;  // analog pin used to connect the potentiometer
int val;    // variable to read the value from the analog pin 

void setup() {
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
}

void loop() {
  val = analogRead(potpin);            // reads the value of the potentiometer (value between 0 and 1023) 
  val = map(val, 0, 1023, 0, 179);
  lcd.home();
  lcd.print(val);
}

If you don't position the cursor, it will simply keep advancing down the screen. Try adding a lcd.setCursor call to position the cursor to where you want it to put the text.

You can make sure that the value is always a specified number of characters, using sprintf, or you can print the maximum number of spaces, reposition the cursor, and print the value.

I just tried using the lcd.setcursor call, instead of the lcd.home call, but it still does the same thing. Works good at first, 1 is displayed as 1, etc. However once I get to the double and triple digits, the extra zeros never go away, so 1 will be 100.

As far as the value being a set amount of characters though, it will always be a different amount, since it will be either 1, 2 or 3 digits depending on the value (from 0 to 179)

When the value is 1, you can use sprintf to generate a string like " 1". When the value is 10, sprintf will generate a string like " 10". Always 3 characters long.

On the other hand, you could print " ", move the cursor back, and print "1" or "10" or "100". When printing "10" after "100", the three characters in "100" will have been overwritten by the spaces, so the 3 positions will contain "10 ".

Its a much easier solution than all that. You are not clearing the display. Get rid of lcd.home and cursor positioning, Just use lcd.clear() at the end of your loop.

Its a much easier solution than all that. You are not clearing the display. Get rid of lcd.home and cursor positioning, Just use lcd.clear() at the end of your loop.

Well, sure you can use that sledge hammer to swat flies.

Riiiiight. :stuck_out_tongue:

Its a much easier solution than all that. You are not clearing the display. Get rid of lcd.home and cursor positioning, Just use lcd.clear() at the end of your loop.

I've been messing around with that lcd.clear() code, however am having issues with it as well. If I simply add it to the code, I end up getting nothing on the display. I think that its just writing and clearing so fast that nothing shows up.

I did write a code where it has the lcd.print, then a 20ms delay, then lcd.clear, and that sort of solved the problem, at least the variables are showing up correctly, however it is having 2 issues as well. First it is adding an unnecessary delay (doesn't matter for this project, but will for others). And second it adds sort of a rolling refresh effect to the text.

As far as the sprintf code, I spent a bit of time looking it up, but its not in the arduino reference at all. I did find other various references to it (mostly non-arduino related), but they were all extremely confusing. Isn't there an easier way than using this exotic piece of code that isn't well documented and seems rarely used with arduino?

I'm just so baffled that I seem to be the only one having this simple problem. I've spent hours combing through around 50 different pages of different arduino projects with lcd's, arduino lcd tutorials, etc. Yet I could not find any mention of anyone having this issue with values displaying correctly. Such an incredibly simple thing, display a variable, yet I didn't find it incorporated into anyone else's project, most people seemed to be displaying either text, or numbers that only increase and never decrease.

I suppose I could add all sorts of extra lines of code, with big "if" tiers that monitor when the number of digits changes, and how many digits, and adds spaces when the digits drop down or whatever, but that just seems so elaboarate and sloppy. Does it really take a massive "if" "else" "else if" tree to get a number such as 1 to display as 1 instead of 100?

Give this a shot:

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int potpin = 0;  // analog pin used to connect the potentiometer
int rawval;      // variable to read the value from the analog pin 
int mapval;      // this will be rawval remapped to be from 0-180 degrees

/* ^^ separation of these two can come in handy sometimes further down in
your program if you ever need to reuse the raw value..                 */


void setup() {
  // set up the LCDs number of columns and rows and init display
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setcursor(0,0);
  lcd.print("Current angle:");
}

void loop() {
  rawval = analogRead(potpin);            // reads the value of the potentiometer (value between 0 and 1023) 
  mapval = map(rawval, 0, 1023, 0, 179);
  
  lcd.setcursor(0,1);     // reset cursor to first character (0), second line (1)
  lcd.print("   ");       // write blank space over the previous value so there are no artefacts from the previous output
  lcd.setcursor(0,1);     // reset the cursor again so that we output cleanly over the fresh blank space
  lcd.print(val);         // output the value to the second line on the screen
  lcd.setcursor(4,1);     // set the cursor after the output so that it won't ever overlap (ie, 5th character position on the line, this puts a space between the output and the following text)
  lcd.print("degrees");   // to make it pretty we add 'degrees' to the output - note that this has to be separate to the output of the value, system limitation :/
  delay(50);              // wait a bit
}

Fancy text aside, the changes I made are that basically whenever you output the current value, you should just write blank space over the previous value to make sure there are no artefacts. the delay i've put in there could theoretically be removed with no issues, however the screen has limitations on how fast it can refresh and i've found around 250ms to the the fastest reasonable time. this is 5 times faster than that again, so you're definitely being limited by the screen and not the driving code..

As far as the sprintf code, I spent a bit of time looking it up, but its not in the arduino reference at all.

The Arduino reference page explains Arduino-specific functions. Nothing else. Well, precious little else.

It is, by no means, an exhaustive guide to everything you can use on the Arduino. Look in ANY C or C++ books, and you'll find hundreds of functions that work perfectly well on the Arduino but that are not listed on the Arduino reference page.

@bilbo
Clearing the whole screen, to erase three characters, is overkill. Re-writing all the data that was displayed on the LCD, to replace what you just removed from the screen may not be possible. It is far better, and FASTER, to write just what you want to the screen, at any given time.

Daneel: Thank you so much for taking the time to write that code. Definetly above and beyond. I'm going to try it later today. I think I'll try without the delay though and see how it works. For this particular project the delay doesn't matter, but I know in the future I won't want to have delays holding up the rest of the code.

PaulS: Could you give an example of the correct syntax for the sprintf command in this situation? I did some seraching for sprintf but all the examples I had found were very confusing and seemed to be used for completely different situations.

Hi Carl,

No problems :slight_smile: had fun doing it.

I revised the code to be a bit more time efficient, and to also have a more stable output. Fixed some compile issues as well:

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int potpin = 0;    // analog pin used to connect the potentiometer
int rawval;        // value from the analog pin 
int mapval;        // rawval remapped to be from 0-180 degrees
int oldval = 200;  // previous value of mapval, set out of range intentionally so it always updates first time through (range: 0-180)

/* ^^ separation of these variables can come in handy sometimes further down in
your program if you ever need to reuse the raw value..                       */


void setup() {
  // set up the LCDs number of columns and rows and init display
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Current angle:");
}

void loop() {
  rawval = analogRead(potpin);            // reads the value of the potentiometer (value between 0 and 1023) 
  mapval = map(rawval, 0, 1023, 0, 179);
  
  if (oldval = mapval)
  { 
    // we're already displaying the right value so we do nothing - nice and fast!
  }
  else
  {
    // this only runs when mapvap is different to the displayed value on the screen, making your display look more stable.
    lcd.setCursor(0,1);     // reset cursor to first character (0), second line (1)
    lcd.print("    ");      // write blank space over the previous value and degree symbol so there are no artefacts from the previous output
    lcd.setCursor(0,1);     // reset the cursor again so that we output cleanly over the fresh blank space
    lcd.print(mapval);      // output the value to the second line on the screen
    lcd.print("°");         // put a degrees symbol after it, hopefully this outputs correctly on the LCD - the character *is* present in the LCD's character map
    oldval = mapval;        // store the displayed value so we can check if it changes
  }
}

The code now stores the displayed value and when it changes, updates the LCD. This stops it continually refreshing and clearing over and over again. The code should be more efficient if you have to drop it into other projects, and there's no delays in sight :slight_smile:

PaulS: Could you give an example of the correct syntax for the sprintf command in this situation?

char valStg[4]; // 3 characters plus a terminating NULL;
int val = 10;
sprintf(valStg, "%3d", val);

A good reference for sprintf: http://www.cplusplus.com/reference/clibrary/cstdio/sprintf/
%d is the format specifier for an integer. Between the % and the specifier (d), there can be several additional values:
%[flags][width][.precision][length]specifier
flags include -, + , space, #, and 0.
width defines the minimum number of characters to be used in the output. For an integer value, it defines how many characters, at a minimum, in the result. Width, in this case, is 3.
The . is required if precision is to be specified. Precision also affects the minimum number of characters to output. For integers, the difference is between digits and characters.
%3d outputs a minimum of 3 characters (" 10"). #.3d outputs a minimum of 3 digits ("010").