RF24Network Arduino/RPi Library - A New Development

What is RF24Network?

It is a library built upon the core RF24 driver for nrf24l01 radio modules that attempts to provide a simple interface for connecting anywhere from a few, to hundreds of wireless radio modules, connected to Arduino or Raspberry Pi devices. Uses don't need to know every detail of the radio modules in order to setup and operate a network of them. The library handles configuration, addressing, interconnections, and routing, so users can focus on creating household or long distance radio sensor networks, with data being routed automatically through the network to its intended destination.

Whats New?

I've been doing quite a bit of work on the core RF24 radio driver, including a wide range of changes and updates, and wanted to test out some of the more interesting features of the modules on a large scale. The existing network layer (originally by ManiacBug) provided a great foundation to begin, as it contained all of the required features of an RF24 radio network, and used a system of routing and addressing that is well suited to small devices like Arduino or ATTiny.
  
Some of the new features may seem unnecessary or overkill, and some will probably be, but others will support scenarios just not possible in the previous configuration. I've created a development repository and associated documentation that will be kept up roughly in accordance with development, since it will all take time to test fully, but it seems to work well and some of the features are pretty interesting. 

Updated 10/14:
Fragmentation support was recently added for the RPi and Arduino Due. Standard AVR devices (Arduino Uno etc) are able to send large, fragmented payloads, but cannot yet reassemble them. See below to enable. Usage is exactly the same, but any very long payload lengths will be accepted.

 Routing changes have been partially reverted to use a limited number of retries (auto-ack) where possible, and function similarly to the master branch, although network ACKs are still utilized.

Some defaults have been changed:
  • Multicasting is enabled by default. This limits the number of normal child nodes/parent to 4.
  • Fragmentation is totally disabled by default on AVR devices
  • Uncomment defines in RF24Network_config.h to disable/enable these options

-end upate-


Addressing Formats:

The development version currently uses a different addressing format, using specific addressing chosen for use in radio transmission. See the datasheet and this blog post for more information. Users simply specify an octal address (01-05) as before for example, and the radio addresses will be configured accordingly.

Routing and Acknowledgement

Previously, payloads that were routed over one or more nodes were not acknowledged, so the transmitting radio had no way of knowing if payloads were delivered. *New: In the development version, the same or less traffic is used to provide network-wide acknowledgement of payloads, so individual nodes are aware if data transmission is successful. users have the ability to specify whether or not the network will respond with an ACK, simply by choosing from a range of message types. Typical user message types of  65 through 127 will be acknowledged, and message types 1 through 64 will not be acknowledged. There are a few benefits to this arrangement, since network acks can automatically help to prevent nodes from flooding the network with traffic, and no-acks can be used when a response is desired from the recipient etc.

Direct Routing and Failovers

Network frames can now be logically addressed to one node, and sent to any other node for routing. This allows some dynamic topology, and can also be used to reduce routed traffic on a network. See the documentation links below for specifics and limitations.

Multicasting

A multicasting feature has also been added that allows, for example, the master node to send out a single network payload, and have it delivered to hundreds of network nodes very rapidly. Multicast relay nodes can be used to disperse messages throughout all or partial areas of the network. Multicasting is enabled by un-commenting #define RF24NetworkMulticast in the RF24Network_config.h file, since it requires additional program space and memory.

Improved Performance

Performance on the network will be similar, but users will see a much higher error rate using the development version, as it gives a real indication of success or failure of payloads. Data throughput should be increased in most cases, due to reduced retry periods, and the new routing protocol.


Additional Technical Details and Information:

Documentation for the development version can be found here, and should be kept pretty consistent with changes. It contains information specific to the new routing and delivery protocols, as well as updated class documentation for usage. 
The development repo is found here, and it can be downloaded here. Features and functions found to be stable and beneficial are likely to be merged into the master branch eventually. The development branch should be relatively stable at any given time, but is in no way guaranteed to be.





Arduino AVR Timers and Waveform Generation

I've had a number of questions regarding Arduino timers and the generation of signals and waveforms, so here is (hopefully) a simple tutorial on using Arduino timers.

