Conmutador remoto de antena, problemas de comunicación.

Estoy trabajando en un proyecto para controlar un conmutador mecánico de antenas de radioaficionado a distancia. Tengo un mando de control en la habitación que hará de master y un remoto en la azotea que moverá un motor paso a paso para seleccionar la posición que necesito, cuando en el master pulso un switch para subir o bajar una posición del conmutador.
Mediante sensores hall y un opto acoplador, sé en que posición se encuentra el conmutador y puedo imprimir en un display del master, la antena que tengo seleccionada.
Por la distancia, unos 30 metros, he pensado en comunicación RS485, pero antes de meterme con la conversión, quiero asegurarme que la comunicación por la UART directamente es correcta.
Actualmente ya me funciona conectando el RX y TX de los dos arduinos, pero creo que se puede depurar porque la presentación en el display del feedback que envía el remoto, tarda en aparece. Me da la sensación que el master está saturado recibiendo los datos del remoto.
Para asegurar que hay comunicación, interrogo al remoto y este me devuelve el feeedback de la posición del selector. Con esto hago parpadear un led en verde o rojo, en función de si master recibe o transmite.
Por el monitor serie puedo ver que la respuesta del remoto es rápida, pero en cambio en el display tarda unos segundos en salir impreso.
¿Podéis ayudarme a que esa comunicación / presentación en display sea mas rápida?
Al tardar unos segundos en indicarme el cambio, puede llevarme a errores y pulsar varias veces pensando que no ha realizado la orden.
Adjunto los códigos del master y del remoto
Gracias de antemano.

Mando_control_3.ino (2.84 KB)

Selector_de_antenas_remoto_3.ino (6.13 KB)

Por favor inserta el código en tu post (ver Normas del foro, punto 7) así resulta mas sencillo verlo. (Es para tu beneficio) Gracias

Hay un trabajo similar hecho hace unos años, te recomiendo lo busques.
No similar en cuanto a la comunicación que no recuerdo como la hizo pero tal vez haya cosas que te sirvan de guía.

gatul:
Por favor inserta el código en tu post (ver Normas del foro, punto 7) así resulta mas sencillo verlo. (Es para tu beneficio) Gracias

He intentado insertarlo pero aparecía un error de exceso de caracteres, volveré a hacer en dos veces.

Bueno pues basándome en mi poca experiencia y pensando que el master estaba saturado de los datos que enviaba el remoto, he utilizado la función millis() para que solo lo interrogue cada 500ms y ahora funciona mucho mejor.
Seguro que cualquiera de vosotros con vuestros conocimientos lo podríais mejorar, pero de momento hasta aquí es donde llego. Ahora queda pendiente implementar la comunicaciones por RS 485.

CODIGO DEL MASTER

/*
11/02/2021
Mando de control para selector remoto de antena.
al pulsar un boton envio por serie el caracter u para subir una posicion la antena
y el d para bajar.

14/02/2021
Incorporo leds para confirmar que hay comunicacion serie
*/

const int sw_up = 2;     // Pulsador giro derecha al pin 2
const int sw_down = 3; //Pulsador giro izquierda al pin 3
const int ReDePin =  4;  // MAX 485 (HIGH = TX / LOW = RX)
const int led_rx = 13;
const int led_tx = 12;

int leo_sw_up = 0; //Valor del pulsador giro derecha
int leo_sw_down = 0; //Valor del pulsador giro down
int dato_serie;
unsigned long tiempo1=0;
unsigned long tiempo2=0;

#include <Arduino.h>
 
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
LiquidCrystal_I2C lcd (0x27, 2, 1, 0, 4, 5, 6, 7); // DIR, E, RW, RS, D4, D5, D6, D7

void setup() 
{
Serial.begin(9600);     // inicializa comunicacion por monitor serie a 9600 bps
Serial.setTimeout(100);
lcd.begin(16, 2);                      
lcd.setBacklightPin(3,POSITIVE);  // puerto P3 de PCF8574 como positivo
lcd.setBacklight(HIGH);
pinMode(sw_up, INPUT);
pinMode(sw_down, INPUT);
pinMode(led_rx, OUTPUT);
pinMode(led_tx, OUTPUT);
pinMode(ReDePin, OUTPUT);
tiempo1=millis();
}

///////////////////    SUBRUTINAS      ///////////////

