UT Clock with Menu
Added the menu system to the UT Clock. Had to add an abort/cancel option (i.e. 'X') to the "Done" menu option. An obvious omission.
Here is the code:
// Include RTC libraries and initialise
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 rtc;
// Include LCD library and initialise
#include <LiquidCrystal.h>
// Define Adruino pin numbers for LCD
#define en 7
#define rs 8
#define d4 9
#define d5 10
#define d6 11
#define d7 12
#define led 13
// Set LCD
LiquidCrystal lcd(rs,en,d4,d5,d6,d7);
/* ROTARY ENCODER AND PUSH BUTTON POLLING CODE */
#define Clk 2
#define DT 3
#define SW 4
// Poll and debounces a Keyes rotary and push button switch
// Inputs:
// Clk (Pin A with 10k pullup resistor)
// DT (Pin B with 10k pullup resistor)
// SW (Switch without a pullup resistor)
// + (Power for pullup resistors)
// Gnd
// Exports:
// UpdateSwitch (bool)
// Switch [LOW|HIGH]
// EncoderPos (int)
volatile bool UpdateSwitch=false;
volatile byte Switch=HIGH;
volatile int EncoderPos=0;
ISR(TIMER0_COMPB_vect) {
static byte testClk=(PIND>>Clk)&1;
static byte testDT=(PIND>>DT)&1;
static byte lastClk=LOW;
static byte lastDT=LOW;
static bool flagClk=false;
static bool flagDT=false;
static int encoderDir=0;
static byte testSW=HIGH;
static byte statusSW=HIGH;
static byte cntSW=0;
// Update Encoder Position
lastClk=testClk; // Save Clk
lastDT=testDT; // Save DT
testClk=(PIND>>Clk)&1; // Get Clk
testDT=(PIND>>DT)&1; // Get DT
if (testClk!=lastClk) { // Change in Clk?
flagClk=true; // Flag Clk has changed
encoderDir=-1; // Assume it is the last flag to change
}
if (testDT!=lastDT) { // Change in DT?
flagDT=true; // Flag DT has changed
encoderDir=1; // Assume it is the last flag to change
}
if (flagClk&&flagDT) { // Both flags have changed
EncoderPos+=encoderDir; // Update the encoder
flagClk=false; // Reset Clk flag
flagDT=false; // Reset DT flag
}
// Update switch with 10ms debounce
testSW=(PIND>>SW)&1;
if (testSW!=statusSW) {
statusSW=testSW;
cntSW=10;
}
if (cntSW>0) {
cntSW--;
if (cntSW==0) {
Switch=statusSW;
UpdateSwitch=true;
}
}
}
/* MENU SET UP */
char* menuName[]={"Done ","Year ","Month ","Day ","Hour ","Minute ","Second "};
int menuSize=sizeof(menuName)/sizeof(char*);
int menuValue[]= { 1, 0, 1, 1, 0, 0, 0};
char menuNumeric[]={'N','Y','Y','Y','Y','Y','Y'};
int menuValueMin[]={ 0, 0, 1, 1, 0, 0, 0}; // Non-numeric values 'N','X' and 'Y'
int menuValueMax[]={ 2,999, 12, 31, 23, 59, 59};
int menuLevel=0;
void setup() {
// Initialise rotary encoder and push button
pinMode(Clk,INPUT_PULLUP); // Rotary Clk
pinMode(DT,INPUT_PULLUP); // Rotary DT
pinMode(SW,INPUT_PULLUP); // Rotary SW
// Turn on polling ISR
OCR0B=0xA0;
TIMSK0|=1<<OCIE0B;
// Initialise LCD
pinMode(led,OUTPUT);
digitalWrite(led,HIGH);
lcd.begin(16,2);
// Print a message to the LCD.
lcd.setCursor(0,0);lcd.print("Universal Time");
// Check RTC
if (!rtc.begin()) {
lcd.setCursor(0,1);lcd.print("RTC not found");
while (true);
} else if (!rtc.isrunning()) {
lcd.setCursor(0,1);lcd.print("RTC not running");
} else {
lcd.setCursor(0,1);lcd.print("RTC found");
}
// Set Universal Time based on Compile Time
int localTimeHour=-8; // Perth Western Australia
int localTimeMin=0; // Perth Western Australia
int uploadTimeSec=7; // For my computer
rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
DateTime now=rtc.now();
rtc.adjust(DateTime(now+TimeSpan(0,localTimeHour,localTimeMin,uploadTimeSec)));
// Set menu to current time
now=rtc.now();
menuLevel=-1; // Don't enter menu on start up
menuValue[0]='X'; // Set "done" status to abort
menuValue[1]=now.year();
menuValue[2]=now.month();
menuValue[3]=now.day();
menuValue[4]=now.hour();
menuValue[5]=now.minute();
menuValue[6]=now.second();
delay(1000);
lcd.clear();
}
bool processMenu(void) {
static int lastPos=0;
static int lastMenuLevel=-1;
// Pre-empt menu level display
if (menuLevel!=lastMenuLevel) {
lastMenuLevel=menuLevel;
lcd.clear();
lcd.setCursor(0,0);
if (menuLevel==0) {
lcd.print("Menu:");
lcd.setCursor(0,1);
lcd.print(menuName[EncoderPos]);
if (menuNumeric[EncoderPos]=='Y') {
lcd.print(menuValue[EncoderPos]);
} else {
if (menuValue[EncoderPos]==0) {
lcd.print("N");
} else if (menuValue[EncoderPos]==1) {
lcd.print("X");
} else {
lcd.print("Y");
}
}
} else if (menuLevel==1) {
lcd.print("Set:");
lcd.setCursor(0,1);
lcd.print(menuName[lastPos]);
if (menuNumeric[lastPos]=='Y') {
lcd.print(menuValue[lastPos]);
} else {
if (menuValue[lastPos]==0) {
lcd.print("N");
} else if (menuValue[lastPos]==2) {
lcd.print("Y");
} else {
lcd.print("X");
}
}
} else {
lcd.print("Rotary Encoder?");
}
}
// If push button pushed toggle menuLevel
if (UpdateSwitch) {
UpdateSwitch=false;
if (Switch==LOW) {
// Re-enter menu if button pushed (for long enough)
if (menuLevel==-1) {
menuLevel=0; // Item menu
lastPos=1; // Trigger menu update
EncoderPos=0; // Done
menuValue[0]=1; // 'X'
} else {
// Toggle menu level
menuLevel=1-menuLevel;
if (menuLevel==0) {
// Save item menu position
EncoderPos=lastPos;
// Exit menu if done!
if ((EncoderPos==0)&&(menuValue[EncoderPos]!=0)) menuLevel=-1;
// Any final work to be done? Set the time!
if (menuValue[EncoderPos]==2) {
rtc.adjust(DateTime(menuValue[1],menuValue[2],menuValue[3],menuValue[4],menuValue[5],menuValue[6]));
}
} else {
// Set value for edit menu
EncoderPos=menuValue[lastPos];
}
}
}
}
// If encoder turned
if (menuLevel==0) { // Select menu item
if (lastPos!=EncoderPos) {
if (EncoderPos>=menuSize) EncoderPos=0;
if (EncoderPos<0) EncoderPos=menuSize-1;
lastPos=EncoderPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Menu:");
lcd.setCursor(0,1);
lcd.print(menuName[EncoderPos]);
if (menuNumeric[EncoderPos]=='Y') {
lcd.print(menuValue[EncoderPos]);
} else {
if (menuValue[EncoderPos]==0) {
lcd.print("N");
} else if (menuValue[EncoderPos]==2) {
lcd.print("Y");
} else {
lcd.print("X");
}
}
}
} else if (menuLevel==1) { // Edit menu item value
if (menuValue[lastPos]!=EncoderPos) {
if (EncoderPos>menuValueMax[lastPos]) EncoderPos=menuValueMin[lastPos];
if (EncoderPos<menuValueMin[lastPos]) EncoderPos=menuValueMax[lastPos];
menuValue[lastPos]=EncoderPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Set:");
lcd.setCursor(0,1);
lcd.print(menuName[lastPos]);
if (menuNumeric[lastPos]=='Y') {
lcd.print(menuValue[lastPos]);
} else {
if (menuValue[lastPos]==0) {
lcd.print("N");
} else if (menuValue[lastPos]==2) {
lcd.print("Y");
} else {
lcd.print("X");
}
}
}
}
return (menuLevel!=-1); // Return true if menu active
}
void loop() {
if (!processMenu()) { // Menu not active
// Present the results
DateTime now=rtc.now();
// Print date on first line of LCD
lcd.clear();
lcd.setCursor(0,0);
lcd.print(now.year());
lcd.print('/');
if (now.month()<10) lcd.print(0);
lcd.print(now.month());
lcd.print('/');
if (now.day()<10) lcd.print(0);
lcd.print(now.day());
// Print time on second line of LCD
lcd.setCursor(0,1);lcd.print("UT: ");
if (now.hour()<10) lcd.print(0);
lcd.print(now.hour());
lcd.print(':');
if (now.minute()<10) lcd.print(0);
lcd.print(now.minute());
lcd.print(':');
if (now.second()<10) lcd.print(0);
lcd.print(now.second());
delay(1000);
}
}
I am happy with the code. Time for the front panel.
Magic
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.