This tutorial requires a basic understanding of the difference between bits and bytes, along what it means to read or write bits and bytes to AVR registers.

Overview:
Timers can be seen as additional components used to offload work from the processor. A timer running on its own can create a signal or waveform without any interaction from the CPU, allowing the CPU to do things like interact with the timer to modify the waveform or handle other tasks.

The most basic function of a timer is to count from 0 to a specified number, lets say 255, and then start all over again and count from 0 to 255 again. Using a standard Arduino board running at 16 million cycles/second, the timer will count from 0 to 255 at a set frequency, which can be determined using a simple calculation:

CPU Freq / TIMER TOP  =   Timer Frequency (cycles/second)
16mHz     /         255          =   62,745 cycles/second  ( 62.75kHz )

The UNO has both 8-bit timers (TIMER0, TIMER2) and a 16-bit timer (TIMER1) which can all be used to perform various functions. This means that timer0 and timer2 can handle or count in increments ranging from 0 to 255, and TIMER1 will handle values from 0 to 65,535.


Timer Parameters & How to generate a signal:

Timer TOP:
Basic timer functionality is simple, we set a TOP value, and the timer will count from 0 to TOP, then start counting at 0 again.

Timer COMPARE:
Timers can also be set to toggle an output or trigger an interrupt when the timer value reaches a set value. Lets say the TOP value has been set to 255, the compare value is 127, and an LED is connected to pin 9 on an Arduino UNO. The following will take place:

a: The timer will increment its value by 1 every 16-millionth of a second, starting at 0
b: The LED stays lit until the counter value reaches 127, then is turned off.
c: The LED turns back on once the counter value is reset to 0 (after reaching its defined TOP value)
d: GoTo b:

Result: This produces an approximately 50% duty cycle PWM signal at 62.75kHz

Example Code:

void setup(){

    pinMode(9,OUTPUT);
 
   //This sets the timer1 mode, prescaler and enables timer PWM output on Uno pin 9 (not covered in this tutorial)
    TCCR1A = _BV(WGM11) | _BV(COM1A1);
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);

    //This sets the TOP value, or the value that the timer will count up to before resetting to 0
    ICR1 = 255;
 
   // This sets the OUTPUT COMPARE value A ( Compare A for pin 9, Compare B is linked to pin 10)
   OCR1A = 127;

}

Changing the Duty Cycle (how long the pin output is HIGH vs LOW):

Changing the duty cycle is as simple as changing the value of the OCR1A register. Since these examples use timer1, 16-bit (0-65535) values could be used, but since the TOP value is set at 255, only those values can be used for this demonstration.

Example Code (Uses the above setup() code) :

void loop(){
    OCR1A = OCR1A + 1;
    delay(15);
    if(OCR1A == 255){
         OCR1A = 0;
    }
}

Result: The example above will increment the duty cycle by 1/255 (0.4%) every 15 milliseconds until it has reached 100%, then it will reset to 0 and continue to increment. A connected LED will be seen to get brighter and brighter until the timer compare register reaches the TOP value, and the duty cycle (OCR1A) will be reset to 0.

Changing the Frequency:

To change the frequency, simply adjust the timer TOP value in the above example code:
ICR1 = 30000;

Calculation:  16,000,000 / 30000 = 533.33... HZ (cycles/second)

Note: The compare register (OCR1A) can be set to any value between 0 and 30,000 in this example.

Prescaling:

The frequency can be made lower by adjusting the prescaler. The system clock signal will be divided by the prescaler before being made available to the timer. This is not required to be understood for this tutorial.

Calculation: CPU Freq. / Pre-Scaler / TIMER TOP = frequency


Basic Theory of Creating Audio:

One of the simplest and most common forms of uncompressed audio is the WAV or PCM format. PCM stands for Pulse-Code-Modulation, which can be replicated using the Arduino timers, and the information provided above.

In the case of recording 8-bit audio, the voltage of an audio signal is converted to a number ranging from 0 to 255. The higher the voltage, the higher the number. This is done at a set frequency, say 16kHz or 16,000 times a second.

