Communicating from low power Arduino to Raspberry Pi via NRF24L01

In this post I will describe how I send data and receive instruction from an Arduino to A Raspberry PI. As this way to communicate is a for home monitoring, I will take care of Arduino Sketch consumption.

Needed Hardware

300px-RaspberryPi

 A Raspberry Pi

 Websitehttp://www.raspberrypi.org/

 Price: less than 50$ (Amazon)

Mine is a B Model

21LtIG91FRL._AA160_

A Atmega 328P with bootloader

 Price: less than 5$ (Amazon)

jpeg-22 NRF24L01 modules

Price: less than $20 (Amazon)

 

Some other components:

 

The Raspberry Pi part

First of all we need to configure and prepare Raspberry Pi. We’ll need to activate the SPI on the Raspberry. So you will probably to upgrade the firmware:

Upgrading the firmaware
#$ sudo apt-get update
...
#$ sudo apt-get upgrade
...
#$ sudo apt-get install git
...
#$ sudo wget http://goo.gl/1BOfJ -O /usr/bin/rpi-update
...
#$ sudo chmod +x /usr/bin/rpi-update
...
#$ sudo rpi-update

Once the update is ok, reboot the Raspberry:

#$ sudo reboot

Sometimes, Raspberry is not able to backup the old firmware. In this case you can skip this step:

#$ sudo SKIP_BACKUP=1 rpi-update

Need more help:
http://www.brianhensley.net/2012/07/getting-spi-working-on-raspberry-pi.html
https://github.com/Hexxeh/rpi-update

Load PiDev to use SPI

You can also load the device drivers dynamically with:

#$ sudo modprobe spi_bcm2708
#$ sudo modprobe spidev

The kernel drivers in Raspbian provide access to the SPI0 peripheral by default. The Raspberry Pi GPIO provides 2 SPI cable select pins and the device driver works by providing a different file for each cable select. You therefore get two device files created for the SPI0 peripheral:

/dev/spidev0.0
/dev/spidev0.1

Need more help: http://raspberrypi.znix.com/hipidocs/topic_spidev.htm

Wiring Raspberry and RF Module

nrfRasp

 

photo

install lib and run exemple

#$ git clone https://github.com/stanleyseow/RF24.git
#$ cd RF24
#$ cd librf24-rpi/librf24/
#$ make
#$ sudo make install

At this point, I changed a little the configuration of the file rpi-lib.cpp in the exemple folder to match to my needs:

#$ cd exemple
#$ vi rpi-hub.cpp
void setup(void)
{
        //
        // Refer to RF24.h or nRF24L01 DS for settings
        radio.begin();
        radio.enableDynamicPayloads();
        radio.setAutoAck(1);
        radio.setRetries(15,15);
        radio.setDataRate(RF24_250KBPS);
        radio.setPALevel(RF24_PA_MAX);
        radio.setChannel(70);
        radio.setCRCLength(RF24_CRC_8);
#$ make
#$ sudo make install

Sources: https://github.com/stanleyseow/RF24

Need more help: http://blog.the-jedi.co.uk/2013/10/07/nrf24l01-and-raspberry-pi/

Run an exemple

#$ sudo ./rpi-hub

Now your raspberry displayed the configured NRF24l01 info and is waiting for receiving data

SPI device	 = /dev/spidev0.0
SPI speed	 = 8000000
CE GPIO	 = 25
STATUS		 = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1	 = 0x7365727631 0xf0f0f0f0e1
RX_ADDR_P2-5	 = 0xe2 0xe3 0xe4 0xe5
TX_ADDR		 = 0x7365727631
RX_PW_P0-6	 = 0x20 0x20 0x20 0x20 0x20 0x20
EN_AA		 = 0x3f
EN_RXADDR	 = 0x3f
RF_CH		 = 0x46
RF_SETUP	 = 0x27
CONFIG		 = 0x0b
DYNPD/FEATURE	 = 0x3f 0x04
Data Rate	 = 250KBPS
Model		 = nRF24L01+
CRC Length	 = 8 bits
PA Power	 = PA_MAX
 
Output below :

Ok, the module works fine on the Raspberry Pi

Arduino side

In order to consume less power as possible I used some tweaks I described in a previous post

So here is the sketch I did:

Capture d’écran 2014-06-09 à 18.54.35

  • NRF24l01is the socket to plug NRF24l01 chip
  • DHT11 the socket for sensor
  • Programming port is used to plug the GND / 3V / RX / TX / Reset from an arduino board (atmega removed) to program the Atmega on this sketch - See my post on Atmega in a standalone mode

The schema

Capture d’écran 2014-06-11 à 19.52.11

pinNRF

The built board

With the arduino board to program it:

photo 1_

In a standalone mode with batteries:

Code to load on the board

Here I use the watchdog to wake up the system each 15min to send humidity and temperature to the arduino. To test the sytem you can reset it via the switch and a measure is sent within 1 minute.

#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include <dht.h>
 
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
 
//DHTSENSOR
dht DHT;
#define DHT11PINDATA 3
#define DHT11PINVCC  2
float temperature;
float humidity;
 
// NRF24L01
// Set up nRF24L01 radio on SPI pin for CE, CSN
RF24 radio(9,10);
// Example below using pipe5 for writing
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0x7365727631LL };
 
