#driver #transceiver #wireless #radio #nrf #userspace

nrf24l01

A pure Rust user space driver for NRF24L01(+) transceivers on Linux

2 unstable releases

Uses old Rust 2015

0.2.0 Sep 12, 2017
0.1.0 Sep 3, 2017

#1358 in Embedded development

38 downloads per month

MIT/Apache

36KB
465 lines

rust-nrf24l01

Version

A pure Rust user space driver for NRF24L01(+) transceivers on Linux.

The aim of this driver is to provide a rustic, easy to use, no non-sense API to drive an NRF24L01(+) transceiver.

This is not a port from another language, this driver has been written from scratch based on the device specs.

For the moment, the driver only exposes an API for the most reliable communication scheme offered by NRF24L01 chips, that is Enhanced Shockburst ™: automatic (hardware) packet acknowlegement with optional payload, dynamic payload length and long CRC (2 bytes).

The code has been tested on a Raspberry Pi with success. It should work on any platform supported by rust-spidev and rust-sysfs-gpio.

Usage

Add a dependency to nrf24l01 to your Cargo.toml:

[dependencies]
nrf24l01 = "0.2.0"

Examples

Simple emitter

extern crate nrf24l01;

use std::time::Duration;
use std::thread::sleep;

use nrf24l01::{TXConfig, NRF24L01, PALevel, OperatingMode};

fn main() {
    let config = TXConfig {
        channel: 108,
        pa_level: PALevel::Low,
        pipe0_address: *b"abcde",
        max_retries: 3,
        retry_delay: 2,
        ..Default::default()
    };

    let mut device = NRF24L01::new(25, 0).unwrap();
    let message = b"sendtest";
    device.configure(&OperatingMode::TX(config)).unwrap();
    device.flush_output().unwrap();

    loop {
        device.push(0, message).unwrap();
        match device.send() {
            Ok(retries) => println!("Message sent, {} retries needed", retries),
            Err(err) => {
                println!("Destination unreachable: {:?}", err);
                device.flush_output().unwrap()
            }
        };
        sleep(Duration::from_millis(5000));
    }
}

Simple receiver listening to the simple emitter

extern crate nrf24l01;

use std::time::Duration;
use std::thread::sleep;

use nrf24l01::{RXConfig, NRF24L01, PALevel, OperatingMode};

fn main() {
    let config = RXConfig {
        channel: 108,
        pa_level: PALevel::Low,
        pipe0_address: *b"abcde",
        ..Default::default()
    };

    let mut device = NRF24L01::new(25, 0).unwrap();
    device.configure(&OperatingMode::RX(config)).unwrap();

    device.listen().unwrap();

    loop {
        sleep(Duration::from_millis(500));
        if device.data_available().unwrap() {
            device
                .read_all(|packet| {
                    println!("Received {:?} bytes", packet.len());
                    println!("Payload {:?}", packet);
                })
                .unwrap();
        }
    }
}

More examples

Cross-compilation

The rust-cross guide has detailled and comprehensive instructions for cross compiling.

Once you are set up for say, ARM, you can cross-compile the examples for a Raspberry Pi as easily as:

cargo build -v --examples --target=arm-unknown-linux-gnueabihf

Then, you can move any of the executables to your test machine:

scp target/arm-unknown-linux-gnueabihf/debug/examples/multiceiver_ack ...

Performance

For SPI communication with the NRF24L01(+), we use the Linux standard SPIDEV kernel driver through the rust-spidev library with excellent efficiency.

For driving the device CE pin, we use GPIO. The current standard Linux way is by using the sysfs-gpio kernel driver. For that we use rust-sysfs-gpio.

Unfortunatly, sysfs-gpio is slow, and rust-sysfs-gpio slower still. On a Raspberry A+ for example, that incurs a ~300 µs lag for each send or read operation (for comparison, that's roughly the time needed by the device to send 32 bytes and receive acknowledgment).

If you need really fast operation and you use a Raspberry Pi, you can activate the rpi_accel feature. That feature uses rppal for direct access to the GPIO registers exposed on Raspberry Pi in /dev/mem or /dev/gpiomem. Thus the lag becomes negligible: only ~130 ns on a Rasberry Pi A+.

To enable the rpi_accel feature for your crate (and disabling the defaults), replace the dependency on this library in your Cargo.toml by:

[dependencies.nrf24l01]
version = "0.2.0"
features = ["rpi_accel"]
default-features = false

If you know a library for such a fast access to GPIO on the Beaglebone, please let me know!

Future

In the future, I'd like to provide :

  • asynchronous operation, with mio;
  • a stream based API for fast, "real time", data transmission at the cost of possible packet loss.

I'm still quite new to Rust, so the code may be suboptimal. Feel free to submit pull requests to improve it!

Dependencies

~2MB
~39K SLoC