Manual

do

Maker

.

com

Módulo NRF24L01 com Arduino

Módulo NRF24L01 com Arduino

NRF24L01 com Arduino

Gosto de escrever sobre RF, acho que é uma das coisas mais interessantes fazer comunicação sem fio entre dispositivos embarcados. Nesse artigo vou mostrar o módulo NRF24L01 com Arduino. Trata-se de um módulo 2.4GHz com um alcance aproximado de 100 metros na superfície lunar sem interferências cósmicas. Isso significa que não é um módulo realmente de longo alcance, não é recomendado para drones, mas pode ser utilizado em robozinhos e carrinhos; abertura de portões handshake de proximidade para abertura de portas, troca de informações entre computadores sem precisar de internet nem rede TCP/IP etc. Esse módulo é bom e barato, sendo o mais indicado para projetos dentro da faixa de alcance.

O NRF24L01 é um transceiver, isto é, ele envia e recebe dados, portanto você precisa de apenas 1 módulo desse por ponto a comunicar. Uma coisa clara é que ele é extremamente superior a esse módulo (par emissor e receptor), bastante utilizado em abertura de portões (inclusive de empresas famosas). Com certeza tem propósitos diferentes do Talk² Whisper Node. Tudo vai da aplicação que você quer dar ao dispositivo. Por exemplo, se quer alcance de centenas de metros e a possibilidade de ampliar ainda mais o alcance utilizando antena externa, sua escolha certamente será o Whisper Node. Se tudo o que você quer é enviar comandos de um canto da sala para um dispositivo que aciona um relé para sua lâmpada, o módulo mais simples lhe serve perfeitamente; esse módulo é utilizado inclusive em portões de garagem, mas por ser muito barato, não por ser bom. Se você quer trocar informações entre dispositivos com mais qualidade e não se preocupando com a questão de alcance muito longo, o módulo NRF24L01 pode ser exatamente o que procura. Por fim, se você precisa de rede de longo alcance para fins industriais em substituição aos dispositivos Xbee (que custam uma verdadeira fábula), o Whisper Node é o que você procura.

Citei 4 dispositivos diferentes e como você pode imaginar, cada qual tem seu preço e aplicação.

Quanto ao alvo desse artigo, um datasheet pode ser encontrado nesse link (pdf)nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf.

Onde comprar

Você encontra em todo o lugar, talvez até no posto Ipiranga. Tem mais barato, mais caro, abusivo etc. Mas para esse artigo, sugiro fortemente a aquisição de um par desses na AutoCore Robótica, porque como vendedor percebeu que o dispositivo precisava de um destaque quanto a sua utilização, solicitou um artigo para tal, que fosse exclarecedor o suficiente. Comprando com o patrocinador você pode não estar economizando "1 real do camelô", mas está garantindo que novos materiais para suas futuras necessidades estejam disponíveis aqui no site.

Pinout

A pinagem da minha board é a mesma desse esquema (vista superior, olhando para os componentes sobre o circuito). Os pinos na board não estão em linha; o pino 1 é o que está o quadradinho. O pino 2 é o que esta ao lado do 1, ou seja, os números estão em pares direita/esquerda, como você verá um pouco mais abaixo com outro esquema dessa placa:

nrf24l02_pinout-300x136.webp

Wiring

Antes de fazer seu wiring, confirme se sua board é igual à board desse artigo. Conforme a biblioteca que utilizar, o wiring pode ocasionalmente ser diferente.

Esse módulo é tolerante a 5V nas entradas, mas sua alimentação é 3V3 e morre em 3.61 portanto, não erre. A pinagem é a seguinte (pino 1 é o pino do quadradinho silkado):

1GND2VCC
3CE4CS
5SCK6MOSI
7MISO8IRQ

nrf-ref-300x196.webp

 

No Arduino UNO o pinout padrão é o seguinte:

UNOv3.webp

 

Os pinos CE e CS você escolhe no código. O wiring entre o UNO e o nRF20L01 fica assim (kibe from google images):

uno-nrf.webp

