Programador de riego

Hola a todos.

Me he hecho. Me he hecho un programador de riego con un NodeMCU. Abajo os pongo el código Que he utilizado junto con la app iOS Blynk. Todo me funciona de categoría.

Ahora estoy pensando en ponerle un Rain Delay que me permita poner en sleep todo el programador según los días que ponga en el Virtual Pin 33. El problema es que no se por donde empezar. Si alguien me puede echar una mano se agradecería.

Adjunto código y foto de GUI.

  Download latest Blynk library here:

  Blynk is a platform with iOS and Android apps to control
  Arduino, Raspberry Pi and the likes over the Internet.
  You can easily build graphic interfaces for all your
  projects by simply dragging and dropping widgets.

    Downloads, docs, tutorials:
    Sketch generator: 
    Blynk community:  
    Follow us:        

  Blynk library is licensed under MIT license
  This example code is in public domain.

  This example runs directly on ESP8266 chip.

  Note: This requires ESP8266 support package:

  Please be sure to select the right ESP8266 module
  in the Tools -> Board menu!

  Change WiFi ssid, pass, and Blynk auth token to run :)
  Feel free to apply it to any other example. It's simple!

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial
#define WIFI_LED 10

// define configuration (number of switches and number of timers)
#define SWITCH_CNT 4
#define TIME_CNT 4

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

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).

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

byte switch_pins[] = {14 , 12 , 16 , 13}; // number of gpio to be used as switch/relay control

bool switch_default[] = {LOW,LOW,LOW,LOW}; // switches that use reverse polarity should be set to HIGH here

// This code can control up to 4 switches                                            //
// For each switch up to 4 schedule start and end times can be configured - (V0..V15) //
// A default duration can be defined per switch                           - (V16..V19)//
// If an end time is not defined at the app the default duration is used              //
// default duration is also used when manually activating the switch       - (V20..V23)//
// for each switch when start time is reached the switch turns on for the duration    //
//                                                                                    //
// maximum duration is used for safety (to limit activation time)          - (V24..V27)//
int  start_time_sec[SWITCH_CNT][TIME_CNT];     // array of 4 start times (in seconds) for 4 switches [switch number][schedual timer number]
bool start_valid[SWITCH_CNT][TIME_CNT];        // is the start time valid ?
bool weekdays[SWITCH_CNT][TIME_CNT][8];        // array of 8 days (day 0 not used) for each schedual time
int  active_duration[SWITCH_CNT][TIME_CNT+1];  // duration per switch per time(in sec)
bool end_valid[SWITCH_CNT][TIME_CNT];          // is the end time valid ?
int  max_duration[SWITCH_CNT];                 // max duration per switch
int auto_off = 1;           // 1 is auto or on

// when activating a switch a timer is set for the configured duration
// when the duration ends the switch is turned off by the timer
// the id of the timer is saved using end_timer_id
// if switch is manually turned off the end_timer_id is used to stop the timer.

// end_timer_id is initialized to 32 (per entry) at the setup section
int end_timer_id[SWITCH_CNT];

// timer object declaration
BlynkTimer timer;

// this code use Real Time Clock widget in the blynk app to keep the clock updated from net
WidgetRTC rtc;

  // Synchronize time on connection
WidgetLED led1(V55);

bool ledStatus = auto_off;

