Hello there, I’m kind of new and a little bit rusty in programming. Hope someone can give me a direction. Working with a MKR WAN 1310 but no need for communicating (YET) over the LORA network. Next step maybe.
Project: water irrigation in a vegetable garden based on a solenoid, a water flow meter and a moisture sensor. Powered by a 12V battery which in turn is charged by a solar panel. All built in a wooden box and gardend by a door switch for which I’m trying to figure out the following with the Millis function:
The door switch is checked every 30 sec and if everything is clear a green LED will confirm this. When the switch is opened a red LED should blink. If the switch remains open for 10 seconds the LED will remain lit and a sirene should sound for 3 seconds, then stop and check if the switch is closed within the 10 seconds. If closed turn to safe mode and turn on the green LED. If still open sound the alarm for 3 seconds.
I tried to work with just Millis() function and also with the switch case function.
This is just part one of the project. Checking the door switch.
Part two is, while all of this is happening I would like to time the solenoid every day at a specific time. Let's say 05:30 am. At that moment it checks the moisture level and if it is below a specific value (i.e. 50%) then the solenoid is activated. Moisture level is checked every 10 minutes and if below 50% it remains on. If higher then 50% then the solenoid should be switched off. The solenoid is on for a maximum of 30 minutes or a maximum of 100 litres measured by the flow meter.
So multiple events happen at the same time, check door switch and check for 05:30 AM. Anyone having an idea or is aware of similar projects what the best way to proceed is? Thanks in advance. Coert
you can use switch() statements to implement a state machine using mills() to move between states
start with a simple implementation and add complexity
for an example of a start machine generating a waveform see sawtooth-ramp-wave-generator-using-arduino-uno
for LoRa it is worth looking at the esp32-sx1276-lora-ssd1306-oled - much cheaper than the MKR WAN 1310 and has an onboard OLED display - I have tested it using LoRa point to point and LoRaWAN to the Things V3 server
/* The door switch is checked every 30 sec and if everything is clear a green LED will confirm this.
When the switch is opened a red LED should blink. If the switch remains open for 10 seconds the LED
will remain lit and a sirene should sound for 3 seconds, then stop and check if the switch is closed
within the 10 seconds. If closed turn to safe mode and turn on the green LED. If still open sound
the alarm for 3 seconds.
While all of this is happening I would like to time the solenoid every day at a specific time.
Let's say 05:30 am. At that moment it checks the moisture level and if it is below a specific
value (i.e. 50%) then the solenoid is activated. Moisture level is checked every 10 minutes
and if below 50% it remains on. If higher then 50% then the solenoid should be switched off.
The solenoid is on for a maximum of 30 minutes or a maximum of 100 litres measured by the flow meter.
*/
const uint16_t BLINK_INTERVAL = 500; // Time interval between toggling LED1 in milliseconds
const uint16_t ALARM_INTERVAL = 3000; // Time interval between toggling LED1 in milliseconds
const uint32_t SOLENOID_INTERVAL = 15000; // Interval_SOLENOID = 86400000 and will be set to 05:30 AM
const uint8_t all_safe_LED = 13; // The all_safe_LED is connected to pin 13
const uint8_t alert_LED = 12; // The alert_LED is connected to pin 12
const uint8_t activate_Sirene = 11; // The activate_Sirene is connected to pin 11
const uint8_t door_Switch = 10; // door_Switch is connected to pin 10
const uint8_t reset_Switch = 9; // Reset_Switch is connected to pin 9
const uint8_t relay_Solenoid = 8; // Relay sensor to activate solenoid on pin 8
enum states { // Define enumerated type for state machine states
NO_MODE,
SAFE_MODE,
ALERT_MODE,
ALARM_MODE,
MEASURE_MOISTURE_MODE,
IRRIGATION_MODE,
WATER_FLOW_MODE
};
states prior_state, state; // Global variables to store the prior and current states
uint32_t alert_modus_time; // Global variable to store the time that alert_LED last changed state
uint16_t alert_modus_count; // Global variable to store the number of times alert_LED has changed
uint32_t alarm_modus_time; // Global variable to store the time that alarm was activated
uint16_t alarm_modus_count; // Global variable to store the number of times alert_LED has changed
unsigned long previousMillisSOLENOID=0; // Global variable to store the time and set to zero
boolean SOLENOID_state = false;
// states
void no_modus() {
// initialzation
if (state != prior_state) {
prior_state = state;
}
// tasks
// transition states
// exit state and clean up
}
void safe_modus() {
// initialzation
if (state != prior_state) {
prior_state = state;
digitalWrite(all_safe_LED, HIGH);
}
// tasks
// transition states
if (digitalRead(door_Switch) == HIGH) {
state = ALERT_MODE;
}
// exit state and clean up
if (state != prior_state) {
digitalWrite(all_safe_LED, LOW);
}
}
void alert_modus() {
uint32_t t;
// initialzation
if (state != prior_state) {
prior_state = state;
digitalWrite(alert_LED, HIGH);
alert_modus_time = millis();
alert_modus_count = 0;
}
// tasks
t = millis();
if (t >= alert_modus_time + BLINK_INTERVAL) {
digitalWrite(alert_LED, !digitalRead(alert_LED));
alert_modus_time = t;
alert_modus_count++;
}
// transition states
if (digitalRead(reset_Switch) == HIGH) {
state = SAFE_MODE;
} else if (alert_modus_count == 10) {
state = ALARM_MODE;
}
// If we are leaving the state, do clean up stuff
if (state != prior_state) {
digitalWrite(alert_LED, LOW);
}
}
void alarm_modus() {
uint32_t a;
// If we are entering the state, do initialization stuff
if (state != prior_state) {
prior_state = state;
digitalWrite(activate_Sirene, HIGH);
alarm_modus_time = millis();
alarm_modus_count = 0;
}
// tasks
a = millis();
if (a >= alarm_modus_time + ALARM_INTERVAL) {
digitalWrite(activate_Sirene, !digitalRead(activate_Sirene));
alarm_modus_time = a;
alarm_modus_count++;
}
// transition states
if (alarm_modus_count == 5) {
state = ALERT_MODE;
}
// exit state and clean up
if (state != prior_state) {
digitalWrite(activate_Sirene, LOW);
}
}
void measure_moisture_modus() {
// initialzation
if (state != prior_state) {
prior_state = state;
}
// tasks
// transition states
// exit state and clean up
}
void irrigation_modus() {
// initialzation
unsigned long currentMillis = millis();
if (state != prior_state) {
prior_state = state;
}
// tasks
if ((unsigned long)(currentMillis - previousMillisSOLENOID) >= SOLENOID_INTERVAL) {
SOLENOID_state = !SOLENOID_state;
digitalWrite(relay_Solenoid, SOLENOID_state);
previousMillisSOLENOID = currentMillis;
// transition states
// exit state and clean up
}
}
void water_flow_modus() {
// initialzation
if (state != prior_state) {
prior_state = state;
}
// tasks
// transition states
// exit state and clean up
}
void setup() {
pinMode(all_safe_LED, OUTPUT); // Configure all_safe_LED pin as a digital output
pinMode(alert_LED, OUTPUT); // Configure alert_LED pin as a digital output
pinMode(activate_Sirene, OUTPUT); // Configure activate_Sirene pin as a digital output
pinMode(door_Switch, INPUT); // Configure door_Switch pin as a digital input
pinMode(reset_Switch, INPUT); // Configure reset_Switch pin as a digital input
pinMode(relay_Solenoid, OUTPUT); // SOLENOID
digitalWrite(all_safe_LED, LOW); // Set all_safe_LED low initially
digitalWrite(alert_LED, LOW); // Set alert_LED low initially
digitalWrite(activate_Sirene, LOW); // Set activate_Sirene low initially
prior_state = NO_MODE;
state = SAFE_MODE;
}
void loop() {
switch (state) {
case NO_MODE:
no_modus();
break;
case SAFE_MODE:
safe_modus();
break;
case ALERT_MODE:
alert_modus();
break;
case ALARM_MODE:
alarm_modus();
break;
case MEASURE_MOISTURE_MODE:
measure_moisture_modus();
break;
case IRRIGATION_MODE:
irrigation_modus();
break;
case WATER_FLOW_MODE:
water_flow_modus();
break;
}
}
looks as though you have started OK
make sure your control functions are non-blocking
you can add another state mahine for part two
does the code work as expected?
if not give details and the serial monitor output (as text not a screen image)
If I close the switch again the the alarm continues to sound and the red LED only blinks every two or three cycles. The green LED doesn't come on. So it doen't enter the safe_modus state.
After startup and door switch (DW) is closed:
state = 1
After DW is opened:
state = 2
After 3 seconds:
state = 3
After DW is closed again:
state = 3 and switches to state = 2. Alarm keeps sounding and red LED alternating. Never comes in state = 1 again.
Looking further into it and I can only reset to safe mode (state = 1) in alert-modus (state = 20 by operating the reset switch. Impossible in alarm-modus (state = 3).
Any ideas? Thanks in advance. Go offline now
Hi Tom, all Led’s are connected to GND via a 270 ohm resistor and borg switches are connected to GND via a 10K resistor. Borg solenoïde and sirene is switched to two relays.
Hope this helps
1st place you need to change, and I like what I see that you are nowhere rusted through!
Issue is how you use time.
Yes I can add unsigned start time and interval and get the right answer,
a problem arises when I test it. My simple example is based on 12 hour round clock.
If the hour is 9, add 8 hours and it will be 5. So at 10, 10 is >5 and in 3 hours it will be 1 which is < 5 while both occur before 5.
The nice easy one solution equation is to check elapsed time since start vs interval.
elapsed time always = end - start most often expressed as now - start or millis - start.
if ( millis - start >= interval ) // time is up, execute the conditional!
if ( millis - start <= interval ) // time is not up, continue executing the conditional!
I do a one-shot that only runs if (desired) interval > 0. When a task in loop() sets interval > 0 it runs and last step is interval = 0 to turn itself off.
With what you know, is that enough?
Do you remember the main loop code from before multitasking OS's ran on PC's?
I spent time in the 80's on that on 6502;s and a Z80 box with terminal. Great stuff.
I find it hard to follow you state-machine logic because assigning a new value to the state-variable named "state" is done inside other functions
The logic would become much easier to understand (and IMHO much easier to expand and maintain) if assigning a different state is done in the case-level
or directly above the switch-statement.
just as an example (of course not full functional code
void loop() {
if (digitalRead(door_Switch) == HIGH) {
state = ALERT_MODE;
switch (state) {
case SAFE_MODE:
// .....
coding this way would have the advantage that jumping back and forth between different functions is not nescessary
So if state zero is waiting for a first data char and upon reading it changes the state to 1 where the rest of the entry is read.... you didn't learn that?
I´ve read your requierement specification.
My recommendation is to split the project in function system blocks like:
Divide et impera
external communication system
watering system using RTC
door alert system
Each sytem block can be coded and tested separtly. For the interprocess system communication a data structure might be used. The reading of the IPO model might help by your software design task.
Some needed basic functions, like timer, state change detector etc, are provided as examples by the IDE.
Got back from work and thanks for all your input. I will look into it was already started to divide the project into three parts. Started with the watering system and got that working with the library's rtczero and scheduler. Then worked on the door alert system and came up with some strange behaviors. Final plan is to incorporate this all into on program. Will definitely try to rewrite the time comparison into a 'now - start >= interval' and will get back to you. Thanks for the tip @GoForSmoke
try a web search for state machine and "arduino state machine"
may give you some idea of how to split the tasks up and move from state to state without hanging up somewhere
aka finite state machine executes one or more case codes per call. How? If you have a series of cases that do not have a break; then string of cases can run with the programmer responsible for the cycles not being hogged, and every case in the string being a possible entry point.
State there meaning Process State being Step Number.
State machines ain't everything but they organize multi-step processes really well and that makes step-sizing no big deal.
By dividing the job into tasks that may run their own state machines and have their own private data you can make effective object code tasks.
Here is a WokSim-Demo-code that shows how the functionality for monitoring the door closed (or opened) can be written.
It uses a state-machine for doing this.
as a prelimary explanation
Your description of how the door-monitoring should work can be devided into multiple phases
if door is closed the green LED is on the red LED is off
if door open is detected
phase A:
blink LED and
check if door is opened for longer than 10 seconds. If 10 seconds open phase B starts
phase B:
beep for 3 seconds after that phase C starts
phase C:
check if another 10 seconds have passed by with door opened
if door has been open another 10 seconds repeat sequence starting with phase A
whenever door is closed return to normal operation