Recreating an audio signal can get fairly complicated and in-depth, but basically just needs to meet the following requirements:

1. Frequency of the timer generally should equal the frequency that the audio was sampled at (sample rate)
2. Sample values need to be fed into the timer every cycle

To create audio of 16khz sample rate, the timer needs to run at 16,000 cycles/second, and a new sample value needs to be fed into the timer every time it reaches its TOP value (every 62.5 microseconds) This generally requires the use of interrupts, which is out of the scope of this tutorial.

The above example code can be used to create simple audio tones by varying the frequency from 0 to about 10,000 hz max, and volume controlled by adjusting the duty cycle.

Summary:

This overview is intended to cover very basic timer usage in Fast PWM mode via TIMER1. Additional timers, modes, details, usage, etc can be determined by referencing the datasheet, which is really the best reference for any kind of advanced timer usage. All of the timers are accessed in very similar ways, but some settings like prescaling are different between timers.








RF24Audio Library Released:
 Wireless Digital Audio Streaming and more with Arduino and NRF24L01 modules


  Its been a little while in the making, but I am finally releasing my RF24Audio library. The library offers a number of features, mainly the ability to wirelessly stream realtime data or audio captured from an analog input to one or more other devices. 'Other devices' can include Arduinos, PCs, or any other device capable of reading the data stream.
  Since the library utilizes the core RF24 radio library, advanced usage and customization can be configured, and allows interaction with audio devices in a number of different ways. The TMRpcm audio library (WAV audio from SD card) has been updated to support the RF24Audio library as well, being capable of multicasting audio to a number of devices. A demonstration of this interaction is shown in the following video:



  The library is designed to be very easy to setup and configure, just connect a radio module, microphone and speaker. External buttons are optional, and are managed internally by the library. This can be disabled to allow further customization via keypads, etc. Users can also utilize the core RF24 library to interact with the audio devices in a number of ways, including capturing audio streams and feeding the data to a PC.

Features:
  • User friendly setup and configuration: For beginners too: Just connect a radio module, microphone, and speaker. The library handles the rest.
  • Recording and broadcasting of audio to multiple devices using only Arduino, RF24 modules and input/output devices (speaker,microphone)
  • Multicast: Enables broadcasting to all nodes, single nodes, or partial groups of nodes
  • External controls: Use external buttons or code to control audio streaming
  • Volume control: Use external buttons or code to control audio volume on receiving devices.
  • Remote control: Start recording remotely via radio commands (Currently cannot stop remote recording)
  • LED Indicator/Visualization: Indicates audio playback and amplitude.
  • Customization: Using the underlying RF24 core library allows custom interaction with audio devices running this library. Receive audio data and stream it to a PC over USB, create and broadcast computer generated audio in realtime, and more! See the USB_Audio example for more info.
  • Create additional node groups: Allows nodes to join private broadcast groups, and multicast only within their group as desired

Pin Assignments:
  • Speakers: Arduino Uno,Nano,etc: pins 9,10 Arduino Mega: 11,12 (Timer pins cannot be changed, but can use 1 pin and ground)
  • pin A0: Microphone/Input pin
  • pin A1: Transmission/Recording Start pin
  • pin A2: Volume Up
  • pin A3: Volume Down
  • pin A4: Trigger remote recording (Only working with dual devices)
  • Cannot be changed: LED Pin: Uno,Nano,etc: pin 6 Mega 2560: pin 13 (main LED pin)

Links:
Library Documentation
Download

Source Code:
RF24Audio Library
RF24 Radio Library
TMRpcm Audio Library



Testing New RF24 Arduino Library Fork for NRF24L01 Radio Modules: Comparative Testing - Transfer Rates

Overview:

Per my previous blog posts, I have been working on some improvements to the RF24 radio library, and the main benefits of the changes have been realized. The following video compares the difference against the fork found at https://github.com/gcopeland/rf24. I did not compare against the main library since the gcopeland fork outperforms it quite a bit already.




The new library, source code and documentation can be found at https://github.com/TMRh20/RF24. Info in previous blog posts.

