Control Sistema de calor/frío por Fancoils

Existen 2 modos de conectar un pulsador y de uno de ellos se deriva otro.

Busca pull-up y pull-down. En pull-up resulta que hay dos como te dije antes. Uno con R y otro usando la R interna del arduino.

Cualquiera de los dos métodos esta bien. R a 5V y cuando pulsas tienes GND o R a GND y cuando pulsas tienes 5V.

Asi que eso no esta mal.

Ya he modificado el esquema y ayer hice unas pequeñas pruebas y, a falta de profundizar más, parece que la cosa funciona más o menos de la forma prevista. Hoy me ha llegado todo el material que me faltaba, por lo que seguramente mañana haré las pruebas de todo.
De momento, dejo enlace al esquema definitivo con -creo- todos los errores subsanados. Lo pongo también en cómo adjunto.

Pues al fin me ha llegado todo y he hecho las primeras pruebas según el último esquema (excepto por la fuente de energía suplementária para los relés, lo he hecho todo con los 5V del Arduino) y compilando el último código. La verdad es que ha sido un éxito a medias, ya que aunque he podido comprobar que se me encendían y apagaban los relés y leds conectados a las salidas digitales y se registraban las lecturas de las entradas digitales, a la hora de funcionar la doble condicional, lo hacía correctamente o no según el estado de OONOFF. Releyendo mi código, usando el sentido común y elaborando una tabla, he logrado dar con el problema y -espero, no he compilado- solucionarlo.

El código inicialmente decía lo siguiente:

  int valor4 = digitalRead(OONOFF) ;                    // Leemos el valor de la orden paro/marcha
  if ( valor4 == true ) {                               // esto es que hay solicitud de marcha
      int valor5 = digitalRead(OFRICALOR) ;             // Leemos si estamos en modo frio o calor
      if (valor5 == true ) {                            // Esto es que estamos en modo calor
          digitalWrite( RONOFF, true) ;                 // Se enciende el rele RONOFF encendiendo la bomba de calor en modo invierno (calor)
          digitalWrite( LEDCALOR, true) ;               // Se enciende el led rojo
      }
      else     { 
          digitalWrite( RONOFF, false) ;                // Se apaga el rele RONOFF encendiendo la bomba de calor en modo verano (frío)
          digitalWrite( LEDFRIO, true) ;                // Se enciende el led azul
      }
  }
  else  {

        int valor6 = digitalRead(OFRICALOR) ;           // Leemos si estamos en modo frio o calor
        digitalWrite( LEDCALOR, valor6) ;               // Encendemos led rojo si esta en modo calor apagamos en modo frio
        digitalWrite( LEDFRIO, !valor6) ;               // Encendemos led azul si esta en modo frio apagamos en modo calor
        digitalWrite( RONOFF, !valor6) ;                // Apagamos el rele en modo calor y lo encendemos en modo frio
        digitalWrite( RFRICALOR, !valor6) ;             // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar 

  }
}

Me he dado cuenta de que, según este código, si OONOFF es False, definía de forma clara e inequívoca los 4 valores en juego LEDCALOR, LEDFRIO, RONOFF y RFRICALOR dependiendo del estado de OFRICALOR, pero si OONOFF era True, entonces -y dependiendo del estado de OFRICALOR- definía únicamente el estado de 2 de los 4 valores en juego, por lo que el comportamiento del Arduino era confuso. He definido los 4 valores en todas las variables posibles para que no haya nunca ningún valor sin definir, por lo que esa parte del código queda así:

  int valor4 = digitalRead(OONOFF) ;                    // Leemos el valor de la orden paro/marcha
  if ( valor4 == true ) {                               // esto es que hay solicitud de marcha
      int valor5 = digitalRead(OFRICALOR) ;             // Leemos si estamos en modo frio o calor
      if (valor5 == true ) {                            // Esto es que estamos en modo calor
          digitalWrite( RONOFF, true) ;                 // Se enciende el rele RONOFF encendiendo la bomba de calor en modo invierno (calor)
          digitalWrite( LEDCALOR, true) ;               // Se enciende el led rojo
          digitalWrite( LEDFRIO, false) ;               // Se apaga el led azul
          digitalWrite( RFRICALOR, false) ;             // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar 

      }
      else     { 
          digitalWrite( RONOFF, false) ;                // Se apaga el rele RONOFF encendiendo la bomba de calor en modo verano (frío)
          digitalWrite( LEDCALOR, false) ;              // Se apaga el led rojo
          digitalWrite( LEDFRIO, true) ;                // Se enciende el led azul
          digitalWrite( RFRICALOR, true) ;              // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar
      }
  }
  else  {                                               // esto es que hay solicitud de paro

        int valor6 = digitalRead(OFRICALOR) ;           // Leemos si estamos en modo frio o calor
        digitalWrite( RONOFF, !valor6) ;                // Apagamos el rele en modo calor y lo encendemos en modo frio
        digitalWrite( LEDCALOR, valor6) ;               // Encendemos led rojo si esta en modo calor apagamos en modo frio
        digitalWrite( LEDFRIO, !valor6) ;               // Encendemos led azul si esta en modo frio apagamos en modo calor
        digitalWrite( RFRICALOR, !valor6) ;             // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar 

  }