char receivePayload[32];
uint8_t counter=0;
 
//count number of entering into main loop
uint8_t loopCounter=109;  
 
//WATCHDOG
volatile int f_wdt=1;
 
ISR(WDT_vect) {
  if(f_wdt == 0) {
    f_wdt=1;
  } else {
    //Serial.println("WDT Overrun!!!");
  }
}
 
void enterSleep(void) {
  radio.powerDown();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
  sleep_enable();
 
  /* Now enter sleep mode. */
  sleep_mode();
 
  /* The program will continue from here after the WDT timeout*/
  sleep_disable(); /* First thing to do is disable sleep. */
 
  /* Re-enable the peripherals. */
  power_all_enable();
}
 
 
void setup() {
 
  Serial.begin(9600);
 
  //WATCHDOG
  /* Clear the reset flag. */
  MCUSR &= ~(1<<WDRF);
  /* In order to change WDE or the prescaler, we need to
   * set WDCE (This will allow updates for 4 clock cycles).
   */
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  /* set new watchdog timeout prescaler value */
  WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */
  /* Enable the WD interrupt (note no reset). */
  WDTCSR |= _BV(WDIE);
 
  //CONFIGURE DHT11
  pinMode(DHT11PINVCC, OUTPUT);
  //make it blink at startup
  digitalWrite(DHT11PINVCC, HIGH);
  delay(500);
  digitalWrite(DHT11PINVCC, LOW);
 
  //CONFIGURE RADIO
  radio.begin();
  // Enable this seems to work better
  radio.enableDynamicPayloads();
  radio.setAutoAck(1);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.setChannel(70);
  radio.setRetries(15,15);
  radio.setCRCLength(RF24_CRC_8);
 
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1,pipes[1]);
 
}
 
void loop() {
  if(f_wdt == 1) {
    loopCounter ++;
    if (loopCounter > 6) {
      // 110 *8 = 880s = 14.6min @ 16mhtz
      readSensor();
      sendOverRadio();
      loopCounter = 0;
    }
    f_wdt = 0;
    enterSleep();
  } else {
    //nothing
  }
}
 
void sendOverRadio() {
  radio.powerUp();
  //prepare 
  uint8_t data1 = 0;
  char temp[5];
  bool timeout=0;
  uint16_t nodeID = pipes[0] & 0xff;
  char buffer[12];
  char outBuffer[32]="";
  unsigned long send_time, rtt = 0;
 
  //data1 is a counter
  data1 = counter++;
  if ( counter > 999 ) counter = 0;
 
  // Append the hex nodeID to the beginning of the payload
  sprintf(outBuffer,"%2X",nodeID);
  strcat(outBuffer,",");
  sprintf(temp,"%03d",data1);
  strcat(outBuffer,temp);
  strcat(outBuffer,",");
 
  //read sensor
  strcat(outBuffer,dtostrf(temperature, 4,2, buffer));
  strcat(outBuffer,",");
  strcat(outBuffer,dtostrf(humidity, 4,2, buffer));
 
  // Stop listening and write to radio
  radio.stopListening();
 
  // Send to hub
  if ( radio.write( outBuffer, strlen(outBuffer)) ) {
    printf("Send successful\n\r");
  } else {
    printf("Send failed\n\r");
  }
 
  //wait response
  radio.startListening();
  delay(20);
  while ( radio.available() && !timeout ) {
      uint8_t len = radio.getDynamicPayloadSize();
      radio.read( receivePayload, len);
 
      receivePayload[len] = 0;
      // Compare receive payload with outBuffer
      if ( ! strcmp(outBuffer, receivePayload) ) {
          rtt = millis() - send_time;
          //Serial.println("inBuffer --> rtt:");
          //Serial.println(rtt);
      }
 
      // Check for timeout and exit the while loop
      if ( millis() - send_time > radio.getMaxTimeout() ) {
          //Serial.println("Timeout!!!");
          timeout = 1;
      }
 
      delay(10);
  } // End while
}
 
