O módulo nRF24L01 é útil em aplicações de transmissão sem fio e ele poderoso e de baixo custo. Portanto, vamos aprender as características do nRF24L01 e como utilizá-lo com o Arduino por meio de bibliotecas.
Informações básicas
O que é
O nRF24L01 é um pequeno CI que pode transmitir e receber dados sem fio (RF). Atualmente já existem versões mais recentes do CI que possuem bem mais recursos. Entretanto, o modulo que utiliza o nRF24L01 é o mais fácil de encontrar pra comprar (e também o mais barato) até a data deste post (dezembro 2019). Veja como é o módulo na imagem abaixo.
O nRF24L01 na realidade é o pequeno CI no centro da imagem acima. Entretanto, o módulo que o utiliza também é comumente chamado de nRF24L01. Este módulo basicamente faz pequenas conexões do CI com alguns capacitores, resistores e com um cristal de 16MHz. Desta forma, sua ligação com um microcontrolador ou um Arduino fica simplificada.
Aplicações
As aplicações do nRF24L01 são, de modo geral, em sistemas que se comunicam sem fio. Exemplo de possíveis aplicações: chave eletrônica, sensores remotos, brinquedos, periféricos de PC (teclado, mouse) etc…
Eu cheguei a implementar ele em um projeto da faculdade para fazer um sistema de chave eletrônica (de um veículo). A ideia funcionou bem, o que ficou ruim foi o consumo de corrente relativamente alto dele. Portanto, conforme veremos nas características abaixo, ele não é recomendado em aplicações em que o consumo seja algo crítico (onde o consumo desejado esteja na faixa dos μA).
Características
As características podem ser lidas no datasheet do CI.
A seguir estão listadas algumas características do nRF24L01. Ele permite que você configure alguns aspectos da comunicação:
- Frequência de operação: 2.4GHz a 2.525GHz (não é configurável).
- 126 canais de comunicação.
- Tamanho máximo do pacote enviado/recebido: 32 bytes.
- Endereço do transmissor/receptor (espécie de ID único):
- Pode ter 3, 4 ou 5 bytes.
- Velocidade de comunicação:
- 1Mbps
- 2Mbps
- Potências do transmissor:
- 0 dBm
- -6 dBm
- -12 dBm
- -18 dBm
- Sensibilidade do receptor (não é configurável diretamente):
- -82 dBm (a 2Mbps)
- -85 dBm (a 1Mbps)
- Consumo de corrente na transmissão (não é configurável diretamente):
- 11.3 mA (com transmissor em 0 dBm)
- 7 mA (com transmissor em -18 dBm)
- Consumo de corrente na recepção (não é configurável diretamente):
- 12.3 mA (a 2 Mbps)
- 11.8 mA (a 1 Mbps)
- Alcance: cerca de 100m (algumas ressalvas serão feitas nos tópicos adiante).
Dentre as várias opções mostradas acima, cabe a você decidir qual utilizar dependendo da sua aplicação. Vou fazer alguns comentários para ajudar a entender estas escolhas. Então, não se preocupe se você não sabe o que dBm representa.
Se você ficar em dúvida sobre qual utilizar, recomendo trabalhar utilizando a menor quantidade de recursos possíveis: menor velocidade (1 Mbps) e menor potência (-18 dBm) para evitar gastos desnecessários.
Velocidade de comunicação
Esta opção não é difícil de entender do que se trata. Mas ainda assim cabe um comentário:
Se sua aplicação não necessita de altas taxas de transmissão/recepção, você pode utilizar 1Mbps para reduzir o consumo de corrente na recepção e melhorar a sensibilidade.
A sensibilidade indica o menor nível de potência que o receptor é capaz de reconhecer como sinal útil. Portanto, quanto menor, melhor. Por isto, a menor velocidade faz com que a sensibilidade melhore (reduz de -82 dBm para -85 dBm).
Alcance (Potência do transmissor)
O alcance da comunicação é bastante variável, pois depende dos parâmetros configurados e do ambiente em que você irá utilizar o nRF24L01.
Como a frequência de operação está na faixa dos GHz, obstáculos como paredes, móveis e pessoas podem ocasionar uma perda significativa do sinal enviado. É o mesmo problema do wi-fi (opera em frequência próxima).
Se você utilizar a máxima potência de transmissão (0 dBm) e a menor velocidade (1 Mbps), o alcance pode chegar a 100m em espaço aberto.
De todo modo, o alcance pode ser aumentado até cerca de 1Km em área aberta se você utilizar uma antena apropriada. O módulo da imagem que mostrei anteriormente já possui uma antena embutida na PCB (antena microstrip). Entretanto, é possível encontrar módulos em que existe um conector para antena como o da imagem abaixo:
126 canais de comunicação
O nRF24L01 é capaz de trabalhar em até 126 canais de frequências diferentes. Entretanto, ele só pode operar em uma frequência por vez. Com isto, você pode ter uma rede de dispositivos comunicando de forma cruzada sem um interferir com o outro (já que as frequências serão diferentes).
Além disto, o nRF24L01 possui uma estrutura interna contendo 6 “data pipes”. Cada “data pipe” pode ter um endereço (ID) de recebimento separado. Ou seja, um mesmo dispositivo pode ser configurado para ouvir outros 6 dispositivos (1 endereço em cada data pipe) considerando que estejam todos na mesma frequência.
Entretanto, não é possível definir mais de 1 endereço de transmissor por dispositivo. Portanto, a ideia desta estrutura, é que existam diversos dispositivos (6) que se comunicam com um dispositivo central que processa os dados de todos os 6.
Veja a estrutura dos “data pipes” abaixo:
Pinos
Para explicar os pinos, utilizaremos a imagem abaixo como referência.
O nRF24L01 se comunica via SPI. Sendo assim, é de se esperar que existam os 4 pinos padrões desta comunicação (MOSI, MISO, SCLK, SS ou CS). Além disto, existem outros pinos. Vejamos:
- MISO, MOSI, SCLK, CSN (chip select): são os pinos da comunicação SPI.
- Vcc e GND são os pinos de alimentação.
- A alimentação pode ir de 1.9 a 3.6V
- CE (chip enable): este pino tem a capacidade de habilitar/desabilitar o CI (funcionar normalmente ou entrar no modo de standby I).
- IRQ: pino de interrupção.
- Vai para nível baixo quando o CI termina de enviar um pacote ou quando ele recebe algum pacote.
Utilizar o nRF24L01 SEM BIBLIOTECA
O objetivo deste post é ensinar a utilizar o modulo com uma biblioteca. Mas se você desejar desenvolver sua própria biblioteca para utilizar em outra plataforma (outro microcontrolador), o processo não será difícil, e sim trabalhoso.
Conforme falei anteriormente, utilizei ele em um projeto da faculdade, e precisei desenvolver uma biblioteca, pois a plataforma da aplicação não era o Arduino. O meu primeiro passo foi desenvolver a biblioteca em C ainda na plataforma do Arduino para facilitar o desenvolvimento e os testes. Só depois deste passo que adequei o código para a outra plataforma.
Bem, se você desejar dar uma olhada no código, entre neste link. E, se você também precisa desenvolver uma biblioteca, precisará dar uma boa lida no datasheet do nRF24L01.
Utilizando o nRF24L01 com o Arduino
Biblioteca utilizada
A biblioteca utilizada foi a RF24: baixe ela aqui.
Circuito
Nos exemplos que mostrarei, não faremos uso do pino de interrupção. Portanto, ele ficará desconectado. Os demais pinos são ligados conforme a imagem abaixo:
O Vcc é ligado ao 3.3V do Arduino e o GND ao GND. Os pinos da comunicação SPI são ligados do pino 11 ao 13 do Arduino (MOSI,MISO e SCK respectivamente).
Por fim, os pinos CE e CSN podem ser ligados em qualquer pino digital do Arduino. Por padrão da biblioteca, resolvi ligá-los nos pinos 7 e 8 respectivamente.
Código do modulo como transmissor
Para utilizar o nRF24L01 com a biblioteca é bem simples. Veja o código abaixo e leia os comentários para entender.
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 26 | #include <SPI.h> #include <RF24.h> RF24 radio(7, 8); // Pinos CE e CSN const byte endereco[6] = "teste"; void setup() { // Inicializa a comunicação com o modulo radio.begin(); // Define o endereço do transmissor radio.openWritingPipe(endereco); // Prepara para o modo de envio radio.stopListening(); } void loop() { const char text[] = "Mundo projetado"; // Envia o texto "Mundo projetado" radio.write(&text, sizeof(text)); delay(1000); } |
Código do modulo como receptor
Veja o código abaixo e leia os comentários para entender.
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 26 27 28 29 30 31 32 33 | #include <SPI.h> #include <RF24.h> RF24 radio(7, 8); // Pinos CE e CSN const byte endereco[6] = "teste"; void setup() { // Inicia a comunicação serial Serial.begin(9600); // Inicia a comunicação com o modulo radio.begin(); // Define o endereço do receptor (data pipe 0) radio.openReadingPipe(0, endereco); // Entra no modo de recebimento radio.startListening(); } void loop() { // Verifica constantemente se recebeu algum pacote if (radio.available()) { char recebido[32] = ""; // Se sim, lê o conteudo na variável recebido radio.read(&recebido, sizeof(recebido)); // Imprime o que foi recebido Serial.println(recebido); } } |
Uma coisa interessante de se reparar aqui é que o comando para definir o endereço do receptor aceita dois parâmetros: o primeiro define qual “data pipe” será alterado (0 a 5) e o segundo é o endereço desejado:
radio.openReadingPipe(data_pipe, endereco);
Modulo como receptor e transmissor
Imagine agora o seguinte exemplo: dois dispositivos se comunicando. Um deles sempre avisa o outro quando um certo evento ocorreu e aguarda uma resposta.
O outro aguarda o aviso e envia uma resposta assim que recebê-lo. É um exemplo bem simples apenas para demonstrar como fazer o envio e recebimento em um mesmo dispositivo.
Enfim, veja o código para este exemplo do dispositivo que fica aguardando o “aviso” no tópico abaixo. O código do dispositivo que envia o “aviso” é similar, portanto não pretendo mostrar como seria.
Código do dispositivo recebendo e transmitindo
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include <SPI.h> #include <RF24.h> RF24 radio(7, 8); // Pinos CE e CSN const byte endereco[6] = "teste"; // Variável para saber se a mensagem recebida foi a esperada uint8_t msg_certa = 0; // Mensagem esperada por este dispositivo char msg_esperada[] = "AVISO"; void setup() { // Inicia a comunicação serial Serial.begin(9600); // Inicia a comunicação com o modulo radio.begin(); // Define o mesmo endereço para o transmissor e o receptor // O endereço tem que ser igual nos dois dispositivos radio.openWritingPipe(endereco); radio.openReadingPipe(0, endereco); // Entra no modo de recebimento radio.startListening(); } void loop() { // Verifica constantemente se recebeu algum pacote if (radio.available()) { char recebido[32] = ""; // Se sim, lê o conteudo na variável recebido radio.read(&recebido, sizeof(recebido)); // Verifica se a mensagem recebida é diferente da esperada msg_certa = 1; for (uint8_t i = 0; i < sizeof(msg_esperada); i++) { if (recebido[i] != msg_esperada[i]) { msg_certa = 0; } } // Se tiver recebido o "aviso" manda uma resposta if (msg_certa) { // Resposta deste dispositivo para o outro char msg_resposta[] = "RECEBIDO"; // Prepara para o modo de envio radio.stopListening(); // Envia a resposta radio.write(&msg_resposta, sizeof(msg_resposta)); // Entra no modo de recebimento novamente radio.startListening(); } // Imprime o que foi recebido Serial.println(recebido); } } |
Configurações do módulo nRF24L01
Para finalizar, pretendo mostrar como fazer algumas configurações no módulo utilizando a biblioteca.
Potência do transmissor
Para alterar a potência transmitida basta utilizar o seguinte comando:
radio.setPALevel(POTENCIA);
O parâmetro POTENCIA deve ser um dos seguintes:
- RF24_PA_MIN (-18 dBm).
- RF24_PA_LOW (-12 dBm).
- RF24_PA_HIGH (-6 dBm).
- RF24_PA_MAX (0 dBm).
Velocidade de comunicação
Para alterar a velocidade de comunicação é só usar o seguinte comando:
radio.setDataRate(VELOCIDADE);
O parâmetro VELOCIDADE deve ser um dos seguintes:
- RF24_1MBPS (1Mbps).
- RF24_2MBPS (2 Mbps).
Alterar canal de frequência
Para alterar o canal da frequência de comunicação, utilize o comando:
radio.setChannel(CANAL);
O parâmetro CANAL é um número de 0 a 125 que corresponde ao canal desejado.
Enfim, existem outras configurações possíveis, mas mostrei as principais dentro do que foi discutido. Se você deseja ir além, recomendo dar uma lida nesta página.
Excelente post, parabéns!
Muito obrigado, Márcio!
É possível conectar 3 módulos nRF24L01 em 1 mesmo Arduíno receptor, e três nRF24L01 em 3 sensores diferentes ?Como seria a ligação
É possível sim. A ligação dos pinos MOSI/MISO/SCK seria a mesma, ou seja, todos os dispositivos compartilhariam estes três pinos. Agora, para o caso do pino CSN, você precisaria conectar cada um em um pino diferente do Arduino. Por fim, se não me engano, o pino CE de todos pode simplesmente ficar ligado no 3.3V (ou 5V).
Mas qual seria a necessidade de utilizar 3 módulos nRF24L01? Como eu disse no post, o nRF24L01 possui 125 canais de comunicação (em frequências diferentes para evitar conflito), então utilizar 1 talvez já seja suficiente.
Excelente post, parabens, foi de grande ajuda.
Estou usando este módulo para trocar mensagens um com outro respondendo mensagens com status de alguns sensores, assim precisaria de uma msg de pelo menos 16 caracteres, vi que o módulo é capaz de transmitir 32 caracteres mas estou atingindo somente 6 caracteres. Não descobri o problema ainda.
Olá, Diego. Muito obrigado!
Bem estranho, talvez tenha relação com a forma como você está enviando os dados. Seria possível mandar o trecho do seu código onde você faz o envio da informação e o trecho onde você cria a variável que armazena os dados a serem transmitidos? Se sim, me mande por e-mail (mundoprojetado@gmail.com) que será mais fácil de te ajudar.
Segui o tutorial mas não consegui, não há comunicação. Já troquei o módulo, já revi as conexões, não sei o que fazer mais…
Olá, Felipe. Bem estranho seu problema. Dificilmente é um problema no código, pois testei ele antes de publicar e funcionou certinho. O mais provável seria a ligação mesmo, mas você disse que já conferiu ela várias vezes né. Recomendo você utilizar o comando abaixo (após o radio.begin()) para saber se pelo menos o Arduino está conseguindo se comunicar com o módulo. Ele printa 1 se a comunicação estiver ok.
Serial.println(radio.isChipConnected());
Se o código acima printar ‘1’ e mesmo assim um nRF24L01 não estiver comunicando com o outro, pode ser que exista alguma coisa obstruindo o sinal que um transmite para o outro. Se ele printar ‘0’, quer dizer que existe algum problema na ligação ou no módulo.
Olá, é possível a comunicação de um módulo (master) com dois módulos simultaneamente. Sendo que todos transmite e receba dados?
Se sim, teria algum exemplo de código?
Olá, Igor. É possível sim e a comunicação seguiria a ideia mencionada no tópico “125 canais de comunicação”. No caso, você pode seguir duas abordagens:
1 – Todos os dispositivos no mesmo canal de frequência e cada receptor teria um endereço diferente de forma a utilizar um “data pipe” diferente do transmissor para enviar a resposta.
Obs: Neste cenário, só é possível um dispositivo comunicando por vez para não haver interferência.
2 – Cada receptor fica em um canal de frequência diferente. Neste sentido, o transmissor teria que ficar trocando o canal para comunicar com um receptor em específico. E enquanto ele está em um canal, ele para de “ouvir” os dispositivos que estão nos outros canais.
Aqui e aqui tem exemplos aplicando o primeiro caso que comentei acima (não testei e não sei se funcionam de fato).
boa noite amigos ! excelente post muito obrigado !
porém estou com um pequeno problema , estou usando dois módulos NRF24L01, um sem antena e outro com antena, o módulo com antena eu não consigo usa-lo como transmissor apenas como receptor e o módulo sem antena consigo usa-lo apenas como transmissor e não consigo usa-lo como receptor. eu gostaria muito de inverter e usar o módulo com antena como transmissor ! se eu usar o módulo sem antena como transmissor funcionam muito bem ! alguém pode me ajudar?
Olá, Maycon. Obrigado!
Bem estranho, porque não faz sentido o módulo não receber comandos se ele consegue transmitir sem problemas (ou vice-versa). Talvez o problema esteja no módulo com antena, pois, se ele não consegue transmitir, o módulo ‘sem antena’ não vai receber nada de toda forma. Possivelmente o módulo ‘sem antena’ está funcionando por completo. Mas, se o problema é no módulo com antena, sugiro verificar a conexão da antena e também a ligação com o Arduino (as vezes a pinagem do módulo com antena é diferente). Enfim, difícil dizer ao certo o problema.
Muito Obrigado pelo retorno ! Acho que vou comprar outro módulo para ver o que acontece ! fico muito grato mesmo!
Sem problemas, Maycon! Beleza, boa sorte então.
Um shield do nrf24L01 para arduino Uno ou Nano ajuda a fazer testes e até mesmo construir uma aplicação final sem necessidade de ficar ligando fios. Este vídeo mostra o shield e faz parte de uma playlist completa sobre esse módulo https://youtu.be/rTjLmNeo_O0
Bem bacana o módulo, Leo.
POR FAVOR FAZ, UM POST ESPLICANDO COMO USAR, COMO FUNICONA O TRASNCEPTOR , POIS QUERO FAZER UM CODGO PARA OUTROS MICROCONTROLADORES , INFELISMENTE O QUE ACHO É APENAS CTRL C CTRL V PARA ARDUINO .
Olá, Gilsomario. Não sei se entendi o seu pedido. Você gostaria de entender como o nRF24L01 é controlado via SPI? Se este for o caso, recomendo ler o post sobre SPI aqui do site e dar uma olhada na biblioteca que fiz para o nRF24L01. A biblioteca foi feita para o Arduino, mas você consegue adaptar ela para outra plataforma sem muitos problemas. Em todo caso, é sempre bom ler o datasheet do nRF24L01 para entender como ele é comandado.
Olá Fábio.
Eu não entendi no seu código onde você endereça o canal, pois existem 125 canais certo?
E no código tem:
onst byte endereco[6] = “teste”;
Onde está o endereço que pode ser de 0 a 125?
Obrigado
Olá, Júlio. Acabei não definindo o canal no código, então ele usa o canal padrão (76). A variável “endereco” do código faz referência à “chave” que os nRF24L01 usam para identificar a mensagem corretamente. Para definir o canal, você precisa usar o comando radio.setChannel(canal). Segundo a documentação da biblioteca, você pode escolher entre os valores 0 a 125 (126 canais). Revisitando os materiais aqui, achei dois datasheets da mesma empresa com informações diferentes da quantidade de canais, um falando 125 e outro 126. Como a biblioteca aceita 126 canais, provavelmente existem 126 canais na verdade.
Olá boa tarde!
Como seria enviar um número float?
Olá, Ronaldo. O processo é um pouco chatinho, pois a função de envio trabalha com uma cadeia de bytes, o que facilita para o envio de uma mensagem de texto (cadeia de caracteres) e não para o resto. No caso do float, que ocupa 4 bytes, é necessário enviar os 4 bytes da variável (idealmente na ordem padrão) e, na recepção, você precisa transformar esses 4 bytes de volta para um float. Pelo que vi aqui na internet, parece que isso pode ser feito de forma simples e direta pelos comandos:
Envio:
float valor = 1.2345;
radio.write(&valor, sizeof(valor));
sizeof(valor) retorna quantos bytes a variável valor ocupa, que, neste caso, são 4.
Recepção:
float valor = 0;
radio.read(&valor, sizeof(valor));
Olá,
Excelente post,
Minha dúvida: Caso as portas 11, 12 e 13 estejam ocupados por um módulo de ponte H em um arduino Mega, eu posso conectar os respectivos pinos SPI em outras portas, tipo 38,40,42?
Olá, Ismael. Muito obrigado!
De acordo com essa imagem, no caso do Arduino Mega, você precisa usar os seguintes pinos para as seguintes correspondências:
Pino 50 -> MISO;
Pino 52 -> SCK;
Pino 53 -> SS;
Pino 51 -> MOSI.
Olá! Gostaria de saber se existe alguma variável que indique se a conexão está estabelecida ou não. Estou desenvolvendo um projeto que depende dessa informação. Caso a comunicação seja perdida o algoritmo deverá seguir outro caminha.
Olá, Joelton. Pelo que pesquisei, não existe. Até, porque, por padrão, a comunicação do nRF24L01 não é orientada a conexão, tipo o Bluetooth. Ou seja, o transmissor envia sem ter a garantia de que o receptor vai receber. O que você pode fazer é criar um fluxo de comunicação que transforma a comunicação em orientada a conexão. Simplificando: você pode fazer o transmissor enviar um pacote específico a cada x segundos. E o receptor pode identificar que a conexão foi perdida quando ele deixa de receber esse pacote específico por mais de x segundos.
Com esse projeto eu consigo acionar um relé para ligar uma bomba d’água e controlar o nível do reservatório através da bóia?
Consegue sim, David. Mas lembre-se de utilizar um transistor para acionar o relé, assim como mostrei neste post aqui.
Vemos o código que recebe dados e retorna X.
Mas como seria o código do dispositivo que envia os dados e depois fica aguardando retorno do segundo dispositivo?
Estou apanhando pra conseguir resolver isso, pois nunca flui de acordo com o que deveria.
Você pode usar o código do tópico “Código do dispositivo recebendo e transmitindo” como base. Nesse código, o dispositivo envia dados depois de receber algo. Para você fazer o contrário, é só colocar a parte do envio primeiro e depois entrar num loop que fica aguardando o recebimento. Algo como:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
// Envia mensagem
radio.write(&msg, sizeof(msg));
// Entra no modo de recebimento
radio.startListening();
while(msg_certa == 0)
{
...código de recebimento...
talvez seja necessário adicionar um timeout para impedir o código de ficar preso aqui em caso de não haver uma resposta
}
// Prepara para o modo de envio
radio.stopListening();
}
Se tiver dificuldade, veja se o chat GPT consegue te ajudar a desenvolver esse código.
Neste código que me encaminhou, não deveria haver um:
radio.read(&msg_certa, sizeof(msg_certa));
para que ele fique sempre recebendo tal valor?
Não exatamente, porque msg_certa é uma variável usada para saber se a mensagem correta foi recebida (idealmente do tipo booleana que recebe 0/false ou 1/true). O código que mandei é só um esqueleto do que deve ser feito e não tem o procedimento completo. Dentro do while você teria que adicionar o código apresentado no tópico “Código do modulo como receptor”, o que inclui o comando radio.read, mas com outra variável (vetor do tipo char).