Entiendo que ahora, además de compilarme, el Arduino debería comportarse conforme a esta tabla: |500x151

(Todas las conexiones a los relés se realizarán en posición normalmente cerrado). Pongo el código completo para referencia: (Siguiente mensaje por exceder caracteres)

(Recordemos que esta tabla define el encendido y apagado y el modo de funcionamiento de una bomba de calor para calefacción contra un depósito de inercia)

int RFRICALOR   = 2 ;
int RBOMBA1     = 3 ;
int RBOMBA2     = 4 ;
int RBOMBA3     = 5 ;
int RONOFF      = 6 ;
int LEDFRIO     = 7 ;
int LEDCALOR    = 8 ;
int OFRICALOR   = 9 ;
int OONOFF      = 10 ;
int OBOMBA3     = 11 ;
int OBOMBA2     = 12 ;
int OBOMBA1     = 13 ;

void setup() {
  pinMode( RFRICALOR , OUTPUT);                         // Rele cambio de temporada (abieto invierno, cerrado verano), entrada 1 Tarjeta regulación unidad interior
  pinMode( RBOMBA1 , OUTPUT);                           // Rele bomba planta 1
  pinMode( RBOMBA2 , OUTPUT);                           // Rele bomba planta 2
  pinMode( RBOMBA3 , OUTPUT);                           // Rele bomba planta 3
  pinMode( RONOFF , OUTPUT);                            // Rele Paro / Marcha, entrada 6 Tarjeta de regulación unidad interior
  pinMode( LEDFRIO , OUTPUT);                           // Led modo frio
  pinMode( LEDCALOR , OUTPUT);                          // Led modo calor
  pinMode( OFRICALOR , INPUT);                          // Cambio modo frio / calor
  pinMode( OONOFF , INPUT);                             // Orden Paro / Marcha
  pinMode( OBOMBA3 , INPUT);                            // Orden bomba planta 3
  pinMode( OBOMBA2 , INPUT);                            // Orden bomba planta 2
  pinMode( OBOMBA1 , INPUT);                            // Orden bomba planta 1
}

