Timers, relay and virtual pins - newbie struggling

I’ve read the example for blink without delay, and most of the threads on the community around timer, millis, delays. Hugely informative but in the end as a newbie I am now even more confused. So I am trying to use timer. I have lifted code for some of the examples and the newbie nightmare is …it is partly working!

I have a garden project. The irrigation is controlled via a solenoid (relay). I have 2 options: scheduled time (twice a day) and manual. The manual option is initiated by Blynk button on V11. To stop the solenoid over heating if it was left on for too long or by accident, I want to turn it on for 10 see then off for 15 secs whilst it is in the loop until the desired level of soil moisture has been achieved. If midway during this I turn the Blynk button to off, I want to close the solenoid. If it runs until the desired level of moisture is reached, then I want ensure the solenoid is closed and flag that watering has been completed.Whilst this is happening I still need to get updates from other sensors (hence not using the delay option).

in Blynk:
waterbutton is Styled Button (Wateron/off) on virtual Pin V11 with values from 0 to 1
This starts the manual watering process.

Moisture is a gauge on V7 (0-100%)
Humidity is a labelled value on V5 (0-100%)
Temperature is a labelled value on V (-10 to 55)

on esp32:
Capacitive moisture sensor is on Pin 33
DT22 temp/humidity sensor is on pin 29
Relay (to power solenoid) is on Pin 19

Code below updated to show full code

Startup sequence works. Turning on the virtual “Water on/off” switch on Blynk (V11), starts the on/off cycle, triggering the relay for 10 secs on then 15 secs off.

The problems are:

  1. The on/off sequence should stop automatically when when moisture sensor reading shows desired Soil moisture level but doesn’t do this.
    2.If you press the "water on/off"button on Blynk V11 it sets the button back to the off state but doesn’t set the relay off

Pseudo code would be:

  1. On Start set solenoid to closed (HIGH in my case as I have High relays);

  2. IF ManualWatering Button pushed then
    check parameter from Blynk button = 1 (pushed)
    Check moisture level before we start to water.
    while the moisture level is less than the desired moisture level
    turn on the water for10 secs, then off for 15 secs until the desire moisture is reached
    Once desired level met, make sure the solenoid is off
    update the button state on Blynk
    set a “we-Watered” Flag to true

  3. If at any stage I press the button to turn off the water, then this needs to immediately stop the loop and turn off the water.

I am using iOS, esp32 over wifi to Blynk server and Arduino IDE for code.

Code has other functions in that work, so I have just extracted this test stub

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <WidgetRTC.h>
#include <TimeLib.h>
#include <DHT.h>
#include <OpenWeatherOneCall.h>

//sensor definitions
#define DHTPIN 19
#define DHTTYPE DHT22   // DHT 11
#define BLYNK_PRINT Serial

//Blynk coloor definitions
#define BLYNK_WHITE     "#FFFFFF"
#define BLYNK_GREEN     "#23C48E"
#define BLYNK_BLUE      "#04C0F8"
#define BLYNK_YELLOW    "#ED9D00"
#define BLYNK_RED       "#D3435C"
#define BLYNK_DARK_BLUE "#5F7CD8"

//Declarations
DHT dht(DHTPIN, DHTTYPE);
BlynkTimer timer;
WidgetRTC rtc;
WidgetLED led1(V2);

//wifi and Oneweatehr definitions
#define ONECALLKEY "REDACTED"
#define HOMESSID "REDACTED"
#define HOMEPW "REDACTED!"
#define ONECALLKEY "REDACTED "

