Persianas - Módulo Relé

Hola buenos días, hacía tiempo que no pasaba por el foro ya que he estado muy ocupado.
Previamente escribí sobre el funcionamiento de las unidades nRF24L01+, y gracias a los compañeros del foro lo pude solucionar

Estoy haciendo un sistema domótico en el cual quiero controlar unas persianas que ya tienen instalado un motor.
Componentes usados

Arduino sirve como controlador de la persiana. Esta se puede accionar de forma inalámbrica a través del RF24 enviándole una instrucción desde otro dispositivo o bien de forma física a través del interruptor que previamente ya estaba instalado en la pared.

El diseño de la placa en principio es correcta, ya que las pruebas así lo indican. Estas pruebas si hicieron a con leds simulando la persiana.

PROBLEMA:
Vídeo en youtube
Al accionar el interruptor de pared, Arduino lo interpreta de forma correcta y envía la señal al módulo de relés. Ahora bien, uno de los canales, el 1, que es el de bajada me salta el contacto de la bobina. En cambio canal 2, que es el de subida funciona correctamente.
En el vídeo no se ve del todo, pero el led de la bobina de la derecha se activa y desactiva casi instanteamente.
Por otro lado alguna vez, muy pocas, si funciona conrrectamente, por lo que he descartado fallos en la programación de arduino

Conjunto:

Conexionado de la placa de relé:

JDVCC:

  • JD-VCC a 5v de la fuente de alimentación (la cual alimenta a arduino)
  • VCC sin conectar
  • GND sin conectar

Control:

  • GND a GND arduino
  • IN1 a pin 6
  • IN2 a pin 7
  • VCC a 5V de arduino

En los foros es contradictorio, debería conectar GND de JD-VCC a GND de la fuente de alimentación?

Esquemático de conexionado:

Programación arduino:
La parte del control de posición de la persiana la tengo comentada ya que no me funciona la solución que había pensado y no es algo que me sea prioritario

// Librerias para RF
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>


/********************* Config *****************************/

    //Variables para RF 
    const int pinCE = 9;
    const int pinCSN = 10;
    RF24 radio(pinCE, pinCSN);
    //Canal comunicación con el nodo 'p''e''r'sianas'h''a'bitacion
    const byte pipe[5]={'p','e','r','h','a'};
    //Variable que guardará lo enviado por el emisor
    char data[2];
    
    /* Variables control Switch
     * El switch estará con resistencias en PULL DOWN
     * Cuando circula corriente HIGH, si no circula corriente LOW
     */
    const int interruptorSubida = 2;
    const int interruptorBajada = 3;
    
    //Variables Control rele
    const int relSubida = 6;
    const int relBajada = 7;
      
    /* Control de posición de la persiana
     * La función millis() retorno el tiempo desde que el sketch funciona
     * en milisegundos, reiniciandose cada 50 días mas o menos.
     * La mejor manera de guardar es con un unsigned long ya que así utilizamos
     * toda la precisión en positivo.
    */
    /*
    volatile char accionActualMotor;
    volatile unsigned long momentoUltimaAccion;
    volatile long posicionActual;
    volatile unsigned long tiempoTranscurrido;
    */

    //Variable auxiliar mostrar datos pantalla
    String msg;

/********************* Config *****************************/


void setup() {
  //Configuración Pins switch
  pinMode (interruptorSubida,INPUT);
  pinMode (interruptorBajada,INPUT);
  /*Configuramos interrupciones para que el interruptor mande 
    sobre el rf. Se controlará con cambios de estado.*/
  attachInterrupt(digitalPinToInterrupt(interruptorBajada),interruptInterruptorBajada,CHANGE);
  attachInterrupt(digitalPinToInterrupt(interruptorSubida),interruptInterruptorSubida,CHANGE);
  
  
  //Configuración Pins rele
  pinMode(relSubida,OUTPUT);
  pinMode(relBajada,OUTPUT);
  /*Recordemos que el relé funciona al revés que la lógica 
   * HIGH posición del relé a Normally Opened 
   */
  digitalWrite(relSubida,HIGH);
  digitalWrite(relBajada,HIGH);

  //Configuración RF
  radio.begin();
  radio.openReadingPipe(1,pipe);
  radio.startListening();
  //Configuramos en posicion cacnelar
  data[0] = 'C';

  
  /*Configuración de la posición del motor
  momentoUltimaAccion = millis();
  */
  Serial.begin(9600);
  
}

