Freezing issue with 16x2 LCD

Hello, I'm having an issue using an arduino Uno (circa 2007?) with a pair of 16x2 LCDs of different uncertain origins. neither has the little serial interface board.

I thought I'd make a little servo calibration device to that reads a potentiometer, calculates a number of microseconds for the servo motor pulse width, and then display it on the screen, so I could center the servos and write down the number. And it worked. Except that the code freezes after a few seconds. At first I thought maybe EMI from the servo was throwing something off, so I took out the servo, and then I moved to 8 bit mode instead of 4 bit mode. Neither helped. I had it also blink the built in LED, and that stops blinking at the same time as the LCD freezes, so it seems like the program is stopping.

I used a "MemoryFree" library I found to print out the free memory, which does not seem to change at all.

I'm a bit puzzled as to why it stops. I could use it like this I guess, by just pressing reset every few seconds, but I'd like to know what I've gotten wrong here.


////Libraries/////////////////////////////////////////////////////////
#include <LiquidCrystal.h> // includes the LiquidCrystal Library 
#include <MemoryFree.h> //troubleshooting memory leak

//#include <Servo.h>

////Definitions/////////////////////////////////////////////////////////
//#define DEBUG 1
//const int lcd_rs = 2;
//const int lcd_en = 3;
//const int lcd_rw = 4;
const int rheostatPin = A0;

////Global variables/////////////////////////////////////////////////////////
int rheostatVal = 0;
int dutyMicros = 0;
int lastDutyMicros = 0;
int lastFrameMillis = 0;
int lastResetMillis = 0;
int frameDelay = 200;         //how long between updating the screen and servos
int resetDelay = 1000;        //how long between resetting the LCD
bool statusLED = false;

LiquidCrystal lcd(2,   4,      3,  5,  6,  7,  8,  9, 10, 11, 12);
//                rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7
                                        //Parameters:
                                        //(rs, enable, d4, d5, d6, d7)
                                        //(rs, rw, enable, d4, d5, d6, d7)
                                        //(rs, enable, d0, d1, d2, d3, d4, d5, d6, d7)
                                        //(rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7)

//in addition, A and VDD go to +5v, K and VSS go to gnd.
//V0 goes to the wiper of a potentiometer between 5v and ground, for setting LCD contrast.
//RW goes to ground if a mode that uses it hasn't been selected.

//Servo testservo;

////Setup/////////////////////////////////////////////////////////
void setup() 
{ 
  //pinMode(a0, INPUT);   //not needed for analogRead();
  Serial.begin(9600);     //just for debugging
  Serial.println("Serial Ready");
  pinMode(LED_BUILTIN,OUTPUT);


  lcd.begin(16,2);      // Initializes the interface to the LCD screen, and specifies the dimensions (width and height) of the display } 

  //testservo.attach(12);  //attaches a servo at digital pin 9
  //testservo.writeMicroseconds(1500); //nominal midpoint

  delay(300);
}

////Main/////////////////////////////////////////////////////////
void loop()
{
  rheostatVal = analogRead(rheostatPin);    //read the potentiometer
  dutyMicros = ServoMicroseconds(rheostatVal);

  //so next we need to make a function that turns the potentiometer value into microseconds for the servo duty cycle
  //and then we display that on the screen instead of the analog read value
  //and then we write that to the servo.
  
  if ( millis() >= (lastFrameMillis + frameDelay) ) //check if the value changed and if it's been at least 0.1 seconds
  {
    lcd.clear();                      //clear the LCD
    lcd.print(dutyMicros);           //print the value
    lastFrameMillis = millis();       //record the current millis()
    lastDutyMicros = dutyMicros;    //record the current read value
    //Serial.println(dutyMicros);
  }
  //testservo.writeMicroseconds(dutyMicros);

  if (millis() >= (lastResetMillis + resetDelay) )
  {
    lcd.begin(16,2);
    //lcd.setCursor(0, 0);
    Serial.println("resetting cursor");
    statusLED = !statusLED;
    digitalWrite(LED_BUILTIN, statusLED);
    lastResetMillis = millis();
    Serial.print("freeMemory()=");
    Serial.println(freeMemory());
  }
} 