More to come...

Arduino Radio/Intercom/Wireless Audio:
Streaming Audio in Real Time using Arduino and NRF24L01



  In my last blog post, I talked about using inexpensive radio modules to transmit audio at reasonable rates using only the radio modules, a microphone or input device, and an Arduino. This started out as what I thought would be a simple project, and ended up being a WAY bigger project than I thought, since it required improving the speed of the extremely well written RF24 library for the radio modules. Although there wasn't a lot of room for improvement, from my (biased) testing, some cases show performance increases up to 25% in some situations over the fastest fork I could identify.

  I think this streaming audio code would be good in library form, but for now, the sketches I am using to develop the library are posted on GitHub Here the whole sketch folder is required. The audio is only 8-bit right now, but even that is not bad. Judging by the transfer speeds, I should be able to do 10-bit audio, but the code in 8bit was just simpler to start with. To put the quality in perspective, telephone quality could be about 8khz, 12bit, 96bps data rate, while CD quality could be said to be 44.1khz, 16bit,706k Data rate. This sketch currently does 8 to 44khz+ at 8-bit, with the possibility to go to 10-bit. Depending on the sample rate and bit rate, the user can adjust the quality according to the application.

  The sketch is simple to configure for testing, just edit the user variables defined at the top of the sketch and upload. It can then be controlled by sending 'r' to start recording/sending and 's' to stop, and '=','-' to raise or lower the volume when receiving. External buttons can also be defined in the user config section to allow users to create things like intercoms or even a portable radio or wireless headset. The audio at 10-bit will not be super high quality, but should be very reasonable for voice transmission.

Recording is simply done from an analog pin, and should work with microphones etc designed for Arduino. It currently uses the 5v voltage reference for recording.

  This sketch could be easily converted to use any transmission medium that can support the streaming data at a consistent sample rate, and the library could support multiple radio devices and more advanced hardware, but that would definitely take some time. If it does end up as a library, it will of course be published on GitHub for anyone to use or modify.

The library may work ok with the standard RF24 library or current forks, but my fork has been designed specifically for these purposes, so don't forget to install it if using the data rate/sample rate combinations shown in the example. Download Lib

Update:

10-bit Audio: I comtemplated a few different ways to include the extra two bits in the transmission, and ended up putting the main 8bits of each sample in the first 25 bytes of the payload. The remaining bit-pairs are then placed into bytes 26 to 31. This seemed like the easiest way to 'encode' and thus 'decode' the data, but it does put a bit of an extra load on the MCU. The max sample rate for 10-bit audio is about 24khz, but with 32khz it will just be 9-bit.

Multicasting: The only changes required to include multicasting involved setting all the radios to listen on the same pipe by default, and setting all radios to transmit on the same pipe when transmitting. Since only one radio should be transmitting at a time, it is pretty straightforward. This also removes the need to configure radios differently. If using the same Arduino board, all the radios can be configured exactly the same unless using remote commands, which would require some code changes.




 Arduino: Using the full potential of NRF24L01 radio modules
A New, Optimized Fork of the RF24 Radio Library: High speed data transfers and more!
Includes updates and new features for the RF24Network Library


  NRF24L01+ radio modules are very inexpensive, and provide a robust interface for transferring data wirelessly between devices with minimal resource and power consumption. I've been working with them more and more as time goes on, but have always struggled with some of the inner workings and limitations of the current libraries available.

After studying the operation of the radio modules and reviewing the details in the data sheet, I was convinced that the modules could perform much faster. Using standard 16mHz Arduino boards, with no additional hardware besides the radio modules, I was able to record and stream audio at sampling speeds up to 70kHz with the radio at 1Mbps air rate, using the on board Analog-to-Digital Converter (ADC). At 250kbps air-data rate, streaming audio with sample rates into the 20kHz+ range is now possible. Thanks to these code modifications, the radio can be utilised closer to its actual air data rate.

Taking Advantage of the Improvements:
From a user perspective, very little is changed from previous forks/libraries. Users will benefit from the improvements just by using the new library with old code. The main change regarding compatibility relates to the use of available() in that it will always return 1 if data is available to simplify things, and align the library with standard Arduino functions.

