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;
}