// V55 LED Widget is ON or OFF
void LedWidget()
{ledStatus = auto_off;
  if (ledStatus) {
  } else {;



Imagen no se ve
Código no se ve
Seguirmos sin respetar las normas @Torque73.
Tomate tu tiempo y edita tranquilo.

Resto del código que no me cabia

// get schedual parameters from App                     //
void set_time(BlynkParam param, byte switch_no, byte time_no){

     TimeInputParam t(param);
  // Process start time

  if (t.hasStartTime())
    Serial.println(String("Start: ") +
                   t.getStartHour() + ":" +
                   t.getStartMinute() + ":" +
    Serial.println(String("Start in sec: ") + param[0].asLong() + String(" for switch") + switch_no + String(" time_no: ") + time_no);
    start_time_sec[switch_no][time_no]= param[0].asLong();
    start_valid[switch_no][time_no] = true;

    // Do nothing
    Serial.println(String("No Start Time Given for switch: ") + switch_no + String(" time_no: ") + time_no);
    start_valid[switch_no][time_no] = false;

  // check if end time is received convert and save it as day time in seconds //
  if (t.hasStopTime())
    Serial.println(String("Stop: ") +
                   t.getStopHour() + ":" +
                   t.getStopMinute() + ":" +
    Serial.println(String("Stop in sec: ") + param[1].asLong() + String(" for switch") + switch_no + String(" time_no: ") + time_no);

    active_duration[switch_no][time_no] = (param[1].asLong()-start_time_sec[switch_no][time_no]);
    // if end time is smaller than start time this means end time is at next day
    if (active_duration[switch_no][time_no]<0) active_duration[switch_no][time_no]=86400+active_duration[switch_no][time_no];
    Serial.println(String("Stop duration: ") + active_duration[switch_no][time_no]);

    end_valid[switch_no][time_no] = true;

  else // if end time is not defined //
    // Do nothing
    Serial.println(String("No Stop Time Given for switch: ") + switch_no + String(" time_no: ") + time_no);
    end_valid[switch_no][time_no] = false;

  // Process weekdays (1. Mon, 2. Tue, 3. Wed, ...)

  for (int i = 1; i <= 7; i++) {
    if (t.isWeekdaySelected(i)) {
      Serial.println(String("Day ") + i + " is selected");
      weekdays[switch_no][time_no][i] = true;
    else {
      weekdays[switch_no][time_no][i] = false;


//this reads status of on/off mode and displays on screen
BLYNK_WRITE(V50) { int Vpin50 = param.asInt();
  auto_off = Vpin50;}

// V0..V3 for switch 0, V4..V7 for switch 1 ...
BLYNK_WRITE(V0)  { set_time(param, 0,0);  }
BLYNK_WRITE(V1)  { set_time(param, 0,1);  }
BLYNK_WRITE(V2)  { set_time(param, 0,2);  }
BLYNK_WRITE(V3)  { set_time(param, 0,3);  }

BLYNK_WRITE(V4)  { set_time(param, 1,0);  }
BLYNK_WRITE(V5)  { set_time(param, 1,1);  }
BLYNK_WRITE(V6)  { set_time(param, 1,2);  }
BLYNK_WRITE(V7)  { set_time(param, 1,3);  }

BLYNK_WRITE(V8)  { set_time(param, 2,0);  }
BLYNK_WRITE(V9)  { set_time(param, 2,1);  } 
BLYNK_WRITE(V10) { set_time(param, 2,2);  }
BLYNK_WRITE(V11) { set_time(param, 2,3);  }

BLYNK_WRITE(V12) { set_time(param, 3,0);  }
BLYNK_WRITE(V13) { set_time(param, 3,1);  }
BLYNK_WRITE(V14) { set_time(param, 3,2);  }
BLYNK_WRITE(V15) { set_time(param, 3,3);  }

// use a slider to define default activation duration (slider count in minute)
BLYNK_WRITE(V16) { active_duration[0][TIME_CNT] = param.asInt()*60; }
BLYNK_WRITE(V17) { active_duration[1][TIME_CNT] = param.asInt()*60; }
BLYNK_WRITE(V18) { active_duration[2][TIME_CNT] = param.asInt()*60; }
BLYNK_WRITE(V19) { active_duration[3][TIME_CNT] = param.asInt()*60; }

// use a slider to define default max duration (slider count in minute)   ***HARD CODE THIS*** NL
BLYNK_WRITE(V24) { max_duration[0] = param.asInt()*60; }
BLYNK_WRITE(V25) { max_duration[1] = param.asInt()*60; }
BLYNK_WRITE(V26) { max_duration[2] = param.asInt()*60; }
BLYNK_WRITE(V27) { max_duration[3] = param.asInt()*60; }

// Handle switch events (from app or from scheduler )          //

// turn off switch after active duration ends
// duration number is not important here 
void turn_off_switch_no_0(){ turn_on_off(0,0,0); Blynk.virtualWrite(V20,0); Serial.println(String("timer turn off switch 0 ") );}
void turn_off_switch_no_1(){ turn_on_off(0,1,0); Blynk.virtualWrite(V21,0); Serial.println(String("timer turn off switch 1 ") );}
void turn_off_switch_no_2(){ turn_on_off(0,2,0); Blynk.virtualWrite(V22,0); Serial.println(String("timer turn off switch 2 ") );}
void turn_off_switch_no_3(){ turn_on_off(0,3,0); Blynk.virtualWrite(V23,0); Serial.println(String("timer turn off switch 3 ") );}

// handle switch state
void turn_on_off(int on_off, byte switch_no , byte time_no){
    long active_duration_ms ;
    char Time_print[16];
    if ((on_off==1) && (auto_off == 1)) //auto_off is a slider in app to shut off the program
      // create time as string to print on activation butten
      sprintf(Time_print, "%02d:%02d", hour(), minute());

      // turn on the switch (or off if default is on)

      // if end time is valid use the active duration assigned to this time
      // (change to msec will be done later)
      if (end_valid[switch_no][time_no])
         active_duration_ms = ((long)active_duration[switch_no][time_no]);
      else // otherwise use the general time duration
         active_duration_ms = ((long)active_duration[switch_no][4]);

      // max duration smaller than two min is not valid
      if ( (max_duration[switch_no]< 120) | (max_duration[switch_no]>active_duration_ms) )
         active_duration_ms = active_duration_ms*1000;
        active_duration_ms = ((long)max_duration[switch_no])*1000;

      // if new timer is set before another one ended then disable previous timer
      if (end_timer_id[switch_no]!=32) timer.deleteTimer(end_timer_id[switch_no]);

      // turn on switch and set timer 
      switch (switch_no) {
        case 0: 
          Blynk.setProperty(V20, "onLabel", String(Time_print));
          end_timer_id[0]=timer.setTimeout(active_duration_ms, turn_off_switch_no_0);
        case 1: 
          Blynk.setProperty(V21, "onLabel", String(Time_print));
          end_timer_id[1]=timer.setTimeout(active_duration_ms, turn_off_switch_no_1);
        case 2: 
          Blynk.setProperty(V22, "onLabel", String(Time_print));
          end_timer_id[2]=timer.setTimeout(active_duration_ms, turn_off_switch_no_2);
        case 3: 
          Blynk.setProperty(V23, "onLabel", String(Time_print));
          end_timer_id[3]=timer.setTimeout(active_duration_ms, turn_off_switch_no_3);
      Serial.println(String("turn ON switch: ") + switch_no + String(" for duration: ") + active_duration_ms/60000 + String("min "));
      Serial.println(String("turn OFF switch: ") + switch_no);

// set switch state from APP
BLYNK_WRITE(V20) { turn_on_off(param.asInt(),0,TIME_CNT); }
BLYNK_WRITE(V21) { turn_on_off(param.asInt(),1,TIME_CNT); }
BLYNK_WRITE(V22) { turn_on_off(param.asInt(),2,TIME_CNT); }
BLYNK_WRITE(V23) { turn_on_off(param.asInt(),3,TIME_CNT); }


mas codigo

// the following function is called every 60 seconds by a timer                            //
//  the function checks if a start time is reached and if yes it will call                 //
//   the turn_on_off function (to turn on the switch and set timer for turning off switch) //
void activetoday(){         // check if schedule #1 should run today

  if(Blynk.connected()) // set wifi led if no connection
  if(year() != 1970){
    unsigned int nowseconds = ((hour() * 3600) + (minute() * 60) + second());
    int dayadjustment = -1;  
    if(weekday() == 1){
      dayadjustment = 6; // needed for Sunday Time library is day 1 and Blynk is day 7
     for (int switch_cnt = 0;  switch_cnt< SWITCH_CNT; switch_cnt++) {
         for (int timer_cnt = 0;  timer_cnt< TIME_CNT; timer_cnt++) {
              if (start_valid[switch_cnt][timer_cnt] == true) {
                if (weekdays[switch_cnt][timer_cnt][weekday() + dayadjustment]==true){
                   if (nowseconds >= start_time_sec[switch_cnt][timer_cnt]){
                      if(nowseconds < start_time_sec[switch_cnt][timer_cnt]+90){
                        Serial.println(String("turn ON switch: ") + switch_cnt);



// Digital clock display of the time
void clockDisplay()
  // You can call hour(), minute(), ... at any time
  // Please see Time library examples for details

  String currentTime = String(hour()) + ":" + minute() + ":" + second();
  String currentDate = String(day()) + " " + month() + " " + year();
  Serial.print("Current time: ");
  Serial.print(" ");

  // Send DATE and DATE to Blynk APP
  Blynk.virtualWrite(V101, currentTime);
  // Send date to the App--Not used but is available--this will show processor date if it is important to you
  Blynk.virtualWrite(V102, currentDate);

void setup()
  Serial.begin(9600);  // Debug console
  // reset output pins used and system variables
  for (int i = 0; i<SWITCH_CNT ; i++){
    pinMode(switch_pins[i], OUTPUT); // reset output pins
    digitalWrite(switch_pins[i],switch_default[i]); // set switch to default
    end_timer_id[i] = 32;            // reset end timer id's

    for (int j = 0; j<TIME_CNT ; j++)
      end_valid[i][j] = false;       // reset end valid array

  pinMode(WIFI_LED, OUTPUT);

  Blynk.begin(auth, ssid, pass);
    rtc.begin();  // Begin synchronizing time
    timer.setInterval(10000L, clockDisplay);
    timer.setInterval(60000L, activetoday);  // check every 60s if ON / OFF trigger time has been reached
    setSyncInterval(10 * 60); // Sync interval in seconds (10 minutes)


void loop()

No veo cual es tu pin 33 conectado al Virtual
No hay ningun V33.

Explicate un poco mejor.


En la GUI he puesto un pin V33 arriba del todo, lo he limitado a 7 días maximo. En el código no he puesto nada aún porque lo que he encontrado en el foro de Blynk no me aclaro a cómo usarlo, o no sé en qué parte de mi código tengo que meterlo.

Esto es parte de un código en el que se hace mención a la función sleep, pero no se que parte tengo que coger y donde meterlo en mi código.

//  Pin ( SCHEDULER_CNT*(TIME_CNT+3) ) +3 used for system sleep button          //
// define disable system input and number of days to disable input

// system disable timer
uint8_t  disable_timer_id = 32;                     // When system is disabled or at sleep a timer is set to wake 
                                                    //   the system disable_timer_id saves this timer ID

// system disable days 
int32_t  system_disable_days_sec;                  // VP_DISBL_DAYS defines days to disable/sleep the system (including today) given in sec

// End of Sleep and Disable Modes                                                   //
//  (They will probably not be used together so if one ends the other must end too) //
void sleep_disable_mode_off()
   system_sleep_mode = false;       
   // iterate over all schedulers and re reactivate tasks if needed (sleep mode).
   for (int scheduler_cnt = 0;  scheduler_cnt< SCHEDULER_CNT; scheduler_cnt++) 
      if(active_end_time_sec[scheduler_cnt]!=TIME_MAX) task_on_off(HIGH, scheduler_cnt); 

   // enable main timer (only if in disable mode)    
   // set disable and sleep buttons to off

   // set disable timer id to 32 to prevent disabling system timer by mistake
   disable_timer_id = 32;
   Serial.println(String("Sleep/Disable mode off"));

  // SYSTEM DISABLE/SLEEP BUTTONS INPUT                                //
  else if((pin == VP_DISBL_SYS )|(pin == VP_SLEEP_SYS)){                   // V31 & V32
       if (param.asInt()!=0){ 
          String currentDate = String(day()) + "\\" + month() + "\\" + year();
          if (pin == VP_SLEEP_SYS){
            system_sleep_mode = true;
            // turn off all tasks (do not disable the schedulers )
            for (int task_cnt = 0;  task_cnt< SCHEDULER_CNT; task_cnt++) 
              task_on_off(LOW, task_cnt);

            // print current data on VP_SLEEP_SYS button
            Blynk.setProperty(VP_SLEEP_SYS, "onLabel", currentDate);
             // disable main time tick 
             // turn off all active schedulers  
             Serial.println(String("Disable all"));
             for (int scheduler_cnt = 0;  scheduler_cnt< SCHEDULER_CNT; scheduler_cnt++) 
             // print current data on VP_SLEEP_SYS button
             Blynk.setProperty(VP_DISBL_SYS, "onLabel", currentDate);              
          // if disable days is 0 then set nowseconds to -1 to prevent timer with negative duration  
          int32_t nowseconds = -1; 
          if (system_disable_days_sec!=0)
             nowseconds = elapsedSecsToday(now());
          // create a timer to wake the system up after system_disable_days_sec are over
          SystemTimer.deleteTimer(disable_timer_id); // make sure no disable timer is active
          disable_timer_id = SystemTimer.setTimeout(1000*(system_disable_days_sec-nowseconds),sleep_disable_mode_off);
        else {// if system is manually out of seep mode delete disable timer and enable schedulers


Me he montado un sistema de riego con Arduino y todo funciona perfecto a nivel software y Hardware. Cuando con Blink le doy a los botones los reles se encienden y apagan bien. El problema ha venido al conectarlo a mis solenoides. Estos estaba con un programador de riego por lo que son los que con un impulso de 9V se abrían y con otro impulso se cerraban. Ahora me hacen falta solenoides NC (normalmente cerrados) para que cuando el rele mande 9V se abra y cuando corte el rele se cierre.
Hay alguna manera de hacer esto por software?
Los solenoides actuales son de pulsos.

Cambia en tu software el modo en que accionas. En lugar de activar desactiva y viceversa.

Si postearas el software (usa etiquetas) te lo indicaría mejor.

Gracias por tu ayuda. Ahora cuando llegue a casa te lo posteo, pero el problema creo que son los solenoides, Simon de pulsos, partiendo de cerrado, recibe un pulso de 9V abre, riega y cuando termina de regar manda otro pulso y cierra.

Puedes poner un link de esa electroválvula. No la conozco.

Sería una como esta

Cuando recibe un impulso positivo abre la válvula. Luego recibe uno negativo y la vuelve a cerrar. Eso lo veo complicado de hacer por lo que no me queda otra que comprarme estas válvulas NC (Normalmente Cerradas)

Hunter Latch Solenoide, 9V Gris 3.5x3.5x7.5 cm


El código lo subi en este hilo:

Eso lo veo complicado de hacer por lo que no me queda otra que comprarme estas válvulas NC (Normalmente Cerradas)

Pero que es lo que ves complicado ?

Un simple inversor utilizando 2 reles como por ejemplo

Sustituye el motor por la electrovalvula

Bueno a ver si nos ponemos de acuerdo, si abres un hilo y sigues preguntando por lo mismo, no puedes abrir otro por una variante levemente diferente.
Ahora estan unidos

Otra corrección, usas Blynk no Blink. Blink es el parpadeo de un led y Blynk es un software para Android/Iphone con el que controlas disponisivos arduino/ESP/Raspberry/etc.

Si necesitas un pulso lo haces basicamente on una salida Arduino, un trasistor una fuente de la tensión adecuada o sea 9V.
Algo tan simple como esto

En este caso 5V a la izquierda y el SW1 son tu pin del arduino, el transitor ya veremos si es el adecuado para tu electroválvula pero con eso un un simple

// pulso 1-0-1

digitalWrite(pin, HIGH);
digitalWrite(pin, LOW)
digitalWrite(pin, HIGH);

// pulso 0-1-0

digitalWrite(pin, LOW);
digitalWrite(pin, HIGH)
digitalWrite(pin, LOW);

Dice que trabaja con pilas de 9V de modo que este transitor es suficiente.

Surbyte, en tu ultimo mensaje me parece que me corriges.

Dice que trabaja con pilas de 9V de modo que este transitor es suficiente.

Antes de mezclar los dos hilos quedaba claro a lo que yo contestaba.

El YA TIENE unas electrovalvulas que se controlan por inversión de tensión, y como NO SABE hacer un inversor propone la compra de otras electrovalvulas.

Yo le contesto con un inversor de tensión a base de reles.

Tu le contestas con un driver para las NUEVAS electrovalvulas que aun no tiene.

Por cierto,Torque 73, las nuevas necesitan tensión durante todo el tiempo de riego, tenlo en cuenta al calcular lo que te durará la bateria de 9V.

Perdón por preguntar por dos sitios, pero es que después de montar todo el tema Arduino me encuentro con esto. Por partes.

  1. Las Electrovalvulas son estas, de 9V y por impulsos:

HIT 210.000

  1. Como van a pilas funcionan de la siguiente forma:das un impulso positivo corto y el solenoide se abre, le das un impulso negativo corto y se cierra. Es decir, no estas todo el rato dando electricidad o gastarías las pilas del programador actual en un momento.

  2. Actualmente tengo estos relés:

Reles Arduino

  1. Si pongo otros reles como los que tengo con las conexiones que me aconseja jordi3sk97 puedo hacer lo que necesitan mis solenoides. El único problema es como programar para que lo hagan funcionar, es decir, programo en Blynk (perdón pero fue el p_ _o corrector) que el riego se encienda todos los días a las 18:00 y se apague a las 18:05, tengo que conseguir que a las 18:00 mande un impulso (corto) positivo para que abra el solenoide, y a las 18:05 mande otro impulso NEGATIVO (entiendo esto lo hará el cruce de redes) para que se cierre, a las 18:06 el siguiente relé hará lo mismo.

Lo voy a estudiar a ver como lo hago. Gracias.


  1. no necesita respuesta.

  2. Ya te respondí pero cuando digo retardo no hablo de delay(mseg) que se entienda porque eso te detiene el Arduino.

  3. no necesita respuesta.

  4. Lo que te indiqué hacer con un transistor lo haces con un RELE.

Positivo Batería de 9V, contacto común del RELE al positivo de la electroválvula. Común del Relé al Negativo de la electroválvula a negativo de Bateria de 9V. Eso es simple electricidad. Un interruptor que controla un solenoide.

el programa es como te indique usando millis() y una máquina de estado para que pueda seguir haciendo otras cosas. Y si los retardos son cortos puedes usar delay() porque no alterará demasiado el resto de las cosas.
Ahora en tu link de la electroválvula no dice o no encontré que es un pulso corto o largo en mseg asi que no puedo ayudarte mas.

Adjunto un link para tu consideracion con un arduino sketch y usa un delay de 10ms para abrir y cerrar la valvula.

Hola a todos.

Al final encontré solenoides 24V NC a 14€ y me he pillado 3. El programador ya está 100% operativo. Es una gozada controlarlo desde el móvil. Ahora solo me queda llevar un par de cables al otro lado del chalet, donde tengo otro solenoide y controlarlo con el 4º relé. Ayer metí la guía y a los 4 metros se para. A ver como lo hago.