Water irrigation project

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

Start by posting what you tried and explain the problems that you had

1 Like

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

1 Like
/* 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)

1 Like

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,
Can we please have a circuit diagram?
An image of a hand drawn schematic will be fine, include ALL power supplies, component names and pin labels.

How have you got your door switches wired, have you got pull up or pull down resistors?

Thanks... Tom.. :smiley: :+1: :coffee: :australia:

1 Like

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.

1 Like

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

best regards Stefan

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?

1 Like

Hello vandenenk

I´ve read your requierement specification.
My recommendation is to split the project in function system blocks like:

Divide et impera

  1. external communication system
  2. watering system using RTC
  3. 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.

Check it, try it and play around.

Have a nice day and enjoy coding in C++.

1 Like

for a similar project using distributed sensors communicating over LoRa see decimals-strings-and-lora

1 Like

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

1 Like

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.

just filling a small gap

If the timing ever has to be close, use micros() not millis().

And one semi-off-topic question if you choose to answer as new to the forum, how do you rate your experience so far?

Do you know that the main site this forum is on has loads of docs and code for online help? Like the hyperlinked Reference.

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

best regards Stefan

1 Like