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


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


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

Updated Apr 4, 2014: Fixed some errors, and updated to match current library functionality.

  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.

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.

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. A sleep functionality has been added that allows nodes to sleep without ever losing payloads (Not working properly). An extended timeout period has also been added to aid in noisy or otherwise troublesome environments. This fork of the network library will be under some form of development for a while, so certain features may or may not function correctly at times. 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 until max retries or a user defined timeout is reached (whichever is greater), or until an ACK is received.

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. 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.

Those are the main changes, and, all in all, they seem to be working very well. 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.

That being said, the wireless audio sketch/library that inspired these improvements will be linked here as soon as I finish creating the library. Direct recording and transmission of anywhere from 8khz to 44kHz audio samples will be the main feature, used as an intercom, wireless headset, etc. with long range at 250kbps(advertised up to 100m or extended range to 1Km with LNA+PA modules) or higher quality/faster speeds at short range. The wireless audio portion of the pcm library will eventually be updated to best utilize these capabilities as well.

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

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.

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:
Arduino: High Speed (8Mhz) Signaling: NTSC TV Output

I recently purchased a cheap 7" color monitor with intentions of using it for FPV flying, but still have not received the transmitting equipment. Of course, in waiting, I began to wonder about interaction with an Arduino, and a few searches led me to the TVOut library, which achieves NTSC video output (B/W) using an Arduino. I downloaded the library, and the more I read through the code, the more I was intrigued by how exactly such fast and accurate timing was acheived.

To give an idea of the requirements, the horizonal sync signal occurs about 15750 cycles/second (every 63.5us), which is relatively easy for an Arduino to produce in itself. In between these signals however, the picture is drawn, which requires much faster signaling and more precise timing, into the Mhz+ range.

In addition to the TVOut library, I also found a simpler form of code here that uses the USART (Serial port) to form the video signal, using each bit in every byte sent to form individual pixels. The main problem I found with this code was timing. It uses sleep mode to help time the video display accurately, but there were still some issues with small video glitches that I could not resolve when replicating the code. It also uses manual writing of the hsync pulse, where the other method uses a timer generated signal.

The first thing to do was to understand the makeup of an NTSC signal. This page provided a straightforward explanation, along with the timing requirements. Then it was on to recreating a hybrid of the code mentioned above, in the simplest possible format. The first part was pretty straightforward, creating horizontal and vertical sync signals. An entire frame consists of 259 horizontal sync signals, and 3 vertical sync signals, which are simply inverted horizontal sync signals to make 262 lines. The following code will produce a sync signal on pin 12, which results in a blank black screen.

#define hLinesPerFrame  262
#define vSync 247
#define hSync 25

volatile unsigned int isrCounter = 0;