Advanced users may want to utilize some of the features described below, including writeFast and txStandBy(); to maximize throughput. The addressing format has been extended to allow the use of 24,32 or 40-bit addresses, as well as defining and handling of addresses via byte arrays or integers.

For example, the following addresses are the same, and either format can be used:

uint64_t myAddress = 0x68524d5431LL;   ( Old Format   )
uint64_t myAddresses[] = { 0x68524d5431LL, 0x68524d5432LL};

byte myAddress[] = "1TMRh";                    ( New Format )
byte myAddress[] = {'1','T','M','R','h'};
byte myAddresses[][6] = {"1TMRh","2TMRh"};

Technical Info:
One of the primary factors in increasing efficiency was eliminating power ups and power downs from the general operation of the radio, which many of the existing forks already had identified. A power up takes 1.5ms or 1500us, where a transition from standby-I or standby-ii takes only 130us. In addition, leaving the CE pin high while data is written allows the possibility to have 0 delay if the TX FIFO buffer is kept busy, which this library makes use of via the writeFast() and writeBlocking() features. Delays have been removed where necessary, and the order of operations modified to improve response times as well.

This changes the operation of the radio a little bit, in that the radio needs to be powered up or powered down manually. In addition, some new commands were added, one being txStandby(). The txStandBy() command will simply wait until the TX FIFO buffer is completely empty, or it will return 0 if a transmission has timed out, and then the radio will be dropped back to standby-I mode. Using txStandBy(1000) will block until the transmission is successful or a payload takes longer than 1 second to deliver. See the class docs link below or the updated Transfer.ino or TransferTimeouts.ino examples included with the library.

Extended Timeouts: As mentioned above, txStandBy has been modified to support extended timeouts. The writeBlocking() function now supports extended timeouts as well, and will automatically retry packets until a user specified timeout period is reached. This would be useful in very noisy or low signal scenarios, where every byte of data is important. Documentation has been updated accordingly.

RF24Network: Due to the changes to the core library, I've created a fork of the network library which has already benefited from improvements. An optional sleep functionality has been added that allows nodes to sleep without ever losing payloads. An extended timeout period has also been added to aid in noisy or otherwise troublesome environments. Links are below.


Other new commands revolve around writing data to the radio:
The write() command is the standard, and is still very similar to the old one, just faster.
The writeFast() command, will allow writing until the FIFO is full, then wait for it to clear or return 0 if a timeout occurs. This allows users to stream data at high speeds without overrunning the buffers
The writeBlocking() command utilizes the auto-retry functionality also, and will write and block only if the FIFO is full, or until max retries or a user defined timeout is reached (whichever is greater).
See the documentation for additional changes.


Class documenation now available here


Another substantial change is that the available() and isAckPayloadAvailable() functions now simply check the FIFO buffer to see if a payload is available. This allowed simplification of the write() function, and should help to ensure that no packets are missed. Previous iterations used the interrupt flag.

With single writes, the FIFO buffer is now cleared when transmission fails, to allow better handling of retries, retransmissions etc. since the payload is not cleared automatically if it fails. The change in response is apparent when running example sketches like the GettingStarted_CallResponse sketch included with the library or the Transfer examples.

Those are the main changes, and, all in all, they seem to be working very well. Many bugs and technical issues have been addressed as well, so overall operation is much improved.

Testing & Results:
As of its initial release I haven't had the opportunity to do a full round of testing on direct data transfer speeds, but have posted a youtube video showing a round of comparative transfer rate testing. Since the initial release, many users including previous RF24 contributors have adopted the new library and provided feedback towards further improvements.

That being said, the wireless audio sketch/library that inspired these improvements is now linked below. The audio library itself is limited to around 16-20khz sample rate, which produces very reasonable sound quality for voice transmission. The limitation is due to the use of interrupts for virtually every part of the library, however, this makes it very simple to configure and use. See here for the development sketch, which was used to design the library, and is capable of higher quality audio. The wireless audio portion of the TMRpcm library has been updated to allow audio streaming and multicasting directly from SD card over RF24 modules as well.

