comunicacion-inalambrica-en-arduino-con-modulos-rf-433mhz

Comunicación inalámbrica en Arduino con módulos RF 433MHz

¿Qué es un módulo RF 433MHz?

Los módulos de radio frecuencia RF 433MHz son transmisores/receptores inalámbricos que podemos emplear como forma de comunicación entre procesadores como Arduino.

Este tipo de módulos emisor (FS1000A) y el receptor (XY-MK-5V) se han hecho populares como medio de comunicación, principalmente, por su bajo coste.

La frecuencia de operación es de 433MHz, aunque también existen módulos similares a 315MHz. Ambas frecuencias pertenecen a bandas libres, por lo que su uso es gratuito.

El alcance depende del voltaje con el que alimentemos el módulo y la antena que usemos. A 5V y con la antena del módulo, el alcance difícilmente excederá de los 2 metros. Alimentando a 12V y con una antena de cobre de 16.5cm el rango en exteriores puede alcanzar 300 metros.

La longitud de la antena óptima depende de la frecuencia del módulo. Antenas habituales para RF 433MHz son un cable de cobre de 16.5cm y la helicoidal 4.5mm de diámetro y 22mm 0 34mm de longitud.

La comunicación es simplex (canal único y unidireccional) y tienen baja velocidad de transmisión (típicamente 2400bps). Se realiza, básicamente, por modulación ASK (amplitude shift keying). No disponen de filtro ni ID por hardware, por lo que si queremos una comunicación robusta tendremos que implementarlo por software

Este tipo de comunicación RF 433MHz es muy frecuente en proyectos caseros de electrónica y robótica, debido a su bajo precio y medio-largo alcance en condiciones adecuadas, aunque tenemos que tener en cuenta que la robustez y calidad de la comunicación puede ser limitada.

Ejemplos de uso son recepción remota de sensores como temperatura presión, aplicaciones de domótica y edificios inteligentes, activación remota de dispositivos como iluminación, alarmas, o control y monitorización de robots y vehículos.

Precio

Cómo hemos comentado, estos módulos son muy baratos. Podemos comprar el par de emisor (FS1000A) y receptor (XY-MK-5V) RF 433MHz por 0.80€, buscando en vendedores internacionales de eBay o AliExpress.

arduino-comunicacion-inalambrica-433-mhz-componente

También podemos encontrar ambos módulos con una antena helicoidal para soldar por 1.70€. Si queréis usar estos módulos en aplicaciones reales donde necesitéis un alcance superior a 2-3 metros, puede resultar interesante adquirirlo con antena.

En cualquier caso, siempre podemos o bien comprar la antena de forma independiente, o hacer una con un cable desnudo de cobre la longitud adecuada (16.5 cm para RF 433MHz)

Esquema de montaje

La conexión es realmente sencilla. En primer lugar, alimentamos los módulos conectando Vcc y Gnd, respectivamente a 5V y Gnd en Arduino.

Como veremos en al ver el código, vamos a emplear la librería Virtual Wire, que funciona con cualquier pin digital. Por tanto, simplemente conectamos los pines de DATA a una salida digital cualquiera.

Por tanto, la conexión del emisor FS1000A sería la siguiente,

arduino-comunicacion-inalambrica-433-mhz-transmisor-fs1000a

Y la del receptor XY-MK-5V la que se ve a continuación.

arduino-comunicacion-inalambrica-433-mhz-receptor-xy-mk-5v

Finalmente, la conexión en ambos casos de los Arduino que controlan cada uno de los módulos emisor/receptor es la misma,

arduino-comunicacion-inalambrica-433-mhz-esquema

Opcionalmente, podéis alimentar el módulo a tensión superior para aumentar el alcance. Si estáis alimentando Arduino a través de su regulador de voltaje (por ejemplo, desde una batería de litio de 7.4V), podéis emplear esta fuente antes del regulador para alimentar el módulo.

Si usáis varias fuentes de alimentación, recordar poner GND en común.

Ejemplos de código

Para realizar la comunicación usaremos la librería Virtual Wire desarrollada por Mike McCauley, disponible en este enlace.

¿Por qué usar una librería en vez de emplear directamente el UART? Para mejorar la robustez de la comunicación. Las transmisiones ASK requieren una serie de impulsos de “entrenamiento” para sincronizar el receptor y el transmisor. También necesitan de un buen balance entre 1s y 0s para mantener el balance DC del mensaje. La UART no realizan estas funciones.

Con la librería Virtual Wire cada transmisión consta de un código de entrenamiento, el mensaje, la longitud del mensaje, y el checksum. Los mensajes son enviados con codificación 4-a-6 bit para mejorar el balance DC.

Por supuesto, estas mejoras contienen su coste, que en este caso es el espacio que ocupa la librería, la alta carga que supone al procesador, y el uso intensivo que hace de interrupciones. Por otro lado, no se dispone de acuse de señal de recibo (ACK) por lo que no sabemos si los mensajes han llegado correctamente.

La librería proporciona ejemplos de código, que resulta aconsejable revisar. Los siguientes ejemplos son modificaciones a partir de los disponibles en la librería

Encender un LED a distancia

