Hardware
Neste meu experimento vou fazer a comunicação entre dois Arduinos Nano, cada um com uma tecla e um LED. Quando a tecla for apertada ou solta em um Arduino, ele enviará uma mensagem para o outro acender ou apagar o LED.
Nada de muito especial na montagem. O botão é ligado entre o pino digital 7 e terra. O LED é ligado ao terra e, via um resistor de 220R, ao pino digital 6. Para selecionar endereços diferentes, um dos Arduinos terá o pino digital 8 ligado a terra e o outro o pino digital 8 desconectado.
A placa com o nRF24L01+ é ligada da seguinte forma (neste teste simples não vou usar o sinal IRQ):
- GND (1)ao GND ou ISP 6
- Vcc (2) ao 3V
- CE (3) ao pino digital 9
- CSN (4) ao pino digital 10
- SCK (5) ao pino digital 13 ou ISP 3
- MOSI (6) ao pino digital 11 ou ISP 4
- MISO (7) ao pino digital 12 ou ISP 1
Software
O software completo pode ser baixado dos arquivos do blog (TestenRF24L01.zip) ou do github.
A comunicação com o nRF24L01+ é feita via SPI, uma transação é iniciada baixando o sinal CSN e finalizada retornando-o ao nível alto. O primeiro byte enviado é um comando, dependendo do comando bytes adicionais podem ser escritos ou lidos. É importante lembrar que a comunicação SPI ocorre sempre simultaneamente nos dois sentidos: a cada byte transmitido é recebido.
Vamos começar vendo duas rotinas simples, para enviar um comando e escrever e ler em um registrador. Existe um comando para leitura e outro para escrita, o número do registrador é colocado no próprio comando:
- // Envia comando ao nRF24L01+
- uint8_t send_command(uint8_t cmd)
- {
- uint8_t status;
- digitalWrite(pinCSN, LOW);
- status = SPI.transfer(cmd);
- digitalWrite(pinCSN, HIGH);
- return status;
- }
- // Escreve um valor em um registrador do nRF24L01+
- uint8_t write_register(uint8_t reg, uint8_t value)
- {
- uint8_t status;
- digitalWrite(pinCSN, LOW);
- status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) );
- SPI.transfer(value);
- digitalWrite(pinCSN, HIGH);
- return status;
- }
- // Le um registrador do nRF24L01+
- uint8_t read_register(uint8_t reg)
- {
- uint8_t result;
- digitalWrite(pinCSN, LOW);
- SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) );
- result = SPI.transfer(0xff);
- digitalWrite(pinCSN, HIGH);
- #ifdef TRACE
- Serial.print ("Reg ");
- Serial.print (reg, HEX);
- Serial.print (" = ");
- Serial.println (result, HEX);
- #endif
- return result;
- }
- // Escreve varios valores em um registrador do nRF24L01+
- uint8_t writeN_register(uint8_t reg, uint8_t *pValue, uint8_t n)
- {
- uint8_t status;
- digitalWrite(pinCSN, LOW);
- status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) );
- while (n--)
- SPI.transfer(*pValue++);
- digitalWrite(pinCSN, HIGH);
- return status;
- }
Para confundir um pouco mais, o pipe 0 é automaticamente usado pelo recurso de ACK automático do nRF24L01+ . Isto significa que o seu endereço será automaticamente alterado após uma transmissão.
No meu caso vou ficar no mais simples possível, recebendo apenas no pipe 1 e deixando o pipe 0 para a recepção do ACK automático.
Com estas explicações, podemos ver a rotina de iniciação do rádio. Conforme indicado nos cometários, adotei uma configuração simples e fixa para os parâmetros.
- // Iniciacao do Radio
- void radioInit()
- {
- uint8_t addr[3];
- Serial.println ("Inciando radio...");
- // Incia o SPI
- SPI.begin();
- // Inicia os sinais de controle
- pinMode (pinCE, OUTPUT);
- digitalWrite(pinCE, LOW);
- pinMode (pinCSN, OUTPUT);
- digitalWrite(pinCSN, HIGH);
- // Configura o radio
- delay(5); // para o caso do radio acabar de ser ligado
- write_register(CONFIG, 0b00001100); // CRC de 16 bits
- write_register(SETUP_RETR, 0x5F);// ate 15 retries, timeout = 1,5ms
- write_register(RF_SETUP, 0x06); // 1Mbps, Potencia maxima
- write_register(FEATURE,0 ); // trabalhar com pacotes de tamanho fixo
- write_register(DYNPD,0); // trabalhar com pacotes de tamanho fixo
- write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));
- write_register(RF_CH, 76); // usar canal 76
- send_command(FLUSH_RX); // limpa a recepcao
- send_command(FLUSH_TX); // limpa a transmissao
- write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); // modo Rx
- powerUp(); // liga o radio
- // Configura os enderecos
- addr[0] = addrTx;
- addr[1] = 0;
- addr[2] = 0;
- write_register(SETUP_AW, 1); // enderecos de 3 bytes
- writeN_register(TX_ADDR, addr, 3); // endereco de transmissao
- writeN_register(RX_ADDR_P0, addr, 3); // auto ACK
- addr[0] = addrRx;
- writeN_register(RX_ADDR_P1, addr,3); // endereco de recepcao
- write_register(EN_RXADDR,2); // recepcao habilitada no pipe 1
- // Pacotes com 1 byte de dado
- write_register(RX_PW_P1, 1);
- Serial.println ("Radio iniciado.");
- }
- // Liga o radio
- void powerUp(void)
- {
- uint8_t cfg = read_register(CONFIG);
- // Se estava desligado, liga e espera iniciar
- if (!(cfg & _BV(PWR_UP)))
- {
- write_register(CONFIG, cfg | _BV(PWR_UP));
- delay(5);
- }
- }
- // Inicia a recepcao
- void startRx(void)
- {
- write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX));
- write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
- digitalWrite(pinCE, HIGH);
- }
- // Testa se tem recepcao
- uint8_t temRx(void)
- {
- return read_register(NRF_STATUS) & _BV(RX_DR);
- }
- // Le o dado recebido e limpa flag de recebido
- uint8_t rxDado(void)
- {
- uint8_t dado;
- digitalWrite(pinCSN, LOW);
- SPI.transfer(R_RX_PAYLOAD);
- dado = SPI.transfer(0xFF);
- digitalWrite(pinCSN, HIGH);
- write_register(NRF_STATUS, _BV(RX_DR));
- return dado;
- }
- // Para a recepcao
- void stopRx(void)
- {
- // Desliga RX
- digitalWrite(pinCE, LOW);
- delayMicroseconds(65); // Tempo de recepcao
- delayMicroseconds(65); // Tempo de envio de ACK
- send_command(FLUSH_TX); // Limpa eventual ACK
- write_register(CONFIG, (read_register(CONFIG) ) & ~_BV(PRIM_RX));
- // Habilita recepcao no pipe 0 (para ACK)
- write_register(EN_RXADDR,read_register(EN_RXADDR) | 1);
- }
- // Envia um byte
- uint8_t txDado (uint8_t dado)
- {
- uint8_t status;
- // Coloca na fila o byte a transmitir
- digitalWrite(pinCSN, LOW);
- SPI.transfer(W_TX_PAYLOAD);
- SPI.transfer(dado);
- digitalWrite(pinCSN, HIGH);
- // Dispara a transmissao
- digitalWrite(pinCE, HIGH);
- // Espera concluir
- while ((read_register(NRF_STATUS) & (_BV(TX_DS) | _BV(MAX_RT))) == 0)
- ;
- // Desligar o transmissor
- digitalWrite(pinCE, LOW);
- // desliga recepcao no pipe 0
- write_register(EN_RXADDR,2);
- // Verifica o resultado
- status = write_register(NRF_STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));
- if( status & _BV(MAX_RT))
- {
- send_command(FLUSH_TX);
- return 0;
- }
- else
- {
- return 1;
- }
- }
- void setup()
- {
- // Serial para debug
- Serial.begin(9600);
- // Iniciacao dos pinos de LED, Botao e Selecao do endereco
- pinMode (pinLED, OUTPUT);
- pinMode (pinBotao, INPUT);
- digitalWrite (pinBotao, HIGH);
- pinMode (pinSelAddr, INPUT);
- digitalWrite (pinSelAddr, HIGH);
- // Seleciona os enderecos
- if (digitalRead(pinSelAddr) == LOW)
- {
- addrRx = 1;
- addrTx = 2;
- }
- else
- {
- addrRx = 2;
- addrTx = 1;
- }
- Serial.print("Recebendo no endereco ");
- Serial.println(addrRx);
- // Iniciacao do Radio
- radioInit();
- startRx();
- }
- void loop()
- {
- uint8_t botao;
- botao = digitalRead(pinBotao);
- if (botao != botaoAnt)
- {
- botaoAnt = botao;
- Serial.print ("Transmitindo: ");
- stopRx();
- if (txDado (botao))
- Serial.println ("Ok");
- else
- Serial.println ("Erro");
- startRx();
- }
- else if (temRx())
- {
- uint8_t dado = rxDado();
- Serial.print("Recebido ");
- Serial.println(dado);
- if (dado == LOW)
- digitalWrite(pinLED, HIGH);
- else
- digitalWrite(pinLED, LOW);
- }
- }
O que sobra (e pode ser visto no programa completo) são as definições das constantes.
No próximo post vamos ver esta mesma aplicação usando a biblioteca RF24.
05/04/16: Deixado mais claro que o pino de seleção de endereço é o digital 8 do Arduino.
7 comentários:
Boa noite, Daniel,
Sou novato em transmissão de dados. Usei um par do módulo RF 433Mhz para enviar e receber dados no mesmo arduino e ficou bom, porém a conexão depende de uma antena bem grande.
Agora estou tentando usar o NRF, mas é bem complicado para quem inicia. Gostaria de montar um circuito onde o mesmo Arduino pudesse transmitir e receber.
Além disso, gostaria de transmitir para outros Arduinos ao redor, como se eu montasse uma rede, por exemplo: aciono um botão no Arduino A e no Arduino B, C, D... liga um LED... não sei até quantas conexões dessa posso conseguir.
Poderia, por favor, me sinalizar de uma forma simples como fazer e se tem como?
Desde já muito obrigado e parabéns pelo seu trabalho e explicação. Vou tentar usar um pouco do que li aqui.
Abraços. Tom
Tom,
O alcance deste módulo que estou usando não é muito grande, para ter um alcance razoável é preciso um amplificador e uma antena externa, como este módulo aqui.
A montagem do hardware é simples, o que é complicado com o nRF24L01+ é o software. O jeito mais fácil de fazer o que você quer é usando a biblioteca RF24, veja os meus posts sobre ela.
boas;
estou tentado o seu exemplos ,mas sempre que pressiono o botão de pressão diz tramiter erro tanto num como no outro .
estou usando dois nano com chip ch304 e não estou usado os isp deles .
quanto ao pin 8 ligado o terra tem de se programar o modulo com pino ligado ao terra ou so se liga depois de programado ?
se poder me dar ai uma mãozinha agradecia
Luís, você deve montar um dos Arduinos com o pino 8 ligado ao terra e o outro com o pino 8 sem conexão. Desta forma eles ficarão com endereço diferente. O pino 8 é testado na iniciação (rotina setup()).
sim eu sei isso mas faço isso e carrego o programa com o pino ligado ou na transferência de dados não importa só quando se liga os 2 e que se liga o pino 8 (irq) ao gnd ?
Luís, é o pino 8 (Digital 8) do Arduino, não do rádio. O pino 8 precisa estar na posição correta no início da execução do programa, não faz diferença na hora da carga. Se você mudar a ligação do pino 8 depois de carregar o programa, dê um reset para o pino ser lido novamente.
já funciona xd.
realmente foi falta de atenção minha , não ter realmente reparado que era o pino 8 do arduino não do radio .
a única coisa fora de anormal e que um dos radio ao transmitir diz transmitir erro mas faz a transmissão .
O outro já não, transmiti e diz transmissão ok .
Postar um comentário