//Leo el puerto serie para ver el feedback del remoto
void leo_serie() 
{
 if (Serial.available()>0) //Si hay datos los leemos
 {
  digitalWrite (led_rx, HIGH);  
  dato_serie = Serial.read(); //leo el dato recibido y lo guardo en la variable
  
  if(dato_serie=='1') 
      {
        lcd.clear();
        lcd.setCursor(5, 0);
        lcd.print("V2000");
      }
      
  if(dato_serie=='2') 
      {
        lcd.clear();
        lcd.setCursor(1, 0);
        lcd.print("DIPOLO CRUZADO");
      }
 
  if(dato_serie=='3') 
      {
        lcd.clear();
        lcd.setCursor(3, 0);
        lcd.print("DIRECTIVA");
      } 
 
   if(dato_serie=='4') 
      {
        lcd.clear();
        lcd.setCursor(4, 0);
        lcd.print("RESERVA");
      }
    
   if(dato_serie=='5') 
      {
        lcd.clear();
        lcd.setCursor(4, 0);
        lcd.print("INICIANDO");
      }  
 digitalWrite (led_rx, LOW); 
 } 
}

void loop() 
{
 tiempo2 = millis();
 
 if(tiempo2 > (tiempo1+500))
 {
  tiempo1 = millis(); //Actualiza el tiempo actual
//digitalWrite(ReDePin, HIGH); //Modo TX MAX485
digitalWrite (led_tx, HIGH); 
Serial.print("?");
delay(50);
digitalWrite (led_tx, LOW);
//delay(50); //Dejo respirara al puerto del remoto
//digitalWrite(ReDePin, LOW); //Modo RX MAX485
 }

leo_serie();

leo_sw_up = digitalRead(sw_up); //Leo el pulsador para detectar si está pulsado
leo_sw_down = digitalRead(sw_down); //Leo el pulsador para detectar si está pulsado

if(leo_sw_up == HIGH) 
  {
   Serial.print("u");
   digitalWrite (led_tx, HIGH); 
   delay(250);
   digitalWrite (led_tx, LOW);
  }

if(leo_sw_down == HIGH) 
  {    
   Serial.print("d");
   digitalWrite (led_tx, HIGH); 
   delay(250);
   digitalWrite (led_tx, LOW);
  }
}

Moderador:
Bueno, el hilo es practicamente una pregunta de software asi que lo muevo a dicha sección.

Colega, mis opiniones sobre el código:

Bien por el uso de millis(). Los delay hay que saber usarlos, mas que todo para generar retardos cortos donde se requieren (por ejemplo, en los LEDs). Pero muy diferente son los tiempos de espera (es decir, esperar a que suceda algo) donde es preferible usar millis() o contadores temporales. En otras palabras, no es lo mismo un delay (retardo) que un wait (espera).

Segundo, en lo personal algo que casi que reverencio en C y sus derivados son las instrucciones Switch case (ahorita estoy trabajando con Python y realmente extraño una instrucción explítica de Switch). Hay un tema de discusión en el ámbito de programación sobre los If...else vs Switch case, y es si realmente un switch case se ejecuta mucho mas rápido (apreciablemente) que un if...else. La teoría y lógica, y experiencia de muchas personas, dicen que sí, debido a que en el switch case el programa va de una a la opción elegida y no recorre comprobando si todas las condiciones se cumplen o no (como en if...else).

En su caso, yo implementaría la función leo_serie() así:

void leo_serie()
{
 if (Serial.available()>0) //Si hay datos los leemos
 {
  digitalWrite (led_rx, HIGH); 
  dato_serie = Serial.read(); //leo el dato recibido y lo guardo en la variable
  lcd.clear();
  switch(dato_serie)
  {
    case '1':
      lcd.setCursor(5, 0);
      lcd.print("V2000");
      break;
    case '2':
      lcd.setCursor(1, 0);
      lcd.print("DIPOLO CRUZADO");
      break;
    case '3':
      lcd.setCursor(3, 0);
      lcd.print("DIRECTIVA");
      break;
    case '4':
      lcd.setCursor(4, 0);
      lcd.print("RESERVA");
      break;
    case '5':
      lcd.setCursor(4, 0);
      lcd.print("INICIANDO");
      break;
  }
  digitalWrite (led_rx, LOW);
 }
}

