16X2 LCD Weird Characters

I may have bitten off more than I can chew for my first project. I have a LCD and DS3231 rtc, with some various buttons, etc.

I have tested the "Hello World" lcd sketch, and it displays fine, so I feel confident it is not a wiring or connection issue.

I have also tested my full sketch with very many Serial.print statements, so I am also confident that the logic of the sketch is working.

What keeps happening is the LCD is displaying very weird characters, as I have seen reported many times, but I don't know what to do to troubleshoot any further.

Please forgive my messy code. I am using an Uno

#include <LiquidCrystal.h>
#include <Wire.h>
#include <RTClib.h>
#include <LowPower.h>
RTC_DS3231 rtc;

/*

*/
const int btnWakePin=3;
const int modeBtn=10;

//variable to mark last btn press
unsigned long lastBtnPress = millis();
int sleepWait = 15000; //wait 15 seconds before going back to sleep
unsigned long delayTime = 120000L; 
const int debounceDelay=150;

unsigned long startEdit = millis();  //timestamp of when edit mode began
int editDuration = 3000;             //milliseconds
int editMode = 0;                    //0-5 modes, time on & off hours & minutes

int lastSec=0;

//lcd setup
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = A2, d7 = 1;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

const int backLight=6; //backlight pin
const int feedOnPin=8;
const int feedOffPin=9;
const int openBtn = A0;
const int closeBtn = A1;
const int resetBtn = 1;

//create a mode counter
bool isEditing = false;
bool wasEditing=false;
int mode = 0;  //0 is time, 1 is feeder on, 2 is feeder off

//set default feeder times
volatile int feedOn[2] = { 12, 15 };
volatile int feedOff[2] = { 12, 16 };
volatile int editVals[6];  //create an array to hold all time values for editing

bool modeBtnState=HIGH;

String editModeName;
String editValue;
int hh = 0;
int mm = 0;
int val = 0;
String modeName;

//long button detection
int longPressDuration = 1000;  // milliseconds
int buttonPressState = 0;      //0-not pressed, 1-long press, 2-short pressf
int lastState=LOW;
int currentState;
unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;
bool isPressing = false;
bool isLongDetected = false;

void setup() {

  pinMode(backLight, OUTPUT);
  pinMode(btnWakePin, INPUT_PULLUP);
  pinMode(modeBtn, INPUT_PULLUP);
  pinMode(openBtn, INPUT_PULLUP);
  pinMode(closeBtn, INPUT_PULLUP);
  pinMode(resetBtn, INPUT_PULLUP);
  pinMode(feedOnPin, OUTPUT);
  pinMode(feedOffPin,OUTPUT);
  

  digitalWrite(feedOnPin, LOW);
  digitalWrite(feedOffPin, LOW);
  
  detachInterrupt(1);

  Serial.begin(9600);
  Serial.println("Setup Started");

  if(!rtc.begin()){
    Serial.println("RTC not found");
    rtc.adjust(DateTime(2025,01,01,12,15,0));
  }

  DateTime alarmTest = rtc.now()+TimeSpan(3*60);

  feedOn[0]=alarmTest.hour();
  feedOn[1]=alarmTest.minute();
  feedOff[0] = alarmTest.hour();
  feedOff[1] = alarmTest.minute()+3;

  Serial.print(feedOn[0]);
  Serial.print(":");
  Serial.println(feedOn[1]);

  lcd.begin(16,2);
  delay(500);
  
  digitalWrite(backLight, HIGH);
  delay(500);
  digitalWrite(backLight, LOW);
  lastSec=rtc.now().second();
  Serial.println("Setup finished");
}