void loop() {
  int valor1 = digitalRead(OBOMBA1) ;                   // Leemos el valor de la primera planta
  digitalWrite( RBOMBA1, valor1) ;                      // Escribimos valor en RBOMBA1


  int valor2 = digitalRead(OBOMBA2) ;                   // Leemos el valor de la segunda planta
  digitalWrite( RBOMBA2, valor2) ;                      // Escribimos valor en RBOMBA3


  int valor3 = digitalRead(OBOMBA3) ;                   // Leemos el valor de la tercera planta
  digitalWrite( RBOMBA3, valor3) ;                      // Escribimos valor en RBOMBA3

  int valor4 = digitalRead(OONOFF) ;                    // Leemos el valor de la orden paro/marcha
  if ( valor4 == true ) {                               // esto es que hay solicitud de marcha
      int valor5 = digitalRead(OFRICALOR) ;             // Leemos si estamos en modo frio o calor
      if (valor5 == true ) {                            // Esto es que estamos en modo calor
          digitalWrite( RONOFF, true) ;                 // Se enciende el rele RONOFF encendiendo la bomba de calor en modo invierno (calor)
          digitalWrite( LEDCALOR, true) ;               // Se enciende el led rojo
          digitalWrite( LEDFRIO, false) ;               // Se apaga el led azul
          digitalWrite( RFRICALOR, false) ;             // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar 

      }
      else     { 
          digitalWrite( RONOFF, false) ;                // Se apaga el rele RONOFF encendiendo la bomba de calor en modo verano (frío)
          digitalWrite( LEDCALOR, false) ;              // Se apaga el led rojo
          digitalWrite( LEDFRIO, true) ;                // Se enciende el led azul
          digitalWrite( RFRICALOR, true) ;              // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar
      }
  }
  else  {                                               // esto es que hay solicitud de paro

        int valor6 = digitalRead(OFRICALOR) ;           // Leemos si estamos en modo frio o calor
        digitalWrite( RONOFF, !valor6) ;                // Apagamos el rele en modo calor y lo encendemos en modo frio
        digitalWrite( LEDCALOR, valor6) ;               // Encendemos led rojo si esta en modo calor apagamos en modo frio
        digitalWrite( LEDFRIO, !valor6) ;               // Encendemos led azul si esta en modo frio apagamos en modo calor
        digitalWrite( RFRICALOR, !valor6) ;             // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar 

  }
}

Si todo va cómo debe, y no veo razón para que no sea así, el siguiente paso será pasar de la protoboard a montar de una forma un poco más elegante con la ayuda de un Screw Shield 1.0 y en una caja IP65 para su instalación en la sala de calderas.

Deseadme suerte con esas soldaduras, que soy muy novato, y gracias por seguir este hilo y ayudarme en lo que creáis oportuno.

Un error que veo en tu código que no luce como error es que el tuyo aunque sea un ambiente hogareño o semi industrial, puede presentar problemas con los pulsadores. Los pulsadores suelen rebotar. Y eso genera problemas en el accionamiento de equipos grandes.

Si hasta el momento no has visto comportamientos erráticos, olvidalo pero ten presente este comentario. Si ocurre, te voy a sugerir una librería que hace lo que se llama una rutina antirebote. Se llama Bounce2 y solo le dices el tiempo que quieres que un valor se confirme en un estado y listo... el devuelve el estado cuando éste esta confirmado.

tu lees esto

 int valor4 = digitalRead(OONOFF) ;

Dicho sea de paso es una exageración usar una variable int para leer el estado 0 1 de un switch/pulsador. Con bool sobra

asi que bien podrias definirlo asi

bool valor4 = digitalRead(OONOFF) ;

y todo lo demás igual.

Buenas!

Cómo no podía ser de otra forma, ya que hay que saber reconocer la experiencia y el conocimiento cuando sobrepasan por tanto los propios, he seguido tu consejo y me he puesto en plan “programador mamporrero” y le he metido un tute al código que ya no lo reconozco ni yo. Admito que no entiendo todo lo que he modificado, pero la cuestión es que me compila, que es mucho más de lo que esperaba. La cosa ha quedado así:

#include <Bounce2.h>                                    // Incluimos biblioteca Bounce para eliminar el debouncing producido por los pulsadores / reles de entrada

bool RFRICALOR   = 2 ;
bool RBOMBA1     = 3 ;
bool RBOMBA2     = 4 ;
bool RBOMBA3     = 5 ;
bool RONOFF      = 6 ;
bool LEDFRIO     = 7 ;
bool LEDCALOR    = 8 ;
bool OFRICALOR   = 9 ;
bool OONOFF      = 10 ;
bool OBOMBA3     = 11 ;
bool OBOMBA2     = 12 ;
bool OBOMBA1     = 13 ;

Bounce debouncer1 = Bounce();                           // Iniciado un objeto Bounce
Bounce debouncer2 = Bounce();                           // Iniciado un objeto Bounce
Bounce debouncer3 = Bounce();                           // Iniciado un objeto Bounce
Bounce debouncer4 = Bounce();                           // Iniciado un objeto Bounce
Bounce debouncer5 = Bounce();                           // Iniciado un objeto Bounce

