O módulo RF 433MHz é um dos componentes mais simples para a criação de uma comunicação sem fio. Portanto, neste post, veremos como utilizá-lo com e sem biblioteca utilizando o Arduino.
Informações importantes
O que é o módulo RF
O módulo RF consiste em um transmissor e um receptor de ondas eletromagnéticas de frequência de 433MHz. Isto é, são dois circuitos capazes de comunicar entre si sem a necessidade de fios, pois se comunicam utilizando ondas eletromagnéticas. A imagem abaixo mostra ambos.
O transmissor possui um circuito relativamente simples. Isso porque o circuito é basicamente uma chave ligada a um gerador de onda de 433MHz. Quando a chave é ativada, o sinal de 433MHz sai na antena do módulo e é capaz de chegar ao receptor.
Já o receptor é mais complexo. Seu circuito possui uma antena para receber a onda de 433MHz e um circuito para filtrar ruídos e amplificar o sinal de interesse. Além disto, ele possui uma malha de captura de fase para sincronizar o sinal de saída.
Mais adiante, veremos como estes circuitos podem ser utilizados.
Pinos
A maioria dos pinos já vêm indicados, mas suas características podem não ser muito óbvias. Sendo assim, vamos analisar os pinos do modulo RF receptor e do transmissor.
Começando com o transmissor:
- DATA. É por meio deste pino que os dados serão enviados para o receptor.
- Vcc. Pino de alimentação que suporta de 3.5-12V. Quanto maior for a tensão utilizada, maior será a distância que o modulo será capaz de transmitir a informação.
- GND. Terra da alimentação.
- Terminal para soldar uma antena.
Passando para o receptor:
- Vcc. Pino de alimentação que deve ser ligado em 5V ou em 3.3V (adiante veremos a diferença entre as duas tensões).
- DATA. É por meio deste pino que os dados serão recebidos do transmissor.
- DATA. Possui a mesma função do pino anterior. Portanto, tanto o pino 2 quanto o 3 poderão ser usados para receber os dados.
- GND. Terra da alimentação.
- Terminal para soldar uma antena
Antena
A utilização da antena é extremamente importante, pois ela garante que o alcance da comunicação aumente significativamente. Tanto é que, se você não utilizar uma antena, o alcance do transmissor será de apenas alguns metros.
E, conforme dito anteriormente, o alcance depende também da tensão de alimentação do transmissor. Sendo assim, se você deseja um alcance máximo, basta alimentar o transmissor com 12V e utilizar uma antena. E o alcance pode chegar a mais de 30 metros em uma área aberta.
Entretanto, o termo “antena” pode ser um pouco vago. Um modelo bem simples de antena que você pode utilizar é um fio enrolado. E o seu comprimento deve ser cerca de 17,3cm. Portanto, enrole dois fios de 17,3cm e solde um em cada terminal de antena dos módulos receptor e transmissor.
O cálculo anterior é feito considerando a quarta parte do comprimento de onda (λ) da onda de 433Mhz (considerando ela viajando no vácuo):
Formas de utilizar o módulo RF
Existem diferentes formas de se utilizar o modulo RF, embora o princípio de funcionamento delas seja o mesmo. Primeiro veremos como enviar um simples bit (0 ou 1) entre dois Arduinos.
Em seguida, veremos como transmitir vários bits (uma informação) de forma simples sem utilizar bibliotecas. Por fim, veremos a mesma coisa anterior, porém utilizando bibliotecas.
Uma outra maneira de utilizar o modulo RF é com o encoder e o decoder HT12E e o HT12D. Entretanto, não pretendo explicá-los neste post. Eles facilitam a comunicação, pois eliminam a necessidade de um microcontrolador no circuito todo. Porém, eles permitem o envio de 4 bits apenas.
Transmitindo um bit
Vamos imaginar que nossa aplicação seja apenas acender um LED que esteja no circuito do receptor. Para isto, iremos enviar no transmissor apenas nível alto ou nível baixo para acender ou apagar o LED.
Circuito
A ligação será feita conforme foi descrito no tópico sobre os pinos. Lembrando que utilizarei dois Arduinos para testar os módulos.
Transmissor (Arduino 1):
- Vcc- 5V do Arduino 1.
- GND – GND do Arduino 1.
- Data – Pino 3 do Arduino 1.
Receptor (Arduino 2):
- Vcc – 3.3V do Arduino 2.
- GND – GND do Arduino 2.
- Data (qualquer DATA) – pino 3 do Arduino 2.
Utilizei o pino 3 nos dois casos sem motivo aparente, pois qualquer pino digital serviria.
Código transmissor
A ideia do código do transmissor é acionar o pino DATA por 3 segundos e depois deixá-lo em nível baixo por 3 segundos. Adicionei também o pino 13 (pino do LED da placa) para enxergar o melhor o que está acontecendo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Pino do transmissor #define TX 3 void setup() { pinMode(TX, OUTPUT); pinMode(13, OUTPUT); } void loop() { // Coloca o pino TX em alta a cada x segundos digitalWrite(TX, HIGH); digitalWrite(13, HIGH); delay(3000); digitalWrite(TX, LOW); digitalWrite(13, LOW); delay(3000); } |
Código receptor
O código do receptor fica lendo o estado do pino do receptor e escreve ele no Serial. E, se o valor for nível lógico alto, o código liga o LED do pino 13 e desliga caso contrário.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Pino do receptor #define RX 3 void setup() { Serial.begin(9600); pinMode(RX, INPUT); pinMode(13, OUTPUT); // Define o pino 13 como saída (LED) } void loop() { // Se o pino RX ficou em alta if(digitalRead(RX)){ digitalWrite(13, HIGH); }else{ digitalWrite(13, LOW); } // Printa o valor de RX e da um pequeno delay entre cada medição Serial.println(digitalRead(RX)); delay(50); } |
Resultados
Após realizar as ligações e fazer upload dos códigos em cada Arduino, o resultado recebido foi o seguinte:
De certa forma, foi dentro do esperado, apesar do último pulso ter tido uma largura diferente do padrão anterior. O curioso nesta parte é que, quando o transmissor envia um nível alto, o receptor recebe um nível baixo e vice-versa.
Portanto, a lógica está trocada. E, se quisermos acionar um LED com um pulso de nível lógico alto do transmissor, bastaria verificar se o receptor detectou um nível lógico baixo.
Obs: o receptor normalmente nao fica em nível alto todo o tempo em que o transmissor manda o sinal de nível baixo. Este sensor serve mais para detectar pequenos pulsos.
Problema de alimentar o receptor com 5V
Agora, o mais importante é entender porquê alimentei o receptor com 3.3V e não 5V, que é a tensão de operação comum dele. Acontece que, quando alimentamos ele com 5V, o sinal recebido fica como o mostrado abaixo:
A imagem acima foi resultado do mesmo código de antes. Portanto, é perceptível que alimentar o receptor com 5V gera bastante ruído no sinal recebido.
É possível perceber alguns momentos onde o sinal fica mais tempo em baixa ou mais tempo em alta. Possivelmente são os momentos em que o transmissor envia algum pulso. Entretanto, fica difícil trabalhar com este sinal sendo que o objetivo é acionar um simples LED.
Sendo assim, basta alimentar de volta o modulo receptor com 3.3V.
Transmitindo uma informação – Sem biblioteca
Vamos imaginar que nossa aplicação agora seja enviar uma mensagem, como uma frase. A forma mais fácil de fazer isto é utilizar a comunicação serial do próprio Arduino.
Por ela, nós iremos enviar um conjunto de bits que formam uma mensagem para, no fim, termos uma frase. No fim das contas, o processo é o mesmo que enviar um bit, porém agora estaremos enviando vários bits em sequência.
Circuito
A ligação é parecida com a anterior, com a diferença sendo a ligação do pino DATA do receptor e do transmissor.
Transmissor (Arduino 1):
- Vcc- 5V do Arduino 1.
- GND – GND do Arduino 1.
- Data – Pino TX (digital 1) do Arduino 1.
Receptor (Arduino 2):
- Vcc – 3.3V do Arduino 2.
- GND – GND do Arduino 2.
- Data (qualquer DATA) – pino RX (digital 0) do Arduino 2.
Código transmissor
A ideia do código é apenas escrever uma frase para ser enviada via comunicação serial. Um detalhe importante é a velocidade máxima que o módulo RX pode trabalhar, que é em torno de 2000 baud. Portanto, criarei uma comunicação serial com uma velocidade de 1200 baud.
Além disto, no fim da mensagem, enviarei o caractere # que será o indicador de fim de mensagem.
1 2 3 4 5 6 7 8 9 10 11 | void setup() { Serial.begin(1200); // Cria a comunicação serial (<2000 baud) } void loop() { Serial.write("Mundo Projetado#"); Serial.flush(); // Aguarda a mensagem ser enviada delay(5000); } |
Código receptor
O código do receptor ficará apenas “ouvindo” o barramento serial do Arduino e armazenando os dados que chegarem. E, caso chegue o caractere “#”, a mensagem é recebida por completo e ele escreve ela no monitor serial.
Vale ressaltar que o receptor deve possuir uma comunicação serial com a mesma velocidade do transmissor para não haver incompatibilidade de comunicação.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | String buf = ""; void setup() { Serial.begin(1200); } void loop() { // Caso tenha algum dado sendo recebido if (Serial.available()) { char a = Serial.read(); // Verifica se o dado recebido é o caractere # if(a == '#'){ // Se sim, printa a mensagem recebida Serial.println(buf); buf = ""; }else{ // Senão, apenas adiciona ele à mensagem buf += a; } } } |
Resultados
As mensagens recebidas vieram com bastante ruído e muitos caracteres extras. Tentei diminuir o mau contato da protoboard mudando os sensores de posição e consegui transmitir e receber as mensagem sem ruído. Entretanto, não deixou de ser algo instável.
A imagem abaixo mostra como ficou minha ligação.
Acabei decidindo visualizar o sinal do receptor utilizando um osciloscópio. Sendo assim, liguei o canal 1 do osciloscópio no pino DATA do receptor e o referencial no GND. A imagem abaixo mostra o sinal recebido:
O sinal não faz muito sentido em relação ao que está sendo enviado, pois parece que é apenas uma onda quadrada. Mas ele se refere apenas a um pedaço do sinal. De qualquer forma, dá para ver que o modulo receptor está recebendo as variações dos bits.
Por fim, uma última observação importante: testei o circuito tanto com 5V, quanto com 3.3V e os dois níveis de tensão funcionaram corretamente, apesar dos caracteres extras.
Transmitindo uma informação – Com biblioteca
A ideia aqui é a mesma do tópico anterior com a diferença que a comunicação será feita por meio de uma biblioteca. A biblioteca comumente utilizada considerando a data do post é a RadioHead. Ela pode ser baixada aqui e sua página de referência é esta.
A vantagem de utilizar a biblioteca é que ela é à prova de ruídos, pois possui um código de verificação de erros robusto. Sendo assim, a mensagem recebida é limpa e livre de erros, porque, se houver um, a mensagem é desconsiderada.
Circuito
A biblioteca, por padrão, utiliza os pinos 11 e 12 do módulo RF. Entretanto, é possível alterá-los editando os arquivos da biblioteca. Enfim, abaixo está mostrada a ligação padrão dos módulos:
Transmissor (Arduino 1):
- Vcc- 5V do Arduino 1.
- GND – GND do Arduino 1.
- Data – Pino 12 do Arduino 1.
Receptor (Arduino 2):
- Vcc – 5V do Arduino 2.
- GND – GND do Arduino 2.
- Data (qualquer DATA) – pino 11 do Arduino 2.
Consegui fazer o modulo funcionar com o receptor alimentado tanto em 5V quanto em 3.3V. Embora o recomendável seja alimentá-lo com 5V.
Código transmissor
Leia os comentários para entender como utilizar os comandos da biblioteca. O código do transmissor fica enviando a mensagem “Mundo Projetado” a cada 3 segundos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Inclui as bibliotecas base #include <RH_ASK.h> #include <SPI.h> // Instancia o objeto da comunicação RH_ASK rf_driver; void setup() { // Inicia a comunicação rf_driver.init(); } void loop() { const char *msg = "Mundo Projetado"; // Cria a mensagem // Envia a mensagem e aguarda terminar de ser enviada rf_driver.send((uint8_t *)msg, strlen(msg)); rf_driver.waitPacketSent(); delay(3000); // Delay entre mensagens } |
Código receptor
Leia os comentários para entender como utilizar os comandos da biblioteca. O código do receptor fica aguardando a mensagem ser recebida, sendo que ele tem noção do tamanho da mensagem que está por vir. E, se a mensagem for recebida corretamente, ele mostra no monitor serial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // Inclui as bibliotecas base #include <RH_ASK.h> #include <SPI.h> // Instancia o objeto da comunicação RH_ASK rf_driver; void setup() { // Inicia a comunicação rf_driver.init(); Serial.begin(9600); } void loop() { // Cria o buffer do tamanho da mensagem esperada uint8_t buf[11]; uint8_t buflen = sizeof(buf); // Verifica se o pacote recebido possui o tamanho certo if (rf_driver.recv(buf, &buflen)){ // Se sim, a mensagem foi recebida corretamente Serial.println((char*)buf); } } |
Resultados
A mensagem foi recebida diversas vezes sem problemas. O único porém foi alguns caracteres que foram printados no fim da frase recebida, embora eles de fato não tenham sido recebidos na mensagem. Portanto, é algo que pode ser facilmente resolvido.
Considerando o que foi exposto, se você não vê problemas em utilizar bibliotecas, eu recomendo bastante utilizar a RadioHead.
Isso porque ela previne muitos ruídos e erros de comunicação entre o transmissor e o receptor. Principalmente se houver algum dispositivo eletrônico que também trabalhe na faixa de frequência de 433MHz, já que ele irá influenciar no sinal do modulo RF.
Muito bom parabéns
Muito obrigado pelo retorno, Maicon!!
Olá com essa biblioteca consigo travar apenas um código de um controle para liberar o circuito ?
Não entendi muito bem sua ideia. Você quer executar uma determinada ação no circuito só quando o módulo receber uma determinada mensagem pre estabelecida?
Seria possível usar um pushbotton para enviar um comando e o segundo arduino enviar um código ir para uma tv?
Olá, Rafael! É totalmente possível sim. Bastaria utilizar o comando ‘send’ da biblioteca que apresentei quando o botão for pressionado. E, no receptor, você poderia utilizar um transmissor infravermelho para comandar a TV, assim como expliquei neste post.
Excelente post Fábio!
Como faria para transmitir e receber um valor numérico (no caso a variação de temperatura) entre 2 arduínos usando estes módulos? Grato
Olá, Carlos. Isto pode ser feito com o comando ‘send’ mesmo. Por exemplo, se a sua temperatura for uma variável inteira entre 0 e 255: rf_driver.send(temp_1, 1);
Agora, se sua variável for do tipo float ou então está fora da faixa de 0 a 255, você vai ter que transformar o valor dela em uma string para então enviar:
String exemplo = String(variavel_de_temperatura);
rf_driver.send((uint8_t *)exemplo, exemplo.length());
Não tenho certeza se o código acima funciona certinho, mas qualquer coisa você testa e me fala que tento corrigir.
Enfim, e no recebimento, você utilizaria uma função para converter a ‘string’ recebida em um valor do tipo float usando o comando toFloat() ou em um valor do tipo int com o comando toInt().
Olá, poderia explicar melhor onde mudar na biblioteca para trocar os pino do transmissor e receptor? Exemplo, quero que o pino do transmissor seja o 7 do arduino e o receptor o 3…
Olá, Rafa. Posso explicar sim. No arquivo ‘RH_ASK.h’ é preciso alterar a linha 261 (talvez mude o valor para você) da seguinte forma:
RH_ASK(uint16_t speed = 2000, uint8_t rxPin = 3, uint8_t txPin = 7, uint8_t pttPin = 10, bool pttInverted = false);
Mas talvez exista um jeito mais fácil de fazer esta troca. Pelo que vi na biblioteca, é possível instanciar a classe da seguinte forma para trocar os pinos:
RH_ASK rf_driver(2000, 3, 7, 10, false); // Rx no pino 3 e Tx no pino 7
(Você substitui o comando acima pelo de antes: RH_ASK rf_driver;)
Sr. Fábio! eu de muito pesquisar acabei encontrando seu site. Primeiro quero agradecer por sua explicação e exemplo, perfeito! consegui comunicação com os arduino, usando a biblioteca sem o código. Mas com o código não. Eu pretendo fazer esse controle remoto e o carrinho como tem no link que vou postar.
https://www.youtube.com/watch?v=ge7FxDsYqa0&t=2s
Mas poderia me ajudar?
Obrigado pela atenção
Olá, Wanderson. Muito obrigado, fico feliz que tenha gostado da explicação! Posso ajudar sim. Só não entendi muito bem qual problema ocorreu aí. Você teve problemas ao executar o código do post do tópico que usa biblioteca ou do tópico que não usa biblioteca?
Olá, primeiramente obrigado pelo conhecimento, me surgiu uma duvida aqui:
Eu tenho 1 transmissor e 5 receptores, como eu posso fazer para escolher qual dos receptores vai receber o sinal utilizando o arduino apenas no transmissor ?
Olá, Victor. Muito obrigado pelo comentário!
Infelizmente não é nada prático enviar o dado para apenas 1 dos receptores, pois a transmissão ocorre em todas as direções e é provável que todos os receptores recebam o dado. Entretanto, o que você pode fazer é criar uma espécie de ID para que o receptor saiba que aquele dado recebido pertence à ele de fato. Por exemplo, você pode usar o primeiro caractere/byte do dado para isso:
O receptor 1 só aceita mensagens que comecem com o caractere ‘#’, então você comunica com ele mandando a seguinte mensagem:
const char *msg = “#” + “resto da mensagem”
E o código do receptor 1 fica assim:
2
3
4
5
6
7
{
if(buf[0] == '#')
{
// Processa a mensagem
}
}
A lógica acima se aplica aos demais receptores e à transmissão para eles (cada caso com um ID diferente).
Passando aqui só para parabenizar seu post. Muitas pessoas buscam informações para fazer alguns tipos de projetos baseados em exemplos práticos e funcionais.
Eu não tenho muita paciência para fazer explicações ao meu filho, mas pelo seu bom e pratico conhecimento ele resolveu um probleminha. Grato!!!!
Caramba, que fantástico, Wanderson. Fico feliz em saber que pude ajudar seu filho. Obrigado pelos cumprimentos!
É possível calcular a distância entre o emissor e receptor?
Não sei dizer ao certo, Felipe. Dá uma olhada nesta discussão, que parece ser possível, mas bem limitado e complicado de alcançar um bom resultado. O pessoal comenta que o módulo tem um controle de ganho automático, o que atrapalha na mensuração da distância baseado na intensidade do sinal recebido. Uma possibilidade que pensei seria fazer o transmissor enviar a mesma informação duas vezes seguidas num intervalo de tempo conhecido. Com base no atraso do recebimento entre as duas informações, talvez dê pra tirar o valor da distância usando a fórmula: d = v*t -> v = velocidade da luz. Digo talvez, pois, em um local fechado, o sinal da onda eletromagnética vai apresentar reflexão (nas paredes e objetos), o que vai atrapalhar na mensuração do atraso e consequentemente da distância.