void loop() {
 
  comprobarRF();
}

/*
 * Comprobamos si se ha recibido alguna instrucción por RF
 * return: Char con la captura de RF
 */
char comprobarRF () {
  if (radio.available()) {
    /* Le hemos de pasar una variable* donde guarde el resultado y el tamaño de ella
     * En este caso le indicaremos que sólo queremos el primer byte.    
    */
    radio.read(data, 1); 
    Serial.print("RF disponible. Dato: ");
    Serial.println(data[0]);
    accionarMotor(data[0]);
  } else {
    Serial.println("No Radio");
  }
}

/*
 * Asegurar que sólo se produza una acción en el motor
 * Parám: (char) accion: 'S'ubir, 'B'ajar, 'C'ancelar 
 */
void accionarMotor (char accion) {
  if (accion == 'S') {
    digitalWrite(relBajada,HIGH);
    digitalWrite(relSubida,LOW);

    /* Control de posición
    momentoUltimaAccion= millis();
    msg =(String) "Accionar Motor SUBIR: accionActualMotor: "+accionActualMotor+" Tiempo: "+momentoUltimaAccion;
    Serial.println (msg);
    accionActualMotor = 'S';
    */
  } 
  else if (accion=='B') {
    digitalWrite(relSubida,HIGH);
    digitalWrite(relBajada,LOW);

    /* Control de posición
    momentoUltimaAccion=millis();
    msg = (String) "Accionar Motor BAJAR: accionActualMotor: "+accionActualMotor+" Tiempo: "+momentoUltimaAccion;
    Serial.println(msg);
    accionActualMotor = 'B';
    */
    
  } 
  else if (accion=='C') {
    digitalWrite(relSubida,HIGH);
    digitalWrite(relBajada,HIGH);

    /* Control de posición
    msg = (String) "Accionar Motor CANCELAR: accionActualMotor: "+accionActualMotor+" Tiempo transcurrido: "+tiempoTranscurrido;
    Serial.println(msg);    
    
    
    if (accionActualMotor == 'S') {
      posicionActual = posicionActual + tiempoTranscurrido;
      if (posicionActual > 24000) {posicionActual = 24000;}
    }
    else if ( accionActualMotor == 'B') {
      posicionActual = posicionActual - tiempoTranscurrido;
      if (posicionActual < 0) {posicionActual = 0;}
    }
    accionActualMotor = 'C';    
    msg = (String) "Posicion actual: "+posicionActual;
    Serial.println(msg);
    */
  }
}

/* Interrupciones
 * De esta manera conseguimos la inmediatez de las acciones
 * de subir y bajar presionando el interruptor
 * 
 */

void interruptInterruptorBajada() {
  if (digitalRead(interruptorBajada)  == HIGH ){
    accionarMotor('B');
  } 
  else {
    accionarMotor('C');
  }
}

void interruptInterruptorSubida() {
  if (digitalRead(interruptorSubida)  == HIGH ){
    accionarMotor('S');
  } 
  else {
    accionarMotor('C');
  }
}

Primeramente pensé que la bobina del relé estaba fundida, y cambié el módulo ya que tenía otro y me hace lo mismo. La mayoría de veces salta y en algún caso funciona. El caso que el que funciona es el de subir persiana que es en teoría el motor hace mas fuerza.

El cableado usado en la parte de potencia es para mas de 10A y en la parte de control es 24AWG (0,6A)

Lo primero que te diré es: para qué o por qué usas interrupciones? Si tienes TODO EL TIEMPO del mundo para actuar, detectar el pulsador y accionar los reles?
Simplemente usar interrupciones con switches puede generar comportamientos como los que tienes. No se si será el problema pero ya me resulta sospechoso.
Un interruptor o switch como el que usas requiere debounce SI o SI o sea rutina antirebotes que con interrupcioenes puedes hacer pero es mas complicada.