Tercero, como buena práctica ingenieril. Agregue un else de excepción al final de los dos if. Me explico, si por x o y motivo tanto leo_sw_up como leo_sw_down son HIGH, puede producirse una ejecución no deseada del programa o ejecutarse ambos IF (al menos que eso no afecte el programa y ambos puedan estar en HIGH) entonces un else es bueno diciendo que pasaría en esa y en otras hipotéticas soluciones. Solo que ahí la sintaxis del código cambiaría. Tendría que poner IF, ELSE IF, y ELSE para que todo quede anidado.
De pronto así usted este totalmente seguro que físicamente no se pueden poner los dos sw en HIGH (para ese caso), es bueno implementar algo de redundancia en el código para agregar seguridad de operación al sistema.

Por último, RS485 es una buena elección, pero podría intentar implementar algún tipo de comunicación inalámbrica acorde al proyecto para ahorrarse el cableado.

Si quiere también puede publicar el código del remoto para comprender mejor como es el funcionamiento de todo el sistema.

@yiosef
Ante todo agradecerte tu respuestas y tus consejos.
He intentado en ocasiones usar Shitch-Case pero he tenido malas experiencias. Reconociendo que es una función simple, no consigo que me funcione. De hecho, lo he implementado en el código como me indicas, y no lee el puerto serie.
En cuanto a tu comentario sobre el uso de ELSE, no deberían de pulsarse los dos botones a la vez, aunque por seguridad, tendré en cuenta tu comentario.
Por último, la opción de comunicación inalámbrica estaba pensada, pero si o si, tengo que subier un cable de alimentación para la electrónica que se aloja en la azotea, por eso no lo hice así. Incluso pense aprovechar los tonos DTMF que generan los equipos de radio que tengo, para realizar los movimientos, pero al final tengo que subir un cable.
Si puedes detectar porqué no funciona el Case, me seria útil para el futuro.
Como no puedo poner los dos códigos, pongo en del master modificado y en otro post pongo el del remoto.
Muchas gracias

CODIGO MASTER

/*
11/02/2021
Mando de control para selector remoto de antena.
al pulsar un boton envio por serie el caracter u para subir una posicion la antena
y el d para bajar.

14/02/2021
Incorporo leds para confirmar que hay comunicacion serie
*/

const int sw_up = 2;     // Pulsador giro derecha al pin 2
const int sw_down = 3; //Pulsador giro izquierda al pin 3
const int ReDePin =  4;  // MAX 485 (HIGH = TX / LOW = RX)
const int led_rx = 13;
const int led_tx = 12;

int leo_sw_up = 0; //Valor del pulsador giro derecha
int leo_sw_down = 0; //Valor del pulsador giro down
int dato_serie;
unsigned long tiempo1=0;
unsigned long tiempo2=0;

#include <Arduino.h>
 
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
LiquidCrystal_I2C lcd (0x27, 2, 1, 0, 4, 5, 6, 7); // DIR, E, RW, RS, D4, D5, D6, D7

void setup() 
{
Serial.begin(9600);     // inicializa comunicacion por monitor serie a 9600 bps
Serial.setTimeout(100);
lcd.begin(16, 2);                      
lcd.setBacklightPin(3,POSITIVE);  // puerto P3 de PCF8574 como positivo
lcd.setBacklight(HIGH);
pinMode(sw_up, INPUT);
pinMode(sw_down, INPUT);
pinMode(led_rx, OUTPUT);
pinMode(led_tx, OUTPUT);
pinMode(ReDePin, OUTPUT);
tiempo1=millis(); //Tiempo que transcurre desde que se inicia arduino
}

///////////////////    SUBRUTINAS      ///////////////

//Leo el puerto serie para ver el feedback del remoto

void leo_serie()
{
 if (Serial.available()>=0) //Si hay datos los leemos
 {
  digitalWrite (led_rx, HIGH);
  dato_serie = Serial.read(); //leo el dato recibido y lo guardo en la variable
 
  switch(dato_serie)
  {
    case 1:
      lcd.clear();
      lcd.setCursor(5, 0);
      lcd.print("V2000");
      break;
    case 2:
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("DIPOLO CRUZADO");
      break;
    case 3:
      lcd.clear();
      lcd.setCursor(3, 0);
      lcd.print("DIRECTIVA");
      break;
    case 4:
      lcd.clear();
      lcd.setCursor(4, 0);
      lcd.print("RESERVA");
      break;
    case 5:
      lcd.clear();
      lcd.setCursor(4, 0);
      lcd.print("INICIANDO");
      break;

  }
  digitalWrite (led_rx, LOW);
 }
}