void loop() {
  attachInterrupt(1,wakeUpBtn,LOW);
  
  if(rtc.now().second() != lastSec){
    Serial.print(rtc.now().hour());
    Serial.print(":");
    Serial.print(rtc.now().minute());
    Serial.print(":");
    Serial.println(rtc.now().second());
    lastSec=rtc.now().second();
  }

  //check if should exit edit mode
  if (isEditing) {
    if ((millis() - lastBtnPress) > editDuration) {
      lcd.clear();
      if (editMode < 5) {
        Serial.println(editMode);
        editMode += 1;
      } else {
        editMode = 0;
        //save all values

        //create new time struct value
        DateTime newDT(rtc.now().year(), rtc.now().day(), rtc.now().month(), editVals[0], editVals[1], rtc.now().second());
        rtc.adjust(newDT);
        feedOn[0] = editVals[2];
        feedOn[1] = editVals[3];
        feedOff[0] = editVals[4];
        feedOff[1] = editVals[5];
        Serial.println("finished editing");
        Serial.print("Feeder On: ");
        Serial.print(feedOn[0]);
        Serial.print(":");
        Serial.println(feedOn[1]);
        Serial.print("Feeder Off: ");
        Serial.print(feedOff[0]);
        Serial.print(":");
        Serial.println(feedOff[1]);
        Serial.print("Time: ");
        Serial.print(rtc.now().hour());
        Serial.print(":");
        Serial.println(rtc.now().minute());
        

        isEditing = false;
        wasEditing = true;
      }
      lastBtnPress = millis();
    }
  }

   //check for short vs long press
  if (!isEditing) {
    currentState=digitalRead(modeBtn);
    if (lastState==HIGH && currentState ==LOW){//PRESS IS DETECTED
      pressedTime=millis();
      isPressing=true;
      isLongDetected=false;
    } else if (lastState==LOW && currentState==HIGH){//BUTTON IS RELEASED
      isPressing=false;
      releasedTime=millis();

      long pressedDuration = releasedTime - pressedTime;
      if(pressedDuration < longPressDuration){
        Serial.println("Short Press");
        buttonPressState=2;
      }
    }

    if(isPressing==true && isLongDetected==false){
      long pressedDuration = millis() - pressedTime;

      if(pressedDuration >= longPressDuration){
        Serial.println("Long Press");
        isLongDetected=true;
        buttonPressState=1;
        isEditing=true;
        Serial.println("Editing Started");
      }
    }
    
    lastState=currentState;
  }

  //if going into edit mode, grab all time values
  if (buttonPressState == 1 && wasEditing == false) {
    isEditing = true;
    wasEditing = true;
    getEditValues();
  }
  if (buttonPressState == 2) {
    lcd.clear();
    //if short press, change mode
      if (mode < 2) {
        mode = mode + 1;
      } else {
        mode = 0;
      }
      modeBtnState = HIGH;
    }

  //logic for edit mode goes here
  if (isEditing) {
    switch (editMode) {
      case 0:
        {  //edit time hours
          editModeName = "Edit Time      ";
          editValue = "HH: ";
          val = editVals[editMode];
        }
        break;
      case 1:
        {  //edit time minutes
          editModeName = "Edit Time      ";
          editValue = "MM: ";
          val = editVals[editMode];
        }
        break;
      case 2:
        {  //edit feeder on hours
          editModeName = "Edit Feeder On ";
          editValue = "HH: ";
          val = editVals[editMode];
        }
        break;
      case 3:
        {  //edit feeder on minutes
          editModeName = "Edit Feeder On ";
          editValue = "MM: ";
          val = editVals[editMode];
        }
        break;
      case 4:
        {  //edit feeder off hours
          editModeName = "Edit Feeder Off";
          editValue = "HH: ";
          val = editVals[editMode];
        }
        break;
      case 5:
        {  // edit feeder off minutes
          editModeName = "Edit Feeder Off";
          editValue = "MM: ";
          val = editVals[editMode];
        }
        break;
    }
  }

//check if button is pressed in edit mode
  if (isEditing) {
    if (debounceButton(modeBtnState) == LOW && modeBtnState == HIGH) {  //press detected
      modeBtnState = LOW;
      Serial.println("mode button pressed");
      //reset sleep timer
      lastBtnPress = millis();

      // increase value
      editVals[editMode] += 1;
      //check if it should be max 24 or 60
      int maxValue;
      if (editMode == 0 || editMode == 2 || editMode == 4) {  //hour value
        maxValue = 23;
      } else maxValue = 59;
      if (editVals[editMode] > maxValue) editVals[editMode] = 0;
    }
    if (debounceButton(modeBtnState) == HIGH && modeBtnState == LOW) {
      modeBtnState == HIGH;
    } else modeBtnState = HIGH;
  }

//switch case to determine which mode
  switch (mode) {
    case 0:
      modeName = "Time             ";
      hh = rtc.now().hour();
      mm = rtc.now().minute();
      break;
    case 1:
      modeName = "Feeder On        ";
      hh = feedOn[0];
      mm = feedOn[1];
      break;
    case 2:
      modeName = "Feeder Off       ";
      hh = feedOff[0];
      mm = feedOff[1];
      break;
  }

  //CHECK IF IT SHOULD OPEN
  if(rtc.now().hour()==feedOn[0] && rtc.now().minute()==feedOn[1]){
    openFeeder();
  }

  //check if it should close
  if(rtc.now().hour()==feedOff[0] && rtc.now().minute()==feedOff[1]){
    closeFeeder();
  }

  //check to see if 15 seconds has passed, then go to sleep
  if((millis() - lastBtnPress)> sleepWait){
     shutDown();
  }

  //logic for LCD display goes here
  if(!isEditing){//regular display mode
    lcd.setCursor(0,0);
    lcd.print(modeName);
    lcd.setCursor(0,1);
    lcd.print(hh);
    lcd.print(":");
    lcd.print(mm);
  }
  if(isEditing){
    lcd.setCursor(0,0);
    lcd.print(editModeName);
    lcd.setCursor(0,1);
    lcd.print(editValue);
    lcd.print(": ");
    lcd.print(val);
  }

  //check for open button
  if(!digitalRead(openBtn)){//button pressed
    digitalWrite(feedOnPin, HIGH);
    Serial.println("Open Button Pressed");
  } else digitalWrite(feedOnPin, LOW);

  //check for close button
  if(!digitalRead(closeBtn)){//button pressed
    digitalWrite(feedOffPin, HIGH);
    Serial.println("Close Button Pressed");
  } else digitalWrite(feedOffPin, LOW);
  
  }