Entonces se puede reescribir todo sin interrupciones usando la librería Bounce2.h antirebotes y solucionas todo.
Veamos si funciona bien sin Bounce2.h.

// Librerias para RF
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>


/********************* Config *****************************/

//Variables para RF 
const int pinCE = 9;
const int pinCSN = 10;
RF24 radio(pinCE, pinCSN);
//Canal comunicación con el nodo 'p''e''r'sianas'h''a'bitacion
const byte pipe[5]={'p','e','r','h','a'};
//Variable que guardará lo enviado por el emisor
char data[2];

/* Variables control Switch
 * El switch estará con resistencias en PULL DOWN
 * Cuando circula corriente HIGH, si no circula corriente LOW
 */
const int interruptorSubida = 2;
const int interruptorBajada = 3;

//Variables Control rele
const int relSubida = 6;
const int relBajada = 7;

bool statusSubida, statusBajada;
  
/* Control de posición de la persiana
 * La función millis() retorno el tiempo desde que el sketch funciona
 * en milisegundos, reiniciandose cada 50 días mas o menos.
 * La mejor manera de guardar es con un unsigned long ya que así utilizamos
 * toda la precisión en positivo.
*/
/*
volatile char accionActualMotor;
volatile unsigned long momentoUltimaAccion;
volatile long posicionActual;
volatile unsigned long tiempoTranscurrido;
*/

//Variable auxiliar mostrar datos pantalla
String msg;

/********************* Config *****************************/


void setup() {
  //Configuración Pins switch
  pinMode (interruptorSubida,INPUT);
  pinMode (interruptorBajada,INPUT); 
  
  //Configuración Pins rele
  pinMode(relSubida,OUTPUT);
  pinMode(relBajada,OUTPUT);
  /*Recordemos que el relé funciona al revés que la lógica 
   * HIGH posición del relé a Normally Opened 
   */
  digitalWrite(relSubida,HIGH);
  digitalWrite(relBajada,HIGH);

  //Configuración RF
  radio.begin();
  radio.openReadingPipe(1,pipe);
  radio.startListening();
  //Configuramos en posicion cacnelar
  data[0] = 'C';

  
  /*Configuración de la posición del motor
  momentoUltimaAccion = millis();
  */
  Serial.begin(9600);
  
}

void loop() {
 
  comprobarRF();

  statusBajada  = digitalRead(interruptorBajada);
  statusSubida  = digitalRead(interruptorSubida);

  if (statusBajada){
      accionarMotor('B');
  } 
  if (statusSubida){
      accionarMotor('S');
  } 

  if (!statusSubida && !statusBajada)
      accionarMotor('C');
}

/*
 * Comprobamos si se ha recibido alguna instrucción por RF
 * return: Char con la captura de RF
 */
char comprobarRF () {
  if (radio.available()) {
      /* Le hemos de pasar una variable* donde guarde el resultado y el tamaño de ella
       * En este caso le indicaremos que sólo queremos el primer byte.    
      */
      radio.read(data, 1); 
      Serial.print("RF disponible. Dato: ");
      Serial.println(data[0]);
      accionarMotor(data[0]);
  } else {
       Serial.println("No Radio");
  }
}

/*
 * Asegurar que sólo se produza una acción en el motor
 * Parám: (char) accion: 'S'ubir, 'B'ajar, 'C'ancelar 
 */
void accionarMotor (char accion) {

  switch(accion) {
    case 'S': digitalWrite(relBajada,HIGH);
              digitalWrite(relSubida,LOW);
              break;
    case 'B': digitalWrite(relSubida,HIGH);
              digitalWrite(relBajada,LOW);
              break;
    case 'C': digitalWrite(relSubida,HIGH);
              digitalWrite(relBajada,HIGH);
              break;
  }
}

Otro error era que tu Subes y si no subes cancelas y por el otro lado Bajas y si no bajas cancelas. Entonces siempre cancelas no importa si subes o bajas. Se entiende?