void setup() {
  pinMode( RFRICALOR , OUTPUT);                         // Rele cambio de temporada (abieto invierno, cerrado verano), entrada 1 Tarjeta regulacion unidad interior
  pinMode( RBOMBA1 , OUTPUT);                           // Rele bomba planta 1
  pinMode( RBOMBA2 , OUTPUT);                           // Rele bomba planta 2
  pinMode( RBOMBA3 , OUTPUT);                           // Rele bomba planta 3
  pinMode( RONOFF , OUTPUT);                            // Rele Paro / Marcha, entrada 6 Tarjeta de regulación unidad interior
  pinMode( LEDFRIO , OUTPUT);                           // Led modo frio
  pinMode( LEDCALOR , OUTPUT);                          // Led modo calor

  pinMode( OFRICALOR , INPUT_PULLUP);                   // Cambio modo frio / calor
  debouncer1.attach(OFRICALOR);
  debouncer1.interval(50);                              // intervalo en ms, mas alto por ser un interruptor manual
  pinMode( OONOFF , INPUT_PULLUP);                      // Orden Paro / Marcha
  debouncer2.attach(OONOFF);
  debouncer2.interval(25);                              // intervalo en ms
  pinMode( OBOMBA3 , INPUT_PULLUP);                     // Orden bomba planta 3
  debouncer3.attach(OBOMBA3);
  debouncer3.interval(25);                              // intervalo en ms
  pinMode( OBOMBA2 , INPUT_PULLUP);                     // Orden bomba planta 2
  debouncer4.attach(OBOMBA2);
  debouncer4.interval(25);                              // intervalo en ms
  pinMode( OBOMBA1 , INPUT_PULLUP);                     // Orden bomba planta 1
  debouncer5.attach(OBOMBA1);
  debouncer5.interval(25);                              // intervalo en ms
}

void loop() {

  debouncer1.update();                                  // Actualizar los estados Bounce
  debouncer2.update();                                  // Actualizar los estados Bounce
  debouncer3.update();                                  // Actualizar los estados Bounce
  debouncer4.update();                                  // Actualizar los estados Bounce
  debouncer5.update();                                  // Actualizar los estados Bounce
  
  bool valor1 = debouncer5.read();                      // Leemos el valor de la primera planta
  digitalWrite( RBOMBA1, valor1) ;                      // Escribimos valor en RBOMBA1


  bool valor2 = debouncer4.read();                      // Leemos el valor de la segunda planta
  digitalWrite( RBOMBA2, valor2) ;                      // Escribimos valor en RBOMBA3


  bool valor3 = debouncer3.read();                      // Leemos el valor de la tercera planta
  digitalWrite( RBOMBA3, valor3) ;                      // Escribimos valor en RBOMBA3

  bool valor4 = debouncer2.read();                      // Leemos el valor de la orden paro/marcha
  if ( valor4 == true ) {                               // esto es que hay solicitud de marcha
      bool valor5 = debouncer1.read();                  // Leemos si estamos en modo frio o calor
      if (valor5 == true ) {                            // Esto es que estamos en modo calor
          digitalWrite( RONOFF, true) ;                 // Se enciende el rele RONOFF encendiendo la bomba de calor en modo invierno (calor)
          digitalWrite( LEDCALOR, true) ;               // Se enciende el led rojo
          digitalWrite( LEDFRIO, false) ;               // Se apaga el led azul
          digitalWrite( RFRICALOR, false) ;             // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor como debe operar 

      }
      else     { 
          digitalWrite( RONOFF, false) ;                // Se apaga el rele RONOFF encendiendo la bomba de calor en modo verano (frio)
          digitalWrite( LEDCALOR, false) ;              // Se apaga el led rojo
          digitalWrite( LEDFRIO, true) ;                // Se enciende el led azul
          digitalWrite( RFRICALOR, true) ;              // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor cómo debe operar
      }
  }
  else  {                                               // esto es que hay solicitud de paro

        bool valor6 = debouncer1.read();                // Leemos si estamos en modo frio o calor
        digitalWrite( RONOFF, !valor6) ;                // Apagamos el rele en modo calor y lo encendemos en modo frio
        digitalWrite( LEDCALOR, valor6) ;               // Encendemos led rojo si esta en modo calor apagamos en modo frio
        digitalWrite( LEDFRIO, !valor6) ;               // Encendemos led azul si esta en modo frio apagamos en modo calor
        digitalWrite( RFRICALOR, !valor6) ;             // Indicamos al rele de cabio frio(true)/calor(false) de la bomba de calor como debe operar 

  }
}

