I've been working on a project (an aquarium controller) that uses a 16x2 LCD display and a rotary encoder to navigate the menus. Some people asked if I could post the code, so here it is. I've stripped out most of the rest of the program, but there is still a fair amount of 'extra' code in there, mainly to make it so I can cut and paste it without much modification. I used a rotary encoder like this one: http://www.mouser.com/ProductDetail/Bourns/PEL12T-4225S-S1024/?qs=sGAEpiMZZMsWp46O%252bq11WTU2mLe3dQ9dMsin9UeDZ%252bM%3D The switch is combined with common cathode LEDs, so you have to add a pull-down resistor to a digital pin that is pulled high when the switch is pressed.
The text for the LCD menus takes up a lot of memory, so I put it all in Flash and access it using strcpy_P to save SRAM. I use the oopinchaneint library and adaencoder library from Adafruit, along with liquidTWI i2c LCD library.
The first bunch of code is just all the variable definitions, etc and some rudimentary code in loop() to get you into the actual menu routines. Feel free to comment, critique, use, or abuse it as you wish. I am not a professional programmer and do not claim this to be the best code. I'm simply posting it in the hopes it may help others.
Part 1: variable definitions, setup() and void() routines:
#include <avr/pgmspace.h> // library for keeping variables in Flash RAM
#include <LiquidTWI.h> // i2c liquid crystal library
#include <ooPinChangeInt.h>
#include <AdaEncoder.h> // adafruit.com eoncoder routine.
#include <Wire.h>
#define menuTimeout 10000 //delay before settings menus time out & return
#define sumpLevelSwitchPin 2
#define EncoderPin1 6 // Rotary Encoder Pins
#define EncoderPin2 7 //
#define buttonPin A2 // pin for encoder button
#define ATOlevel 20
#define FanPin 0
#define Heater1Pin 1
#define Heater2Pin 2
#define Light1Pin 3
#define Light2Pin 4
#define Light3Pin 5
#define ATOPin 6
#define SkimmerPin 7
#define Powerhead1Pin 8
#define Powerhead2Pin 9
#define Powerhead3Pin 10
#define ReturnPumpPin 11
#define CircPumpPin 12
#define Doser1Pin 13
#define Doser2Pin 14
#define AuxPin 15
unsigned long lastTime;
unsigned long lastMills=0;
unsigned long timeout;
byte time[] = {
12, 00, 00, 00}; // hr, min, sec, msec
byte Date[] = {
01, 01, 13}; // month, day, year
byte Light1On[] = {
9, 00}; // hr, min
byte Light1Off[] = {
22, 00}; // hr, min
byte Light2On[] = {
10, 00};
byte Light2Off[] = {
21, 00};
byte Light3On[] = {
21,00};
byte Light3Off[] = {
9,00};
byte ATO[] = {
1, 30}; // full, empty depth of ATO in cm
byte Doser1On[] = {
0,0};
byte Doser1Off[] = {
0,0};
byte Doser2On[] = {
0,0};
byte Doser2Off[] = {
0,0};
byte Outlets[16] = {
1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1};
char buffer[18];
prog_char mainMenu1[] PROGMEM = "1.Water Change ";
prog_char mainMenu2[] PROGMEM = "2.Feed ";
prog_char mainMenu3[] PROGMEM = "3.Man Override ";
prog_char mainMenu4[] PROGMEM = "4.Settings ";
prog_char mainMenu5[] PROGMEM = "5.Exit ";
PROGMEM const char *mainMenu[] = {
mainMenu1, mainMenu2, mainMenu3, mainMenu4, mainMenu5};
prog_char OverrideMenu0[] PROGMEM = "1. Fan ";
prog_char OverrideMenu1[] PROGMEM = "2. Heater 1 ";
prog_char OverrideMenu2[] PROGMEM = "3. Heater 2 ";
prog_char OverrideMenu3[] PROGMEM = "4. Light1 ";
prog_char OverrideMenu4[] PROGMEM = "5. Light2 ";
prog_char OverrideMenu5[] PROGMEM = "6. Light3 ";
prog_char OverrideMenu6[] PROGMEM = "7. ATO ";
prog_char OverrideMenu7[] PROGMEM = "8. Skimmer ";
prog_char OverrideMenu8[] PROGMEM = "9. Powerhead 1 ";
prog_char OverrideMenu9[] PROGMEM = "10.Powerhead 2 ";
prog_char OverrideMenu10[] PROGMEM = "11.Powerhead 3 ";
prog_char OverrideMenu11[] PROGMEM = "12.Return Pump ";
prog_char OverrideMenu12[] PROGMEM = "13.Circ Pump ";
prog_char OverrideMenu13[] PROGMEM = "14.Dosing Pump1";
prog_char OverrideMenu14[] PROGMEM = "15.Dosing Pump2";
prog_char OverrideMenu15[] PROGMEM = "16.Aux Outlet ";
prog_char OverrideMenu16[] PROGMEM = "17.Exit ";
PROGMEM const char *OverrideMenu[] = {
OverrideMenu0, OverrideMenu1, OverrideMenu2, OverrideMenu3, OverrideMenu4, OverrideMenu5,
OverrideMenu6, OverrideMenu7, OverrideMenu8, OverrideMenu9, OverrideMenu10, OverrideMenu11,
OverrideMenu12, OverrideMenu13, OverrideMenu14, OverrideMenu15, OverrideMenu16};
prog_char OverrideSelect0[] PROGMEM = "1. Auto ";
prog_char OverrideSelect1[] PROGMEM = "2. Off ";
prog_char OverrideSelect2[] PROGMEM = "3. On ";
PROGMEM const char* OverrideSelect[]={
OverrideSelect0, OverrideSelect1, OverrideSelect2};
prog_char settingsMenu0[] PROGMEM = "1. Time ";
prog_char settingsMenu1[] PROGMEM = "2. Date ";
prog_char settingsMenu2[] PROGMEM = "3. Light Times ";
prog_char settingsMenu3[] PROGMEM = "4. Temps ";
prog_char settingsMenu4[] PROGMEM = "5. ATO Level ";
prog_char settingsMenu5[] PROGMEM = "6. Dosing Pumps";
prog_char settingsMenu6[] PROGMEM = "7. Return ";
PROGMEM const char* settingsMenu[]={
settingsMenu0, settingsMenu1, settingsMenu2, settingsMenu3, settingsMenu4, settingsMenu5, settingsMenu6};
prog_char lightTimeMenu0[] PROGMEM = "1. Low On ";
prog_char lightTimeMenu1[] PROGMEM = "2. High On ";
prog_char lightTimeMenu2[] PROGMEM = "3. High Off ";
prog_char lightTimeMenu3[] PROGMEM = "4. Low Off ";
prog_char lightTimeMenu4[] PROGMEM = "5. Return ";
PROGMEM const char* lightTimeMenu[]={
lightTimeMenu0, lightTimeMenu1, lightTimeMenu2, lightTimeMenu3, lightTimeMenu4};
prog_char tempMenu0[] = "1. Heater On ";
prog_char tempMenu1[] = "2. Fan On ";
prog_char tempMenu2[] = "3. Lights Off ";
prog_char tempMenu3[] = "4. Return ";
PROGMEM const char* tempMenu[]={
tempMenu0, tempMenu1, tempMenu2, tempMenu3};
prog_char ATOMenu0[] = "1. Set Full ";
prog_char ATOMenu1[] = "2. Set Empty ";
prog_char ATOMenu2[] = "3. Add 1 cm ";
prog_char ATOMenu3[] = "4. Return ";
PROGMEM const char* ATOMenu[]={
ATOMenu0, ATOMenu1, ATOMenu2, ATOMenu3};
byte heatOnTemp = 77; // Temp to turn Heat on
byte fanOnTemp = 79; // Temp to turn fan on
byte lightOffTemp = 82;
char id=0;
int total = 0;
int8_t clicks;
char *LCDline1[4]; // pointer array for pointers to LCD line 1 strings
char Line1Msg0[] = "00:00 Temp 88 F";
char Line1Msg1[] = "***Temp High ***";
char Line1Msg2[] = "*** Temp Low ***";
char Line1Msg3[] = "*** ATO Low ***";
char Line1Msg4[] = "**No Temp Probe*";
char Line1Msg5[] = "**No Echo Probe*";
char LCDline2[] = "Fan: Off Htr: Off ATO: 100% ";
LiquidTWI lcd(0);
AdaEncoder encoderA = AdaEncoder('a', EncoderPin1, EncoderPin2);
AdaEncoder *knob=NULL;
void setup(){
Serial.begin(9600);
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, LOW);
lcd.begin(16,2);
Serial.print("free ram: ");
Serial.println(freeRam());
}
void loop(){
lcd.clear();
lcd.print("push button");
lcd.setCursor(0,1);
lcd.print("to start...");
while(!digitalRead(buttonPin)){
}
knob = AdaEncoder::genie(); // check for input from rotary encoder
if (knob != NULL || digitalRead(buttonPin)){
while(digitalRead(buttonPin));
delay(20);
lcd.clear();
lcd.print("going to menu");
delay(1000);
menu();
}
}
int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}