Reference Material:
TMRh20 RF24 Fork on GitHub  - Download
TMRh20 RF24Network Fork on GitHub - Download
NRF24L01 Data sheet
 
RF24Audio Library

Original/Old RF24 Library by ManiacBug
Alternative: RadioHead NRF24 Library - (Supports multiple radio devices)




Arduino Audio: WAV Playback from SD Card  
*TMRpcm Library update*

Due to popular demand, the PCM audio library now supports 32khz sample rate audio for improved sound quality, and better performance at lower rates. Some of the notable improvements in this round of updates are documented below. See the Wiki on GitHub for usage information.

Multi Track Playback
Dual tracks can be played at once on separate pins. Limited to about 16-20kHz sample rate, see wiki page for updated usage and info.

SdFat Library Support
The library now optionally supports direct use of the SdFat library for lower program and memory usage and increased performance.

Metadata (ID3v2.3 and Info/List) Tag Support
The library now has a configurable option ( pcmConfig.h ) to handle and bypass metadata when reading audio files. It also provides limited support for retrieving song, artist and album information from audio files. Standard info (LIST) tags are supported as well as ID3 tags.

Easy WAV file generation and playback
One command to generate a standard WAV file and header, another to finalize it once data is written. Raw data from analog inputs or other sensors or information sources can be written to the file to generate digital audio that can be played on any device that supports WAV files, or easily converted to other standard formats.

Recording
Commands have been added to enable recording of WAV files to SD card. Performance is very much dependant on the write speed of SD card as well as the amount of SRAM used. Results seem to be ok with a class4 SD card.

Audio Formats and Options
The library is now capable of Stereo or pseudo 16-bit playback with a resistor ladder to a limited degree.
An option to operate using TIMER2 for compatility was added.
A function to start playback at a given number of seconds was added

Buffered SD Reads
After a bit of messing around with the standard SD library, I realized there was a buffered read that would improve the response quite a bit. It doesn't seem to be documented in the library reference, but the usage is as follows:

Normal Read:
byte buffer[32];
for(int i=0; i<32; i++){
   buffer[i] = File.read();
}

Buffered Read:
byte buffer[32];
int bytesRead = File.read((byte*)buffer,32);

*Update*PWM 'Pop' sounds on timer start/stop
*Edit* - There seems to be differences in how the timers are engaged on different boards. The library now autoselects between the ramping methods. See pcmConfig.h to manually select.
I suspected that the typical pwm 'popping' sound could be removed by creating an appropriate ramp, and was able to remove it via the following process on the Arduino Mega.

On Startup:
1. OCR1A and OCR1B are set for opposing PWM. If both are set at 0, one will be high and one will be low.
2. Before enabling the timer, OCR1A is set to 0, OCR1B set to maximum (Timer TOP value) so that there is no power flow on startup.
3. OCR1B is then ramped down to 0, and power is applied gradually
4. OCR1A and OCR1B can now be fed with the same value, without a popping noise

Between Tracks:
1. The OCR1A register always contains the value of the last sample from the previous track, unless disabled.
2. Read OCR1A, and read the first value of the next track.
3. Ramp the value of OCR1A from its current value, to the value of the next track 

On Disable:
1. Get the current value of OCR1A/OCR1B
2. Ramp OCR1B up to maximum (Timer TOP value) from the current value
3. Ramp OCR1A down to minimum (0) from the current value

On a Nano or Duemilanove board, the process is a bit different:

On Startup:
No ramp needed, handled by track transition ramp. There will be a pop the first time PWM is enabled, and no popping thereafter if disabled or enabled.

Between Tracks: 
Same as with Mega 

On Disable:
1. Get the current value of OCR1A/OCR1B
2. Ramp both OCR1A/OCR1B down to 0 from the current value

I applied a few more tweaks to bring the performance up a bit more, mainly converting 16bit variables to 8bit where possible, and improving on details of the playback process. The quality/oversampling option now defaults to 0. The update is available on GitHub:
Download