void readSensor() {
  digitalWrite(DHT11PINVCC, HIGH);
  delay(200);
  int chk = DHT.read11(DHT11PINDATA);
  //Serial.print("Read sensor: ");
  switch (chk) {
    case DHTLIB_OK: 
      //Serial.println("OK"); 
      break;
    case DHTLIB_ERROR_CHECKSUM: 
      //Serial.println("Checksum error"); 
      break;
    case DHTLIB_ERROR_TIMEOUT: 
      //Serial.println("Time out error"); 
      break;
    default: 
      //Serial.println("Unknown error"); 
      break;
  }
  temperature = DHT.temperature;
  humidity = DHT.humidity;
 
  digitalWrite(DHT11PINVCC, LOW);
}

Back to the Raspberry Pi

Now we can check if values are received on the raspberry: It just works
Capture d’écran 2014-06-09 à 19.07.40

21 Comments

  1. Hi, it looks like the code for Arduino has some issues. Maybe a a wrong codeset when pasting the text to the webpage. Also the loop session is missing. Can you check it? Great idea, thanks for sharing!

    Reply
  2. hi
    there is a small typo above
    #$ cd exemple
    #$ vi rpi-lib.cpp <—– rpi-hub.cpp
    after following your instructions I get an error when running rpi-hub: can't open device: no such file or directory
    I'm new in raspian. have you any suggestion?
    thanks, urs

    Reply
  3. hi vincent (very uncool – I lost the whole comment in case of a faulty captcha)
    The arduino part runs well too, but I’ve too many sensors attached (DHT22, DS18B20, UV, TSL2561, BMP085, MQ135; missing windspeed & -direction, rain)
    So have you any idea for a good procedure handling multiple buffers on both sides?
    —————————–
    the other “problem” is, that after each boot the spidev are not installed. how to do that automatically?
    you see, I’m no unix guru ;)

    Reply
    • hi
      the “problem” has been solved by reading ;) some pages
      ———————————–
      now – how to write the data into a (mysql)database for further processing???

      Reply
  4. seems that librf24-rpi is no more present in package. Can you check it pls. BTW. DHT pin definition are wrong with respect to diagram. Should be:
    #define DHT11PINDATA 3
    #define DHT11PINVCC 2
    Anyway tnhx for the nice project.

    Reply
  5. Hi all,

    I have got my arduino and raspberry setup and it seems that they are both working fine individually.

    But, when it comes to communication, they don’t talk much.

    I had an arduino talking to other arduino just fine. But, not to rpi.

    The status prints out some useful information.

    ================ SPI Configuration ================
    CSN Pin = CE0 (PI Hardware Driven)
    CE Pin = Custom GPIO22
    Clock Speed = 8 Mhz
    ================ NRF Configuration ================
    STATUS = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
    RX_ADDR_P0-1 = 0xf0f0f0f0d2 0xf0f0f0f0e1
    RX_ADDR_P2-5 = 0xe2 0xe3 0xf1 0xf2
    TX_ADDR = 0xf0f0f0f0d2
    RX_PW_P0-6 = 0×20 0×20 0×20 0×20 0×20 0×20
    EN_AA = 0x3f
    EN_RXADDR = 0x3f
    RF_CH = 0x5a
    RF_SETUP = 0×27
    CONFIG = 0x0f
    DYNPD/FEATURE = 0x3f 0×04
    Data Rate = 250KBPS
    Model = nRF24L01+
    CRC Length = 16 bits
    PA Power = PA_MAX

    This is from RPi, and similarly I get the same printouts… does that means, the Pi was actually setup
    correctly?

    is there any other idea what I need to do to at least find out which one need tweaking….
    Thanks guys

    Reply
  6. Hi dkosasih,

    Yes it seems right configured for me.
    Just a question:
    RX_ADDR_P0-1 = 0xf0f0f0f0d2 0xf0f0f0f0e1
    Are you using this pipes with the arduino?

    Best

    Reply
    • yes I do.
      I even flip them around to test if I got the configuration wrong.

      Do you think I should add a cap on the rpi as well? Last time I added 22uf cap on two Arduinos and it works.

      Reply
      • I dkosasih

        You can try. It is a quick try, who knows!
        Did you try to change the channel? Sometimes noise (due to wifi, bluetooth, etc..) make the communication not really reliable.

        Reply
        • Hi Vincent,

          Thank you for your help.

          I have figured it out finally.
          The newer fork for this library for RPi was (I think) Hardcoded to GPIO22 and CE0.

          The debug trace above for CSN and CE was never change regardless what you pass in to the constructor. Then I decided to change the cabling to the debug trace says and voila!!…

          I hope this can hope anyone who are using newer library too!

          Reply
    • Hi,

      Thanks for your comment,
      I didn’t compute exactly how long the battery should last. But from my experience it should last for months.
      This device is running in my house since August.

      Hope this helps, If I’m able to found out some spare time, I will try to compute exactly this duration.

      Best,

      Reply

Leave a Comment.


× 6 = twelve