Si subes cancelas porque no bajas y si Bajas cancelas porque no subes.

hola @surbyte
Tu solución es muy elegante pero existe varios problemas.

  1. La lectura del interruptor machaca lo leído por el nRF24.
    Por eso lo hice con interrupciones, ya que de esa manera el flujo principal sólo se preocupaba por lo recibido a través de la radio y la interrupción con lo otro.
  2. lo hago a través de interrupciones para que el interruptor tenga prioridad sobre la lectura del rf.
  3. Y la última a cosa a tener en consideración, el interruptor de pared, al accionarlo se queda "pulsado" subir o bajar, me interesa que tenga prioridad pero que si aún así estando pulsado luego se envía otra acción por el rf que actúe.
    Con lo que sólo encontré esa solución, no se si existe algo mas elegante.

A todo esto, he probado tu solución y el interruptor funciona correctamente, pero como he dicho no funciona el rf

  1. La lectura del interruptor machaca lo leído por el nRF24.

Esperaba que le dieras tu la prioridad, no es tan dificil.

De todos modos no creo que tengas claro que tal como esta NINGUNO de los dispositivos tiene el control mas que quien se accione al final, y por eso responde al manual y "macha" al RF como has dicho.

Para mi gusto cada vez que dos dispositivos controlan algo surge el mismo problema, la prioridad.
Nadie tiene la prioridad, porque tu quieres que ambos dispositivos funcionen. Pero si uno le dice al otro que hacer siempre habrá conflicto.

Una solución sería tener algo que me diga quien esta enviando ordens y cambiar el control de un modo al otro y entonces sigo en ese modo hasta que el OTRO hace algo.

Digamos que envias SUBIR por RF y luego estas junto al pulsado y quieres cancelar?
Bueno esto lo permitiría

// Librerias para RF
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>


/********************* Config *****************************/

//Variables para RF 
const int pinCE = 9;
const int pinCSN = 10;
RF24 radio(pinCE, pinCSN);
//Canal comunicación con el nodo 'p''e''r'sianas'h''a'bitacion
const byte pipe[5]={'p','e','r','h','a'};
//Variable que guardará lo enviado por el emisor
char data[2];

/* Variables control Switch
 * El switch estará con resistencias en PULL DOWN
 * Cuando circula corriente HIGH, si no circula corriente LOW
 */
const int interruptorSubida = 2;
const int interruptorBajada = 3;

//Variables Control rele
const int relSubida = 6;
const int relBajada = 7;

bool statusSubida, statusBajada;
bool statusSubidaAnt = false, statusBajadaAnt = false;
bool statusRFBajada, statusRFSubida;
  
/* Control de posición de la persiana
 * La función millis() retorno el tiempo desde que el sketch funciona
 * en milisegundos, reiniciandose cada 50 días mas o menos.
 * La mejor manera de guardar es con un unsigned long ya que así utilizamos
 * toda la precisión en positivo.
*/
/*
volatile char accionActualMotor;
volatile unsigned long momentoUltimaAccion;
volatile long posicionActual;
volatile unsigned long tiempoTranscurrido;
*/

//Variable auxiliar mostrar datos pantalla
String msg;
bool control = true;     // true es nRF24 y false es manual.
                         // cada vez que haya un cambio el control y la prioridad cambian.

/********************* Config *****************************/


void setup() {
  //Configuración Pins switch
  pinMode (interruptorSubida,INPUT);
  pinMode (interruptorBajada,INPUT); 
  
  //Configuración Pins rele
  pinMode(relSubida,OUTPUT);
  pinMode(relBajada,OUTPUT);
  /*Recordemos que el relé funciona al revés que la lógica 
   * HIGH posición del relé a Normally Opened 
   */
  digitalWrite(relSubida,HIGH);
  digitalWrite(relBajada,HIGH);

  //Configuración RF
  radio.begin();
  radio.openReadingPipe(1,pipe);
  radio.startListening();
  //Configuramos en posicion cacnelar
  data[0] = 'C';

  
  /*Configuración de la posición del motor
  momentoUltimaAccion = millis();
  */
  Serial.begin(9600);
  
}