void setup(){

  //This sets up the timer to produce the horizontal sync pulses
  //ICR1 is the timer TOP value, OCR1B controls the hSync pulse width
  //OCR1A will control the timing of the video display
  ICR1 = 1019;

  OCR1A = 1;
  OCR1B = 30;
  TCCR1A =  _BV(WGM11) | _BV(COM1B1) | _BV(COM1B0);
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
  TIMSK1 = _BV(OCIE1A); 


          case vSync: OCR1B = 1019-hSync; break;

          case vSync+3: OCR1B = hSync; break;
          case hLinesPerFrame:  isrCounter = 0; break;

void loop(){ 


So that part is easy enough, a few lines of code, a 1k resistor from pin 12, and we have a basic NTSC signal with vertical and horizontal sync. Actually creating video will be a bit more complicated, and I've chosen an Arduino Mega due to the amount of SRAM (8K) available, which is needed for higher resolutions.

What I found in experimenting with different methods for timing, is that the timing of interrupts on a standard Arduino board can vary quite a bit. Using sleep mode to idle the board prior to triggering works 98-99% of the time, but still leaves a few interupts coming a bit late, producing glitches in the video. The method used in the TVOut library appears to work the best, as it monitors the counter to trigger at exactly the same time every time. Of course, it is written in assembly, and I made a slight modification to exit immediatly if the wait time has been missed. My explanation may be a slight bit off technically, but should be pretty close:

static void inline wait_until(uint8_t time) {

__asm__ __volatile__ (
    "sub    %[time], %[tcnt1l]\n\t" //Subtract the low byte of TCNT1 from the wait time
    "brmi   102f\n\t"               //If result is negative, we missed it. Branch to 102

    "subi    %[time], 3\n\t"         //Subtract 3 from time
    "brcc    100b\n\t"               //Loop until we get a negative
    "subi    %[time], 0-3\n\t"       //Add 3 back to our value
    "breq    101f\n\t"               //If equal to 0, branch to 101 and delay 1/16 of a uS (No OPeration)
    "dec    %[time]\n\t"            //Decrement our value by 1
    "breq    102f\n\t"               //If equal to 0, branch to 102 and don't delay
    "rjmp    102f\n"                 //Else Jump to the end

     :                                 //Output variables (none)
     : [time] "a" (time),              //Input variables
       [tcnt1l] "a" (TCNT1L)



That takes care of the basic signaling, and provides accurate enough timing to begin looking at displaying the video. I chose to use the USART to draw the video, as it simplifies things quite a bit when compared to using functions written in assembly, and should be able to use a baud rate of 8Mhz in Master SPI mode according to the datasheet. The USART also includes a double-buffering capability, which seems to make it more suitable here than using SPI.

We can calculate our BAUD rate, and the required setting for the UBBRn register at maximum speed:

16Mhz / 2(UBRRn + 1)  -->  16 / 2(0 + 1)  -->  16 / 2 -->  BAUD: 8Mhz  UBRRn: 0

Now, to configure the USART, only two lines of code are required in setup() :

  UCSR1C = _BV(UMSEL01) | _BV(UMSEL00); //Set USART to Master SPI mode
  UBRR1 = 0;                            //Baud rate = 8Mhz

Then to create a buffer for the pixel data of 5400 bytes:
    int totalBytes = (400/8) * 108;   
  byte data[totalBytes];

Now, all there is to do is start drawing pixels at the correct time, which (simplified) is done with the following lines of code, which get added to the TIMER1_COMPA_vector interrupt shown above.

wait_until(153);                      //See the assembly function above

UCSR1B |= _BV(TXEN1);                 //Enable USART TX

for (int x =0; x < 400/8; x++){       //Load bytes until we have drawn 1 horizontal line:
  while ( !(UCSR1A & _BV(UDRE1))){ }  //Wait until the USART is ready for more data
  UDR1 = data[x+start];               //Load a byte

UCSR1B = 0 ;
                                             //Disable the USART TX

...And thats all there is to it. Of course, the last part of it requires a bit more code to control the screen position and count lines, etc, but those are the fundamentals of producing NTSC video with an Arduino. A complete sketch is posted below.

a: The timing of the video display in relation to the horizontal sync pulse is very important. It specifically triggers before the h-sync pulse ends so as to have enough time to smooth out variations in triggering.
b: The loading of data into the buffer (UDR1) must be done as quickly as possible, and requires efficient code to work at 8Mhz.
c: Using the USART requires accurate screen positioning due to the pulse created whenever it is enabled. This creates a vertical line, which can be positioned off screen via the wait timing.
d: This code does not use the defined standard timing for parts of the signal, due to better results with slightly modified settings. (ie: 2uS horizontal pulse instead of 4.7uS)
e: The maximum resolution is mainly limited by the memory available, as 1 byte is required for every 8 pixels. (400 x 108 uses 5.4KB)

For me, this was an interesting look at the maximum limitations of signaling using an Arduino, and gave me an opportunity to learn a little more about assembly code as well. I found a great tutorial here showing how to view your Arduino code in assembly, and it was a great help in beginning to understand the code behind the code.
All in all, I am pretty happy with the results, as the methods used above seem pretty comparable to the TVOut libary, but with a bit higher resolution. The main benefits of using the USART are simplified code, as the baud rate can be changed to scale the video horizontally, and feeding it data is very straightforward.

The code used to create the above video can be found here and borrows heavily from the code it is based on.

A Smaller Custom Arduino Quadcopter

The new 13.5" (34cm) custom quad             

  I've enjoyed building and flying my first custom Quadcopter based on an Arduino and FreeIMU, so decided to build another smaller version. The original really needs some wide open spaces to open up and fly, and I wanted something I could fly in more confined areas. This one is quite a bit smaller, measuring in at 13.5" compared to 21" and is much lighter. It is very similar to the first electronically, but uses a GY-86 module and Arduino Nano for the FC board. The IMU module has a gyro, barometer, compass, and accelerometer. It uses the same APC220 transceiver for control, but a standard RC unit is recommended in most cases.

Video from one of the initial test flights 

  As seen in the pics, the FC board is very simple, and I used the complex design method of 'connect the dots'. Just below the orange transceiver, jumpers can be seen which are used to disconnect it from the hardware serial port when connecting to USB for programming. The other pins shown just beside the Arduino are for connecting the ESCs, one of which powers the board. The Nano is mounted on header pins, so can be easily removed. This one was modelled somewhat after the first, but uses 10mm square carbon fiber booms and no aluminium mounts. The setup in MultiWii is pretty much the same, but with the GY-86 chosen under sensor definitions.

  The 1.5mm sheet was cut into two squares measuring 1.75" x 5" for the main body, and holes were drilled through the booms to bolt them directly between the sheets. Drilling through the booms may not provide the most strength, but with a small well balanced quad, I'm hoping it will hold up. The motor mounts required both a top and bottom plate as well due to the thin layer of carbon fiber in the centre of the boom. Drilling through the carbon fiber sheets completely dulled a standard drill bit as well.

  The two quads side-by-side

  A quick test flight with a 3S 1000maH battery and 3-blade 5045 props provided about 6 minutes of casual flight time, but it should be better with a 2S battery and 6" props according to eCalc. The tool also says the props will stall, but it hovers and moves nicely so not sure about that. The FC board is attached directly to the frame using some nylon standoffs, but vibration is minimal after balancing.
  I had a lot of difficulty trying to balance such small props individually, so admitted defeat and went directly to dynamic balancing. I came across laser balancing in this video and I always keep some spare lasers around (who doesn't?) so I tried it, and the balance was the best I've achieved. I balanced the motors before adding the props/adaptors and re-balancing.


  I had a few issues with these motors, since the mounts are a bit flimsy, and will break if there is excessive vibration. I've since attached the motors using hard rubber between the metal mounts and the carbon fiber to provide some give, and it seems to be working ok so far. Something also seems to have gotten into one of them, tearing apart the wiring, but it was easy to rewind with new magnet wire. Luckily, I bought 5 motors, but I may need to purchase a different type of motor if troubles keep popping up.

Main Parts List:
4X HobbyKing Donkey ST2004-1550kv Brushless Motor
4X Propeller Adaptor (Colet Type) 3MM
2X Counter Rotating Three Blade 5x4.5 Propeller
2X Counter Rotating Three Blade 6x4.5 Propeller
4X Turnigy Multistar 10 Amp Multi-rotor Brushless ESC
2X 1000mAh 3S 25C Lipo Pack
2X 1000mAh 2S 25C Lipo Pack
1X 10mm Carbon Fiber Square Tube - 750mm
1X Woven Carbon Fiber Sheet 300x100 (1.5MM Thick)

FC Board:
1X Arduino Nano
1X GY-86 10DOF IMU Module: MS5611 HMC5883L MPU6050
1X APC220 Wireless Serial Data Module

FC Board PinOut:
pin3: front motor
pin9: rear motor
pin10: right motor
pin11: left motor
A4: GY-86 SDA
A5: GY-86 SCL
RX: APC220 TX   

MultiWii: DIY Wireless LCD Config and PID Tuning using Stick Controls:

   The MultiWii flight control software has the built-in functionality of using an attached LCD for viewing and adjusting settings. This takes it a little further by sending the data wirelessly, which can be very handy for adjusting PID settings and other configuration options while out flying. There are only three main components: Arduino Mega, APC220 wireless data module, and LCD Screen. I might incorporate a smaller screen directly into my controller using this sketch.

 Wireless LCD in configuration menu during testing
   This is designed specifically to work with my custom XBox controller and QuadCopter, but will work with standard controllers and MultiWii boards that support or have a serial data transceiver. The status indicator portion does require slight modifications to MultiWii, but the LCD menus will work with the stock software/boards.


Wireless LCD showing status (left,center) and configuration (right)

   Menu navigation takes place via the RC transmitter/controller, so all you have to do is land before entering the LCD menu and adjusting PID or configuration settings. This has been very handy for tuning PID settings in the field and understanding exactly how those changes affect the performance of the aircraft. This could easily be modified to do other things like log or display telemetry, flight or sensor data as well.

- Long range (up to 1000m with APC220)
- Capable of receiving standard MSP commands and displaying or storing info
- Adjust PID settings wirelessly
- Adjust configuration options wirelessly
- Displays status: 
        - Battery Voltage
        - I2C Error Count
        - Running Time in minutes
        - Flight Mode Indicators

- MultiWii based FC Board w/serial port for wireless serial module (APC220, XBee, etc), or built-in
- DIY Wireless LCD: Arduino Mega + LCD (compatible with LCD library) + APC220(Any wireless serial module)

Hardware Setup:
1. Enable LCD in MultiWii if needed (Config.h)
    a: Un-Comment: #define LCD_CONF, #define LCD_TTY, #define LCD_SERIAL_PORT 3
    b: Set LCD_SERIAL_PORT to the correct port for your transceiver

    c: Upload to FC Board
2. Connect LCD Screen to Arduino Mega and ensure it works with Arduino LCD library. Adjust pins in sketch to match.

3. Attach APC220 or other serial module to Arduino Mega port 3
4. Upload this sketch
5. Enter LCD Menu and adjust as desired
LCD Useage:
1. Move Pitch stick up, Yaw right to enter LCD Menu
3. Navigate menus usings stick movements: 

      Pitch Fwd/Back: Prev/Next     Pitch R/L: Value Up/Dn
4. a: Abort/Exit:   Pitch Fwd-Yaw Right

    b: Save/Exit:    Pitch Fwd-Yaw Left 

Wireless LCD
MultiWii (Customized to send additional status data)