RF24 (Again!)

I’m still playing with my nRF24L01+’s, I’ve now got the RPi running the receiver as a daemon and the Arduino transmitter in low-power mode. The 3.3v 8MHz ATmega328p-pu now takes a reading (well at the moment its still a dumb counter!) and transmits it, then turns off the radio and goes to sleep for 30 seconds.

This is why its important that the Pi is constantly listening (well, sleeps every 5secs to reduce CPU load) and only overwrites the file if it receives a good reading. The Pi appends the unix time to the reading, which can then be parsed with Python’s time.localtime() function.

This also means that setAutoAck() and setRetries() aren’t such an issue, as we’ll always have the last reading and the time it was taken if this reading fails, but at once every 30secs we have enough time to retry transmissions.

Arduino code:

#include <SPI.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include "RF24.h"
 
/*
radio 7 (miso) to uno d12 (pin 18) or mega d50
radio 6 (mosi) to uno d11 (pin 17) or mega d51
radio 5 (sck)  to uno d13 (pin 19) or mega d52
radio 4 (csn)  to uno d10 (pin 16) or mega d48
radio 3 (ce)   to uno d9  (pin 15) or mega d49
radio 2 (3.3v) to uno 3.3v
radio 1 (gnd)  to uno gnd
*/
 
// ce,csn pins
RF24 radio(9,10);
 
// init sleep
typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, 
    wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e;
void setup_watchdog(uint8_t prescalar);
void do_sleep(void);
const short sleep_cycles_per_transmission = 4;
volatile short sleep_cycles_remaining = sleep_cycles_per_transmission;
 
// init counter
unsigned long count = 0;
 
// configure the prr
void setup_watchdog(uint8_t prescalar)
{
    prescalar = min(9,prescalar);
    uint8_t wdtcsr = prescalar & 7;
    if (prescalar & 8)
    {
        wdtcsr |= _BV(WDP3);
    }
    MCUSR &= ~_BV(WDRF);
    WDTCSR = _BV(WDCE) | _BV(WDE);
    WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE);
}
 
// watchdog interrupt
ISR (WDT_vect)
{
  --sleep_cycles_remaining;
}
 
void do_sleep(void)
{
    // sets mcu sleep mode
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
    // sleep mcu
    sleep_enable();
    sleep_mode();
 
    // wake mcu and radio upon watchdog timeout
    sleep_disable();
    radio.powerUp();
}
 
void setup(void)
{
    // set sleep to 4x8=32secs
    setup_watchdog(wdt_8s);
 
    // init radio for writing
    radio.begin();
    radio.enableDynamicPayloads();
    radio.setAutoAck(1);
    radio.setRetries(15,15);
    radio.setDataRate(RF24_250KBPS);
    radio.setPALevel(RF24_PA_MAX);
    radio.setChannel(76);
    radio.openWritingPipe(0xF0F0F0F0E1LL);
}
 
void loop(void)
{
    // 32 bytes is maximum payload
    char txBuffer[32]= "";
 
    // pad numbers and convert to string  
    sprintf(txBuffer,"%lu",count);
 
    // increment counter
    count++;
 
    // transmit
    radio.write(&txBuffer, strlen(txBuffer));
 
    // sleep radio
    radio.powerDown();
 
    // sleep mcu
    while (sleep_cycles_remaining)
    {
        do_sleep();
    }
    sleep_cycles_remaining = sleep_cycles_per_transmission;
}

Raspberry Pi code:

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <time.h>
#include "../RF24.h"
 
using namespace std;
 
/*
radio 7 (miso) to gpio9/bcm21
radio 6 (mosi) to gpio10/bcm19
radio 5 (sck)  to gpio11/bcm23
radio 4 (csn)  to gpio8/bcm24
radio 3 (ce)   to gpio25/bcm22
radio 2 (3.3v) to 3.3v
radio 1 (gnd)  to gnd
*/
 
// spi device, spi speed, ce gpio pin
RF24 radio("/dev/spidev0.0",8000000,25);
 
void setup(void)
{
    // setup radio for listening
    radio.begin();
    radio.setAutoAck(1);
    radio.setRetries(15,15);
    radio.enableDynamicPayloads();
    radio.setDataRate(RF24_250KBPS);
    radio.setPALevel(RF24_PA_MAX);
    radio.setChannel(76);
    radio.openReadingPipe(1,0xF0F0F0F0E1LL);
    radio.startListening();
}
 
void loop(void)
{
    // clear char array
    char rxBuffer[32] = "";
 
    while (radio.available())
    {
        // read from radio
        int len = radio.getDynamicPayloadSize();
        radio.read(&rxBuffer, len);
 
        // get time - can be passed to time.localtime()
        time_t rawtime;
        time (&rawtime);
 
        // write to file and close
        ofstream myfile;
        myfile.open ("/var/tmp/rf24weather.txt");
        myfile << rxBuffer << ':' << rawtime << endl;
        myfile.close();
    }
 
    // pause to reduce cpu load
    sleep(5);
}
 
int main(int argc, char** argv)
{
    setup();
    while(1)
        loop();
 
    return 0;
}

3 thoughts on “RF24 (Again!)

  1. I just wanted to thank you for your three most recent posts regarding the RF24L01, Arduino, and RPi. I was bashing my head against the wall for hours trying to figure out what *I* was doing wrong with the other examples I’ve found… it turns out that the following quote (from you) could not be more true, “There’s a lot of incorrect documentation, and even incorrect comments in the libraries, so I thought I’d better note down what I’ve done.”

    As of right now, Oct 2013, your blog posts are the only source of information that has worked correctly.

    Again, thank you.

  2. thanks for the comment! i thought i’d share what i found so that more people don’t go through the hell of incorrect info. next i’ve got to write a python wrapper to tweet my weather info, and i might take a photo using my camera board!

  3. Indeed! Your code got me back on track with Project Ripcord …

    I now have a website (Python’s Flask + Tornado running on the RPi) to control my garage door (connected directly to the RPi), monitor the doors in my house (communicating to the RPi via Arduino Nanos & RF24L01+), and monitor soil moisture levels (via Arduino Nanos, RF24L01+, and a YwRobot sensor).

    It supports basic plugin functionality such that I can be alerted when one of the doors open via Prowl, or Tweet when the soil moisture sensor detects the plants need water.

    I hope to release the code in the coming week — again, thanks for your RF24L01+ examples!