//Declare Moisture and watering Variables
bool we_watered = false;          //flag to show whether we watering has occurred in case of rain being forecast
bool manual_water = false;        //flag to show whether we have manually watere
bool it_rained = false;           //flag to show whether it has rained or not
bool rain_forecast = false;       //flag to show whether it rain is forecast or not
bool scheduled_water_time = false;
bool watering_needed = false;     //flag to show whether a bed needs watering because it is below the set moisture level
int solenoid = 23;                //solenoid to control water flow
const float desiredMoisture = 38; //Set desired bed moisture to 38%
const int AirValue = 3483;        // Upper value of the Capcity mositure sensor for this bed
const int WaterValue = 1696;      //Lower value of the Capcity mositure sensor for this bed
const int moistureSensorPin = 33; //pin 33 on the esp32
int soilMoistureValue = 0;        // Set iniitial Value to zero to make sure we get a clean reading
int soilmoisturepercent = 0;      // Set iniitial Value to zero to make sure we get a clean reading
int waterLevel = 0;               //Set iniitial Value to zero to make sure we get a clean readinf
int waterButton =0;               //takes the value form the blynk virtual button
int onTime =10;                   //time solenoid is open dispensing water
int offTime = 15;                 //time solenoid is close
int timerNo;                      // ID of timer to start stop
boolean isOn = false;             // toggle flag for relay

void checkMoisture()
{
  soilMoistureValue = analogRead(moistureSensorPin);  //put Sensor insert into soil
  Serial.print("Value: ");
  Serial.println(soilMoistureValue);
  soilmoisturepercent = map(soilMoistureValue, AirValue, WaterValue, 0, 100);
  Serial.print(soilmoisturepercent);
}


//if the manual watering button is pressed in Blynk
BLYNK_WRITE(V11){
 waterButton = param.asInt();
   if (waterButton == 1)  
   {
       timer.enable(timerNo); // Turn on the solenoid/relay for stated time (set to 10 secs)
       Serial.print ("Button on pressed. Value is :");
       Serial.println(waterButton);
   }
   else
   {
       timer.disable(timerNo); // Turn off the solenoid/relay for stated time (set to 15 secs)
       Serial.print ("Button off pressed. Value is :");
      Serial.println(waterButton);
  }
}


// MCU operations
void onOffToggler()
{   
   timer.deleteTimer(timerNo);
   if (!isOn)
   {
       digitalWrite(solenoid, LOW);
       timerNo = timer.setInterval(onTime * 1000L, onOffToggler);
       isOn = true;
   }
   else
   {
      digitalWrite(solenoid, HIGH);
      timerNo = timer.setInterval(offTime * 1000L, onOffToggler);
      isOn = false;
   }
}


void turnOnOffWater()
{
  if (waterButton ==1) // Assumes if 1  - manual water tiggered
  {  
    checkMoisture(); //Check Moisture levels to see how long to water for
    while (soilmoisturepercent < desiredMoisture) //loop until the desired mositure has been reached.
    {
      
      Serial.print ("Button pressed, detected and we are now in the loop. Value is :");
      Serial.println(waterButton);
      timerNo = timer.setInterval(onTime * 1000L, onOffToggler); // Do function every 10 sec, if enabled
      checkMoisture();
    }
    //moisture level reached so set watered flag to true;
    we_watered = true;
    digitalWrite(solenoid, HIGH); //check solenoid is closed
    Blynk.virtualWrite(V11, 0); //set Blynk manual water button back to off
  }
  else 
    {//manual interrupt occurred so the button was switched off
    digitalWrite(solenoid, HIGH);
    Blynk.virtualWrite(V11, 0); //ensure the button/switch goes back to the off position
    isOn=false;
    //insert code for autmated schedule
    
    }
}

void sendSensor1()
{
  float h = dht.readHumidity();
  float t = dht.readTemperature(); // or dht.readTemperature(true) for Fahrenheit
    
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  // You can send any value at any time.
  // Please don't send more that 10 values per second.
   Serial.print("Temp: ");
  Serial.println(t);
  Serial.print("Humidity: ");
  Serial.println(h);

  Blynk.virtualWrite(V5, h);
  Blynk.virtualWrite(V4, t);

}