////functions/////////////////////////////////////////////////////////
int ServoMicroseconds(int a)
{
  int m = -1;
  m = map(a, 0, 1023, 475, 2550 );  //map(value, fromLow, fromHigh, toLow, toHigh)
  return m;
}

It sounds like you might be trying to power the servo from the 5V output, which can cause all kinds of trouble, including destruction of the Arduino. If so, don't. Use a 4xAA battery pack instead, and connect the grounds.

There is nothing obviously wrong with the code (see below), nor any reason to suspect a memory leak, so remove the memory free library and try again. With separate servo power.

Note: you are using millis() incorrectly. To avoid overflow problems, always subtract the last stored value of the millisecond counter from the current value, and compare that result with the timeout period.

Thanks.

At the moment there is no servo present, just the Arduino and the 16x2 LCD, running off the computer's USB. I've had it count out seconds, and it seems to get to 31 each time.

i'll try fixing my use of millis(), that could be the problem.

These must be unsigned long, not int (which is the problem: leads to 32767 milliseconds, maximum).

1 Like

Ah, thank you. After fixing my use of millis(), it did not freeze but instead stopped delaying at all after 31 seconds, this seems to be why. Now it is still running properly after 44 seconds and counting.

Everything works now. Final impementation:

/******************************
* 16x2 LCD test
* 7/24/2023
* 
*/

////Notes/////////////////////////////////////////////////////////////
//This is meant to serve as a servo tester.
//

////Libraries/////////////////////////////////////////////////////////
#include <LiquidCrystal.h> // includes the LiquidCrystal Library 
#include <Servo.h>

////Definitions/////////////////////////////////////////////////////////
//#define DEBUG 1
const int rheostatPin = A0;
const int frameDelay = 200;         //how long between updating the screen and servos

////Global variables/////////////////////////////////////////////////////////
unsigned long lastFrameMillis = 0;  //this is compared to millis(), which returns an unsigned long. Used for delaying without delay.

//LiquidCrystal lcd(2, 3, 4, 5, 6, 7); // Creates an LCD object. Parameters: (rs, enable, d4, d5, d6, d7)

LiquidCrystal lcd(2,   4,      3,  5,  6,  7,  8,  9, 10, 11, 12);
//                rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7
                                        //Parameters:
                                        //(rs, enable, d4, d5, d6, d7)
                                        //(rs, rw, enable, d4, d5, d6, d7)
                                        //(rs, enable, d0, d1, d2, d3, d4, d5, d6, d7)
                                        //(rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7)

//in addition, A and VDD go to +5v, K and VSS go to gnd.
//V0 goes to the wiper of a potentiometer between 5v and ground, for setting LCD contrast.
//RW goes to ground if a mode that uses it hasn't been selected.

Servo testservo;

////Setup/////////////////////////////////////////////////////////
void setup() 
{ 
  //pinMode(a0, INPUT);   //not needed for analogRead();
  lcd.begin(16,2);      // Initializes the interface to the LCD screen, and specifies the dimensions (width and height) of the display } 
  testservo.attach(13);  //attaches a servo at digital pin 9
  testservo.writeMicroseconds(1500); //send servo to nominal midpoint first.
  delay(300);
}

////Main/////////////////////////////////////////////////////////
void loop()
{
  int rheostatVal = analogRead(rheostatPin);          //read the potentiometer
  int dutyMicros = ServoMicroseconds(rheostatVal);    //convert the potentiometer reading to a microseconds value for the servo
  testservo.writeMicroseconds(dutyMicros);            //write that value to the servo
  
  if ( (millis() - lastFrameMillis) >= frameDelay )   //check if the value changed and if it's been at least 0.1 seconds
  {
    lcd.clear();                                      //clear the LCD
    lcd.print(dutyMicros);                            //print the value
    lastFrameMillis = millis();                       //record the current millis()
  }
}


////functions/////////////////////////////////////////////////////////
int ServoMicroseconds(int a)
{
  int m = -1;
  m = map(a, 0, 1023, 475, 2550 );  //map(value, fromLow, fromHigh, toLow, toHigh)
  return m;
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.