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


arduino-comunicacion-inalambrica-433-mhz

¿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);
        }
    }
}
Si te ha gustado esta entrada y quieres leer más sobre Arduino puedes consultar la sección
TUTORIALES DE ARDUINO
5 3 votes
Article Rating
Previous Arduino y mando a distancia de 4 canales 315MHz IC2262
Next Comunicación inalámbrica a 2.4Ghz con Arduino y NRF24L01
16 Comments
oldest
newest
Inline Feedbacks
View all comments
luisllamas
4 years ago

Hola Jose Luis.
Realmente pienso que queda mucho más claro con el esquema de conexión (que por cierto, cuestan mucho más que hacer la foto del montaje). Estoy de acuerdo en que quedaría bien, y ya la monda sería poner un video en youtube. ¡Pero cada entrada me costaría la vida!
En proyectos y en algunas entradas avanzadas, si que aporta tener una foto del montaje final. Pero en estas entradas de nivel intermedio no creo que merezca mucho el esfuerzo.
Un saludo!

luisllamas
4 years ago

Todos los módulos que he visto son de 1 canal. Los mandos que comentas también son de un canal, simplemente tienen 4 salidas porque están pensados para poderse usar sin un procesador.
Lo que me lleva a ... para que necesitas 2 canales?? Si quieres mandar 2 señales, puedes usar un mismo canal, simplemente manda una trama diferente.

luisllamas
4 years ago

Pues sin conocer los detalles del mando, con casi total probabilidad no. La libraría Virtual Wire está pensada para establecer una comunicación robusta inalámbrica (algo que no es tan fácil como parece) entre dos dispositivos que usan la librería.
Sin embargo existe una librería llamada RCSwitch pensada para copiar y emular mandos a distancia RF, e interactuar con ellos. Puedes intentarlo, o intentar saber que protocolo tiene el mando de 8 canales que quieres usar.

Pablo
3 years ago

Hola, agradezco primeramente por el aporte.
Intente modificar el código fuente para enviar un String como trama y al recibirlo realizar una instrucción (como el ejemplo de encender un led a distancia con una variable char), o que pretendo hacer es recibir una palabra como instrucción. Ejemplo: Si envío la palabra "Encender" pueda encender el led, si envío "encender" no debe hacer nada... Agradecería me puedan orientar con esa modificación en el código. Quedo atento a la respuesta.

javi
3 years ago

Solo escribo para felicitar por el sitio, que acabo de conocer (y estudiaré a fondo) y agradecer la claridad de las explicaciones. Así da gusto. Tengo muchas lagunas en Arduino, y justo hoy tengo entremanos un proyecto que implica estos modulos RF. Si no logro avanzar, me gustaría saber si puedo consultarte 🙂 gracias!!

ANGEL MARIA
3 years ago
Reply to  javi

Estoy de acuerdo, artículos muy buenos y muy bien explicados.

Lucas
3 years ago

Excelente tutorial! Probe los ejemplos y pude adaptarlo a mi proyecto. Muchas gracias.

Lucas
3 years ago

Hola, tengo dos dudas:

- Es normal que a la hora de transmitir titile el led rx? No debería ser el tx?
- El led rx debe estar siempre titilando en señal de estar escuchando?

Desde ya te agradezco, saludos,
Lucas

John Almeida
3 years ago

Excelente trabajo. Felicitaciones , sigue así ayudando con tus conocimientos.
Saludos

javier
2 years ago

Luis, una duda que no soy capaz de solucionar: Sigo ayudando a mi hijo con su robot. Queremos que la comunicación sea bidireccional. El robot tiene que mandar información y recibirla y el mando también. Hemos comprado 2 juegos y los hemos puesto. Solo somos capaces de que la información viene en un sentido.

No tiene que ser en acto, a la vez. Es como un dialogo.

No me cansare de decirlo: Enhorabuena por tu blog. Bien explicado, bien resumido, muy interesante. Mil gracias,

Comunicaciones Arduino | Aprendiendo Arduino
2 years ago
Javier
2 years ago

Gracias, yo lo he entendido.

Javi
2 years ago

Hola, he comprado dos módulos con un mando cada uno, al probarlo me he dado cuenta de que no son independientes. Quiero decir que ambos mandos funcionan con el mismo módulo. Quería que fueran independientes . hay alguna manera ?

tpo88
2 years ago

Solo comentar que Virtual Wire ha sido superado por RadioHead

Jose María
1 year ago

Después de haber hecho estos circuitos y otros sencillos, quiero hacer una cosa que se me está resistiendo. Encender 4 luces simultáneamente con un solo emisor y 4 receptores RF. COmo vienen en pares parece que lo que emite un emisor solo lo recibe su receptor pareja; yo quiero que el emisor envíe un comando y lo obedezcan 4 receptores simultáneamente.
¿Cómo se puede lograr esto? Muchas gracias.

Oscar
11 months ago
Reply to  Jose María

Hola, puedes agregar un identificador a cada uno de los receptores, y con el emisor puedes enviar el identificador y la indicacion de que hacer, por ejemplo la intruccio 0001 con el identificador 1000 entonces seria que envies 1001, o el 2001 para la instruccion 0001 pero identificador 2000, espero haberme explicado.