LCD shows garbage, usual fixes won't work

Good evening everyone. I will attach a couple of photos (cellphone camera, not best quality I'm sorry) to better illustrate my problem.

I am using an Arduino Uno with an Everbouquet MC1604B-SYR 16x4 lcd display. However the display shows a lot garbage whenever I press one of the four pushbuttons I'm using (2nd attachment).

I promptly got to investigating, and I saw a lot of people having trouble with either connecting the lcd to the 0/1 RX/TX pins, or having to change some parameters in the serial port (nr. of bits and parity). These have now been taken care of, but the problem persists. The connections and solderings have been checked as well, and all seems good.

The quirky aspect to it though: the arduino works normally UNTIL I press one of the 4 physical buttons I have in my design. When I press one of these 4 buttons (but not all the time, about 50% of the times), the display flips out and starts showing a lot of characters which it wasn't supposed to.

As you can see on the 3rd attachment, I have 4 pushbuttons (red lines), 6 lcd pins (blue lines) and 2 analog sensors attached to analog pins (green lines) - ignore the gray lines, it's a different problem I'm having.

EDIT: In another thread I was told that the SPI.h library uses pins 12 and 13 which I am using for two of the buttons. Thought it would be worth to mention that. But the buttons on pins 8 and 9 are also causing the "screen flip out"

Now, some relevant parts of code:

void processData(void)
{
.
.
.
  lcd.clear();                 
  
  lcd.setCursor (0,0);
  lcd.print("T=");
  lcd.print(temp_str);
  lcd.print((char)223);         // degree symbol
  lcd.print("C");
  
  lcd.setCursor (0,1);
  lcd.print("SetPoint=");
  lcd.print(sp_str);
  
  lcd.setCursor (-4,2);
  lcd.print("pH=");
  lcd.print(ph_str);

  lcd.setCursor (-4,3);
  if (comm_status == 0) {lcd.print("Com:off");} 
  if (comm_status == 1) {lcd.print("Com:on");}

  lcd.setCursor (5,3);
  if (valve_status == 0) {lcd.print("Vlv:off");}  
  if (valve_status == 1) {lcd.print("Vlv:on");}
.
.
.
}

void loop(void)
{
.
.
.
  buttonState0 = digitalRead(Calibrate);
  buttonState1 = digitalRead(Up);
  buttonState2 = digitalRead(Down);
 
  if (buttonState0 == HIGH)
  {
      calibrate_ph7();                                          //run the pH sensor calibration routine
      calibrate_ph4();
  }
  
  if (buttonState1 == HIGH)
  {
      setPoint = setPoint + interval_SetPoint;          //increase the pH setpoint
      delay(200);
  }
  
  if (buttonState2 == HIGH)
  {
      setPoint = setPoint - interval_SetPoint;          //decrease the pH setpoint
      delay(200);
  }
.
.
.
}

Many hours have been put on this and no luck yet, I'm getting a little desperate now. I appreciate any and all help or input that you can provide!

How exactly are your "4 buttons in" wired?

It may also help to post your code.

The buttons are wired exactly as shown here http://www.arduino.cc/en/Tutorial/button. Directly into one digital input each (pins 8, 9, 12, 13). The fact that I was unaware that SPI uses some of those pins, namely 12 and 13, might have something to do with it. Tomorrow at the lab I'll try to change those but I'm pretty sure, if I recall correctly, that the ones on 8 and 9 were also causing trouble.

Have to run to work now, but tomorrow I'll post more of the code. Thank you for the input!

Maybe this is a reason (in short maybe pin 7 is not good for LCD, try other pins):

http://arduino.cc/forum/index.php/topic,64256.0.html

I have an UNO but I don't have to hook it to an LCD. I just use my shields and serial LCDs I make but I'll give it a try to see how bad it gets.

I don't understand why you didn't give us all the information up front instead of having us beg you for it.

You have not specified the connections between your LCD module and your Arduino. We know which pins are used, but for example, which one is used for 'E'?

You have not specified the conditions that cause the processData() function to run. What triggers it? How often?

You really shouldn't clear the display unless you have to. Why rewrite the headings that never change? If you do clear the display then you don't have to set the cursor to (0,0), it's already there.

Wouldn't it be nice if they actually used the information in lcd.begin()? Then you wouldn't have to cobble up the offsets on lines 3 and 4.

Don

Thanks guys. Sorry I stepped over some of the finer details, wasn't entirely sure what other info would be needed and was strapped for time at the moment. Will give as much info as possible and almost full code will be attached in this post (the important parts).

@ floresta:

1 - the lcd-arduino connections are as follows:

V0 - trimpot (for contrast); RS - dpin 7; E - dpin 6
D4 - dpin 5; D5 - dpin 4; D6 - dpin 3; D7 - dpin 2

2 - processData() at the moment is being called every single loop (it will have a time condition, ie to only be called every 10 seconds or so, but it hasn't been implemented yet), under no other condition than simply being called inside the loop, every single cycle.

3 - I cleared the display because I thought it was inocuous enough, and standard procedure before writing something. And was worried that when changing from, for example, 10.05 to 9.90 (5 chars vs 4 chars), the last digit would still show up (ie 9.905).

4 - I'm calling lcd.begin(16,4) at the setup, however I was still having an offset when trying to change lines, hence why I had to cobble up those offsets. Maybe I'm missing something here, as I'm pretty sure this shouldn't be needed.

  • As I said earlier, buttons are on dpins 8, 9, 12, 13.

  • Using Arduino Uno and Arduino 0022 IDE.

Alright, code!

#include <LiquidCrystal.h>
#include <SPI.h>
#include <Ethernet.h>
#include <stdio.h>
#include <string.h>
#include "float2string.h"


#define GAIN  10
#define VREF  2.366
#define PHSENSOR  0                  //apin 0
#define TEMPSENSOR  1              //apin 1
#define GAS_CONSTANT_R  8.31451
#define FARADAY_CONSTANT_F  96485
#define NERNST  59.16
#define SAMPLES  200


//DIGITAL IN/OUT PINS FOR BUTTONS
int OK = 13;
int Calib = 12;
int Up = 9;
int Down = 8;


//VARIABLES
float setPoint = 7.00;                  //set point pH, 7.00 Default
float interval_SP = 0.05;              //resolution interval for pH SetPoint, on button presses
int buttonState0 = 0;                   //Calib button state
int buttonState1 = 0;                   //Up button state
int buttonState2 = 0;                   //Down button state
int buttonState3 = 0;                   //OK button state
float temp_mv = 0;                      //mV sensed by temperature sensor
float ph_mv = 0;                        //mV sensed by pH sensor, x10 hardware gain
float ph_calib7 = 0;                    //calibration offset (zero) on buffer solution ph7
float ph_calib4 = 1;                    //calibration offset (gain) on buffer solution ph4
float T = 0;                            //temperature value
float E = 0;                            //electrolitic potential
float PH = 0;                           //real pH, considering calib7, calib4 and temperature compensation
char temp_str[6];                     //temperature in string, to display in lcd
char ph_str[6];                         //pH in string
char sp_str[6];                         //setPoint in string
int comm_status = 0;                //state of ethernet link


byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x6F, 0x2C };
byte ip[] = { 10, 0, 0, 177 };
byte server[] = { 173, 203, 98, 29 };            // www.pachube.com
byte gateway[] = { 10, 0, 0, 1 };
byte mask[] = { 255, 255, 255, 0 };              //subnet
int port = 80;

Client client(server, port);                

LiquidCrystal lcd(7, 6, 5, 4, 3, 2);



void setup()
{
  lcd.begin(16, 4);                    
  
  pinMode (OK, INPUT);           
  pinMode (Calib, INPUT);       
  pinMode (Up, INPUT);         
  pinMode (Down, INPUT);        
  
  Ethernet.begin(mac, ip, gateway, mask);   
  Serial.begin(9600);
  delay(1000);                                        

}



unsigned int readADC(unsigned char channel) {

  double d = 0;
  int i = 0;
 
  for (i = 0; i < SAMPLES; i++)
  {  
    d = d + analogRead(channel);   
  }
  d = d / i;                        

  return (unsigned int)(d);
}



void processData(void)
{

  ph_mv = readADC(PHSENSOR);                                                    
  temp_mv = readADC(TEMPSENSOR);                                                  
 
  T = (100 * temp_mv * 5.0 / 1024);                                                        
  E = (((ph_mv - ph_calib7) * 5.0 / 1024) - VREF) / (GAIN * ph_calib4);   
  PH= ((-1 * FARADAY_CONSTANT_F * E) / (2.303 * GAS_CONSTANT_R * (273.15 + T))) + 7.0;    
  
  floatToString(temp_str, T, 1);
  floatToString(ph_str, PH, 2);
  floatToString(sp_str, setPoint, 2);
    

  lcd.clear();                  

  lcd.setCursor (0,0);
  lcd.print("T=");
  lcd.print(temp_str);
  lcd.print((char)223);         // degree symbol
  lcd.print("C");
  
  lcd.setCursor (0,1);
  lcd.print("SetPoint=");
  lcd.print(sp_str);
  
  lcd.setCursor (-4,2);
  lcd.print("pH=");
  lcd.print(ph_str);

  lcd.setCursor (-4,3);
  if (comm_status == 0) {lcd.print("Com:off");}      
  if (comm_status == 1) {lcd.print("Com:on");}

  lcd.setCursor (5,3);
  if (valve_status == 0) {lcd.print("Vlv:off");}     
  if (valve_status == 1) {lcd.print("Vlv:on");}

}


float calibrate_ph7()                           //calibration routine for pH 7 buffer solution
{
  
  lcd.clear();
  lcd.print("Calibration pH7:");

      
  buttonState3 = digitalRead(OK);
      
  if (buttonState3 == HIGH)
  {
    if (ph_mv != 0) 
    {
      ph_calib7 = ph_mv;
    }
    else 
    {
      ph_calib7 = 0;
    }
    
      lcd.clear();
      lcd.setCursor(1,1);
      lcd.print("Calibrated pH7!");
      delay(2500);
      lcd.clear();
      digitalWrite (Calib, LOW);
      buttonState0 = 0;
      digitalWrite (OK, LOW);
      buttonState3 = 0;
   }
   else
   {
     delay(200);              //reduces lcd flicker
     calibracao_ph7();      //recursive until OK button press
   }
}


    
float calibrate_ph4()                     //calibration routine for pH 4 buffer solution
{
  
  float difference = 0;
  float new_gain = 0;

  lcd.clear();
  lcd.print("Calibration pH4:");
 
      
  buttonState3 = digitalRead(OK);
  
  if (buttonState3 == HIGH)
  {
    if (ph_mv != ((NERNST * 3) * GAIN) + (VREF - ph_calib7))      
    {
      difference = ((NERNST * 3) * GAIN) + (VREF - ph_calib7) - ph_mv;       
      new_gain = (((((NERNST * 3) * GAIN) + (VREF - ph_calib7)) - difference) * GAIN) / (((NERNST * 3) * GAIN) + (VREF - ph_calib7));   
      ph_calib4 = new_gain / GAIN;                                   
    }
    
    else 
    {
      ph_calib4 = 1; 
    }
    
    lcd.clear();
    lcd.setCursor(1,1);
    lcd.print("Calibrated pH4!");
    delay(2500);
    lcd.clear();
    digitalWrite (Calib, LOW);
    buttonState0 = 0;
    digitalWrite (OK, LOW);
    buttonState3 = 0;
  }
  
  else
   {
     delay(200);                //reduces lcd flicker
     calibrate_ph4();          //recursive until OK button is pressed
   }
}


void loop(void)
{

  if (client.connected() == 0)
  {
    comm_status = 0;
    client.connect();
  }
  else
  {
    comm_status = 1;
  }
    
   
  processData();
     
  buttonState0 = digitalRead(Calib);
  buttonState1 = digitalRead(Up);
  buttonState2 = digitalRead(Down);
 
  if (buttonState0 == HIGH)               //starts calibration routine
  {
      calibrate_ph7();
      calibrate_ph4();
  }
  
  if (buttonState1 == HIGH)                    //increases setpoint
  {
      setPoint = setPoint + interval_SP;
      delay(200);
  }
  
  if (buttonState2 == HIGH)                   //decreases setpoint
  {
      setPoint = setPoint - interval_SP;
      delay(200);
  }
  
}

I think that pretty much covers it all. If I did miss something important again, I'll supply the info if you let me know.

Thank you so much!

Your recursive calls may be part of the problem. As they are tail-recursive, it is possible that the compiler will have optimised them away; but if it hasn't, each recursive call will use several bytes of stack and you will very quickly overflow the stack into the data. Avoid recursive calls, use a loop instead.

floresta's tutorial on character LCD addressing should help you understand the offset that 16*4 displays have:

http://web.alfredstate.edu/weimandn/lcd/lcd_addressing/lcd_addressing_index.html

I recommend a 20*4 display. It's much easier to use, no surprises.

Thanks dc42, I'll try to use a do...while loop to avoid potential problems with that.

I made some experiments and came to some results. As I said I'm using pins 8, 9, 12 and 13 for the buttons. I came to know that 12 and 13 are used in SPI.h, and 8 is the reset pin. When I took out the SPI.h library, buttons 12 and 13 stopped causing problems, and only 8 did it. So it probably has to do with those pins serving for some other things. Next I'll try running it with SPI.h but with all the buttons as analog inputs rather than digitals, to see if it sorts it out.

Thanks guys

Referencing reply #5:
E - dpin 6 You have to make sure that nothing else in your program is messing with this line. Every time this goes from high to low information is sent to the LCD controller and this can really screw up your display. You can inadvertently put signals on the other lines and you may never see any problems.

I cleared the display because I thought it was inocuous enough The clear display command takes much longer for the LCD controller to complete than almost any other command. In a time sensitive situation you could miss something while the LCD has the processor tied up.

And was worried that when changing from, for example, 10.05 to 9.90 (5 chars vs 4 chars), the last digit would still show up (ie 9.905). This is a valid concern that many people do not forsee. To get around this you position the cursor, print enough 'spaces' to cover up the longest possible previous message, reposition your cursor, and display your new data.

however I was still having an offset when trying to change lines This is a result of one of the few flaws in the LiquidCrystal library. The current library does use (or even look at) the first argument (number of characters per line) in the lcd.begin() statement. It merely assumes that all displays use the same address for the start of rows 3 and 4 which is incorrect. Your approach is the cleanest workaround without editing the library files.

Don

floresta's tutorial on character LCD addressing should help you understand the offset that 16*4 displays have:

http://web.alfredstate.edu/weimandn~~/lcd/lcd_addressing/lcd_addressing_index.html~~

I updated it two days ago after I found out that I had some incorrect information about the memory map for the rare double chip 16x1 display.

I hate broken links that one frequently finds in old posts. Please link to the main page so that if the underlying web pages get rearranged people can still find the information. It's like a jump table in programming (if you ever wrote or examined a monitor program for a microprocessor trainer you will know what I mean).

Follow the LCD Addressing link at http://web.alfredstate.edu/weimandn is the way I do it. I moved the LCD stuff up to the top of the page.

Don

Seems fine now! I changed the buttons to the analog pins to not interfere with the SPI pins, I am using a do...while in the calibration routine instead of recursive (so much smoother), but I think the main point was a huge idiocy of mine: I am supplying the board through a 2.1mm jack (2 wires, +9V and gnd), and I thought that took care of the grounding, but apparently not, because when I tried wiring the GND power pin to ground, the LCD just stopped showing garbage. I've done a stress test (pressing buttons for half an hour, at different rates, pressing several buttons at once, etc) and it behave perfectly! So I guess I had some floating voltages in the board that made the digital pins behavior erratical, thus screwing up the display.

Thank you so much everyone, your help was invaluable. Hope it's smooth sailing from here on out