/**
Arduino Electronic Safe
Copyright (C) 2020, Uri Shaked.
Released under the MIT License.
*/
#include <LiquidCrystal.h>
/* Locking mechanism definitions */
#define BUTTONS A7
#define A5_Val A5
#define A4_Val A4
#define A3_Val A3
#define PUMP_D5 5
#define PUMP_D6 6
#define PUMP_D9 9
#define PUMP_D10 10
#define PUMP_D11 11
int iVoltageA5 = 0;
int iVoltageA4 = 0;
int iVoltageA3 = 0;
//////////////////////
/// BUTTONS & MENU ///
//////////////////////
// B1, B2, B3, B4//
// byte bSet = 0;
// byte bUp = 511;
// byte bDown = 683;
// byte bEnter = 773;
const byte bMainMenu = 7;
const byte bSetMenu = 11;
byte bSec = 1; //set the timer to count in seconds rather than 0.5 sec
byte bWait = 0;
byte bPauseMenu = 0;
byte bPauseEnter = 0;
int A7_VAL = 0; // BUTTON INPUT
int bMenuTimerMax = 1000; //250ms
int bMenuTimer = bMenuTimerMax;
int iMenuSelectMin = 1000;
int iMenuSelectMax = 8000;
int iMenuSelect = iMenuSelectMin;
int iMenuSelectStep = iMenuSelectMin;
/////////////////////////////////
// Nutrient Controller Setting //
/////////////////////////////////
int iWtrQty = 10;
int iNutrient_to_Water = 4;
///////////////
// pH Sensor //
///////////////
const int cCalibrateMenuMin = 1100;
const int cCalibrateMenuMax = 1600;
byte bMeasure4_0 = 0;
byte bMeasure6_8 = 0;
byte bMeasure8_0 = 0;
int ipHCalibrateCntMax = 100; //250ms 1000/4000
int ipHCalibrateCnt = ipHCalibrateCntMax;
long lpH_Calibration = 0;
double dpH_4_0 = 0;
double dpH_6_8 = 0;
double dpH_8_0 = 0;
double dCoeffA = 0;
double dCoeffB = 0;
double dCurrentpH = 0;
double dpHLower = 5.0;
double dpHUpper = 8.0;
int ipH_CalibrationRaw = 0;
int ipH_Points = 1000;
int ipH_PointsCnt = 0;
byte bCalibrateState = 0;
byte bpHDisplayUpdate = 1;
int iPrevMenuSelect = 0;
//DOSING
int ipHReadCntMax = 100; //250ms 1000/4000
int ipHReadCnt = ipHReadCntMax;
int ipH_TestPointsCnt = 0;
int ipHTestCnt = 3;
byte bMeasurepHState = 1;
int ipH_Raw = 0;
long lpH_Sum = 0;
double dpH_Test = 0;
int iBasePumpTimerCnt = 0;
int iBasePumpTimer = 3;
int iAcidPumpTimerCnt = 0;
int iAcidPumpTimer = 3;
byte bBasePumpPower = 200;
byte bAcidPumpPower = 200;
byte bBasePumpState = 3;
byte bAcidPumpState = 3;
int iPumpWaitTimerMax = 60; //Wait 60 minutes before enabling the base pump
int iBasePumpWaitTimer = iPumpWaitTimerMax * 60;
int iAcidPumpWaitTimer = iPumpWaitTimerMax * 60;
int iPrevMenu = 0;
byte bPumpMenuCnt = 200;
/* Display */
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
byte bBackLightTimer = 10; //seconds
byte bBackLight = bBackLightTimer;
void setup() {
//LCD
lcd.begin(16, 2);
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("* Hydroponics *");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("* Menu System *");
cli();//stop interrupts
//set timer0 interrupt at 2kHz
//set timer1 interrupt at 1Hz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 1hz increments
OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12 and CS10 bits for 1024 prescaler
TCCR1B |= (1 << CS12) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
//set timer2 interrupt at 8kHz
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 8khz increments
OCR2A = 249;// = (16*10^6) / (8000*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
}
void loop() {
if (bMenuTimer==0) {
pMenu();
}
}
void pMenu(){
bMenuTimer = bMenuTimerMax;
//A7_VAL = analogRead(BUTTONS);
iVoltageA5 = analogRead(A5_Val);
iVoltageA4 = analogRead(A4_Val);
iVoltageA3 = analogRead(A3_Val);
A7_VAL = 1024;
if (iVoltageA5 == 0) {
A7_VAL = 683;
}
if (iVoltageA4 == 0) {
A7_VAL = 511;
}
if (iVoltageA3 == 0) {
A7_VAL = 10;
}
//Serial.println(A7_VAL);
// byte bUp = 511;
// byte bDown = 683;
// byte bEnter = 773;
//MENU NAVIGATION
if ((A7_VAL > 500) && (A7_VAL < 550) && (iMenuSelect < iMenuSelectMax) && (bPauseMenu == 0)) { //DOWN
iMenuSelect = iMenuSelect + iMenuSelectStep;
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL > 630) && (A7_VAL < 730) && (iMenuSelect > iMenuSelectMin) && bPauseMenu == 0) { //UP
iMenuSelect = iMenuSelect - iMenuSelectStep;
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL >= 0) && (A7_VAL < 50) && (bPauseEnter == 0) && bPauseMenu == 0) { //ENTER
iMenuSelectStep = iMenuSelectStep / 10;
iMenuSelect = iMenuSelect + iMenuSelectStep;
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep)+", PauseMenu:"+String(bPauseMenu) );
if (iMenuSelect == 1000) {
pMainMenu1000();
}
if ((iMenuSelect == 2000) && (bpHDisplayUpdate==0)) { //Display Current pH
// pMainMenu2000();
}
if (iMenuSelect == 2100) {
// pMainMenu2100();
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
if (iMenuSelect == 3000) {
pMainMenu3000();
}
if (iMenuSelect == 3100) {
pMainMenu3100();
}
if (iMenuSelect == 3110) {
pMainMenu3110();
}
if (iMenuSelect == 3200) {
pMainMenu3200();
}
if (iMenuSelect == 3210) {
pMainMenu3210();
}
if (iMenuSelect == 3300) {
pMainMenu3300();
}
if (iMenuSelect == 3310) {
pMainMenu3310();
}
if (iMenuSelect == 4000) { // Base Pump
pMainMenu4000();
}
if (iMenuSelect == 4100) { // Base Pump
pMainMenu4100();
}
if (iMenuSelect == 5000) { //Acid Pump
pMainMenu5000();
}
if (iMenuSelect == 5100) { //Acid Pump
pMainMenu5100();
}
if (iMenuSelect == 6000) { //Prime Base Line
pMainMenu6000();
}
if (iMenuSelect == 6100) { //Prime Base Line
pMainMenu6100();
}
if (iMenuSelect == 6110) { //Prime Base Line
pMainMenu6110();
}
if (iMenuSelect == 7000) { //Prim Acid line
pMainMenu7000();
}
if (iMenuSelect == 7100) { //Prime Acid line
pMainMenu7100();
}
if (iMenuSelect == 7110) { //Prime Acid line
pMainMenu7110();
}
if (iMenuSelect == 8000) { //Dosing Delay
pMainMenu8000();
}
if (iMenuSelect == 8100) { //Dosing Delay
pMainMenu8100();
}
iPrevMenuSelect = iMenuSelect;
}
void pMainMenu1000() {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Water Qty:"+String(iWtrQty)+" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Nut:Water:"+String(iNutrient_to_Water)+" ");
lcd.setCursor ( 12, 1 ); // go to the next line
lcd.print("ml/l");
bpHDisplayUpdate = 1;
//Serial.println("Current PH:"+String(dCurrentpH));
iMenuSelectStep = 1000;
}
void pMainMenu1100() {
iMenuSelect = 1000;
iMenuSelectStep = 1000;
}
void pMainMenu2000() { // Set pH Limits
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Set Water Qty ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Qty:"+String(iWtrQty)+" ");
}
void pMainMenu2100() { // Set pH Limits
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Lower pH Limit: ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHLower)+" ");
iMenuSelectMin = 2100;
iMenuSelectMax = 2300;
}
void pMainMenu2110() { // Set pH Limits
if (bPauseMenu == 0) {
bPauseMenu = 1;
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Set Lower pH: ");
}
else {
if ((A7_VAL > 500) && (A7_VAL < 550)) { //UP
dpHLower = dpHLower - 0.1;
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHLower)+" ");
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL > 630) && (A7_VAL < 730)) { //DOWN
dpHLower = dpHLower + 0.1;
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHLower)+" ");
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL >= 0) && (A7_VAL < 50)) { //ENTER
iMenuSelectStep = 100;
iMenuSelect = 2100;
iMenuSelectMin = 2100;
iMenuSelectMax = 2200;
bPauseMenu = 0;
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep)+", PauseMenu:"+String(bPauseMenu) );
}
}
}
void pMainMenu2300() { // Set pH Limits Exit
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Return To Main ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Enter to Exit ");
}
void pMainMenu2310() {
iMenuSelect = 2000;
iMenuSelectStep = 1000;
iMenuSelectMin = 1000;
iMenuSelectMax = 8000;
}
void pMainMenu3000() { // Set pH Limits
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Set pH Limits ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Lmts:"+String(dpHLower)+" ~ "+String(dpHUpper));
}
void pMainMenu3100() { // Set pH Limits
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Lower pH Limit: ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHLower)+" ");
iMenuSelectMin = 3100;
iMenuSelectMax = 3300;
}
void pMainMenu3110() { // Set pH Limits
if (bPauseMenu == 0) {
bPauseMenu = 1;
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Set Lower pH: ");
}
else {
if ((A7_VAL > 500) && (A7_VAL < 550)) { //UP
dpHLower = dpHLower - 0.1;
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHLower)+" ");
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL > 630) && (A7_VAL < 730)) { //DOWN
dpHLower = dpHLower + 0.1;
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHLower)+" ");
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL >= 0) && (A7_VAL < 50)) { //ENTER
iMenuSelectStep = 100;
iMenuSelect = 3100;
iMenuSelectMin = 3100;
iMenuSelectMax = 3300;
bPauseMenu = 0;
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep)+", PauseMenu:"+String(bPauseMenu) );
}
}
// iMenuSelectMin = 3110;
// iMenuSelectMax = 3110;
}
void pMainMenu3200() { // Set pH Limits
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Upper pH Limit: ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHUpper)+" ");
}
void pMainMenu3210() { // Set pH Limits
if (bPauseMenu == 0) {
bPauseMenu = 1;
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Set Upper pH ");
}
else {
if ((A7_VAL > 500) && (A7_VAL < 550)) { //UP
dpHUpper = dpHUpper - 0.1;
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHUpper)+" ");
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL > 630) && (A7_VAL < 730)) { //DOWN
dpHUpper = dpHUpper + 0.1;
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(String(dpHUpper)+" ");
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep) );
}
else if ((A7_VAL >= 0) && (A7_VAL < 50)) { //ENTER
iMenuSelectStep = 100;
iMenuSelect = 3200;
iMenuSelectMin = 3100;
iMenuSelectMax = 3300;
bPauseMenu = 0;
//Serial.println("Menu Select:"+String(iMenuSelect)+", MenuStep:"+String(iMenuSelectStep)+", MenuMax:"+String(iMenuSelectMax) );
}
}
}
void pMainMenu3300() { // Set pH Limits Exit
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Return To Main ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Enter to Exit ");
}
void pMainMenu3310() {
iMenuSelect = 3000;
iMenuSelectStep = 1000;
iMenuSelectMin = 1000;
iMenuSelectMax = 8000;
}
void pMainMenu4000() {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Base Pump ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Set dosing timer");
}
void pMainMenu4100() { //
if (bPauseMenu == 0) {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Pump On Timer: ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iBasePumpTimer);
bPauseMenu = 1;
}
else if ((A7_VAL > 500) && (A7_VAL < 550)) { //UP
if (iBasePumpTimer > 0) {
iBasePumpTimer = iBasePumpTimer - 1;
}
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iBasePumpTimer);
}
else if ((A7_VAL > 630) && (A7_VAL < 730)) { //DOWN
if (iBasePumpTimer < 20) {
iBasePumpTimer = iBasePumpTimer + 1;
}
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iBasePumpTimer);
}
else if ((A7_VAL >= 0) && (A7_VAL < 50)) { //ENTER
iMenuSelectStep = 1000;
iMenuSelect = 4000;
iMenuSelectMin = 1000;
iMenuSelectMax = 8000;
bPauseMenu = 0;
}
}
void pMainMenu5000() {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Acid Pump ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Set dosing timer");
}
void pMainMenu5100() { //
if (bPauseMenu == 0) {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Pump On Timer: ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iAcidPumpTimer);
bPauseMenu = 1;
}
else if ((A7_VAL > 500) && (A7_VAL < 550)) { //UP
if (iAcidPumpTimer > 0) {
iAcidPumpTimer = iAcidPumpTimer - 1;
}
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iAcidPumpTimer);
}
else if ((A7_VAL > 630) && (A7_VAL < 730)) { //DOWN
if (iAcidPumpTimer < 20) {
iAcidPumpTimer = iAcidPumpTimer + 1;
}
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iAcidPumpTimer);
}
else if ((A7_VAL >= 0) && (A7_VAL < 50)) { //ENTER
iMenuSelectStep = 1000;
iMenuSelect = 5000;
iMenuSelectMin = 1000;
iMenuSelectMax = 8000;
bPauseMenu = 0;
}
}
void pMainMenu6000() {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Prime Base Line ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Enter to start ");
}
void pMainMenu6100() { //
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Prime Base Line ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Enter to stop ");
digitalWrite(PUMP_D5, HIGH); //ON
}
void pMainMenu6110() { //
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Prime Base Line ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Done ");
digitalWrite(PUMP_D5, LOW); //OFF
iMenuSelect = 6000;
iMenuSelectStep = 1000;
}
void pMainMenu7000() {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Prime Acid Line ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Enter to start ");
}
void pMainMenu7100() { //
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Prime Acid Line ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Enter to stop ");
digitalWrite(PUMP_D6, HIGH); //ON
}
void pMainMenu7110() { //
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Prime Acid Line ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Done ");
digitalWrite(PUMP_D6, LOW); //OFF
iMenuSelect = 7000;
iMenuSelectStep = 1000;
}
void pMainMenu8000() {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Dosing Delay ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("Set Dosing Delay");
}
void pMainMenu8100() { //
if (bPauseMenu == 0) {
lcd.setCursor ( 0, 0 ); // go to the next line
lcd.print("Dosing Delay: ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iPumpWaitTimerMax);
bPauseMenu = 1;
}
else if ((A7_VAL > 500) && (A7_VAL < 550)) { //UP
if (iPumpWaitTimerMax > 0) {
iPumpWaitTimerMax = iPumpWaitTimerMax - 1;
}
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iPumpWaitTimerMax);
}
else if ((A7_VAL > 630) && (A7_VAL < 730)) { //DOWN
if (iPumpWaitTimerMax < 180) {
iPumpWaitTimerMax = iPumpWaitTimerMax + 1;
}
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(" ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print(iPumpWaitTimerMax);
}
else if ((A7_VAL >= 0) && (A7_VAL < 50)) { //ENTER
iMenuSelectStep = 1000;
iMenuSelect = 8000;
iMenuSelectMin = 1000;
iMenuSelectMax = 8000;
bPauseMenu = 0;
}
}
ISR(TIMER0_COMPA_vect){//timer0 interrupt 2kHz toggles pin 8
//generates pulse wave of frequency 2kHz/2 = 1kHz (takes two cycles for full wave- toggle high then toggle low)
}
ISR(TIMER1_COMPA_vect){//timer1 interrupt 1Hz toggles pin 13 (LED)
//generates pulse wave of frequency 1Hz/2 = 0.5Hz (takes two cycles for full wave- toggle high then toggle low)
if (bSec == 0) {
bSec = 1;
if (bBackLight > 0) {
bBackLight--;
}
if (bWait > 0) {
bWait--;
}
if (bpHDisplayUpdate > 0){
bpHDisplayUpdate--;
}
if (iBasePumpTimerCnt > 0) {
iBasePumpTimerCnt--;
}
if (iAcidPumpTimerCnt > 0) {
iAcidPumpTimerCnt--;
}
if (ipHTestCnt > 0) {
ipHTestCnt--;
}
if (iBasePumpWaitTimer > 0) {
iBasePumpWaitTimer--;
}
if (iAcidPumpWaitTimer > 0) {
iAcidPumpWaitTimer--;
}
}
else {
bSec--;
}
}
ISR(TIMER2_COMPA_vect){//timer1 interrupt 8kHz toggles pin 9
//generates pulse wave of frequency 8kHz/2 = 4kHz (takes two cycles for full wave- toggle high then toggle low)
if (bMenuTimer > 0) {
bMenuTimer--;
}
if (ipHCalibrateCnt > 0) {
ipHCalibrateCnt--;
}
if (bPumpMenuCnt > 0 ) {
bPumpMenuCnt--;
}
if (ipHReadCnt > 0) {
ipHReadCnt--;
}
}