O wiring é bastante simples e direto. Do lado da alimentação,o pino de IRQ não está conectado a nada mesmo. No sketch do exemplo que estou dispondo mais abaixo, os pinos 7 e 8 estão configurados para CE e CS, respectivamente. No pinout (mais acima) você encontra a relação, mas esse desenho do wiring é tudo o que você precisa para a coisa acontecer. Para deixar um pouco mais claro (caso não lhe interesse, pule para o próximo passo) a comunicação é feita pelo barramento SPI, onde você deverá utilizar os chamados "pinos de controle", que são o CS (Chip Select) e CE (Chip Enable). Os pŕoximos pinos do módulo são o SCK (Slave ClocK - slave é o módulo, Arduino é o master, portanto trata-se do clock do dispositivo NRF e deve ser conectado obrigatóriamente ao pino 13 do Arduino UNO pois é o pino do barramento SPI) e MOSI (Master Output - Slave Input - obrigatoriamente ligado à porta 11 do Arduino UNO). Finalizamos ligando o MISO (Master In Slave Out, obritgatóriamente ligado ao pino 12 do Arduino UNO).

Biblioteca

Existem diversas bibliotecas para esse módulo, dentre as quais, o RF24. Vou utilizar essa porque é recheada de exemplos nos sketches e não terei trabalho nenhum, assim posso ater-me às explicações.

Para instalá-la, a maneira mais simples é acessar o menu Sketch->Include Library->Manage Librariese procurar pela RF24, então instalá-la.

No menu File->Examples->RF24 você encontrará montes de exemplos, dos quais você pode simplesmente abrir, compilar, subir e desfrutar. Mas para um entendimento mais sólido de uma comunicação básica, vamos escrever o sketch manualmente (ou você pode copiar e colar, mas pelo menos leia e entenda o código). Abra a IDE do Arduino e faça o include da biblioteca, citando-a no header:

#include <RF24.h>

