/* Christian Couderc 2015-07-15
https://www.voilec.com/pages/arduino_menu.php
Menu_LCD2004_RotaryEncodeur_Voilec.ino
Status : OK
Tree exemple for this demo of menus: add a new menu by adding in menuSwitch only -> case wxyz / menu name / break
Level 0 . . . . . . . . . . . . . Level 1 . . . . . . . . . . . . . . Level 2 . . . . . . . . . . . . . . Level 3
1000 ______________________________ 1100 ______________________________ 1110 ______________________________ 1111
! !
! !______________________ 1200 ______________________________ 1210
! ! !
! ! !_____________________ 1220 ______________________________ 1221
! ! !
! ! !_____________________ 1222
! ! !
! ! !_____________________ 1223
! !______________________ 1300
!
!
2000
!
!
3000
!
!
4000 ______________________________ 4100 ______________________________ 4110 ______________________________ 4111
!
!______________________ 4112
(9000) With 4 levels 10 branches, maximum: 9999
(Easy to extend....)
*/
// http://www.baldengineer.com/arduino-f-macro.html
/*
Option : RollingH :
No : 1 > 2 > 3=last > (Beep + return 3)
Yes : 1 > 2 > 3 > 1 > 2 > 3 ...
*/
//////////////////////////////////////// Bibliotheques
#include <OneWire.h> // Dallas
//#include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC RTC + AT24C32
//#include <Time.h> //http://www.arduino.cc/playground/Code/Time
#include <Wire.h> //http://arduino.cc/en/Reference/Wire (included with Arduino IDE)void setup()
///*#include <Adafruit_BMP085.h> // Pressure
#include <Encoder.h>
//#include <RotaryEncoder.h>
#include <LiquidCrystal_I2C.h>
char buffer [50] ;
char buffer2 [50] ;
///////////////////////////// LCD
byte PCF8574_8IO = 0x20 ; // I2C basic adress of expander port
byte I2C_LCD = 7 + PCF8574_8IO ; // (configuration of the 3 adresse lines) + I2C basic adress =Final address of display port
byte nbcharparligne = 20 ; // LCD display size
byte nblignes = 4 ; // LCD display size
/////////////////////////////////////// Led Colors
// 1 2 4
#define Black 0 // . . .
#define Red 1 // R . .
#define Green 2 // 0 G .
#define Yellow 3 // R G .
#define Blue 4 // . . B
#define Magenta 5 // R . B
#define Cyan 8 // . G B
#define White 7 // R G B
char partition [] = {Red, Green, Blue, Yellow, Cyan, Magenta, Black, White, Black} ; // Tri color led animation
boolean anode ; // Common anode = TRUE or cathode = FALSE
// definition du nom du sketch
#define ProgName "Menu_LCD2004_RotaryEncodeur_Voilec.ino"
///////////////////////////////////// General purpose /////////////////////////////////
long Chrono ;
byte Cmpt ;
///////////////////////////////////// Hardware Wiring for Uno /////////////////////////////////
const int DS3232Alarm = 0 ; // Pin 19 Mega2560
// Encoder knobLeft(3, 4) ; // Good Performance: only the first pin has interrupt capability
// Pins 0,1 not avalaible (Rx Tx reserved for serial USB)
// Pins 2 (int 0 UNO) Alarm fronm clock) // Pin 3 (int 1 UNO) + 4 used by optical encoser
const int PushButton = 5 ; // Pushbutton encodeur optique Fil vert eau
// 6 ; Free
const int Buzzer = 7 ; // Small loudspeaker
const int LedBurn = 8 ; // 0 : Active chaudière (led R sur platine éteinte)
// 9 : Free pin
//const int SS = 10 ; // SD card https://tutoarduino.com/les-cartes-sd/
//const int MISO = 12 ; // SD card http://www.geeetech.com/wiki/index.php/Arduino_SD_card_Module
//const int MOSI = 11 ; // SD card
//const int SCLK = 13 ; // SD card
const int PushS = 9 ;
const int LedTriRed = 10 ; // Commun cathode Fils bonne couleur
const int LedTriGreen = 11 ;
const int LedTriBlue = 12 ;
const int LedCard = 13 ;
const int Pot10t = A0 ; // Potentiomètre 10p Analog A1 10 bits : Fil violet
const int Pot270 = A1 ; // Potentiomètre 270 Analog A0 10 bits : Fil blanc
const int VbatCel = A2 ; //
const int VbatMot = A3 ; //
// A4, A5 : Free pins
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
long NewLeft, PositionLeft = -999;
byte Order[4] = {0, 0, 0, 0} ; // For hierarchical menu 1,2,3,..9 ,11, 12,.. 99, 111,112,...999, 1111, 112, ..9999 : weight 10 ^ i Fix level allowed (unliited)
byte BkLevel, BkOrd, ActualLevel = 0 ; //indice of 4 levels array 0 to 3
byte OrderSize = sizeof (Order) ;
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Initialisations, must to be placed here before setup for global visibility
//time_t myTime; // Clocm DS3232
//tmElements_t tm;
Encoder knobLeft(3, 4) ; // Opto constructor
OneWire ds(6); // Dallas DS18s20 constructor
//Adafruit_BMP085 bmp; // Initialise pressure sensor
LiquidCrystal_I2C lcd(I2C_LCD, nbcharparligne, nblignes); // LCD constructor
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void setup()
{
Serial.begin(115200); // Start the serial interface
// affiche le nom du sketch
Serial.println(ProgName);
Serial.println("\n");
Wire.begin(); // Start the I2C interface
initPorts () ; // For leds and pushbutton
initLcd20x4() ; // initialize the lcd
chronoStart () ;
//knobLeft.write(0);
menuSelect (1) ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void loop()
{
menuSelect (ckeckEncoderOrder ()) ;
delay (100) ;
Cmpt ++ ;
flashLeds (Yellow, Cmpt / 16) ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void menuSelect (byte posEnc) ////////////////////// Menu Niveau + Branche
{
// byte Order[4] = [0,0,0,0] ; // For hierarchical menu 1000..9999 : weight 10 ^ i
// byte Level = 0 ; //indice of 4 levels array 0 to 3
if (posEnc != 128) // New event
{
if (posEnc == 129)
soundLeft () ;
else
{
BkLevel = ActualLevel ; // actual valid menu, Save Vertical
BkOrd = Order[ActualLevel] ; // Horizontal
if (posEnc == 255)
encreaseLevel () ; // Push on button : Level Encrease
else
{
posEnc = 1 + ((posEnc / 4) % 10) ; // Rotate : 0 .. 9, 10 branches max by level
Order[ActualLevel] = posEnc ;
}
}
}
menuSwitch (posEnc) ; // For dynamics menus only
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
byte ckeckEncoderOrder ()
{
if (digitalRead (PushButton) == 0 ) // Action level up down
{
debounce ();
return 255 ;
}
else
{
NewLeft = knobLeft.read(); // Optical encoder
if (NewLeft != PositionLeft)
{
PositionLeft = NewLeft ;
if (PositionLeft >= 0)
return ((PositionLeft) % 127) ;
else
{
if (PositionLeft > -5)
return 129;
else
return (decreaseLevel () ) ;
}
}
else // Knob don't move
return 128 ;
}
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
long ckeckEncoderValue () // For menus adjusting parameter only !
{
if (digitalRead (PushButton) == 0 ) // Validation of value > encrease
{
encreaseLevel();
menuSelect (0) ; // 5400 -> 5410 = Write position
}
else
return ( knobLeft.read()); // Optical encoder
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int menuCalc ()
{
// byte Order[4] = [0,0,0,0] ; // For hierarchical menu 100 to 9999 , without 0 inside : weight 10 ^ (3-i)
if (ActualLevel < 3) // Clean by security
Order[3] = 0 ;
if (ActualLevel < 2)
Order[2] = 0 ;
if (ActualLevel < 1)
Order[1] = 0 ;
int calc = 1000 * Order[0] + 100 * Order[1] + 10 * Order[2] + Order[3] ; //indice of 4 levels array 0 to 3
return calc ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void menuDebug () // Shows all menu parameters, to be replaced by true menu
{
lcd.setCursor (0, 0) ;
lcd.print(buffer);
lcd.setCursor (0, 1) ;
sprintf (buffer, "Order %d %d %d %d ", Order[0], Order[1], Order[2], Order[3] );
lcd.print(buffer);
lcd.setCursor (0, 2) ;
sprintf (buffer, "Menu %d Level %d ", menuCalc (), ActualLevel);
lcd.print(buffer);
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void menuNotExist () // Restaute aat ald good value
{
/*sprintf (buffer, "!!! Menu absent!!!") ;
menuDebug ();
delay (3000) ;*/
ActualLevel = BkLevel; // Vertical
Order[ActualLevel] = BkOrd ; // Horizontal
NewLeft = 987 ; // To re read encoder if push knob
knobLeft.write(4 * (BkOrd - 1)) ;
soundMenuNotExist ();
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void encreaseLevel () // Enter by knob push only from <menuselect>
{
if (ActualLevel < OrderSize - 1)
{
ActualLevel++ ;
Order [ActualLevel] = 1 ;
knobLeft.write(0) ;
PositionLeft = 543 ; // To avtivate menuswitch
}
// else no action
soundEncreaseLevel ();
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
byte decreaseLevel () // Rotate negative
{
byte knobVal ;
if (ActualLevel > 0)
ActualLevel = ActualLevel - 1 ; // Restore before increment
knobLeft.write( 4 * (Order[ActualLevel]) - 1 ) ;
PositionLeft = 765 ; // To avtivate menuswitch
soundDecreaseLevel ();
return 0 ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void restoreValidState ()
{
ActualLevel = BkLevel; // Vertical
Order[ActualLevel] = BkOrd ; // Horizontal
NewLeft = 987 ; // To re read encoder if push knob
knobLeft.write(4 * (BkOrd - 1)) ;
soundRestoreValidState ();
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void menuSwitch (byte oldOrdLev1)
{
int menuName = menuCalc() ; // clean bad values , return 1000...9999
switch (menuName)
{
case 1000:
menu_1000();
break ;
case 1100:
menu_1100();
break ;
case 1110:
menu_1110();
break ;
case 1111:
menu_1111();
break ;
case 1200:
menu_1200();
break ;
case 1210:
menu_1210();
break ;
case 1220:
menu_1220();
break ;
case 1221:
menu_1221();
break ;
case 1222:
menu_1222();
break ;
case 1223:
menu_1223();
break ;
case 1300:
menu_1300();
break ;
case 2000:
menu_2000();
break ;
case 3000:
menu_3000();
break ;
case 4000:
menu_4000();
break ;
case 4100:
menu_4100();
break ;
case 4110:
menu_4110();
break ;
case 4111:
menu_4111();
break ;
case 4112:
menu_4112();
break ;
default :
menuNotExist () ;
break ;
}
// else no move, nothing to do !
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void menu_1000 ()
{
sprintf (buffer, "**** Menu 1000 ****") ;
menuDebug () ; // Good menu, no error in increasing level
}
void menu_1100 ()
{
sprintf (buffer, "**** Menu 1100 ****") ;
menuDebug () ;
}
void menu_1110 ()
{
sprintf (buffer, "*Dernier menu 1110**") ;
menuDebug () ;
}
void menu_1111 ()
{
sprintf (buffer, "**** Menu 1111 ****") ;
menuDebug () ;;
}
void menu_1200 ()
{
sprintf (buffer, "**** Menu 1200 ****") ;
menuDebug () ;
}
void menu_1210 ()
{
sprintf (buffer, "**** Menu 1210 ****") ;
menuDebug () ;
}
void menu_1220 ()
{
sprintf (buffer, "**** Menu 1220 ****") ;
menuDebug () ;
}
void menu_1221 ()
{
sprintf (buffer, "**** Menu 1221 ****") ;
menuDebug () ;
}
void menu_1222 ()
{
sprintf (buffer, "**** Menu 1222 ****") ;
menuDebug () ;
}
void menu_1223 ()
{
sprintf (buffer, "**** Menu 1223 ****") ;
menuDebug () ;
}
void menu_1300 ()
{
sprintf (buffer, "*Dernier menu 1300**") ;
menuDebug () ;
}
void menu_2000 ()
{
sprintf (buffer, "**** Menu 2000 ****") ;
menuDebug () ;
}
void menu_3000 ()
{
sprintf (buffer, "**** Menu 3000 ****") ;
menuDebug () ;
}
void menu_4000 ()
{
sprintf (buffer, "*Dernier Menu 4000 *") ;
menuDebug () ;
}
void menu_4100 ()
{
sprintf (buffer, "*Dernier Menu 4100 *") ;
menuDebug () ;
}
void menu_4110 ()
{
sprintf (buffer, "*Dernier Menu 4110 *") ;
menuDebug () ;
}
void menu_4111 ()
{
sprintf (buffer, "*Dernier Menu 4111 *") ;
menuDebug () ;
}
void menu_4112 ()
{
sprintf (buffer, "*Dernier Menu 4112 *") ;
menuDebug () ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++ Not specific for menus +++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ LEDS
void triColorLeds (int color)
{
if (anode)
color = 7 - color ;
digitalWrite(LedTriRed, ((color & 1) == 1)); // 0 allume la led
digitalWrite(LedTriGreen, ((color & 2) == 2));
digitalWrite(LedTriBlue, ((color & 4) == 4));
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void flashLeds (int color, int indx) // Visual animation
{
if (indx % 2)
triColorLeds (color);
else
triColorLeds (Black); // éteint
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void flashLedLoop (int color, byte nbLoops)
{
for (int x = 0 ; x < nbLoops ; x++)
{
flashLeds (color, x) ;
delay (50) ;
}
triColorLeds (Black);
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ I/O ports
void initPorts (void) // For leds and pushbutton
{
pinMode(LedTriRed, OUTPUT) ;
digitalWrite(LedTriRed, 0);
pinMode(LedTriGreen, OUTPUT) ;
digitalWrite(LedTriGreen, 0);
pinMode(LedTriBlue, OUTPUT) ;
digitalWrite(LedTriBlue, 0);
pinMode(LedCard, OUTPUT) ;
digitalWrite(LedCard, 0);
pinMode(LedBurn, OUTPUT) ;
digitalWrite(LedBurn, 0);
pinMode (PushButton, INPUT_PULLUP) ;
pinMode (PushS, INPUT_PULLUP) ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ LCD
void initLcd20x4()
{
lcd.init(); // initialize the lcd
lcd.backlight();
lcd.setCursor(0, 0);
lcd.clear ();
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void ClearLineLCD (byte line)
{
lcd.setCursor(0, line);
for (int x = 0 ; x < nbcharparligne ; x++)
lcd.print (" ");
lcd.setCursor(0, line); // Cursor start of line
}
// cursor // noCursor // blink // noBlink
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Buzzer
void beepDurFreq (int duration, int Frequency)
{
//digitalWrite(ledPin, LOW);
tone (Buzzer, Frequency ); // Example : 440 = La 3
delay(duration);
//digitalWrite(ledPin, HIGH);
noTone(Buzzer);
}
// ++++++++++++++++++++ Sounds utilities
void soundRecord ()
{
beepDurFreq (25, 1000 ) ; // Duration Frequency
}
// ++++++++++++++++++++
void soundLeft ()
{
for (int i = 20 ; i > 0 ; i--)
beepDurFreq (25, i * 5) ; // Alarm left
//delay (1000) ;
}
// ++++++++++++++++++++
void soundMenuNotExist ()
{
for (int i = 0 ; i < 6 ; i++)
beepDurFreq (25, 220 * (1 + 4 * (i % 2))) ; // 220 La 3 // 880 La 5
}
// ++++++++++++++++++++
void soundEncreaseLevel ()
{
for (int i = 0 ; i < 6 ; i++)
beepDurFreq (25, 1046 * (1 + 4 * (i % 2))) ; // 1046 Do 5 // 4186 Do 7
}
// ++++++++++++++++++++
void soundDecreaseLevel ()
{
for (int i = 0 ; i < 12 ; i++)
beepDurFreq (25, 41 * (1 + 4 * (i % 2))) ; // 41 Mi 0 // 165 Mi 3
}
// ++++++++++++++++++++
void soundRestoreValidState ()
{
for (int i = 0 ; i < 6 ; i++)
beepDurFreq (25, 220 * (1 + 4 * (i % 2))) ; // 220 La 3 // 880 La 5
}
// ++++++++++++++++++++
void debounce ()
{
triColorLeds (Cyan) ;
while (digitalRead (PushButton) == 0 )
delay (100);
triColorLeds (Black) ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Debug tools
void chronoStart (void)
{
Chrono = millis ();
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
long chronoStop (void)
{
long bkCh = Chrono ;
return ((millis () - bkCh)) ; // Dont modify starting value
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
long chronoReset (void) // Reset count value
{
long bkCh = Chrono ;
Chrono = millis ();
return (Chrono - bkCh) ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void showTree ()
{
char buffer1 [60] ;
Serial.print (chronoReset ()) ;
sprintf (buffer1, " ... Level: [%d] . <%d> .. Position: %u New: %u ...", ActualLevel, menuCalc(), (int)PositionLeft, (int)NewLeft) ;
Serial.println (buffer1) ;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LedCard
LedBurn
Pot10t
Pot270
(0x27)