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

13 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

Leave a Comment.


eight − = 5