Finally using timers but after feedback

I could use delay() but I am moving on and trying to write my code using timers as it seems to be advised in general.

Could someone comment on whether there are better ways to write this?

I am setting up a watering system that opens a solenoid valve (run off a SSR attached to the NodeMCU

I will increase the time for all timers eventually, but leaving them short for the first trial…

I want to read the analog input pin every second and determine if this is below a certain value (moisture level) and if so water the garden. Only water for 1 second (run the stopWatering function to close the solenoid valve) and then not allow watering to happen again for another minute to allow the water to be absorbed by the soil.

  #include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "####";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "#####";
char pass[] = "######";

BlynkTimer timer;


int relayPin = 4; // rleay pin for solenoid on D2
int moisture = 512;

unsigned long timeSinceLastWatering = 0;
unsigned long lastWateringTime = millis();

void setup()
{

  // Debug console
  Serial.begin(9600);

  Blynk.begin(auth, ssid, pass);
  // You can also specify server:
  //Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8442);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8442);

  timer.setInterval(1000L, checkmoisture); //  check for moisture every 1 second
}

void checkmoisture()
{
  moisture = analogRead(A0); // read moisture
  Blynk.virtualWrite(V1, moisture); // write moisture to blynk database
  if (moisture < 512) // check if moisture is too low
  {
timeSinceLastWatering = millis() - lastWateringTime;
if (timeSinceLastWatering > 60000) // make sure it can only run a maximum of once every minute to allow water to seep into soil
{
  startWatering();
}
  }
}

void startWatering()
{
  digitalWrite(relayPin, HIGH);
  lastWateringTime = millis();
  timer.setTimeout(1000L, stopWatering);  // Deactivare Relay/Watering after 1 seconds by running stopWatering 1 second from when this code runs
  }

void stopWatering()
{
  digitalWrite(relayPin, LOW); // stop watering, after the duration/timeout specified in startWatering
}

void loop()
{
  Blynk.run();
}

If you are using NodeMCU, than it should be:

Serial.begin(115200);

Off the top-o-me head I don’t have code for it… but yes, all that can be done with a combination of an interval timer (for the analog function check), one or two countdown timers (to account for the watering time and delay until next allowed time) and some flags (triggered by the countdown timers) and boolean logic to guide it all.

Search this forum for relay pulses, timed door latches, and other timer oriented functions that I and others have created examples of.

1 Like

OK. I have had a look and got some ideas…

I am hoping this is an easy question to answer, but respect if suggested to do further reading…

If I put this in my setup()

timerCheck = timer.setInterval(checkFrequency, checkmoisture); // check for moisture every # second

If I change the variable checkFrequency while the code is running using a Blynk slider, the timer won’t change as this line only runs on setup yes?

If I include it like below I should be able to change the interval yes (checkFrequency variable)? I am changing the variable checkFrequency once the watering is needed and being carried out, then increase the time between checking once the moisture level is reached. I don’t need to do this but figured that it doesn’t really need to check every 5 seconds unless its watering time…

or Do I need to delete or disable the timer, then change the checkFrequency variable then put the timer.setinterval again?

void checkmoisture()
{
  timerCheck = timer.setInterval(checkFrequency, checkmoisture); //  check for moisture every # second
  moisture = analogRead(A0); // read moisture
  Blynk.virtualWrite(V1, moisture); // write moisture to blynk database
  if (moisture < 512) // check if moisture is too low
  {
    checkFrequency = 5000;
    timeSinceLastWatering = millis() - lastWateringTime;
    if (timeSinceLastWatering > waterFrequency) // make sure it can only run a maximum of once every minute to allow water to seep into soil
    {
      startWatering();
    }
  }
  else
  {
    checkFrequency = 60000;
  }

}

void startWatering()
{
  digitalWrite(relayPin, HIGH);
  lastWateringTime = millis();
  timerStop = timer.setTimeout(stopDuration, stopWatering);  // Deactivare Relay/Watering after 1 seconds by running stopWatering 1 second from when this code runs
}

void stopWatering()
{
  timer.deleteTimer(timerStop);
  digitalWrite(relayPin, LOW); // stop watering, after the duration/timeout specified in startWatering
}

Just leave that interval timer as is… but add in a flag check each time the called function runs, to see if it needs to continue or exit…

If continue, then set the “don’t continue” flag, set a timeout timer to clear that flag later, do the watering, run a timeout timer to stop the watering, exit function.

1 Like

Edit: I think I misunderstood, but have left my comment in case it will work anyway… I will give your suggestions some more thought later today. Thanks.

So you mean check to see if the variable has changed (flag), if it has then delete (exit) the timer interval, change the interval, then run the the interval timer again. If the variable hasn’t changed then no need to delete or set the timer interval again as it’s already running with the right values.

I did code like this but have a feeling I need to think/read a little more

void checkmoisture()
{
  if (checkFrequency =! checkFrequencyOld) // compare the variable checkFrequency with the previous value it was called
  {
  timer.deleteTimer(timerCheck);
  timerCheck = timer.setInterval(checkFrequency, checkmoisture); //  check for moisture every # second
  }
.........
}

I still have sooo much to learn. It is such a grey area for me to decide whether to use the arduino ide forum or here? I tend to incorporate Blynk into all my projects (3rd one, but first one using simple (Blynk) timers… so it’s easy for me to fall into this forum.

Just pseudo code, and rather simplistic layout, but I think this covers the basics

void checkmoisture()    {
if (flag = 0) { // if flag = 1 exit
flag = 1; // set exit flag
set timeout timer to run function to reset flag
start to water stuff
set timeout timeout to run function to stop watering stuff
} else (
do nothing    }
}

void resetFlag()    {
flag = 0 // clear exit flag
}

void stopWateringStuff()    {
stop watering stuff
}

The one key here is use of the TIME OUT timer. In this case it runs once then stops until called again. e.g.

timer.setTimeout(60000L, resetFlag);  // in one minute, run the resetFlag() function

https://playground.arduino.cc/Code/SimpleTimer#F_setTimeout

Fantastic. Thanks. Looks like what I’ve been writing since has been along the lines of that.

Here is my code. Its a move in the right direction with using the blynk timers I feel but stil expect more improvements in the efficiency of the code…

I will give it a test run when I return from holidays overseas in a weeks time or so…

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "######"; // Blynk code

// Your WiFi credentials.
char ssid[] = "#####"; // wifi name
char pass[] = "#####"; // wifi password

BlynkTimer timer;

// default variables
long periodBetweenSoaks = 600000; // default minimum period between soaks in minutes in millis
long durationToWater = 10000; // default length of time per watering/soak in millis
float moistureLimit = 512; // default lower limit of moisture between 0 and 1023

//blynk variables that change defaults above
unsigned long virtualpin1 = 10; // virtual pin for blynk - minimum period between soaks in minutes
unsigned long virtualpin2 = 1; // virtual pin for blynk - length of time per watering/soak in minutes
unsigned long virtualpin3 = 50; // virtual pin for blynk - lower limit of moisture  between 0 and 100

// hardware variables
int relayPin = 5; // relay pin for solenoid on D2
float moisture = 512; // current moisture value

// checking moisture frequencies
long checkSoilMoistureSensorFrequency = 1000; //default soil moisture period/frequency
long checkSoilMoistureSensorFrequencyOld = 1000; // default previous soil moisture period reading used to detect whether to intiate the timer interval again

//timer variables
unsigned long timercheckSoilMoistureSensorFrequency = 1000; // variable used to name the timer interval for period between reading soil moisture
unsigned long timerPeriodBetweenSoaks = 600000; // variable used to name the timer interval used for period between soaks
unsigned long timerStopWatering = 10000; // variables used to name the timer countdown
int period = 1; // used to monitor when the timer countdown has been reached to allow another soak

BLYNK_WRITE(V1)
{
  virtualpin1 = param.asInt(); // assigning incoming value from pin V1 to a variable
  periodBetweenSoaks = virtualpin1 * 60000; // period between soaking (milli) defined by blynk slider (minutes)
}

BLYNK_WRITE(V2)
{
  virtualpin2 = param.asInt(); // assigning incoming value from pin V2 to a variable
  durationToWater = virtualpin2 * 60000;  // length of soaking (milli) defined by blynk slider (minutes)
}

BLYNK_WRITE(V3)
{
  virtualpin3 = param.asInt(); // assigning incoming value from pin V3 to a variable
  moistureLimit = virtualpin3 * 10.23;  // minimum soil moisture (0-1023) defined from blynk slider (0-100%)
}

void setup()
{

  Blynk.begin(auth, ssid, pass);

  timercheckSoilMoistureSensorFrequency = timer.setInterval(checkSoilMoistureSensorFrequency, checkSoilMoistureSensorFunction); //  check for moisture every # second
  timerPeriodBetweenSoaks = timer.setInterval(periodBetweenSoaks, periodBetweenSoaksFunction); //  check for moisture every # second

  Blynk.virtualWrite(V1, virtualpin1); //set virtual pins to defaults
  Blynk.virtualWrite(V2, virtualpin2); //set virtual pins to defaults
  Blynk.virtualWrite(V3, virtualpin3); //set virtual pins to defaults
}

void checkSoilMoistureSensorFunction()
{
  moisture = analogRead(A0); // read moisture
  Blynk.virtualWrite(V4, moisture / 10.23); // write moisture to blynk database
  if (moisture < moistureLimit) // check if moisture is too low
  {
    if (period == 1) // has period between soaks been exceeded - yes =1, no =0 . If no then likely the moisture will stay low until period = 1 and it soaks again or rain comes
    {
      checkSoilMoistureSensorFrequency = 1000; // make moisture check more frequent while watering until moisture exceeded
      wateringFunction(); // begin watering
      period = 0; //reset the flag until next minimum watering period is exceeded so this if statement only runs once per period
    }
    else
    {
    }
  }
  else
  {
    checkSoilMoistureSensorFrequency = 600000; // longer period between checking moisture a soil is above levels. Good for graph/history reduction in data
  }

  if (checkSoilMoistureSensorFrequency = ! checkSoilMoistureSensorFrequencyOld) // if frequency or period between soaks has changed then reset the timer interval
  {
    timer.deleteTimer(timercheckSoilMoistureSensorFrequency);
    timercheckSoilMoistureSensorFrequency = timer.setInterval(checkSoilMoistureSensorFrequency, checkSoilMoistureSensorFunction); //  check for moisture every # second
  }
  checkSoilMoistureSensorFrequencyOld = checkSoilMoistureSensorFrequency; // assign current frequency to an old variable to compare with next frequency
}

void periodBetweenSoaksFunction() // called in periods defined by an interval timer
{
  period = 1; // period between soaks exceeded
}

void wateringFunction()
{
  digitalWrite(relayPin, HIGH); // pin high to close SSR and turn solenoid/water on
  timerStopWatering = timer.setTimeout(durationToWater, stopWateringFunction);  // Deactivare Relay/Watering after # seconds by running stopWatering # second from when this code runs
}

void stopWateringFunction()
{
  timer.deleteTimer(timerStopWatering); // delete timer just because I read it is safer somewhere
  digitalWrite(relayPin, LOW); // stop watering, after the duration/timeout specified in startWatering
}

void loop()
{
  Blynk.run();
}

I wrote this again as after I added hysteresis the code got more confusing (although still seemed OK).

I need to check the function when it waters as I am not fully confident about it. I think I have stopped it starting to water again (checking every minute while its dry) before the previous watering and soaking period has finished (11 minutes duration)… but this I may need to debug a bit more.

I have uploaded this to the Arduino IDE as I thought the question about how best to do the hysteresis was more Arduino IDE related…

But uploading it here in case someone else finds it useful… or wishes to comment.

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "###"; // Blynk code

// Your WiFi credentials.
char ssid[] = "###"; // wifi name
char pass[] = "###"; // wifi password

BlynkTimer timer;

// variables that change
long periodBetweenSoaks = 600000;
long wateringPeriod = 60000;
int moistureLimit = 512;
int moistureLimitHysteresis = 52;

// default variables
int virtualpin1 = 10;
int virtualpin2 = 1;
int virtualpin3 = 50;
int virtualpin5 = 5;

// other variables
int moistureLimitMet = 0;
long moistureCheckPeriod = 1000;
long moistureCheckPeriodOld = 1000;
int currentWateringCycle = 0;

//timer variables
long moistureCheckTimer = 1000;
long timerForWateringPeriod = 60000;
long timerForSoakingPeriod = 600000;
long timerForWaterCycle = 600000;

// hardware variables
int moisture = 1023;
int signalStrength = 0;
int relayPin = 5;

BLYNK_WRITE(V1)
{
  virtualpin1 = param.asInt(); // assigning incoming value from pin V1 to a variable
  periodBetweenSoaks = virtualpin1 * 60000; // period between soaking (milli) defined by blynk slider (minutes)
}

BLYNK_WRITE(V2)
{
  virtualpin2 = param.asInt(); // assigning incoming value from pin V2 to a variable
  wateringPeriod = virtualpin2 * 60000;  // length of soaking (milli) defined by blynk slider (minutes)
}

BLYNK_WRITE(V3)
{
  virtualpin3 = param.asInt(); // assigning incoming value from pin V3 to a variable
  moistureLimit = virtualpin3 * 10.23;  // minimum soil moisture (0-1023) defined from blynk slider (0-100%)
}

BLYNK_WRITE(V5)
{
  virtualpin5 = param.asInt(); // assigning incoming value from pin V3 to a variable
  moistureLimitHysteresis = virtualpin5 * 10.23;
}

void setup()
{
  Blynk.begin(auth, ssid, pass);

  Blynk.virtualWrite(V1, virtualpin1); //set virtual pins to defaults
  Blynk.virtualWrite(V2, virtualpin2); //set virtual pins to defaults
  Blynk.virtualWrite(V3, virtualpin3); //set virtual pins to defaults
  Blynk.virtualWrite(V5, moistureLimitHysteresis); //set virtual pins to defaults

  moistureCheckTimer = timer.setInterval(moistureCheckPeriod, MoistureCheckFunction);
}

void MoistureCheckFunction()
{
  moisture = analogRead(A0); // read moisture
  Blynk.virtualWrite(V4, moisture / 10.23); // write moisture to blynk database
  wifiSignal();

  if (moisture <= moistureLimit - moistureLimitHysteresis)
  {
    if (currentWateringCycle == 0)
    {
      moistureLimitMet = 1;
      moistureCheckPeriod = 10000;
      moistureCheckTimerResetFunction();
      wateringStartFunction();
    }
  }
  else if (moisture >= moistureLimit + moistureLimitHysteresis)
  {
    moistureLimitMet = 0;
    moistureCheckPeriod = 600000;
    moistureCheckTimerResetFunction();
    wateringStopFunction();
  }

}

void moistureCheckTimerResetFunction()
{

  if (moistureCheckPeriod = ! moistureCheckPeriodOld)
  {
    timer.deleteTimer(moistureCheckTimer);
    moistureCheckTimer = timer.setInterval(moistureCheckPeriod, MoistureCheckFunction);
  }

  moistureCheckPeriodOld = moistureCheckPeriod;
}

void wateringStartFunction()
{
  if (moistureLimitMet == 1)
  {
    currentWateringCycle = 1; // 1 is yes there is a current water cycle to stop request to water before current cycle has finished.
    digitalWrite(relayPin, HIGH); // start watering
    timerForWateringPeriod = timer.setTimeout(wateringPeriod, wateringStopFunction);
  }
}

void wateringStopFunction()
{
  digitalWrite(relayPin, LOW); // stop watering

  if (moistureLimitMet == 1)
  {
    timerForWaterCycle = timer.setTimeout(periodBetweenSoaks, wateringFunction);
    timerForSoakingPeriod = timer.setTimeout(periodBetweenSoaks, wateringStartFunction);
  }

}

void wateringFunction()
{
  currentWateringCycle = 0;
}

void wifiSignal()
{
  signalStrength = WiFi.RSSI();
  Blynk.virtualWrite(V6, signalStrength + 100); // write singal strength to blynk database but add 100 so weak = 0 and strong = 100
}

void loop()
{
  Blynk.run();
}