Transmisor y receptor SYN115 / SYN480R 433MHZ

Saludos al amable foro, tengo una duda que quizas sea mas obvia para los demas, el caso es que yo no entiendo porque sucede, aun cuando lo resolvi con un truco pero quisiera saber que es lo que no sé o porque sucede.

primero que nada el codigo tanto del Transmisor como el del Receptor

TRANSMISOR

C++:
/*
control remoto usando la libreria VirtualWire
en Modulos Inalambricos SYN115 / SYN480R 433MHZ

Este es el codigo del Transmisor

El PIN 12 del arduino es el que usa Virtual Wire para "TX"
 y este,  a su vez, se conecta a "Data" del Modulo SYN115 con una resistencia de 10Kohms
 ya que el modulo SYN115 se alimenta con 3.3 volts

Por otro lado, se conectan 2 resitencias varibles de 10Kohms (Yoystick) a las entradas analogicas A0 y A1
y por ultimo un boton al Pin 4 del arduino.

 */



#include <VirtualWire.h>

//Declaramos las tres variables que vamos a enviar en el ejemplo
  byte Eje_X; //Variable para el potenciometro Eje X
  byte Eje_Y; //Variable para el potenciometro Eje Y
  byte  Boton;//Variable para el Boton



void setup()
{
 pinMode(4,INPUT_PULLUP);//configuramos el pin4 como entrada y resistencia PullUp
 
    Serial.begin(115200);      // inicializamos puerto serie para monitoreo
    Serial.println("Iniciando");

    // se inicializan las interrupciones que ocupa la Libreria VirtualWire
    vw_set_ptt_inverted(true); // esta linea no se ocupa ya que es usada en otras aplicaciones como WalkieTalkies
    vw_setup(2000);     //Configuramos los Bits por segundo



}
//funcion para mapear los valores obtenidos de los potenciometros

int mapJoystickValues(int val, int Bajo, int medio, int superior, bool invertir)
{
  val = constrain(val, Bajo, superior);
  if ( val < medio )
    val = map(val, Bajo, medio, 0, 128);
  else
    val = map(val, medio, superior, 128, 255);
  return ( invertir ? 255 - val : val );
}


void loop()
{
//declaramos la variable char usada para enviar los datos por Virtualire
char msg[VW_MAX_MESSAGE_LEN];
//Obtenemos los datos ya mapeados en sus respectivas variables
Eje_X=mapJoystickValues( analogRead(A0), 0, 512, 1023, true );
Eje_Y=mapJoystickValues( analogRead(A1), 0, 512, 1023, true );
Boton=digitalRead(4)+1;//se suma un uno ya que el CHAR (que sera usado mas adelante) no soporta el cero

//Monitoreamos los datos a enviar
Serial.println(String(Eje_X)+","+String(Eje_Y)+","+String(Boton));
//Formamos el mensage a enviar con los datos obtenidos
msg[0]=char(Eje_X);
msg[1]=char(Eje_Y);
msg[2]=char(Boton);

   //Encendemos el Led del arduino para ver que inicia la transmision
    digitalWrite(13, true);
    vw_send((uint8_t *)msg, strlen(msg));//enviamos el mensage
    vw_wait_tx(); // esperamos a que el mensage se halla enviado por completo
    digitalWrite(13, false);//Apagamos el led al finalizar la transmision
    
}


RECEPTOR

C++:
/*
Control remoto usando la Libreria VirtualWire
En modulos inalambricos SYN115/SYN480r 433mhz
Codigo del receptor

El pin 11 en el arduino es ocupado como RX por la libreria VirtualWire
Y es ahi donde se conecta el pin "Data" del Modulo SYN480r

por el momento solo se reciben los caractares Char validos
 */
#include <VirtualWire.h>

void setup()
{
    Serial.begin(115200);    // inicializamos el puerto serie para monitore
    Serial.println("Iniciando");

    // Iniciamos las interrupciones ocupa la libreria VirtualWire
    vw_set_ptt_inverted(true); // esta linea en realidad no se ocupa ya que sirve para Walkie Talkies
    vw_setup(2000);     // Configuramos los Bits por segundo

    vw_rx_start();       // Iniciamos el servicio de "escucha" de la libreria
}

void loop()
{
    
    //iniciamos las Variables usadas por la libreria y es donde se contiene la informacion recibida
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    
    if (vw_get_message(buf, &buflen)) //  si hay un mensage valido en el Buffer
    {   
        digitalWrite(13, true); // Encendemos el Led del arduino para saber que inicio una recepcion
    
   Serial.println("Informacion recibida: ");
    
  //Mostramos el contenido del Buffer recibido
    for (int i = 0; i < buflen; i++)
    {
      
      Serial.print((buf[i]),DEC);
      Serial.print(" ");
    }
    Serial.println("");
        digitalWrite(13, false);
        
    }
}