No início do sketch tem a definição dos pinos utilizados para acender e apagar os LEDs. Utilize os mesmos pinos em ambos os Arduino utilizados para o teste. Os LEDs estão com resistores de 330ohms. O define DEVICE é 0 para UNO e se for o Leonardo (ou um segundo UNO, ou outro) mude para 1. Só tem um if/else, portanto qualquer coisa diferente de 0 inverterá o número dos pipes (não se preocupe se não entendeu, leia o #if #else #endif dentro do código). Isso é muito importante; um pipe utilizado no programa do Arduino funciona de forma semelhante ao pipe no sistema operacional; entra de um lado, sai de outro. Quando você define um pipe de escrita como sendo 1, você deve definir o pipe de leitura como 1 do outro lado, porque é de onde você lerá a informação enviada. Para ficar mais claro, pense em um cano de água. A água que entra de um lado sairá pelo outro lado desse mesmo cano. Se você quer mandar água no sentido oposto, deve fazê-lo por meio de um outro cano. Esse segundo cano despejará a água do outro lado. É como uma rua de 2 vias; indo pela direita, vindo pela esquerda. Inspira, expira. Etc. Entendeu? Não? Bem, seja qual for sua resposta, o código estará extremanente mais claro.

Montando seu próprio código

Agora que está compreendido como a comunicação acontece graças ao exemplo, vamos tentar nosso próprio sketch. Acender o LED é a mesma coisa que acionar um relé, explodir uma bomba nuclear, disparar um alarme etc. Fazendo essa comunicação para acender 2 LEDs já lhe habilitará a fazer automação de portões, alarmes, luzes, solenóides etc. Disporei 2 sketches diferentes por uma razão; temos o SENDER e o RECEIVER. Como o processo será automático (isto é, sem interação humana), não quis deixar ambos enviando mensagens, por isso o SENDER atuará e fará também o envio do comando, enquanto o RECEIVER apenas executará as ordens advindas do SENDER. O sketch para o sender ficaria desse jeito (de modo que ele também está hábil a escutar, ainda que não o esteja fazendo nesse exemplo prático):

#include <RF24.h>

//definicao dos pinos. CE e CS podem ser selecionados 
//do modo que desejar, mas eh quase um legado utilizar
//pino 7 e 8
#define CE 7
#define CS 8

//definicao dos enderecos dos nos
#define NODE1 "UNO"
#define NODE2 "Leonardo"

//identificador do disposito; UNO (0) ou Leonardo (1)
#define DEVICE 0

//pinos dos LEDs
#define LED1 4
#define LED2 5
#define LED_TO_TURN_ON_OFF 3

byte addr[][6] = {NODE1,NODE2};

int COMM[2] = {0};

//criar um objeto a partir da classe RF24
RF24 radio(CE,CS);

/* um inteiro para alterar e mandar para o outro lado. regras
de comunicacao podem ser estabelecidas apenas atraves de
numeros, se desejar.
*/
int stat         = 0;
int receivedData = 0;
int count        = 0;
int var[]        = {0,0,0,1,1,0,1,1};

void setup(){
    //GND fica nos pinos e 5V nos LEDS (nao esquecer o resistor)
    digitalWrite(LED1,HIGH);
    digitalWrite(LED2,HIGH);
    
    //inicializa a instancia da classe RF24 (radio)
    radio.begin();
    #if DEVICE == 0
        radio.openWritingPipe(addr[0]);
        radio.openReadingPipe(1,addr[1]);//Le do Leonardo
    #else
        radio.openWritingPipe(addr[1]);
        radio.openReadPipe(1,addr[0]);  
    #endif

    //inicializa escuta
    radio.startListening();
}

void loop(){
  delay(1000);
  /*O dispositivo soh pode ler ou escrever. Se quiser escrever,
  deve-se primeiramente parar a escuta:
  */
  radio.stopListening();

  count = count > 7 ? 0 : count;
  
  /*entao pode-se escrever. Qualquer coisa que esteja sendo
  enviada pela outra ponta nesse momento sera perdido:
  */
  for (int i=0;i<2;i++){
      stat = radio.write(&var[count], sizeof(int)); //(valor,2 bytes)
      count++;
      delay(800);
  }
  if (!stat){
    //nada... mas tratar para o caso de erro na escrita
  }

  //agora podemos voltar a escutar
  radio.startListening();
  /*Toda a vez que ele passar pelo loop, verificara se
  tem dados disponiveis no radio. Se tiver, le para uma
  variavel*/
  if (radio.available()){
    //estamos lidando com int, entao facamos o mesmo na volta
    radio.read(&receivedData,sizeof(int));

    if (receivedData == 1 || receivedData == 2){
        /*Essa variavel COMM tem duas posicoes para o LED 1 e LED
        2. As posicoes na variavel sao 0 ou 1, mas na comunicacao
        se refere ao LED 1 ou LED2, entao basta subtrair 1 do que
        vier no pipe para acertar a posicao. Os simbolos "=!" sao
        utilizados para inverter o valor; se 0, vira 1, se 1, vir
        0.*/
        COMM[receivedData-1] =! COMM[receivedData-1];
        /*LED_TO_TURN_ON_OFF tem o valor 3. Somado a 1, ele sera
        assimilado  ao  pino 4, LED1. Somado a 2, a relacao sera 
        com o pino 5, LED2*/
        digitalWrite(LED_TO_TURN_ON_OFF+receivedData,COMM[receivedData-1]);
    }
  }
}


Agora precisamos do código para o dispositivo que irá receber, e esse código fica tão simples quanto o do emissor. Se desejar mudar os pinos CE e CS, não tem problema, mas mantenha o endereço do pipe igual, senão não haverá comunicação. Esse código do receptor deixei apenas para receber "mesmo", porque não consegui pensar em nada mais elaborado para fazer sem interação humana. Lembre-se, o código acima funcionará para comunicação bi-direcional, mas no momento vamos testar apenas a comunicação de um a outro.

NRF24L01 com Arduino Leonardo

leonardo.webp

 

Aí você pega um UNO e um Leonardo e faz o mesmo wiring; "pow", não funciona. Isso porque os pinos de SPI do Leonardo não são os mesmos do Arduino UNO, os pinos de SPI do Leonardo estão no header frontal, aqueles 6 pinos de ICSP. Mas vale reforçar - use o GND, mas não use o 5V de forma alguma porque esse dispositivo é 3v3! O wiring do Leonardo fica assim (kibe from google images):

leonardo-wiring-spi.webp

E o código fica assim:

#include <RF24.h>

//definicao dos pinos. CE e CS podem ser selecionados 
//do modo que desejar, mas eh quase um legado utilizar
//pino 7 e 8
#define CE 7
#define CS 8

//definicao dos enderecos dos nos
#define NODE1 "UNO"
#define NODE2 "Leonardo"

//identificador do disposito; UNO (0) ou Leonardo (1)
//Eh utilizado dentro do sketch pra escolher o comando a utilizar
#define DEVICE 1

//pinos dos LEDs
#define LED1 4
#define LED2 5
#define LED_TO_TURN_ON_OFF 3

byte addr[][6] = {NODE1,NODE2};

//criar um objeto a partir da classe RF24
RF24 radio(CE,CS);

/* um inteiro para alterar e mandar para o outro lado. regras
de comunicacao podem ser estabelecidas apenas atraves de
numeros, se desejar.
*/
int stat         = 0;
int receivedData = 0;
int count        = 0;
int var[]        = {0,0,0,1,1,0,1,1};

void setup(){
    //inicializa a instancia da classe RF24 (radio)
    radio.begin();
    #if DEVICE == 0
        radio.openWritingPipe(addr[0]);
        radio.openReadingPipe(1,addr[1]);//Le do Leonardo
    #else
        radio.openWritingPipe(addr[1]);
        radio.openReadingPipe(1,addr[0]);  
    #endif
}

void loop(){
  delay(1000);
  stat = radio.write(&var[count], sizeof(int)); //(valor,2 bytes)
  count = count > 7 ? 0 : count+1;
}


 

Como eu quis fazer um teste rápido porque estou atraso com os artigos, coloquei 2 LEDs mas tem um erro de lógica no código que só um está acendendo. Então eu vi que precisa de uma leve mudança, apenas no sketch receptor. Consegue fazê-lo?

O sketch básico para recepção ficou assim (no Arduino UNO):

#include <RF24.h>

//definicao dos pinos. CE e CS podem ser selecionados 
//do modo que desejar, mas eh quase um legado utilizar
//pino 7 e 8
#define CE 7
#define CS 8

//definicao dos enderecos dos nos
#define NODE1 "UNO"
#define NODE2 "Leonardo"

//identificador do disposito; UNO (0) ou Leonardo (1)
//Eh utilizado dentro do sketch pra escolher o comando a utilizar
#define DEVICE 0

//pinos dos LEDs
#define LED1 4
#define LED2 5
#define LED_TO_TURN_ON_OFF 6

byte addr[][6] = {NODE1,NODE2};

int COMM[2] = {0};

//criar um objeto a partir da classe RF24
RF24 radio(CE,CS);

/* um inteiro para alterar e mandar para o outro lado. regras
de comunicacao podem ser estabelecidas apenas atraves de
numeros, se desejar.
*/
int stat         = 0;
int receivedData = 0;
int count        = 0;
int var[]        = {0,0,0,1,1,0,1,1};

void setup(){
    pinMode(LED1,OUTPUT);
    pinMode(LED2,OUTPUT);
    //GND fica nos pinos e 5V nos LEDS (nao esquecer o resistor).
    //Iniciando em HIGH, eles permanecem apagados
    digitalWrite(LED1,HIGH);
    digitalWrite(LED2,HIGH);
    
    //inicializa a instancia da classe RF24 (radio)
    radio.begin();
    #if DEVICE == 0
        radio.openWritingPipe(addr[0]);
        radio.openReadingPipe(1,addr[1]);//Le do Leonardo
    #else
        radio.openWritingPipe(addr[1]);
        radio.openReadPipe(1,addr[0]);  
    #endif

    //inicializa escuta
    radio.startListening();

    //teste inicial dos LEDs
    digitalWrite(4,LOW);
    delay(1000);
    digitalWrite(5,LOW);
    delay(1000);
    digitalWrite(4,HIGH);
    digitalWrite(5,HIGH);
}

void loop(){
  delay(1000);
  radio.startListening();
  /*Toda a vez que ele passar pelo loop, verificara se
  tem dados disponiveis no radio. Se tiver, le para uma
  variavel*/
  if (radio.available()){
    //estamos lidando com int, entao facamos o mesmo na volta
    radio.read(&receivedData,sizeof(int));

    COMM[receivedData-1] =! COMM[receivedData-1];

    digitalWrite(LED_TO_TURN_ON_OFF-receivedData,COMM[receivedData-1]);
 }
}


Pensei em fazer um video, mas seria simplesmente do LED piscando. Use o código do Leonardo e do UNO e você terá imediatamente o LED piscando. Se quiser, pode fazer com 2 UNOs, 2 Leonardos ou qualquer outro, apenas veja onde estão os pinos SCK, MOSI e MISO na sua board para não errar no wiring.

  Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.