AdSense

Friday 26 July 2013

Atmega/Arduino: nRF24L01+ basics

(Deutsche Version)

Today I will write another "guest post". This time, I'm going to present the wireless modules called nRF24L01+ (or nRF24L01P) by Nordic Semiconductor. These modules can be bought very cheap on ebay. The modules are "Ultra low power 2.4GHz RF Transceivers" - so the can send AND receive and they work in 2.4GHz band. According to Nordic Semiconductor, they are very energy efficient and consume only 15mA. The transfer rate is up to 2MBit/s. The modules are interfaced using the SPI bus.

That's enough of theory, further information can be found in the datasheet, which is going to be important later.

For the nRF24L01+ there are "ready to use" libraries for Arduino and Atmega. As described  in my last post I prefer to use the Arduino IDE. So I'm going to explain how to use the modules with an Arduino board or an Atmega8 with Arduino libraries.


As an easy example, I will use the ping programm included in the Arduino library. The Arduino Uno sends its "system time" to the Atmega, which sends it back immediately. Then the Uno calculates the ping from its current "system time" and the received time.


Hardware

At first, we have to wire everything. The modules have 8 pins: VCC and GND for voltage supply (IMPORTANT: the modules need a supply voltage of ~3V), MISO, MOSI and SCK for SPI communication, CSN (tells the module that there is going to be a new command), CE and IRQ. IRQ is an interrupt pin and can be active low on 3 events: data has been received, data was send and ending failed. These interrupts can be (de)activated as desired.


Most of the modules have te following pin mapping:
IRQ     8  7  MISO
MOSI  6  5  SCK
CSN    4  3  CE
VCC    2  1  GND

These pins have to be connected to the Arduino Uno or the Atmega:
MISO to pn 12 of the Arduino (pin 18 of the Atmega, pin 50 for the Arduino Mega)
MOSI to pin 11 (pin 17, 51)
SCK to pin 13 (pin 19, 52)
IRQ to pin 2 or 3 (pin 4 or 5, there are too many interrupts for the Arduino Mega, so I will not specify a certain pin)
CSN to pin 7 (pin 13, 7)
CE to in 8 (pin 14, 8)



As alreday mentioned, the supply voltage has to be 3V. The signal pins accept 5V.

Software Arduino

For this example I used the example included in the library.

The library can be found HERE.

The following code runs on the Arduino board:

#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>

void setup(){
  Serial.begin(9600);
  
  //if CE and CSN pins aren't connected to the standard pins
  
  //Mirf.cePin = 7;
  //Mirf.csnPin = 8;
  
  Mirf.spi = &MirfHardwareSpi;
  Mirf.init();
  
  //set receive address   
  Mirf.setRADDR((byte *)"clie1");
  
  //set payload length   
  Mirf.payload = sizeof(unsigned long);
  
  //switch to 250kbit/s (0x26) (2Mbit => 0x0f)
  Mirf.configRegister(RF_SETUP, 0x26);
 
  //change channel
  //Mirf.channel = 10;

  //write settings to configuration
  Mirf.config();
  
  Serial.println("Beginning ... "); 
}

void loop(){
  unsigned long time = millis();
  
  Mirf.setTADDR((byte *)"serv1");
  
  Mirf.send((byte *)&time);
  
  while(Mirf.isSending()){
  }
  Serial.println("Finished sending");
  delay(10);
  while(!Mirf.dataReady()){
    if ( ( millis() - time ) > 1000 ) {
      Serial.println("Timeout on response from server!");
      return;
    }
  }
  
  Mirf.getData((byte *) &time);
  
  Serial.print("Ping: ");
  Serial.println((millis() - time));
  
  delay(1000);
}  
 
In the setup() function, the modul is initialised using the init() command. Then the address of the module and the payload length are set. The payload length tells how many data bytes are send when transmitting. Up to 32 bytes are possible but you shouldn't choose more than necessary. To increase the range of the modules I have set the transmission rate from 2MBit/s to 250kBit/s. Therefor the registers of the modules have to be accessed. Thanks to the library this can be done with only one line of code. The command configRegister(RF_SETUP, 0x26) writes the value 0x26 to the register RF_SETUP and sets the transmission rate. The names of the registers and their functions can be found in the datasheet (page 57 and following).
At the end of the setup, the settings are applied using the config() command.

The loop() is easy and self-explaining. With the setTADDR command you can choose to which address the data is sent. The send() command send the desired data (here it's the system time). The while loop let's the Arduino do nothing until transmission is completed. With the next while loop and the condition !Mirf.dataReady, the µC waits until the wireless modul hast received some data. getData() reads this data from the module.

Software Atmega8

#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>

void setup(){

  Mirf.spi = &MirfHardwareSpi;
  Mirf.init();
  
  //set receive address    
  Mirf.setRADDR((byte *)"serv1");
  
  //set payload length   
  Mirf.payload = sizeof(unsigned long);
  
  //switch to 250kbit/s (0x26) (2Mbit => 0x0f)
  Mirf.configRegister(RF_SETUP, 0x26);
  
  ///change channel
  //Mirf.channel = 10;

  //write settings to configuration
  Mirf.config();
  
}

void loop(){

  //cache for received data
  byte data[Mirf.payload];
   
  if(!Mirf.isSending() && Mirf.dataReady()){
         
    Mirf.getData(data);
         
    Mirf.setTADDR((byte *)"clie1");
   
    Mirf.send(data);
    
  }
}

The setup() function is almost the same as in the Arduino code, so I'm not going to explain it again.


The loop() is very short. The programm is running in a loop and waits for the wireless modul to receive some data (Mirf.dataReady()). The data is written to a cache, the target address is set and the data is sent back. The !Mirf.isSending() condition prevents overlapping on sending.


Of course, the code on the Atmega is not very efficient because the controller doesn't do anything but waiting for new data. It would be much better to use interrupts. I'm going to explain the usage of interrupts in my next post.

No comments:

Post a Comment