He actualizado tras eliminar todas las tildes de los comentarios (por si acaso)

Verás que he dado 25ms a todas entradas provinientes de un relé (en el esquema muchos los represento con pulsadores, pero todo son relés excepto el interruptor basculante) y al interruptor basculante le he dado 50ms.

La verdad es que, siguiendo con mi constante abuso de tu tiempo, me gustaría que revisases este nuevo código a ver si te percatas de algún disparate.
Por mi parte, parece que hasta mediados de la próxima semana no podré cargarlo en mi Arduino, así que no podré probarlo físicamente hasta dentro de unos días, pero si tengo un código seguro y estable para entonces, habré avanzado mucho.

Gracias ante todo.

Por ahora luce bien. Luego miraré a ver que otro consejo darte.

Buenas de nuevo.

Por fin he podido compilar el nuevo código en el Arduino y, ¡sorpresa!, no funcionaba.

Por probar, y porque estaba seguro de que todo lo referente a la biblioteca antirebote lo había hecho bien, he probado de modificar todos los "bool" por "int" otra vez y ¡sorpresa!, ahora funciona perfectamente. No se si tú surbyte le verás la lógica a esto, pero la cuestión es que ahora esto chuta, mientras que del otro modo estaba muerto.

Ya sólo me queda comprar los Display AKO, la caja para montarlo y liarme a soldar el shield y montar cablecitos. Gracias mil por la ayuda, seguiré posteando sobre la marcha del proyecto.

La librería dice

// Returns the updated pin state
    bool read();

De modo que debería funcionar. Sin embargo el ejemplo dice

 // Get the updated value :
  int value = debouncer.read();

No se que responderte. Debo revisar mis casos. Yo no uso definiciones como tu dentro del mismo código, las hago locales al comienzo del void loop o globales.

¡Seguimos con el proyecto!

Ya he montado la caja con el Arduino y mañana o pasado iré a colocarla en su lugar. En las siguientes fotos puede verse cómo ha quedado por dentro (a falta de conectar todo lo externo al sistema, la señal de los fancoils, las bombas, la maniobra de paro/marcha y el cambio de temporada de la bomba de calor) y cómo queda por fuera a falta de colocar el segundo display Ako (en el momento de montar, sólo tenía uno). Podéis ver cómo al mover el interruptor se apaga o enciende el led correspondiente y el display que le corresponde.

Una vez montado, el sistema respondía a las pruebas según lo esperado, por lo que supongo que no dará mayores problemas una vez instalado.

Ya os mantendré informados.

Buen trabajo Werttty!!

Gracias por las felicitaciones, la verdad es que estoy bastante orgulloso con los resultados obtenidos por el momento.

No he probado el sistema completo a falta de la puesta en marcha del sistema aerotérmico por parte del SAT oficial, pero de momento he conseguido que los radiadores de cada planta me activen / desactiven la bomba correspondiente a su planta y que los relés marcha/paro y cambio de temporada actúen con el interruptor o los displays Ako; sólo falta verificar que estas dos últimas órdenes hagan lo que corresponde a la bomba de calor aerotérmica.

Paso las fotos de todo instalado:

Desde luego son muchos cables, pero es todo más sencillo de lo que parece. Ya comentaré cómo va el sistema cuando lo tenga todo encendido y funcionando, pero preveo un éxito absoluto, ya que de momento todo me ha funcionado a la primera según lo revisto. Nuevamente gracias surbyte por hacerlo posible, sin tu ayuda este proyecto no habría salido adelante. Admito que cómo primer proyecto de Arduino y primer contacto en el mundo de la electrónica, tal vez ha sido demasiado ambicioso; espero que no sea la última vez que me meto en uno de estos fregados. Cuando las últimas pruebas estén hechas, intentaré compilar toda la información del proyecto de forma cuidada y ordenada en un último post para simplificar la labor a quien sea que encuentre útil este proyecto.