void loop() 
{
 tiempo2 = millis();
 
 if(tiempo2 > (tiempo1+500))
 {
  tiempo1 = millis(); //Actualiza el tiempo actual

digitalWrite(ReDePin, HIGH); //Modo TX MAX485
digitalWrite (led_tx, HIGH); 
Serial.print("?");
delay(50);
digitalWrite (led_tx, LOW);
//delay(50); //Dejo respirara al puerto del remoto
//digitalWrite(ReDePin, LOW); //Modo RX MAX485
 }

leo_serie();


leo_sw_up = digitalRead(sw_up); //Leo el pulsador para detectar si está pulsado
leo_sw_down = digitalRead(sw_down); //Leo el pulsador para detectar si está pulsado

if(leo_sw_up == HIGH) 
  {
   digitalWrite(ReDePin, HIGH); //Modo TX MAX485
   Serial.print("u");
   //digitalWrite(ReDePin, LOW); //Modo RX MAX485
   digitalWrite (led_tx, HIGH); 
   delay(250);
   digitalWrite (led_tx, LOW);
  }

if(leo_sw_down == HIGH) 
  {
   digitalWrite(ReDePin, HIGH); //Modo TX MAX485 
   Serial.print("d");
   //digitalWrite(ReDePin, LOW); //Modo RX MAX485
   digitalWrite (led_tx, HIGH); 
   delay(250);
   digitalWrite (led_tx, LOW);

  }

delay(50);
}

CODIGO REMOTO

/* 
18/12/2020
Mando remoto para manejar un selector de antena mecanico de 4 posiciones
mediante un motor paso a paso SM-42BYG011-25 y un driver L298
04/02/2021 Implemento sensores hall para deteccion de posiciones y envio feedback
05/02/2021 Implemento control serie
14/02/2021 Mientras se posiciona al inicio, aparece mensaje en display
*/
 

const int ENABLE = 8; //Salida control ENABLE. (En HIGH motor desactivado)
const int ReDePin = 2;  // MAX 485 (HIGH = TX / LOW = RX)
const int opto = 3; //Optoacoplador final de carrera para detectar posición inicial.
const int sw_up = 4;     // Pulsador giro derecha al pin 12
const int sw_down = 5; //Pulsador giro izquierda al pin 13
const int inicio_output = 13; //Enciendo el led rojo de la placa para saber cuando está en la posicion 1.
const int hall_input = 0; //Uso entrada analogica A0 para leer el divisior de tensión de los sensores hall

int valor_hall = 0; //Variable para el valor de tension de los hall
int pos_actual = 1; //Posicion actual del conmutador
int leo_sw_up = 0; //Valor del pulsador giro derecha
int leo_sw_down = 0; //Valor del pulsador giro down
int leo_opto = 0; //Valor de optoacoplador
int flag_inicio = 0; //Bandera para mover a la posicion inicial
int paso = 0; //Movimiento de una posicion a otra
int antena = 0; //Entrada seleccionada del 1 al 4
int dato_serie;
//int contador_tx = 0;

#include <Arduino.h>
//#include<EEPROM.h>
#include "BasicStepperDriver.h"

// Pasos del motor por vuelta.  200 pasos de 1.8º para el motor SM-42BYG011-25
#define MOTOR_STEPS 200
#define RPM 100

// Dado que los micropasos se configuran externamente con M0. M1, M2, asegurarse de que coincida con el modo seleccionado
// Si no es así, el motor se moverá a un RPM diferente al elegido
// 1=full step, 2=half 4=un cuarto, 8 un octavo, step etc.
#define MICROSTEPS 2

// Conexiones necesarias para que funcione
#define DIR 6  //Pin6
#define STEP 7 //Pin7
//Uncomment line to use enable/disable functionality
//#define SLEEP 13

// 2-wire basic config, microstepping is hardwired on the driver
BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);

//Uncomment line to use enable/disable functionality
//BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP, SLEEP);


void setup()
{
Serial.begin(9600);     // inicializa comunicacion por monitor serie a 9600 bps
//Serial.setTimeout(1000);
pinMode(sw_up, INPUT);
pinMode(sw_down, INPUT);
pinMode(ENABLE, OUTPUT); 
pinMode(ReDePin, OUTPUT);
pinMode (inicio_output, OUTPUT);
digitalWrite (inicio_output, LOW);
stepper.begin(RPM, MICROSTEPS);
  // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
  // stepper.setEnableActiveState(LOW);
  
}

//////////////// SUBRUTINAS //////////////