//end of loop

void shutDown(){
  Serial.println("Good night");
  Serial.flush();
  digitalWrite(backLight, LOW);
  lcd.clear();
  lcd.noDisplay();
  attachInterrupt(1, wakeUpBtn, LOW);
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);  
  wakeUp();
  detachInterrupt(1);
}

//simple handler for 8s timer wake up
void wakeUp(void){
  Serial.println("Wakeing up");
  detachInterrupt(1);
}

//interrupt when wake button is pressed
void wakeUpBtn(void){
  Serial.println("Wake Button Pressed");
  detachInterrupt(1);
  digitalWrite(backLight, HIGH);
  lcd.display();
  lastBtnPress=millis();
}

//opens the feeder
void openFeeder(void){
  Serial.println("Opening");
  digitalWrite(feedOnPin, HIGH);
  delay(delayTime);
  Serial.println("Opened");
  digitalWrite(feedOnPin, LOW);
}

//closes the feeder
void closeFeeder(void){
  Serial.println("Closing");
  digitalWrite(feedOffPin, HIGH);
  delay(delayTime);
  Serial.println("Closed");
  digitalWrite(feedOffPin, LOW);
}

void getEditValues(){
  editVals[0]=rtc.now().hour();
  editVals[1]=rtc.now().minute();
  editVals[2]=feedOn[0];
  editVals[3]=feedOn[1];
  editVals[4]=feedOff[0];
  editVals[5]=feedOff[1];
}

//function to debounce
boolean debounceButton(boolean state) {
  boolean stateNow = digitalRead(modeBtn);
  if (state != stateNow) {
    delay(debounceDelay);
    stateNow = digitalRead(modeBtn);
  }
  return stateNow;
}

Welcome to the forum

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = A2, d7 = 1;
const int resetBtn = 1;

    pinMode(resetBtn, INPUT_PULLUP);

Are you using pin 1 for 2 different purposes

I am not wanting to use it for 2 purposes. That is a mistake. I will change the resetBtn to 13.

Note too that pin 1 is used by the Serial interface of the Uno so is best avoided altogether

1 Like

Thanks. I wasn't aware of that.

Thanks. That was the fix. I changed to pin 2 and all is working.

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