Ahora explico: Se trata de hacer un radio control (R/C) para manejar algunos juguetes de hace mucho tiempo que ya no tienen su transmisor o receptor, asi que me dia a la tarea de traerlos a la vida fabricando mis propios controles de manera muy economica, de echo ya lo consegui y funcionan de maravilla. con estos modulos
SYN115 (transmisor) y SYN480r (Receptor) he alcanzado algo asi como 70 mts y en vista directa alcanzan hasta 100 metros lo cual para un juguete esta de sobra. Asi que recomiendo estos modulos para esos fines ya que en conjunto (ATMEGA+Modulos+otros pertrechos) resulta muy economico y eficaz lograr buena telemetria.


Pero vamos a mi duda, en estos ejemplos que pongo, reuduzco el codigo y me enfoco a transmitir tres Bytes y como podras ver estos estan contendios en:
byte Eje_X; //Variable para el potenciometro Eje X
byte Eje_Y; //Variable para el potenciometro Eje Y
byte Boton;//Variable para el Boton

Estas Variables ya con el contenido obtenido y listo para enviar, los convierto a un arreglo CHAR:

msg[0]=char(Eje_X);
msg[1]=char(Eje_Y);
msg[2]=char(Boton);

y por ultimo se envia via la funcion disponible en la Libreria VirtualWire.

Ahora bien, si compilan y ejecutan el codigo, podran ver que todo funciona a la perfeccion, pero OjO y ahi viene mi duda:

Esta la siguiente linea:
Boton=digitalRead(4)+1;//se suma un uno ya que el CHAR (que sera usado mas adelante) no soporta el cero

