600 lines (and thanks for looking at it). I you see someting unorthodox, i will leave it for know because everything works. And yes lots of global variables, but for all of them i don’t see a possibility of making them local. And lots of passing values is sometimes blocking the oversight.
And it beats me why the code is not in different collors (made a copy for the forum in Arduino IDE)!? thx
/*CREATED WITH ARDUINO IDE v2.3.5 & TESTED WITH ARDUINO UNO R3 & ARDUINO NANO*/
/*Code for project with waterproof ultrasonic UART distance sensor A02YYUW and I²C 16x2 LCD display*/
/*The SoftSerial lib is used to create a second UART connection because the imbedded is used to load sketches and uses the IDE serial monitor (embedded in Arduino bootloader to communicate via the USB/UART chip on the Arduino board ). */
/*This skech is al water level meter for watertanks (or other fluids that don't damage the waterproof distance sensor). The sensor is the waterproof version of ultrasonic SR04 (sonar).
Min reading distance is 4cm, max. reading distance is 400cm. Min. & max. level kan be set in a separate diplay menu. The display shows the level in % and surface distance in cm.
There is a adjutable refill function (refill setting and stoprefill setting). The refill circuit (valve circuit) needs to be commanded by the refill output led.
A refill alarm is set at a standard (no user adjustment) time of 2 hours (7.200.000ms but can be adjusted in this sketch) and the state of the refill valve (circuit) need to be read (commanded valve state needs to = read out state).
The sensor generates many short errors in normal operation and that has been delt with in the distance() function. Only a broken sensor wire or a faulty sensor wire needs to be detected(=a certain amount of errors in a certain amount of time).
In the project circuit three leds:
-blue for power and not commanded by µController
-green led for refill ongoing
-red led (+loud high pitch buzzer) for refill alarm (valve open while AlarmTimer has counted to setting limit=alarm and/or reading 'valve open' via external readout while valve is not commanded open=alarm). Read via optocoupler at pin 5.
Two pushbuttons:
-on pin 3 for toggling the lcd screen menu's (big button prefered)
-on pin 4 for 10min alarm muting (small button prefered)
*/
//include all used libraries
#include <DistanceSensor_A02YYUW.h> //lib for ultrasonic sensor
#include <SoftwareSerial.h> //lib standard available in Arduino IDE for second UART (first UART is the embedded in the Arduino bootloader to communicatie with the USB chip on pin 0 and pin 1)
#include <LiquidCrystal_I2C.h> //lib for I²C 16x2 LCD display
#include <Wire.h> //lib standard available in Arduino IDE to enable I²C communication
#include <EEPROM.h> //library standard available in Arduino IDE used to store the settings in the EEPROM memory
//all declarations of variables and defining of constants before void setup
//pins 0 and 1 are not used to avoid conflicts while using the serial monitor (uses the standard USB/UART connection)
#define MAINBUTTON_PIN 3 //defining main push button (Arduino)pin
#define ALARMRESET_PIN 4 //defining alarm reset button (Arduino)pin
#define VALVEREADOUT_PIN 5 //defining valve controle (Arduino)pin
#define SOFTWARE_SERIAL_PIN_RX 10 //this is where the tx (white wire) of the sensor goes (Arduino pin)!! has been changed from Arduino pin 11 to pin 10 because pin 11 is the MOSI pin from the ICSP connection used to program µC directly
#define SOFTWARE_SERIAL_PIN_TX 9 //(Arduino pin) has been changed from Arduino pin 10 to 9 because pin 10 was original pin 11 (reason above this line)
#define LED_REFILL 7 //info: the refill valve relais will be DIRECTLY COMMANDED BY THE LED PIN (optocoupler). Arduino pin!
#define LED_ALARM 8 //info: the alarm led output also needs to be connected to a high pitch buzzer (direct or indirect). Arduino pin!
//creating instances of the library objects
SoftwareSerial mySerial(SOFTWARE_SERIAL_PIN_RX, SOFTWARE_SERIAL_PIN_TX);
DistanceSensor_A02YYUW distanceSensor(&mySerial);
LiquidCrystal_I2C lcd(0x27, 16, 2); //I²C adres, 16x2 digit display
int lcdMenu = 5; //int used to in the switch/case condition and selecting variables in the three array's. Here initial starting loop at lcdMenu 0.
char menuText[][10] = { "MinLevel", "MaxLevel", "FillStart", "FillStop", "Alarmtime" }; //this is an arry char's but these 'values' are alway the same
uint16_t menuVariables[5]; //field [0]=MinLevel; [1]=MaxLevel; [2]=FillsStart ;[3]=FillStop; [4]=Alarmtime//use of 16bit int's instead of normal(32bit) int's for minimalising EEPROM space
const uint16_t defaultVariables[5] = { 180, 30, 170, 150, 60 }; //loaded in the EEPROM at first startup (so if eeKey is not present in EEPROM)
uint8_t firmwareVersion[3] = { 1, 0, 0 };
char menuUnits[][4] = { "cm", "cm", "cm", "cm", "min" }; //3 char's + trailing 'null' character
const int eepromStart = 0;
const uint32_t eeKey = 0xDEADBEAD; //EEPROM key to flag backup creation
uint32_t readKey;
char buf[16]; //used in two void functions so global declaration
//unsigned long maxRefilltime = 5000; //this is a variable for TESTING PURPOSE ONLY. Is used in case 1 for the alarm timer istead of menuVariables[5]
unsigned long alarmTimer; //this variable to store the time that has passed sins the beginning of the refill command. Is limited to 72000000ms=120min=2h in case 5. unsigned long max. count is 4.294.967.295 so max time is 1.192 hours
unsigned long timeoutTime = 10000; //When no input is given for one minute switch/case goes to case 0 and backlight goes off. Note:frist push on mainbutton 'awakes' the backlight and ables further input on the main button
unsigned long timeoutTimer;
unsigned long defaultflipTime; //time used in lcdMenu 8 to flip lcd text on the second line
unsigned long currentdefaultTime; //current timed time in lcdMenu 8 to flip lcd text on the second line
unsigned long defaultTime = 2000;
unsigned long SensorerrorTime = 0; //setting memory to zero. THis is a timer to check if the sensor is permanently functioning (check in the distance() function) certain amounts of errors in a certain amount of time
uint32_t SensorerrorCounter = 0; //setting memory to zero. this is a counter to check if the sensor is permanently functioning (check in the distance() function) certain amounts of errors in a certain amount of time
unsigned int dis; //declaring variable for the distance
float percent;
bool valveAlarmonoff = true; //false=off, true=on
//the following int's also need to be globaly declared because they are used in different functions
unsigned int highLimit; //counter that sets set highest level in reservoir THIS VALUE NEEDS TO BE RESTRICTED TO AT LEAST 1CM ABOVE counterMin (this means counterMin -1)
unsigned int lowLimit; //counter that sets lowest level in reservoir
//these variables need to be set once outside the various loops @ startup. With local declaration this sketch wouldn't work.
bool risingEdge = false; //for reading a one-time rising flank (=button returning after being pushed)
int directionInt = -1; //needs to be int instead of boolean to increase or decrease the int counter in the dimmingCounter function
bool enterLong = LOW;
bool firstLoop = true;
bool firstFlip = true;
bool firstdefaultFlip = true;
bool defaultFlag = false; //flag to stop the risingedgeTest from detecting while pushing button to get from lcdMenu 0 to lcdMenu 8
bool firstButtonread = LOW;
bool secondButtonread = LOW;
bool buttonState = LOW;
bool risingFlag = LOW;
bool droppingFlag = LOW;
unsigned long longTimer = 0;
bool first7passFlag = false;
bool defaulttextFlip = false; //used in lcdMenu 8 to flip lcd text on the second line
//case 'levels' for the swich condition = easier instead of using if/else statements
//const int SCREEN_LEVEL = 5; //tanklevel needs to be displayd in % instead of cm
void setup() {
Serial.begin(9600);//for testing
mySerial.begin(9600);
lcd.init();
//lcd.begin(); // I2C lcd dislplay
lcd.backlight(); //I2C lcd backlight on
lcd.clear(); //I2C clear screen
delay(1000); //was here in LCD example sketch for unknown reason (waiting time for lcd to start up?)
pinMode(MAINBUTTON_PIN, INPUT_PULLUP); //set mainbutton as input with internal pullup
pinMode(ALARMRESET_PIN, INPUT_PULLUP); //set alarmresetbutton as input with internal pullup
pinMode(VALVEREADOUT_PIN, INPUT_PULLUP); //set valvereadout pin as input with internal pullup
pinMode(LED_REFILL, OUTPUT); //set led 'refill' as output
pinMode(LED_ALARM, OUTPUT); //set led 'alarm' as output
alarmTimer = millis(); //because the valveAlarmonoff is at startup true (safety first for un-mindfull users) the alarmtimer needs to be set (in the loop of case 0 impossible)
//WRITING FIRMWARE VERSION TO EEPROM
for (int i = 0; i < 3; i++) {
EEPROM.put(eepromStart + sizeof(eeKey) + i, firmwareVersion[i]); //unint8_t = one byte hence 'i' instead of 'i*2'
}
//GETTING THE VARIABLES FOR menuVariables WITH A FIRST STARTUP CHECK (with EEPROM eeKey check)
EEPROM.get(eepromStart, readKey);
if (eeKey != readKey) {
EEPROM.put(eepromStart, eeKey); //STORING eeKey at eeepromStart of EEPROM
loadvarinEEPROM(); //STORING defaultVariables in EEPROM
}
//filling the menuVariables array with the content in the EEPROM
for (int i = 0; i < 5; i++) {
EEPROM.get(eepromStart + sizeof(eeKey) + sizeof(firmwareVersion) + (i * 2), menuVariables[i]); //uint16_t 16bit = two bytes
//Serial.println(menuVariables[i]); //for testing
}
}
void loop() {
switch (lcdMenu) {
case 0: //SECOND DISPLAY MENU = minimum level parameter set
if (timeoutCheck()) { //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
lcdMenu = 7;
break;
}
lowLimit = 400;
highLimit = menuVariables[2]; //MinLevel can't be higher than current FillStart
longpushTest(); //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
risingedgeTest();
if (risingEdge == true) {
Serial.println((String) "risingEdge " + risingEdge);
resetFlags();
lcdMenu = 1;
} else {
dis = distanceSensor.getDistance()/10;
lcdFirstline();
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print((String) "limits " + lowLimit + "-" + menuVariables[2] + " ");
//Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
alarmCheck();
}
break;
case 1: //THIRD DISPLAY MENU = maximum level parameter set
if (timeoutCheck()) { //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
lcdMenu = 7;
break;
}
lowLimit = menuVariables[3];
highLimit = 4; //the closest measuring distance of the sensor is advised at 3cm but i'm adding 1cm to compensate for temperature fluxuations (aproximately 3%/10°C). It seems by testing 20° is the most accurate measuring temperature
longpushTest(); //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
risingedgeTest();
if (risingEdge) {
resetFlags();
lcdMenu = 2;
} else {
dis = distanceSensor.getDistance()/10;
lcdFirstline();
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print((String) "limits " + highLimit + "-" + menuVariables[3] + " ");
//Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]); testing
alarmCheck();
valveCommand();
}
break;
case 2: //FOURTH DISPLAY MENU = refillStart level set
if (timeoutCheck()) { //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
lcdMenu = 7;
break;
}
lowLimit = menuVariables[0]; //the lowest refill start limit may go to MinLevel
highLimit = menuVariables[3] + 1; //the highest refill start limit needs to be at least 1cm under the refillStop so that there is minimum 1cm space between refillStart & refillSTop
longpushTest(); //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
risingedgeTest();
if (risingEdge) {
resetFlags();
lcdMenu = 3;
} else {
dis = distanceSensor.getDistance()/10;
lcdFirstline();
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print((String) "limits " + menuVariables[0] + "-" + (menuVariables[3] + 1));
//Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
alarmCheck();
valveCommand();
}
break;
case 3: //FIFTH DISPLAY MENU = refillStop level set
if (timeoutCheck()) { //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
lcdMenu = 7;
break;
}
lowLimit = menuVariables[2] - 1; //the lowest refill stop limit needs to be at least 1cm above the RefillStart
highLimit = menuVariables[1]; //the highest refill stop limit by MaxLevel
longpushTest(); //longpushTest BEFORE risingedgeTest in case of entering the counterloop to cancel (disrupt) the risingedgeTest (after a long push the program needs to stay in this menu)
risingedgeTest();
if (risingEdge) {
resetFlags();
lcdMenu = 4;
} else {
dis = distanceSensor.getDistance()/10;
lcdFirstline();
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print((String) "limits " + (menuVariables[2] - 1) + "-" + menuVariables[1]);
//Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
alarmCheck();
valveCommand();
}
break;
case 4: //SIXTH DISPLAY MENU = alarm timer set//this is a temporary menu because the timmer check for abnormal long filling time needs to be done independent from the Arduino
if (timeoutCheck()) { //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
lcdMenu = 7;
break;
}
lowLimit = 120; //120min in case 5 'lowLimit' is misleading but this needs to be like this because case 5 uses the same functions as case 1,2,3 & 4
highLimit = 1; //1min in case 5 highLimit is misleading...
longpushTest(); //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
risingedgeTest();
if (risingEdge) {
resetFlags();
lcdMenu = 6;
} else {
dis = distanceSensor.getDistance()/10;
lcdFirstline();
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print("limits 1-120min");
//Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
alarmCheck();
valveCommand();
}
break;
case 5: //FIRST DISPLAY MENU = distance/level in percentage
if (timeoutCheck()) { //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
lcdMenu = 7;
break;
}
longpushTest(); //in longpushTest if lcdMenu 0 then make lcdMenu 8 active
risingedgeTest();
if (risingEdge == true) {
//Serial.println((String) "risingEdge " + risingEdge);
resetFlags();
lcdMenu = 0;
} else {
//dis = 160; //this line for testing puspose instead of the next line
//dis = sr04.Distance();//read distance
distance();
percent = ((menuVariables[0] - (float)dis) / (menuVariables[0] - menuVariables[1])) * 100; //percentage//dis needs to be float so a float can be devided by a float, otherwise warning that result might be wrong
lcd.setCursor(0, 0);
lcd.print((String) "tanklevel " + (int)percent + "% "); //with two extra spaces after % to clear the screen from very 120% reading at startup
lcd.setCursor(0, 1);
lcd.print((String) "distance " + dis + "cm ");//with three spaces after cm to clear the screen
alarmCheck();
valveCommand();
}
break;
case 6: //SEVENTH DISPLAY MENU = alarm on/off set (off if there is no refill valve used)
//LongpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
//There is in the longpushTest a if-statement branch to the flipSwitch function instead of the counterloop
if (timeoutCheck()) { //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
lcdMenu = 7;
break;
}
longpushTest();
risingedgeTest();
if (risingEdge) {
resetFlags();
lcdMenu = 5;
} else {
dis = distanceSensor.getDistance()/10;
displayAlarmstate();
alarmCheck();
valveCommand();
}
break;
case 7: // EIGHT DISPLAY MENU = this is a 'ghost timeout menu' that keeps on watching parameters but shuts down LCD (backlight=I2C version, only text non-I2C version) and waits for the main button
//to be pushed to re-light the LCD backlight and go menu 0. When a timeout occures (mainbutton not pressed) in one of the other CASEs/Menu's
//the menu get's set to this CASE 7.
char fields[3][7]; //declare a char type variable with 7 character size in an array of 3.
alarmCheck();
if (digitalRead(LED_ALARM) == 1) {
if (valveAlarmonoff == true && (millis() - alarmTimer) > (menuVariables[4] * 60000)){
strncpy(fields[0], "VlvTi ", 7); //Valve Timer
}else {
strncpy(fields[0], " ", 7);
};
if (valveAlarmonoff == true && (digitalRead(VALVEREADOUT_PIN) == 0 && digitalRead(LED_REFILL) == 0)){
strncpy(fields[1], "Vlv ", 7); //Valve (open or closed)
} else {
strncpy(fields[1], " ", 7);
};
if ((menuVariables[0] < dis) || (dis < menuVariables[1])){
strncpy(fields[2], "TnkLLi", 7); //Tank Level Limit
} else {
strncpy(fields[2], " ", 7);
};
lcd.backlight();//I2C backlight on
lcd.display(); //I2C display lighted
lcd.setCursor(0, 0); //cursor set line 1, character 0//next line limit info
lcd.print(" ALARM DETAILS ");
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print((String)fields[0] + fields[1] + fields[2]);
} else {
lcd.clear(); //I2C lcd clear
lcd.noDisplay(); //I2C display no dots lighted
lcd.noBacklight(); //I2C backlight off
};
risingedgeTest(); //on pushing button go to menu case 5
if (risingEdge) {
resetFlags();
lcd.clear(); //I2C clear screen
lcd.backlight();//I2C backlight on
lcd.display(); //I2C display lighted
lcdMenu = 5;
} else {
distance();
alarmCheck();
valveCommand();
};
break;
case 8: //NINTH MENU = THE 'RESET TO DEFAULT' MENU. THIS ONE IS ONLY ACCESABLE IN lcdMenu case 5 VIA THE LONGPUSHTEST
//NO TIMEOUT CHECK IN THIS MENU
if (defaultFlag == false) {
if (digitalRead(MAINBUTTON_PIN) == 1) { //if the pushbutton is not pushed next risingedgeTest needs to be disrupted otherwise the droppingFlag stays true from the last 'loop'
droppingFlag = false; //disrupting next risingedgeTest
defaultFlag = true; //after setting defaultFlag to 'true' this will change ONLY at EXIT lcdMenu 8 (so after risingedge true)
//Serial.println((String)"defaultFlag " + defaultFlag); //testing
}
}
if (defaultFlag) { //if the pushbutton is not pressed any more after switching from lcdMenu 0 to lcdMenu 8, pushbutton is ready to be used in risingedgeTest
longpushTest();
risingedgeTest();
}
if (risingEdge) {
//Serial.println((String) "risingEdge occured"); //testing
defaultFlag = false; //with exit lcdMenu 8 reset condition for the next time lcdMenu 8 is entered
//Serial.println((String) "defaultFlag at risingEdge " + defaultFlag); //testing
resetFlags(); //needs to be here a otherwise after exeting lcdMenu 8 a immediate dorpppongFlag and risingedgeFlag are triggered
firstdefaultFlip = !firstdefaultFlip; //resetting so when entering lcdMenu 8 the next time defaultflipTime will be resetted to millis()
lcd.clear();
lcdMenu = 5;
} else {
dis = distanceSensor.getDistance()/10;
if (firstdefaultFlip) {
lcd.clear();
lcd.setCursor(0, 0); //cursor set line 1, character 0//next line limit info
lcd.print("TO DEFAULTS? ");
//Serial.println((String) "first defaulttextFlip: " + defaulttextFlip); //testing
defaultflipTime = millis();
firstdefaultFlip = !firstdefaultFlip;
}
currentdefaultTime = millis();
if (defaulttextFlip == false && (currentdefaultTime - defaultflipTime) > defaultTime) { //time between changing text on second line 2 seconds
//Serial.println("entering LONG"); //testing
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print("LONGPUSH=CONFIRM");
Serial.println((String) "LONGdefaulttextFlip: " + defaulttextFlip); //testing
defaulttextFlip = !defaulttextFlip; //next cycle other text line
//Serial.println((String) "LONGdefaulttextFlip: " + defaulttextFlip); //testing
//Serial.println(defaultflipTime); //testing
defaultflipTime = millis(); //setting timer to zero
//Serial.println(defaultflipTime); //testing
}
currentdefaultTime = millis();
if (defaulttextFlip == true && (currentdefaultTime - defaultflipTime) > defaultTime) { //time between changing text on second line 2 seconds
Serial.println("entering SHORT"); //testing
lcd.setCursor(0, 1); //cursor set line 1, character 0//next line limit info
lcd.print("SHORTPUSH=EXIT ");
//Serial.println((String) "SHORTdefaulttextFlip: " + defaulttextFlip); //testing
defaulttextFlip = !defaulttextFlip; //next cycle other text line
//Serial.println((String) "SHORTdefaulttextFlip: " + defaulttextFlip); //testing
defaultflipTime = millis(); //setting timer to zero
}
//Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
alarmCheck();
}
break;
}
}
void distance() { //aside of measuring this function needs to detect a certain amount of sensor not-ok readings in a certain time to keep looping with "SENSOR ERROR" on the display
DistanceSensor_A02YYUW_MEASSUREMENT_STATUS meassurementStatus;
unsigned long Millis; //declaring localy because only used in this function
do {
meassurementStatus = distanceSensor.meassure();
if (meassurementStatus == DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) {
dis = distanceSensor.getDistance()/10; //divided to convert to cm
SensorerrorCounter = 0; //test with no effect
Serial.println((String) "reading normal distance=sensor OK");
} else { //this else code is here in case the sensor is dead or a sensor wire is broken
Serial.println((String) "entering sensorerror else condition=sensor NOT OK"); //for testing
Millis = millis();
++SensorerrorCounter;
Serial.println(SensorerrorCounter);//testing
Serial.println(Millis); //testing
Serial.println(SensorerrorTime); //testing
if ((Millis - SensorerrorTime) > 5000 && SensorerrorCounter > 4) { //if there are more then 4 sensor errors in 5 sec(5000ms) then the "SENSOR ERROR" message needs to be displayed!!attention shis needs to be 5sec and 4 errors because the µC is to slow (long cycles)
lcd.clear();
lcd.setCursor(0, 0); //cursor set line 1, character 0//next line limit info
lcd.backlight();
lcd.print("SENSOR ERROR ");
Serial.println((String) "conditions SensorerrorTime and SensorerrorCounter");//testing
SensorerrorTime = millis(); //reset the 1 sec timer to do a new cycle of 1sec to check if sensor doensn't generate a not-ok status
SensorerrorCounter = 0; //reset counter together with a new 1sec cycle
} else {
if ((Millis - SensorerrorTime) > 5000) {
SensorerrorTime = millis(); //reset the 1 sec timer
SensorerrorCounter = 0;
Serial.println((String) "condition SensorerrorTime only");//for testing
}
}
}
} while (meassurementStatus != DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK); //repeats the code between do and while untill DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK is true
}
/*void distance() {
DistanceSensor_A02YYUW_MEASSUREMENT_STATUS meassurementStatus;
meassurementStatus = distanceSensor.meassure();
while (meassurementStatus != DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) {
meassurementStatus = distanceSensor.meassure();
if (meassurementStatus == DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) {
dis = distanceSensor.getDistance()/10;
};
}
}*/
void longpushTest() {
unsigned long delayTime = 1000;
if (!digitalRead(MAINBUTTON_PIN)) { //if the button is pushed then go to timercheck
if (firstLoop == true) {
longTimer = millis();
firstLoop = false;
firstFlip = true; //resetting the condition to 'flip' one time while pushing long BEFORE entering the next if statement
//Serial.println((String) "longTimer " + longTimer + "second");
//if (lcdMenu >= 1 && lcdMenu <= 5) {
//Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
//};
}
if ((millis() - longTimer) > delayTime) { //timercheck: if the difference reaches 1000ms it is bigger than the delaytime the counterLoop wil be called
//Serial.println((String) "entering counterLoop or flipSwitch"); testing
if (lcdMenu == 6) {
//Serial.println((String) "entering flipSwitch"); testing
flipSwitch();
} else if (lcdMenu == 8) { //once in the default setting menu (after 'if (defaultFlag)'!) going to default settings with a longpush
loadvarinEEPROM(); //STORING defaultVariables in EEPROM
size_t numBytes = sizeof(defaultVariables);
memcpy(menuVariables, defaultVariables, numBytes );//copying default const values to the variables that can be changed in running time
lcd.clear();
lcd.setCursor(0, 0); //cursor set line , character 0//next line limit info
lcd.print(" DEFAULT READY ");
delay(3000); //freezing all activity 3sec with message the device is set to default
} else if (lcdMenu == 5) { //going from lcdMenu (display tanklevel) to default setting menu with a longPush
lcdMenu = 8;
} else { //if ((lcdMenu != 6) && (lcdMenu != 0)) {
//Serial.println((String) "entering counterLoop");//testing
counterLoop(); // AS LONG AS THE BUTTON IS BEING PUSHED THE SKETCH KEEPS 'LOOPING' IN THE WHILE LOOP OF THE COUNTERLOOP FUNCTION
droppingFlag = false; //disrupting the risingedgeTest after exiting the the 'long push' in the longpushtest //increase counter as long as button is pushed (=low)
EEPROM.put(eepromStart + sizeof(eeKey) + sizeof(firmwareVersion) + ((lcdMenu) * 2), menuVariables[lcdMenu]); //saving the altered variable in the EEPROM without comparison because after certainty of change
};
}
//Serial.println((String) "BUTTON PRESSED " + (millis() - longTimer) + "ms");
} else if (digitalRead(MAINBUTTON_PIN)) { //if button is not pushed (=input high) reset the timer by forcing longTimer = millis() the next cycle
firstLoop = true;
}
}
void risingedgeTest() { //it is no use to make this a bool fucntion. although the value can be directly returned to the calling function the parameters have to be reset after every jump to the next lcdMenu so these need to be declared globaly (so risingEdge is also globaly declared)
firstButtonread = digitalRead(MAINBUTTON_PIN); //reading button first time. There is no need compensate jitter in running time with 'millis()' because this takes more extra processing time (aprox 12ms and minimum 5 extra variables) and the delay(10) can't cause failures or unresponsive main pushbutton in this sketch
delay(10); //two readouts with a small delay to compensate jitter advised 50ms but 10ms works better and with a 68nF capacitor parallel in button works even better.
secondButtonread = digitalRead(MAINBUTTON_PIN); //reading button second time
if (firstButtonread == LOW && secondButtonread == LOW) { //LOW/LOW = no jitter
buttonState = LOW; //=LOW means the button is pushed!! input pulling to GND!
timeoutTimer = millis(); //by each press on the main button resetting the timer for the timeout. NEEDS TO BE IN RISINGEDGETEST BECAUSE RISINGEDGETEST IS THE ONLY FUCTION THAT IS CALLED IN ALL THE CASSES
}
if (firstButtonread == HIGH || secondButtonread == HIGH) { //one of the two HIGH means jitter or button just released
buttonState = HIGH;
}
if (buttonState == LOW) { //testing if a dropping edge has occured
droppingFlag = HIGH; //the droppingEdge needs to be flaged if occurring
//Serial.println((String) "droppingFlag " + droppingFlag); //for testing
} //when the button is continuous pushed this droppingFlag STAYS HIGH!
//if droppingFlag is high, testing if a risingEdge is occuring in the next 'loops'(droppingFlag stays HIGH no matter what!)
if (droppingFlag == HIGH) {
if (firstButtonread == HIGH && secondButtonread == HIGH) {
buttonState = HIGH;
};
if (firstButtonread == LOW || secondButtonread == LOW) {
buttonState = LOW;
}
if (buttonState == HIGH) {
risingFlag = HIGH;
//Serial.println((String) "risingFlag " + risingFlag + " ");
}
//if the butten has been pushed (=droppingFLag) en some cycles later been pushed (=risingFlag) the condition for toggle to the next menu is ok
if (droppingFlag == HIGH && risingFlag == HIGH) {
risingEdge = true;
} else {
risingEdge = false;
}
}
}
void counterLoop() {
int Speed = 0;
int counterSpeed = 1;
while (!digitalRead(MAINBUTTON_PIN)) {
//Serial.println("COUNTERLOOP"); // testing purpose
//Serial.println((String) "MinLevel " + counter + " cm inside while loop"); // testing
valueCounter(); //increase or dercease counter one step
//DISPLAYING values of current lcdMenu 'while' adjusting limits up or down
sprintf(buf, "%-10s%3d%-3s", menuText[lcdMenu], menuVariables[lcdMenu], menuUnits[lcdMenu]); //put everything as a string in a buffer buf
lcd.setCursor(0, 0);
lcd.print(buf);
if (menuVariables[lcdMenu] == highLimit || menuVariables[lcdMenu] == lowLimit) {
lcd.setCursor(0, 1); //cursor set line 1, character 0//
lcd.print(" !LIMIT! ");
}
enterLong = HIGH; //setting variable to know if the while loop has been entered
//the next six lines adjust the toggle speed in setting a value 'while' pushing the button longer then one seconde
if (counterSpeed >= 1 && counterSpeed < 6) {
Speed = 400; //250ms
} else if (counterSpeed >= 6 && counterSpeed < 15) {
Speed = 200; //125ms
} else if (counterSpeed == 15) {
Speed = 50; //50ms
};
delay(Speed); //the toggle speed on the counter/display value. This needs to changed to 3 automatic speed levels for user friendly use
if (counterSpeed >= 1 && counterSpeed <= 15) {
counterSpeed++;
};
//Serial.println((String) "counter " + counter + " directionInt " + directionInt); //testing purpose
};
if (enterLong == HIGH) { //enterLong is used to make the counterdirection change AFTER from the moment the button is released
directionInt = -directionInt; //flip the sign/counter direction after 1sec push
enterLong = LOW; //re-setting to LOW so the next time directioInt can be fliped in case of going tru while loop
//Serial.println((String) "directionInt " + directionInt); //testing purpose
};
}
//void fuction because it does not need to return a value. int counter is declared globaly
void valueCounter() {
if (menuVariables[lcdMenu] > highLimit && menuVariables[lcdMenu] < lowLimit) { //THE LIMIT VALUES NEED TO BE CHANGED ACCORDING TO THE MENU!!!SO THESE VALUES (2 & 401) ARE FOR MENU 2 ONLY
menuVariables[lcdMenu] = menuVariables[lcdMenu] - directionInt;
}
//the next two if functions are used to 'push' the counter in one desired direction
//after the button has been released on highLimit or lowLimit and re-pressed; longer than a second
//in order to get in to the if function above. So it's a special method so the counter doens't get 'outside the limits hense blocking the function above
if (menuVariables[lcdMenu] == highLimit && enterLong == LOW) {
menuVariables[lcdMenu] = menuVariables[lcdMenu] + 1;
}
if (menuVariables[lcdMenu] == lowLimit && enterLong == LOW) {
menuVariables[lcdMenu] = menuVariables[lcdMenu] - 1;
}
//Serial.println((String) "directionInt " + directionInt); // testing
}
void valveCommand(){
if (dis >= menuVariables[2] && dis > menuVariables[3]) {
digitalWrite(LED_REFILL, 1);
} else {
digitalWrite(LED_REFILL, 0);
};
}
void resetFlags() {
droppingFlag = LOW; //resetting buttonreading function of rising edge after led switch
risingFlag = LOW; //resetting buttonreading of rising edge after led switch
risingEdge = false; //resetting buttonreading of rising edge after led switch
}
void flipSwitch() { //instead of a counterLoop is the function to set the alarm active (on) or inactive and is entered in the longpushTest function
while (!digitalRead(MAINBUTTON_PIN)) { //after experimenting there is a while loop implemented because the droppingEdge kempt going back up with random results at pushbuttonrelease
if (firstFlip == true) {
valveAlarmonoff = !valveAlarmonoff;
displayAlarmstate();
if (valveAlarmonoff == true) {
alarmTimer = millis();
//Serial.println(" alarm on in flipswitch"); // testing
};
//if (valveAlarmonoff == false) {
//Serial.println(" alarm off in flipswitch");// testing
//};
firstFlip = false; //making shure when the butting is still pushed in the second pass true the flipSwitch function that valveAlarmonoff stays the same
//Serial.println((String) "firstFlip" + firstFlip);//for testing
//} else {
//Serial.println((String) " alarm " + valveAlarmonoff + " in flipSwitch Whileloop");// testing
};
};
droppingFlag = false; //disrupting next risingedgeTest when the buttom is nog pushed anymore
}
//these are the two alarm checks. One for detection exceeding refill time (menuVariables[4]) and one if the valve is still open while the rifill command has stoped
//first one timecheck
void alarmCheck() {
//Serial.println((String) alarmTimer);//for testing
//when alarm is on: check the refill timer, check if the valve is closed (VALVEREADOUT_PIN) while not commanded open (LED_REFILL, this is reading a output!!but should work) and check if tank is not to 'empty' or to 'full'
//if ((millis() - alarmTimer) > maxRefilltime || digitalRead(VALVEREADOUT_PIN) == digitalRead(LED_REFILL)){ //this line is for testing purpose only. Here you can shorten the time to a few seconds instead of a one minute minimum
if ((valveAlarmonoff == true && ((millis() - alarmTimer) > (menuVariables[4] * 60000) || (digitalRead(VALVEREADOUT_PIN) == 0 && digitalRead(LED_REFILL) == 0))) || ((menuVariables[0] < dis) || (dis < menuVariables[1]))) { //menuVariables[4] is a value that represents minutes hense *60000 to convert to ms
digitalWrite(LED_ALARM, 1);
} else {
digitalWrite(LED_ALARM, 0);
};
//the next is a buttonread to reset the alarm TIMER. This means the alarm timer detection is only 'delayed' but the alarms (timer and 'valve open while not commanded') stay ON.
//note!: the button readout is done with a simple anti-jitter delay instead of the millis() readout methode because in this case some ms extra
//processing time is not a isseu AND: the millis() readout methode also takes a minimum of 12ms extra and at least five extra unsigned long values!!
if ((valveAlarmonoff == true) && (millis() - alarmTimer) > (menuVariables[4] * 60000)) { //only reset timer if valveAlarmonoff is on and timer alarm has reached limit
if (!digitalRead(ALARMRESET_PIN)) { //using analog pin A4 as digital input with internal pullup
delay(10);
if (!digitalRead(ALARMRESET_PIN)) {
//Serial.println((String) alarmTimer + " before alarmTimer()=millis()"); //for testing
alarmTimer = millis(); //if alarmresetbutton is pushed, reset the alarmtimer
//Serial.println((String) alarmTimer + " after alarmTimer()=millis()");//for testing
};
};
//if (valveAlarmonoff == false) {
//digitalWrite(LED_ALARM, 0);
//};
};
}
void lcdFirstline() {
sprintf(buf, "%-10s%3d%-3s", menuText[lcdMenu], menuVariables[lcdMenu], menuUnits[lcdMenu]); //put the array's of chars as a string in the buffer buf
lcd.setCursor(0, 0);
lcd.print(buf);
}
void displayAlarmstate() {
if (valveAlarmonoff == false) {
lcd.setCursor(0, 1); //cursor set line 1, character 0
lcd.print(" "); //erasing the second line the previous (16 characters) on the lcd display
lcd.setCursor(0, 0);
lcd.print("Alarm off ");
//Serial.println((String) " alarm off ");
} else if (valveAlarmonoff == true) {
lcd.setCursor(0, 1); //cursor set line 1, character 0
lcd.print(" "); //erasing the second line the previous (16 characters) on the lcd display
lcd.setCursor(0, 0);
lcd.print("Alarm on ");
//Serial.println((String) " alarm on ");
}
}
bool timeoutCheck() {
if ((millis() - timeoutTimer) > timeoutTime) {
first7passFlag = false;
return true;
} else {
return false;
};
}
void loadvarinEEPROM(){
for (int i = 0; i < 5; i++) { //STORING defaultVariables in EEPROM
EEPROM.put(eepromStart + sizeof(eeKey) + sizeof(firmwareVersion) + (i * 2), defaultVariables[i]); //uint16_t 16bit = two bytes
}
};