En el primer ejemplo, basado en el que vimos al ver el puerto serie, enciende de forma remota el LED integrado de un Arduino desde otro conectado a un ordenador. El Arduino emisor recibe un byte desde puerto serie y lo reenvía al Arduino receptor. Si se transmite ‘a’ el receptor enciende el LED, y si transmite ‘b’ lo apaga.

Código emisor

#include <VirtualWire.h>

const int dataPin = 9;

void setup()
{
    Serial.begin(9600);    
    vw_setup(2000);
    vw_set_tx_pin(dataPin);
}

void loop()
{
    while (Serial.available() > 0) 
    {
      char data[1];
      data[0] = Serial.read();
      vw_send((uint8_t*)data,sizeof(data));
      vw_wait_tx();         
    }
    delay(200);
}

Código receptor

#include <VirtualWire.h>

const int dataPin = 9;
const int ledPin = 13;

void setup()
{
    vw_setup(2000);
    vw_set_rx_pin(dataPin);
    vw_rx_start();
    
    pinMode(ledPin, OUTPUT);
    digitalWrite(ledPin, false);
}

void loop()
{
    uint8_t data;
    uint8_t dataLength=1;

    if (vw_get_message(&data,&dataLength))
    {
        if((char)data=='a')
        {
            digitalWrite(ledPin, true);
        }
        else if((char)data=='b')
        {
            digitalWrite(ledPin, false);
        }            
    }
}

Enviar un string

El siguiente ejemplo muestra el envío de una cadena de texto desde un Arduino emisor a un Arduino receptor, que al recibir el texto lo muestra por puerto serie.

Código emisor

#include <VirtualWire.h>

const int dataPin = 9;
const int ledPin = 13;

void setup()
{
    vw_setup(2000);
    vw_set_tx_pin(dataPin);
}

void loop()
{
    const char *msg = "Hola mundo";

    digitalWrite(ledPin, true);
    vw_send((uint8_t *)msg, strlen(msg));
    vw_wait_tx();
    digitalWrite(ledPin, false);
    delay(200);
}

Código receptor

#include <VirtualWire.h>

const int dataPin = 9;
const int ledPin = 13;

void setup()
{
    Serial.begin(9600);
    vw_setup(2000);
    vw_set_rx_pin(dataPin);
    vw_rx_start();
}

void loop()
{
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    
    if (vw_get_message(buf, &buflen)) 
    {
    digitalWrite(ledPin, true);
    Serial.print("Mensaje: ");
    for (int i = 0; i < buflen; i++)
    {
      Serial.print((char)buf[i]);
    }
    Serial.println("");
        digitalWrite(ledPin, false);
    }
}

Enviar una variable integer y float

El siguiente ejemplo muestra el envío de variables integer y float desde un Arduino emisor a otro receptor, que muestra los datos por puerto serie. El Arduino emisor envía dos datos cualquiera, en el ejemplo millis()/1000 como integer y 3.14 como float, para lo cual tiene que convertirlos en un array de char. Además, añade un identificador ‘i’ o ‘f’ para distinguir el tipo de variable enviado.

Por su parte, el receptor recibe los datos, y en función del identificador convierte los datos recibidos a integer o float, y muestra el resultado por el puerto serie.

Código emisor

#include <VirtualWire.h>

const int dataPin = 9;
  
void setup()
{
    vw_setup(2000);
    vw_set_tx_pin(dataPin);
}

void loop()
{
  String str;  
    char buf[VW_MAX_MESSAGE_LEN];
  
  // Ejemplo de envío int
  int dataInt = millis() / 1000;
    str = "i" + String(dataInt); /// Convertir a string
    str.toCharArray(buf,sizeof(buf)); // Convertir a char array
    vw_send((uint8_t *)buf, strlen(buf)); // Enviar array
    vw_wait_tx(); // Esperar envio
    
  // Ejemplo de envío float
  float dataFloat = 3.14;
    str = "f" + String(dataFloat); // Convertir a string
    str.toCharArray(buf,sizeof(buf)); // Convertir a char array
    vw_send((uint8_t *)buf, strlen(buf)); // Enviar array
    vw_wait_tx(); // Esperar envio
    
    delay(200);
}

Código receptor

#include <VirtualWire.h>

const int dataPin = 9;

void setup()
{
    Serial.begin(9600);
    vw_setup(2000);
    vw_set_rx_pin(dataPin);
    vw_rx_start();
}

void loop()
{
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

  // Recibir dato
    if (vw_get_message((uint8_t *)buf,&buflen))
    {
    String dataString;
        if((char)buf[0]=='i')
        {
            for (int i = 1; i < buflen; i++)
            {
        dataString.concat((char)buf[i]);
            }
            int dataInt = dataString.toInt();  // Convertir a int
            Serial.print("Int: ");
            Serial.println(dataInt);
        }
        else if((char)buf[0]=='f')
        {
            for (int i = 1; i < buflen; i++)
            {
        dataString.concat((char)buf[i]);
            }
            float dataFloat = dataString.toFloat();  // Convertir a float
            Serial.print("Float: ");
            Serial.println(dataFloat);
        }
    }
}

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full