void loop() {
 
  comprobarRF();  // chequeo acciones nRF24


  statusBajada  = digitalRead(interruptorBajada);
  statusSubida  = digitalRead(interruptorSubida);

  if (statusBajada != statusBajadaAnt || statusSubida != statusSubidaAnt) 
      control = false;  // control manual

  statusBajadaAnt = statusBajada;
  statusSubidaAnt = statusSubida;

  if (control)
      if (statusRFBajada){
          accionarMotor('B');
      } 
      if (statusRFSubida){
          accionarMotor('S');
      } 

      if (!statusRFSubida && !statusRFBajada)
          accionarMotor('C');
  else {
      if (statusBajada){
          accionarMotor('B');
      } 
      if (statusSubida){
          accionarMotor('S');
      } 

      if (!statusSubida && !statusBajada)
          accionarMotor('C');
  }
}

/*
 * Comprobamos si se ha recibido alguna instrucción por RF
 * return: Char con la captura de RF
 */
char comprobarRF () {
  if (radio.available()) {
      /* Le hemos de pasar una variable* donde guarde el resultado y el tamaño de ella
       * En este caso le indicaremos que sólo queremos el primer byte.    
      */
      radio.read(data, 1); 
      control = true;    // tenemos control nRF24
      Serial.print("RF disponible. Dato: ");
      Serial.println(data[0]);
      switch(data[0]) {
        case 'S': statusRFSubida = true;
                  break;
        case 'B': statusRFBajada = true;
                  break;
        case 'C': statusRFSubida = false;
                  statusRFBajada = false;
                  break;
      }
  } else {
       Serial.println("No Radio");
  }
}

/*
 * Asegurar que sólo se produza una acción en el motor
 * Parám: (char) accion: 'S'ubir, 'B'ajar, 'C'ancelar 
 */
void accionarMotor (char accion) {

  switch(accion) {
    case 'S': digitalWrite(relBajada,HIGH);
              digitalWrite(relSubida,LOW);
              break;
    case 'B': digitalWrite(relSubida,HIGH);
              digitalWrite(relBajada,LOW);
              break;
    case 'C': digitalWrite(relSubida,HIGH);
              digitalWrite(relBajada,HIGH);
              break;
  }
}

Acá la versión que te sugiero si le falta algo por favor tomate el tiempo para corregirlo.

NOTA: Perdona pero fui profesor de electrónica/programación y no puedo dejar de hacer observaciones.
Cuando haces un esquema electrónico, los electrónicos queremos ver lineas ortogonales o sea a 90° no linéas oblicuas como las del esquema Fritzing que publicaste.
De todos modos valoro que lo hayas hecho porque la mayoría no lo hace y aunque cueste leerlo se puede, no es de mi agrado pero puedo entender lo que has hecho.
Gracias y toma nota para la proxima que jamás verás un esquema de ese modo.

Hola @surbyte
Me ha ido perfecto, ahora estoy buscando información de la librería debounce, aunque tambien se puede estabilizar.
Supongo que optare por la solución a nivel de software por eso.
De hecho desconocía el fenómeno de los rebotes hasta ahora. Tengo conocimientos pero estan bastante oxidados.
Tienes toda la razón por lo de las lineas ortogonales, ademas las cambiare de color para hacer la documentación para aque se vea mas claro.
Quise poner la máxima información y no presté atención a esos detalles
Un saludo y gracias!

Gracias a ti por tomar positivamente los comentarios.

Busca Bounce2.h y elige la opcion que dice arduinio Playground

Hi,
Una sugerencia es de que al regulador de 78xx normalmente se le debe anadir un condensador de .33ufd a la entrada y a la salida de uno de .1 ufd en paralelo al existente. El condesador de .1 ufd te filtra la interferencia que se genera cuando usas componentes digitales como los micro.Si quires tambienn le puedes anadir otro a la entrada de .1udf a la entrada DC del NANO.