En la que obtengo el "estado" del PIN4 del arduino, que como sabemos solo puede ser 0 o 1, pero en este caso le sumo un Uno (para obtener 1 o 2. ¿Poque? porque si no le sumo el uno; al transmitir el dato con la funcion de VirtualWire pues simplemente no se ve reflejado en el codigo del receptor...y no entiendo porque sucede esto...como ya comente, al final lo resolvi con este truco, pero mi mente queda con la duda de saber porque. Ojala alguien me pueda quitar este trabucles.

Saludos y muchas gracias.
 
El problema está en que al hacer strlen(...), sobre el buffer, busca un string y al llegar a un valor en 0 ('\0') corta.

Entonces, si no tuvieras ese +1 y el puerto está en 0, el resultado de strlen valdría 2 (si los bytes anteriores fueran distinto de 0). Lo cual acá tendrías el mismo problema con los bytes de los ejes en caso de valer 0 (supongo que lo podrías salvar, cambiando la escala para que inicie en 1).

El tema está en si es necesario que "msg" sea considerado como un string o si simplemente es un buffer con datos binarios y la función vw_send(...) lo puede tomar en ese formato.

La prueba sería ver que pasa si en vez de usar strlen, directamente le ponés un 3.
 
El problema está en que al hacer strlen(...), sobre el buffer, busca un string y al llegar a un valor en 0 ('\0') corta.

Entonces, si no tuvieras ese +1 y el puerto está en 0, el resultado de strlen valdría 2 (si los bytes anteriores fueran distinto de 0). Lo cual acá tendrías el mismo problema con los bytes de los ejes en caso de valer 0 (supongo que lo podrías salvar, cambiando la escala para que inicie en 1).

El tema está en si es necesario que "msg" sea considerado como un string o si simplemente es un buffer con datos binarios y la función vw_send(...) lo puede tomar en ese formato.

La prueba sería ver que pasa si en vez de usar strlen, directamente le ponés un 3.


Es correcto tu comentario @cosmefulanito04, cambie los valores al Mapear los ejes X e Y, para que dieran un valor desde 0 y ZAZ,; sucedió lo mismo que con el Botón, es decir la rutina al encontrar un CERO, pues corta la cadena. así que queda Aclarada mi duda y el Tema que por medio de la Rutina propia del VirtualWire, no es posible mandar Caracteres con valor CERO, teniendo en cuenta esto, todo funciona muy bien y sin errores o perdidas.

Reitero que esta es una forma muy Barata y eficiente de contar con un Transmisor/Receptor para afrontar proyectos de Modelismo ya sea en Autos, barcos, aviones o Ferrocarriles a escala que no requieran mas de 100 Mts de cobertura.

Saludos y gracias
 
El tema está en si es necesario que "msg" sea considerado como un string o si simplemente es un buffer con datos binarios y la función vw_send(...) lo puede tomar en ese formato.

Ahí estamos.
Porque si la función vw_send lo admite, podemos definir la variable Boton como tipo booleana y darle un valor:

bool Boton = False;

Eso asegura además que la variable sólo va a tomar un valor True o False, no valores extraños.


Asimismo, habría que ver si las variables Eje_X y Eje_Y, pueden ser de tipo int

Todo ello porque estamos trabajando con valores numéricos.

Es decir: Se trata -si es posible-, de evitar emplear funciones de manejo de cadenas ni funciones que conviertan tipos numéricos a tipo string cuando estamos trabajando exclusivamente con valores numéricos.
 
Última edición:
Es decir: Se trata -si es posible-, de evitar emplear funciones de manejo de cadenas ni funciones que conviertan tipos numéricos a tipo string cuando estamos trabajando exclusivamente con valores numéricos.
Justamente, has dado en el meollo de este tema, que en un principio; el que desarrollo la libreria VirtualWire considero el intercambio de "cadenas' con contenido valido para los humanos, es decir, con caracteres ASCII reconocibles que se pueden manejar como tal; considerando al cero binario como nullo y por tanto no procesable, que en su momento no considere.

Saludos

Nota.- En un principio, con esta libreria, en el receptor maneje como un string el buffer recibido; lo que ocupo una rutina considerable para obtener la informacion y, es por eso que busque aprovechar que en solo tres Bytes o, el largo del buffer; recibiera lo que requeria...Minimalismo!

________________________________________
Rubio


“Cuando tengas que disparar, dispara, no hables.”
 
Última edición:
Exactamente: Minimalismo. Código elegante, eficiente y el mínimo imprescindible.
Que facilite la detección de bugs y su depuración.

A veces no hay más remedio pero, es eso. Yo evito convertir valores de cadena a numéricos y viceversa, porque esas funciones a veces devuelven resultados que luego dan complicaciones en otras partes del código.



Dices que ese código funciona bien. No seré yo quien te diga que lo enredes. Apliquemos pues, nuestro sacrosanto principio:

"SI funciona...NO LO TOQUES"

Pero, si guardas una copia y te quieres entretener en optimizarlo sin usar cadenas, verás que consigues lo mismo en menos líneas de código.
 
Si la librería permitiera manejar datos binarios, directamente hubiera enviado una estructura con los datos que se necesitan, incluso sin perder la resolución de los ADC, enviaría 2 bytes por c/eje (hasta 1023); para finalmente agregarle una verificación sencillita como un CRC, quedando algo de 6 bytes. Del lado del receptor, proceso inverso.

Ahora, si la librería está pensada para manejar cadenas, para mantener las formas, yo hubiera usado 4 caracteres para c/eje (hasta 1023), otro para el botón y tres para un CRC al final. ¿Es mayor cantidad de datos?, si, pero va en línea para lo que fue pensada esa función y si bien duplicaste la cantidad de datos en base a la estructura mencionada antes, por las velocidades que maneja el módulo, le es indistinto.

Tercera alternativa, modificar/crear la función para que acepte datos binarios en el envío y hacer lo mismo del lado de la recepción.

Si querés, subí las dos funciones, transmisión y recepción, para ver que tipo de datos aceptan y en caso de ser necesario, como modificarlas para que usen datos binarios.
 
Bueno, no puedo editar el mensaje anterior. Buscando, la funciones serían estas:

C:
// 4 bit to 6 bit symbol converter table
// Used to convert the high and low nybbles of the transmitted data
// into 6 bit symbols for transmission. Each 6-bit symbol has 3 1s and 3 0s
// with at most 3 consecutive identical bits
static uint8_t symbols[] =
{
    0xd,  0xe,  0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c,
    0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34
};

// Wait until transmitter is available and encode and queue the message
// into vw_tx_buf
// The message is raw bytes, with no packet structure imposed
// It is transmitted preceded a byte count and followed by 2 FCS bytes
uint8_t vw_send(uint8_t* buf, uint8_t len)
{
    uint8_t i;
    uint8_t index = 0;
    uint16_t crc = 0xffff;
    uint8_t *p = vw_tx_buf + VW_HEADER_LEN; // start of the message area
    uint8_t count = len + 3; // Added byte count and FCS to get total number of bytes

    if (len > VW_MAX_PAYLOAD)
    return false;

    // Wait for transmitter to become available
    vw_wait_tx();

    // Encode the message length
    crc = _crc_ccitt_update(crc, count);
    p[index++] = symbols[count >> 4];
    p[index++] = symbols[count & 0xf];

    // Encode the message into 6 bit symbols. Each byte is converted into
    // 2 6-bit symbols, high nybble first, low nybble second
    for (i = 0; i < len; i++)
    {
    crc = _crc_ccitt_update(crc, buf[i]);
    p[index++] = symbols[buf[i] >> 4];
    p[index++] = symbols[buf[i] & 0xf];
    }

    // Append the fcs, 16 bits before encoding (4 6-bit symbols after encoding)
    // Caution: VW expects the _ones_complement_ of the CCITT CRC-16 as the FCS
    // VW sends FCS as low byte then hi byte
    crc = ~crc;
    p[index++] = symbols[(crc >> 4)  & 0xf];
    p[index++] = symbols[crc & 0xf];
    p[index++] = symbols[(crc >> 12) & 0xf];
    p[index++] = symbols[(crc >> 8)  & 0xf];

    // Total number of 6-bit symbols to send
    vw_tx_len = index + VW_HEADER_LEN;

    // Start the low level interrupt handler sending symbols
    vw_tx_start();

    return true;
}

// Get the last message received (without byte count or FCS)
// Copy at most *len bytes, set *len to the actual number copied
// Return true if there is a message and the FCS is OK
uint8_t vw_get_message(uint8_t* buf, uint8_t* len)
{
    uint8_t rxlen;
   
    // Message available?
    if (!vw_rx_done)
    return false;
   
    // Wait until vw_rx_done is set before reading vw_rx_len
    // then remove bytecount and FCS
    rxlen = vw_rx_len - 3;
   
    // Copy message (good or bad)
    if (*len > rxlen)
    *len = rxlen;
    memcpy(buf, vw_rx_buf + 1, *len);
   
    vw_rx_done = false; // OK, got that message thanks
   
    // Check the FCS, return goodness
    return (vw_crc(vw_rx_buf, vw_rx_len) == 0xf0b8); // FCS OK?
}

Se ve que ya contempla un CRC, por lo tanto lo que puse arriba con respecto al tema queda descartado.

También se ve que hace una conversión de los nibbles a 6 bits usando la tabla "symbols", por lo tanto c/byte pasará a ser de 12bits y se usaran dos bytes para su transmisión.

Finalmente, lo más importante, la función no distingue caracteres de binarios, por lo tanto se podría enviar directamente un dato binario.

Más allá de que el proyecto ya funcione, yo probaría esto:

- Transmisor:

C:
...
struct RFData
{
    uint16_t eje_x;
    uint16_t eje_y;
    uint8_t  boton;
};

...
   
void loop()
{
    struct RFData data;
   
    data.eje_x = analogRead(A0);
    data.eje_y = analogRead(A1);
    data.boton = digitalRead(4);
       
    //Monitoreamos los datos a enviar
    Serial.println(String(data.eje_x)+","+String(data.eje_y)+","+String(data.boton));

    //Encendemos el Led del arduino para ver que inicia la transmision
    digitalWrite(13, true);
    vw_send((uint8_t*)&data, sizeof(data));//enviamos el mensaje
    vw_wait_tx(); // esperamos a que el mensaje se halla enviado por completo
    digitalWrite(13, false);//Apagamos el led al finalizar la transmision  
}

- Receptor:

C:
...
struct RFData
{
    uint16_t eje_x;
    uint16_t eje_y;
    uint8_t  boton;
};
...
   
void loop()
{
    struct RFData data;
    uint8_t *buf = (uint8_t*)&data;
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) //  si hay un mensaje valido en el Buffer
    {  
        digitalWrite(13, true); // Encendemos el Led del arduino para saber que inicio una recepcion
   
           Serial.println("Informacion recibida: ");
   
        //Mostramos el contenido del Buffer recibido
        for (int i = 0; i < buflen; i++)
        {    
              Serial.print((buf[i]),DEC);
              Serial.print(" ");
        }
       
        Serial.println("");
        digitalWrite(13, false);      
    }
}

Lo único es que deberías verificar que el tamaño de la estructura sea la definida por sus elementos, por ej. en GCC, el último byte lo toma de 2 para una optimización de memoria, para evitar eso, tuve que definirla de esta forma:

C:
struct RFData
{
    uint16_t eje_x;
    uint16_t eje_y;
    uint8_t  boton;
}__attribute__((packed));

Será de cuestión de tirar un mensaje en consola:

C:
//Tamaño de la estructura
Serial.println("Struct size ["+String(sizeof(data))+"]");
 
Atrás
Arriba