void sendSensor2() //sensor for the Capactive mositure sensor
{
  //waterLevel = 80;//analogRead(waterLevelSensor);
  //waterLevel = map(waterLevel,0,2000,0,20);
  checkMoisture();
  if (soilmoisturepercent > 100)
  {
    Serial.println("Moisture=100%");
    Blynk.setProperty(V7, "color", BLYNK_YELLOW);
  }
  else if (soilmoisturepercent >= 81 && soilmoisturepercent <= 100) //getting wet so set to blue
  {
    Blynk.setProperty(V7, "color", BLYNK_DARK_BLUE);
    Serial.print("Moisture is betweeb 81 and 1000 so it should be DARK BLUE ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  else if (soilmoisturepercent >= 31 && soilmoisturepercent <= 80)
  {
    Blynk.setProperty(V7, "color", BLYNK_GREEN);
    Serial.print("Moisture is betweeb 15 and 80 so it should be GREEN: ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  else if (soilmoisturepercent >= 15 && soilmoisturepercent <= 30) //dry set it to white
  {
    Blynk.setProperty(V7, "color", BLYNK_WHITE);
    Serial.print("Moisture is between 15 and 30 so it should be WHITE ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  else if (soilmoisturepercent < 15) //error way too dry. set it to red
  {
    Blynk.setProperty(V7, "color", BLYNK_RED);
    Serial.print("Moisture ia less than 15 so it should be RED: ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  //update the dashboard guages with new values
  Blynk.virtualWrite(V6, waterLevel);
  Blynk.virtualWrite(V7, soilmoisturepercent);

}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(solenoid, OUTPUT);
  timerNo = timer.setInterval(onTime * 1000L, onOffToggler); // Do function every 10 sec, if enabled
   digitalWrite(solenoid, HIGH);
  timer.disable(timerNo); // start disabled
  Blynk.begin(auth, ssid, pass);
  Blynk.virtualWrite(V11, 0);//makes sure Manual button is set to off or "Water now"
  timer.setInterval(5000L, sendSensor1);
 timer.setInterval(5000L, sendSensor2);
  turnOnOffWater();
  checkForecast();

}

void loop() {
  // put your main code here, to run repeatedly:
  Blynk.run();
  timer.run();
}

I’m not inclined to start looking at this and trying to make some sense of it, until you clarify the buttons and switches that you’re taking about in your introduction.
I’d suggest that you go back and edit your post (using the pencil icon at the bottom) and add some clarity around whether you are talking about physical switches and buttons or widget. Either way, adding information about which pins these switches/buttons are connected to, and what their functions are will make your post much simpler to understand and to relate to your code.

Pete.

my apologies
I hope I have clarified with the edits. very grateful for the support.

For clarity, I will repeat the key edits:

Blynk Styled button (labelled water on/off) is set to output (for 0 to 1). This is the “switch” that sets off the manual watering process.

I want to to turn on a relay (connected to a solenoid valve) that is connected on the esp32 to GPIO Pin 23.
There is a moisture sensor that is connected not the esp32 on GPIO pin33.

I want o test whether the soil is moist enough ( a nominal value of 38%). If it is most enough then the manual watering should be stopped with a notification (I’ve yet to implement this).

If it is not moist enough, then I want the water to turn on for 10 secs, then the pump off for 15 secs and keep checking the soil moisture until it is wet enough. then stop the 10 sec/15 sec cycle and make sure the relay/solenoid is off. (I dont mind if it reaches the correct moisture level midway through a cycle because with only 10 secs of dispensing it is never going to get over saturated).

it is possible that I make choose to stop watering before it is moist enough, by manually clicking the button in Blynk and turning the water on/off to off. In this case I would want to switch the relay off immediately.

Hope this is clear, and with the edits above and the full code, less troublesome.

Okay, that’s better - thanks.

I’ve redacted your confidential data. You have two identical entries define your ONECALLKEY, and no definition for your Blynk auth token, I guess this is a copy/paste issue?

I’m reading this code on a mobile phone at the moment, so apologies if I’m missing things.

I would have expected your void checkMoisture() function to have contained some code that compared the moisture reading to the target, and shut-off your solenoid if it was at or higher than the target.

Also, the way you’ve done the timers is a bit strange. If you want to use timer IDs to stop/start/delete timers then you really need separate variables to hold each timer ID. You also need a dummy/sacrificial timer that will occupy the ID zero slot, to overcome the simple timer bug.

Personally though, I’d probably use timeout timers to do the on/off pulsing of the solenoid rather than an interval timer.

Pete.

thanks - yes the Blynk Auth Token exists in the real code -that was a copy paste error!

I looked at the Timeout but obviously implemented it wrong. I’ll have another go.
Also I’ll have a crack at redoing the checkMoisture function.
To be honest I am after something simple and I think I am over thinking it/complicating it.
In the timeOut function, as I understand it you need to pass a value and a function call. I tried passing a null function but it wouldn’t compile.
From what you understand of the code as-is, if I was to use timeout what function should I then call? I had it originally with delays which worked except of course for blocking other updates form sensors, hence having a go with timers
By the way - I and many others - are including grateful for your help, expertise and the promptness with which you respond. For a newbie it is very comforting to know there is both the expertise and the patience out there!

I think that this bit of pseudocode is what I’d do…

BLYNK_WRITE(V11)
{
 get the value of the switch widget and store this as global variable "Manual_Watering" 
 if Manual_Watering  ==1
   call the take_a_moistuire_reading() function
else
  do nothing
}

void take_a_moistuire_reading()
{
  Read the soil sensor
  if soil sensor reading < target moisture level then...
    energise the solenoid    
    start a 10 second timeout timer which calls the wait_15_seconds function
  else
    close the solenoid
    turn the V11 Manual Watering button off with a Blynk.virtualWrite(V11,0)
    Manual_Watering  = 0
    update some indicators in the app if desired
}


void  wait_15_seconds()
{
if Manual_Watering ==1   (V11 button still ON)
  Start a 15 second timeout timer which calls the take_a_moistuire_reading() function
else
  do nothing
{

This would create a cycle which re-triggers itself until either the soil moisture is no longer below target or the manual watering widget is turned off.

Get this working first, then you can start to look at adding-in the automatic watering cycles that could utilise the same functions.
You’ll probably need a to do things like:

if Manual_Watering ==1  or Auto_Watering == 1

to make your functions multi-purpose.

Does this make sense?

Thank You!

Pete.

It makes complete sense.
Now just to implement it without endless newbie compile errors, but that is part of the fun of learning isn’t it!

thanks you for your help. I’ll have a go right now and let you know

Marco

You think that’s just a newbie problem :wink:

Pete.

Pete - you are a star. thank you. It works perfectly!
I did spend about 25 mins debugging using serial monitor to print out what was being called when and where - which really helped to focus step by step on the logic.
I’ve posted the updated code below for others - as it seems that garden irrigation, Blynk, timers, relays etc is a fairly common use case.
Now I am going to add the twice a day schedule; and the logic that if rain is forecast then the automatic scheduled is cancelled for 12 hours, then the forecast checked again etc. Very satisfying,

thank you again for your support.
Marco

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <WidgetRTC.h>
#include <TimeLib.h>
#include <DHT.h>
#include <OpenWeatherOneCall.h>

//sensor definitions
#define DHTPIN 19
#define DHTTYPE DHT22   // DHT 11
#define BLYNK_PRINT Serial

//Blynk coloor definitions
#define BLYNK_WHITE     "#FFFFFF"
#define BLYNK_GREEN     "#23C48E"
#define BLYNK_BLUE      "#04C0F8"
#define BLYNK_YELLOW    "#ED9D00"
#define BLYNK_RED       "#D3435C"
#define BLYNK_DARK_BLUE "#5F7CD8"

//Declarations
DHT dht(DHTPIN, DHTTYPE);
BlynkTimer timer;
WidgetRTC rtc;
WidgetLED led1(V2);

//wifi and Oneweatehr definitions
#define ONECALLKEY "REDACTED" //my private key for the openweather API calls. Redacted for security!

//Declare Moisture and watering Variables
bool we_watered = false;          //flag to show whether we watering has occurred in case of rain being forecast
bool manual_water = false;        //flag to show whether we have manually watere
bool it_rained = false;           //flag to show whether it has rained or not
bool rain_forecast = false;       //flag to show whether it rain is forecast or not
bool scheduled_water_time = false;
bool watering_needed = false;     //flag to show whether a bed needs watering because it is below the set moisture level
int solenoid = 23;                //solenoid to control water flow
const int desiredMoisture = 38; //Set desired bed moisture to 38%
const int AirValue = 3483;        // Upper value of the Capcity mositure sensor for this bed
const int WaterValue = 1696;      //Lower value of the Capcity mositure sensor for this bed
const int moistureSensorPin = 33; //pin 33 on the esp32
int soilMoistureValue = 0;        // Set iniitial Value to zero to make sure we get a clean reading
int soilmoisturepercent = 0;      // Set iniitial Value to zero to make sure we get a clean reading
int Moistenough=false;
int waterLevel = 0;               //Set iniitial Value to zero to make sure we get a clean readinf
int manual_Watering = 0;               //takes the value form the blynk virtual button
int onTime =10;                   //time solenoid is open dispensing water
int offTime = 15;                 //time solenoid is close
int timerNo;                      // ID of timer to start stop
boolean isOn = false;             // toggle flag for relay


//Blynk weather forecast variables
String mylocationString;          //to display in the blynk terminal
String  currentTempStr;           //to display in the blynk terminal
String currentHumidityStr;        //to display in the blynk terminal

// OpenWeatherOneCall variables
float myLATITUDE = 51.51119916275342;   //<-----Crowsley, Henley-on Thamesr
float myLONGITUDE = -0.9597222296547302;//<-----Crowsley, Henley-on-Thames
int myCITYID = 2639577;                 //For City ID Location setting if used - READING
int myUNITS = METRIC;                   //<-----METRIC, IMPERIAL, KELVIN (IMPERIAL is default)
int myHISTORY = NULL;                   //<-----Only required for historical data up to 5 days. NULL means no historical data as cant get current and historical
int myEXCLUDES = NULL;                  //<-----0 Excludes is default
//for debugging loop counting
int nextCall = 0;
//int callAttempt = 1;

OpenWeatherOneCall OWOC;              // <------ Invoke Library like this

//Blynk and wifi Credentials
char auth[] = "REDACTED";
// My WiFi credentials.
char ssid[] = "REDACTED";
char pass[] = "REDACTED";

//-----------main functions called in code

void checkForecast()
{
  // First set your Key value
  OWOC.setOpenWeatherKey(ONECALLKEY);
  // Second set your position (can be CITY ID, IP ADDRESS, GPS, or Manual LATITUDE/LONGITUDE)
  OWOC.setLatLon(myLATITUDE, myLONGITUDE);
  OWOC.setDateTimeFormat(DMY24H);
  OWOC.setExcl(myEXCLUDES); // Third set any EXCLUDES if required (Here we are not using any
  OWOC.setHistory(myHISTORY);  // Set History if you want historical weather other wise NULL
  OWOC.setUnits(myUNITS);  // Set UNITS of MEASURE to Metric otherwise default is IMPERIAL
  
  //Now call the weather. Please notice no arguments are required in this call
  OWOC.parseWeather();
  //Now display some information
  printf("\nLocation: % s, % s % s\n", OWOC.location.CITY, OWOC.location.STATE, OWOC.location.COUNTRY);
  mylocationString = mylocationString + "\nLocation: " +  OWOC.location.CITY + ", " + OWOC.location.STATE + ", " + OWOC.location.COUNTRY;
  Blynk.virtualWrite(V1, mylocationString);

  //Verify all other values exist before using
  if (myHISTORY) //Remember you can't get historical and current weather at the same time
  {
    if (OWOC.history)
    {
      printf("Mean Temp for % s : % .0f\n", OWOC.history[0].weekDayName, OWOC.history[0].temperature);
      //Blynk.virtualWrite(V1, ("Mean Temp for % s : % .0f\n", OWOC.history[0].weekDayName, OWOC.history[0].temperature);
    }
  } else
  {
    if (OWOC.current)
    {
      currentTempStr = "Current Temp : " + String(OWOC.current->temperature, 4);
      Blynk.virtualWrite(V1, currentTempStr);
      printf("Current Temp : % .0f\n", OWOC.current->temperature);
      printf("Feels like : % .0f\n", OWOC.current->apparentTemperature);
      Blynk.virtualWrite(V1, ("Feels like : % .0f\n", OWOC.current->apparentTemperature));
      printf("Current Humidity : % .0f\n", OWOC.current->humidity);
      currentHumidityStr = "Current Humidity :" + String(OWOC.current->humidity, 4);
      Blynk.virtualWrite(V1, currentHumidityStr);
      printf("Current time iz : %s\n", OWOC.current->readableDateTime);
      Blynk.virtualWrite(V1, ("Current time iz : %s\n", OWOC.current->readableDateTime));
      printf("Current pressure is : % .0f\n", OWOC.current->pressure);
      Blynk.virtualWrite(V1, ("Current pressure is : % .0f\n", OWOC.current->pressure));
      printf("Current cloudcover is : % .0f\n", OWOC.current->cloudCover);
      Blynk.virtualWrite(V1, ("Current cloudcover is : % .0f\n", OWOC.current->cloudCover));
      printf("Current Main :is :  %s\n", OWOC.current->main);
      Blynk.virtualWrite(V1, ("Current Main :is :  %s\n", OWOC.current->main));
      printf("Current Summary is :  %s\n", OWOC.current->summary);
      Blynk.virtualWrite(V1, ("Current Summary is :  %s\n", OWOC.current->summary));
      printf("Current cloudcover is : % .0f\n", OWOC.current->cloudCover);
      Blynk.virtualWrite(V1, ("Current cloudcover is : % .0f\n", OWOC.current->cloudCover));
      printf("Current sunrise is : %s\n", OWOC.current->readableSunrise);
      Blynk.virtualWrite(V1, ("Current sunrise is : %s\n", OWOC.current->readableSunrise));
      printf("Current Sunset is :  %s\n", OWOC.current->readableSunset);
      Blynk.virtualWrite(V1, ("Current Sunset is :  %s\n", OWOC.current->readableSunset));
      printf("Current icon is :  %s\n", OWOC.current->icon);
      Blynk.virtualWrite(V1, ("Current icon is :  %s\n", OWOC.current->icon));
    }

    if (OWOC.forecast)
    {
      printf("\nForecast Temp Tomorrow : % .0f\n", OWOC.forecast[1].temperatureHigh);
      Blynk.virtualWrite(V1, ("\nForecast Temp Tomorrow : % .0f\n", OWOC.forecast[1].temperatureHigh));
      printf("Current WeekDayname is :  %s\n", OWOC.forecast[1].weekDayName);
    }
    if (OWOC.alert) {
      printf("ALERT *** ALERT *** ALERT\n");
      printf("Sender : % s\n", OWOC.alert->senderName);
      printf("Event : % s\n", OWOC.alert->event);
      printf("ALERT : % s\n", OWOC.alert->summary);
    }

  }
}

BLYNK_WRITE(V11)
{
 manual_Watering = param.asInt(); //get the state of the Blynk button and assign to maunual_watering. We'll use this to track the watering state.
 if (manual_Watering  ==1)
 {
    take_a_moisture_reading();  //call the take_a_moisture_reading function
 }
 else
 {
  digitalWrite(solenoid, HIGH);//close the solenoid
  Blynk.virtualWrite(V11,0);
  }
}

void update_moisture_reading()  //this function is called from both take_a_moisture_reading (Manual or autmated) AND Send_sensor2 function to update the sensor readings in blynk with no further action
{
  
  soilMoistureValue = analogRead(moistureSensorPin);  //put Sensor insert into soil
  soilmoisturepercent = map(soilMoistureValue, AirValue, WaterValue, 0, 100); //map its reading to a percent. Each sensor has dry (variable is airvalue) and wet (variable is watervalue)
}

void take_a_moisture_reading()
{
  update_moisture_reading(); //Get the latest percentage moisture
  if (soilmoisturepercent < desiredMoisture) //desired Moisture is constant set to 38 in the declarations at the top.
  {
    digitalWrite(solenoid, LOW);//energise the solenoid    
    timer.setTimeout(10000L, wait_15_seconds); //start a 10 second timeout timer which calls the wait_15_seconds function
  }
  else
  {
    digitalWrite(solenoid, HIGH);//close the solenoid
    Blynk.virtualWrite(V11,0);  //turn the V11 Manual Watering button off with a Blynk.virtualWrite(V11,0)
    manual_Watering  = 0;  //set the manual_watering status to false
    //update some indicators in the app if desired
  }
}

void wait_15_seconds()
{
if (manual_Watering ==1)
  {       //(V11 button still ON)
    digitalWrite(solenoid, HIGH);//close the solenoid
    timer.setTimeout(15000L, take_a_moisture_reading);//Start a 15 second timeout timer which calls the take_a_moistuire_reading() function
  }
}

void sendSensor1() // get the reading for tempertare and humidity from the the DT22
{
  float h = dht.readHumidity();
  float t = dht.readTemperature(); // or dht.readTemperature(true) for Fahrenheit
    
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  Serial.print("Temp: ");
  Serial.println(t);
  Serial.print("Humidity: ");
  Serial.println(h);
  Blynk.virtualWrite(V5, h);
  Blynk.virtualWrite(V4, t);
}
 

void sendSensor2() //sensor for the Capactive mositure sensor
{
  //waterLevel = 80;//analogRead(waterLevelSensor);
  //waterLevel = map(waterLevel,0,2000,0,20);
  update_moisture_reading(); //get the latest percentage moisture
  if (soilmoisturepercent > 100)
  {
    Serial.println("Moisture=100%");
    Blynk.setProperty(V7, "color", BLYNK_YELLOW);
  }
  else if (soilmoisturepercent >= 81 && soilmoisturepercent <= 100) //getting wet so set to blue
  {
    Blynk.setProperty(V7, "color", BLYNK_DARK_BLUE);
    Serial.print("Moisture is betweeb 81 and 1000 so it should be DARK BLUE ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  else if (soilmoisturepercent >= 31 && soilmoisturepercent <= 80)
  {
    Blynk.setProperty(V7, "color", BLYNK_GREEN);
    Serial.print("Moisture is betweeb 15 and 80 so it should be GREEN: ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  else if (soilmoisturepercent >= 15 && soilmoisturepercent <= 30) //dry set it to white
  {
    Blynk.setProperty(V7, "color", BLYNK_WHITE);
    Serial.print("Moisture is between 15 and 30 so it should be WHITE ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  else if (soilmoisturepercent < 15) //error way too dry. set it to red
  {
    Blynk.setProperty(V7, "color", BLYNK_RED);
    Serial.print("Moisture ia less than 15 so it should be RED: ");
    Serial.print(soilmoisturepercent);
    Serial.println("%");
  }
  //update the dashboard guages with new values
  Blynk.virtualWrite(V6, waterLevel);
  Blynk.virtualWrite(V7, soilmoisturepercent);

}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(solenoid, OUTPUT);
  digitalWrite(solenoid, HIGH);
  Blynk.begin(auth, ssid, pass);
  Blynk.virtualWrite(V11, 0);//makes sure Manual button is set to off or "Water now"
  timer.setInterval(5000L, sendSensor1);
  timer.setInterval(5000L, sendSensor2);
  checkForecast();

}

void loop() {
  // put your main code here, to run repeatedly:
  Blynk.run();
  timer.run();
}

No shortage of horse manure for the garden either!

Pete.