Wednesday, April 3, 2013

New RF24 Driver Release - A Fork

New RF24 Driver Release

The RF24 driver was forked to add new features and fix bugs. These improvements are outlined below. This release's primary aim is to improve performance and increase operational reliability. I believe those aims were achieved.

The intention is to once again have my features and bug fixes merged back into Maniacbug's RF24 driver. While this is currently a fork, I hope in the near future this code will be part of the official RF24 driver repository.

My RF24 Fork: https://github.com/gcopeland/RF24

Auto-Acknowledgement Retry Bug Fix

The default timeout for auto acknowledgements is wrong. If using maximum payload size at 250Kbps, an erroneous timeout may cause errors in transmission, resulting in a failure to send. This timeout has been changed. Users who explicitly set their own timeouts via setRetries() function call are immune to this issue, assuming the provided values comply with operating requirements set out by the data sheet.


Reliability Improvements

Following each write(), the radio was explicitly powered down by the driver. I classify the previous behaviour as a bug. In order to improve reliability and performance, write() no longer powers down the radio

Additionally, powering down the radio after each write also means the radio will not receive data until a startListening() method is called. This in turn means the radio is completely deaf between the end of the write() call and the end of the following startListening() call. This latency increases the likelihood of a missed transmission for a busy, multi-node network. In doing so, needlessly adds additional SPI bus traffic. This bug decreases radio reliability and wastes time on the SPI bus. Fixing this behaviour means the radio can now function optimally from standby mode while using less application time.

By allowing the radio to enter standby mode, the radio will continue to listen for messages, including ACKs/NAKs, and transmissions which might otherwise be missed; as intended. The radio will respond dramatically faster from standby activation than it does from a powered down state. This in turn is a performance optimization. In turn reducing the window for lost packets.

This is an important improvement because while the radio is free to process both ACK/NAK reply messages, it can also receive other messages even though startListening() has not been called. The messages will simply wait in the corresponding rx pipe until it is processed by the application. As such, this change also increases the radio's parallel pipe performance. Another performance optimization.

See the Compatibility section for more details.

Higher Performance

The driver now has fewer delays and blocking calls. Fewer SPI read/writes now take place within various radio method call. This in turn means more CPU is available for applications. In doing so, a number of internal function calls were removed. This has the effect of very minor memory footprint improvements.

Multicasting

This driver adds support for multicasting. This allows a single transmitter to transmit exactly once, allowing for multiple concurrent receivers, without changing the radio's operational mode. This means use of this feature does not interfere with the use of auto-acknowledgement. This is because auto-acknowledgement is a radio's operational mode, whereby, the new multicasting implementation leverages a message's mode. As such, it does not interfere with auto-acknowledgements in any way.

Multicasted messages are inherently unreliable. Even with auto-acknowledgement enabled, multicast messages will never be ACK'd or NAK'd. They are fire and forget. Either the message is received, or its not.

To multicast a message, use the write() or startWrite() methods. There is now a third optional argument. If the third argument is not provided, or 'false' is used, it will transmit exactly as previously. If the third argument is, 'true', the packet will be multicasted.

Example: radio.write( &msg, sizeof(msg), true ) ;

Closing Pipes

A new method, closeReadingPipe() has been added. This allows for a previously opened reading pipe to be shut down. A pipe which has been closed will no longer accept messages for the corresponding pipe address.

Variable Timeouts

A new method, getMaxTimeout(), is now available. This method returns the maximum number of microseconds a read/write operation will take to successfully complete. The value is calculated based on radio configuration at the time the method is called. Reconfiguration of the radio via setRetries() will invalidate the results return from getMaxTimeout().

Compatibility

Compatibility should not be an issue unless your application depends on a power management side effect. If it does, see Power Management.

Power Management

Power management is now an explicit mechanism. Applications which errantly rely on the driver to handle power management as a side effect, will find higher power demands. The fix is for the application to properly implement powerDown() and powerUp() calls as needed.

Battery powered projects might see a minuscule increase of power requirements but ultimately its up to the application to match what was previously a side effect of the driver.

Timeout Calculation

If you previously have loops which look something like the following, where 'myTimeoutValue' is a fixed value, a better solution is now available.

unsigned long t = millis() ;
unsigned long myTimeoutValue = 250UL ;
while( !radio.available() || millis() - t < myTimeoutValue ) {
}

Now, you can initialize myTimeoutValue as follows. Notice it rounds up to the next millisecond.
unsigned long myTimeoutValue = 1 + (radio.getMaxTimeout()/1000) ;

Doing so will ensure the minimal amount of time is spent waiting for a transmission to complete.

As a reminder, if you attempt to use the code above, and if you enter that loop immediately after a write, unless you are using enableAckPayloads(), and a payload is immediately available for transmission on the remote's end, the timeout provided above will not provide time for remote's code execution to process its message and reply. As such, some experimentation may be required on a per application basis. Regardless, this mechanism allows for timed loop optimizations.

Testing

At this point, this fork has been tested by three users (me being one), on nine radios, two platforms (Arduino and Raspberry Pi), and four different makes and models of Arduino (uno, nano, mega2560, and due) of hardware. Thus far its been 100% compatible.

Update: At this point many people on both Arduino and rPi platform have tested this fork. All reports are good, confirming the validity of these changes.


3 comments:

  1. Thank you Greg.
    I have tested your fork and my first test runs do produce far fewer lost packets!

    ReplyDelete
    Replies
    1. BTW, by any chance were you using 250Kbps mode with the default retry values? Just curious.

      Delete
  2. Wow. That's wonderful to hear.

    Thank you for sharing.

    ReplyDelete