void inicio()
{
//leo_opto = digitalRead(opto); //Leo el optoacoplador
 delay(1000); //Espero a que el master esté preparado para empezar a recibir datos
 digitalWrite(ReDePin, HIGH); //Modo TX MAX485
 delay(100);
 Serial.print(5); // Envio la identificación de INICIO
 //digitalWrite(ReDePin, LOW); //Modo RX MAX485
  while (leo_opto == LOW && flag_inicio == 0) 
    {
      digitalWrite (ENABLE,LOW); //Activo motor
      stepper.rotate(-1.8);  //Muevo al inicio posicion antena 1
      leo_opto = digitalRead(opto); //Leo el optoacoplador
      delay(20); //Este delay es necesario para que le motor gire
    }
    
  if (leo_opto == HIGH) 
    {
      stepper.rotate(-1.8*2); //Doy un paso mas despues de leer el final para asegurar que no vuelve mecanicamente
      flag_inicio = 1;
      pos_actual = 1;
      digitalWrite (ENABLE, HIGH); //Desactivo motor      
      Serial.print(1); 
      digitalWrite(ReDePin, LOW); //Modo RX MAX485
    }
 
}

// Muevo a derecha
void right()
{
   if (pos_actual == 1) paso = 53; // Necesito 53 pasos desde la posicion 1 a la posicion 2
   if (pos_actual == 2) paso = 76;
   if (pos_actual == 3) paso = 54;
   digitalWrite (ENABLE, LOW); //Activo motor
   stepper.rotate(1.8*paso);  //Muevo en un sentido X pasos de 1,8º cada uno
   pos_actual = pos_actual + 1;
   digitalWrite (ENABLE, HIGH); //Desactivo motor 
}

// Muevo a izquierda
void left()
{
  if (pos_actual == 4) paso = 54;
  if (pos_actual == 3) paso = 76;
  if (pos_actual == 2) paso = 54;
  digitalWrite (ENABLE, LOW);//Activo motor
  stepper.rotate(-1.8*paso);  //Muevo en sentido contrario
  pos_actual = pos_actual - 1;
  digitalWrite (ENABLE, HIGH);//Desactivo motor
}

void loop()
{
if (flag_inicio == 0) inicio(); //Al encender voy a la posicion de inicio
valor_hall = analogRead(hall_input);

leo_opto = digitalRead(opto); //Leo el optoacoplador
if (leo_opto == HIGH) pos_actual = 1;

/////////////////////  LEO PUERTOS SERIE  /////////////////

delay(50); //Doy un tiempo para que la UART se prepare para leer datos.Si no no aparecen los mesajes en el display
digitalWrite(ReDePin, LOW); //Modo RX MAX485

if (Serial.available()>0) //Si hay datos los leemos
  {
    dato_serie = Serial.read(); //leo el dato recibido y lo guardo en la variable
    
      if((dato_serie=='u') && (flag_inicio == 1) && (pos_actual < 4))
        {
          right();
        }
      
      if((dato_serie=='d') && (flag_inicio == 1) && (leo_opto == LOW) && (pos_actual > 1))
        {
          left();
        }
      
      if(dato_serie=='?') //Si interroga el master, envio la posicion en que esta.
        {
          digitalWrite(ReDePin, HIGH); //Modo TX MAX485
          delay(100); //Le doy tiempo al MAX 485 pra que conmute de RX a TX
          Serial.print(antena);
        }        
 }

leo_sw_up = digitalRead(sw_up); //Leo el pulsador de servicio de la PCB para detectar si está pulsado 

if((leo_sw_up == HIGH) && (flag_inicio == 1) && (pos_actual < 4)) //Muevo con el pulsador
 {
  right(); //Voy a la subrutina de giro a derecha
 }

leo_sw_down = digitalRead(sw_down); //Leo el pulsador de servicio de la para detectar si está pulsado

if((leo_sw_down == HIGH) && (flag_inicio == 1) && (leo_opto == LOW) && (pos_actual > 1))
 {
  left();  //Voy a la subrutina de giro a izquierda
 }

///////////FEEDBACK ENTRADA SELECCIONADA////////////

if((leo_opto == 1) && (valor_hall >= 900)) antena = 1;
if((leo_opto == 0) && (valor_hall <= 100)) antena = 2;
if((leo_opto == 0) && (valor_hall >= 300) && (valor_hall <= 700)) antena = 3;
if((leo_opto == 0) && (valor_hall >= 900)) antena = 4;

}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.