Commit 327f0609 authored by TMRh20's avatar TMRh20

Add optional write timeout/failure handling

Per issue #5 by sven337
- Add optional failure handling/timeout
- Un-comment #define FAILURE_HANDLING in RF24_config.h to enable
- Add radio.errorDetected variable - indicates if an error/timeout was
detected
example:
if(radio.failureDetected){
delay(1000);
radio.begin();
radio.failureDetected = 0;
radio.openWritingPipe(addresses[1]);
radio.openReadingPipe(1,addresses[0]);
report_failure(); //blink leds, send a message, etc.
}

Additional:
- removed unused wide_band boolean
- remove rx buffer flushes for RPi also
parent 1d180150
......@@ -329,7 +329,7 @@ void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty)
/****************************************************************************/
RF24::RF24(uint8_t _cepin, uint8_t _cspin):
ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false),
ce_pin(_cepin), csn_pin(_cspin), p_variant(false),
payload_size(32), dynamic_payloads_enabled(false), addr_width(5)//,pipe0_reading_address(0)
{
}
......@@ -338,9 +338,6 @@ RF24::RF24(uint8_t _cepin, uint8_t _cspin):
void RF24::setChannel(uint8_t channel)
{
// TODO: This method could take advantage of the 'wide_band' calculation
// done in setChannel() to require certain channel spacing.
const uint8_t max_channel = 127;
write_register(RF_CH,min(channel,max_channel));
}
......@@ -568,6 +565,13 @@ void RF24::powerUp(void)
}
}
/******************************************************************/
#if defined (FAILURE_HANDLING)
void RF24::errNotify(){
IF_SERIAL_DEBUG(printf_P(PSTR("HARDWARE FAIL\r\n")));
failureDetected = 1;
}
#endif
/******************************************************************/
//Similar to the previous write, clears the interrupt flags
......@@ -577,9 +581,18 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast )
startFastWrite(buf,len,multicast);
//Wait until complete or failed
//ACK payloads that are handled improperly will cause this to hang
//If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { }
#if defined (FAILURE_HANDLING)
uint32_t timer = millis();
#endif
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) {
#if defined (FAILURE_HANDLING)
if(millis() - timer > 75){
errNotify();
return 0;
}
#endif
}
ce(LOW);
......@@ -615,6 +628,12 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout )
reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag
if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0
}
#if defined (FAILURE_HANDLING)
if(millis() - timer > (timeout+75) ){
errNotify();
return 0;
}
#endif
}
......@@ -642,6 +661,10 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast )
//Return 0 so the user can control the retrys and set a timer or failure counter if required
//The radio will auto-clear everything in the FIFO as long as CE remains high
#if defined (FAILURE_HANDLING)
uint32_t timer = millis();
#endif
while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail
if( get_status() & _BV(MAX_RT)){
......@@ -650,7 +673,12 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast )
return 0; //Return 0. The previous payload has been retransmitted
//From the user perspective, if you get a 0, just keep trying to send the same payload
}
#if defined (FAILURE_HANDLING)
if(millis() - timer > 75 ){
errNotify();
return 0;
}
#endif
}
//Start Writing
startFastWrite(buf,len,multicast);
......@@ -696,7 +724,9 @@ void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){
}
bool RF24::txStandBy(){
#if defined (FAILURE_HANDLING)
uint32_t timeout = millis();
#endif
while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){
if( get_status() & _BV(MAX_RT)){
write_register(STATUS,_BV(MAX_RT) );
......@@ -704,6 +734,12 @@ bool RF24::txStandBy(){
flush_tx(); //Non blocking, flush the data
return 0;
}
#if defined (FAILURE_HANDLING)
if( millis() - timeout > 75){
errNotify();
return 0;
}
#endif
}
ce(LOW); //Set STANDBY-I mode
......@@ -723,7 +759,15 @@ bool RF24::txStandBy(uint32_t timeout){
ce(LOW); flush_tx(); return 0;
}
}
#if defined (FAILURE_HANDLING)
if( millis() - start > (timeout+75)){
errNotify();
return 0;
}
#endif
}
ce(LOW); //Set STANDBY-I mode
return 1;
......@@ -1115,13 +1159,11 @@ bool RF24::setDataRate(rf24_datarate_e speed)
uint8_t setup = read_register(RF_SETUP) ;
// HIGH and LOW '00' is 1Mbs - our default
wide_band = false ;
setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ;
if( speed == RF24_250KBPS )
{
// Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0
// Making it '10'.
wide_band = false ;
setup |= _BV( RF_DR_LOW ) ;
}
else
......@@ -1130,14 +1172,8 @@ bool RF24::setDataRate(rf24_datarate_e speed)
// Making it '01'
if ( speed == RF24_2MBPS )
{
wide_band = true ;
setup |= _BV(RF_DR_HIGH);
}
else
{
// 1Mbs
wide_band = false ;
}
}
write_register(RF_SETUP,setup);
......@@ -1146,10 +1182,6 @@ bool RF24::setDataRate(rf24_datarate_e speed)
{
result = true;
}
else
{
wide_band = false;
}
return result;
}
......
......@@ -47,12 +47,13 @@ class RF24
private:
uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */
uint8_t csn_pin; /**< SPI Chip select */
bool wide_band; /* 2Mbs data rate in use? */
bool p_variant; /* False for RF24L01 and true for RF24L01P */
uint8_t payload_size; /**< Fixed size of payloads */
bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */
uint8_t pipe0_reading_address[5]; /**< Last address set on pipe 0 for reading. */
uint8_t addr_width;
public:
......@@ -770,7 +771,30 @@ public:
*
*/
void disableCRC( void ) ;
/**
* Enable error detection by un-commenting #define FAILURE_HANDLING in RF24_config.h
* If a failure has been detected, it usually indicates a hardware issue. By default the library
* will cease operation when a failure is detected.
* This should allow advanced users to detect and resolve intermittent hardware issues.
*
* In most cases, the radio must be re-enabled via radio.begin(); and the appropriate settings
* applied after a failure occurs, if wanting to re-enable the device immediately.
*
* Usage: (Failure handling must be enabled per above)
* @code
* if(radio.failureDetected){
* radio.begin(); // Attempt to re-configure the radio with defaults
* radio.failureDetected = 0; // Reset the detection value
* radio.openWritingPipe(addresses[1]); // Re-configure pipe addresses
* radio.openReadingPipe(1,addresses[0]);
* report_failure(); // Blink leds, send a message, etc. to indicate failure
* }
* @endcode
*/
#if defined (FAILURE_HANDLING)
bool failureDetected;
#endif
/**@}*/
/**
* @name Deprecated
......@@ -974,6 +998,11 @@ private:
*/
uint8_t spiTrans(uint8_t cmd);
#if defined (FAILURE_HANDLING)
void errNotify(void);
#endif
/**@}*/
};
......@@ -1124,7 +1153,7 @@ private:
*
* This library fork is designed to be...
* @li More compliant with the manufacturer specified operation of the chip, while allowing advanced users
* to work outside the reccommended operation.
* to work outside the recommended operation.
* @li Utilize the capabilities of the radio to their full potential via Arduino
* @li More reliable, responsive and feature rich
* @li Easy for beginners to use, with well documented examples and features
......@@ -1132,17 +1161,17 @@ private:
*
* @section News News
*
* <b>April 2014: Official Release: Still some work to do, but most benefits have been realized </b><br>
* <b>Main changes: </b><br>
* - The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds
* - Changes to read() and available () functionality have increased reliability and response
* - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments
* - Delays have been removed where possible to ensure maximum efficiency
* - Full Due support with extended SPI functions
* - ATTiny 24/44/84 25/45/85 now supported.
* - ATTiny now supported.
* - Raspberry Pi now supported
* - More! See the links below and class documentation for more info.
*
* If issues are discovered with the documentation, please report them here: <a href="https://github.com/TMRh20/tmrh20.github.io/issues"> here</a>
* If issues are discovered with the documentation, please report them <a href="https://github.com/TMRh20/tmrh20.github.io/issues"> here</a>
* @section Useful Useful References
*
* Please refer to:
......@@ -1150,7 +1179,7 @@ private:
* @li <a href="http://tmrh20.github.io/">Documentation Main Page</a>
* @li <a href="http://tmrh20.github.io/RF24/classRF24.html">RF24 Class Documentation</a>
* @li <a href="https://github.com/tmrh20/RF24/">Source Code</a>
* @li <a href="https://github.com/tmrh20/RF24/archives/master">Downloads Page</a>
* @li <a href="https://github.com/TMRh20/RF24/archive/master.zip">Download</a>
* @li <a href="http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf">Chip Datasheet</a>
* @li <a href="https://github.com/maniacbug/RF24">Original Library</a>
*
......@@ -1162,13 +1191,13 @@ private:
* Most standard Arduino based boards are supported:
* - ATMega 328 based boards (Uno, Nano, etc)
* - Mega Boards (1280, 2560, etc)
* - ARM based boards (Arduino Due) Note: Do not include printf.h or use printf begin. This functionality is already present. Must use one of the
* hardware SS/CSN pins as extended SPI methods are used.
* - Arduino Due: Must use one of the hardware SS/CSN pins as extended SPI methods are used.
* Initial Due support taken from https://github.com/mcrosson/RF24/tree/due
* - ATTiny board support added from https://github.com/jscrane/RF24
* Note: ATTiny support is built into the library. Do not include SPI.h. <br>
* ATTiny 85: D0(pin 5): MISO, D1(pin6) MOSI, D2(pin7) SCK, D3(pin2):CSN/SS, D4(pin3): CE <br>
* ATTiny 84: PA6:MISO, PA5:MOSI, PA4:SCK, PA7:CSN/SS, CE as desired <br>
* See https://github.com/TCWORLD/ATTinyCore/tree/master/PCREL%20Patch%20for%20GCC for ATTiny patch
* - Raspberry Pi Support: See the readme at https://github.com/TMRh20/RF24/tree/master/RPi
*
* @section More More Information
......@@ -1177,7 +1206,7 @@ private:
*
* @li Project blog:
* @li <a href="http://TMRh20.blogspot.com"> TMRh20.blogspot.com </a>
* @li <a href="https://github.com/TMRh20"> RF24 Wireless Audio Library (Coming Soon) </a>
* @li <a href="http://tmrh20.github.io/RF24Audio/"> RF24 Wireless Audio Library </a>
* @li <a href="https://github.com/TMRh20/RF24Network"> Optimized RF24 Network Layer </a>
* @li <a href="https://github.com/maniacbug/RF24"> ManiacBug on GitHub (Original Library Author)</a>
*/
......
......@@ -20,9 +20,12 @@
#include <stddef.h>
//TMRh20:
/*** USER DEFINES: ***/
//#define FAILURE_HANDLING
//#define SERIAL_DEBUG
//#define MINIMAL
/**********************/
// Define _BV for non-Arduino platforms and for Arduino DUE
#if defined (ARDUINO) && !defined (__arm__)
#include <SPI.h>
......@@ -47,8 +50,7 @@
#endif
#undef SERIAL_DEBUG
#ifdef SERIAL_DEBUG
#define IF_SERIAL_DEBUG(x) ({x;})
#else
......
......@@ -265,18 +265,15 @@ void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty)
/****************************************************************************/
RF24::RF24(uint8_t _cepin, uint8_t _cspin, uint32_t _spi_speed):
ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), wide_band(true), p_variant(false),
ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed),p_variant(false),
payload_size(32), dynamic_payloads_enabled(false),addr_width(5)//,pipe0_reading_address(0)
{
{
}
/****************************************************************************/
void RF24::setChannel(uint8_t channel)
{
// TODO: This method could take advantage of the 'wide_band' calculation
// done in setChannel() to require certain channel spacing.
const uint8_t max_channel = 127;
write_register(RF_CH,min(channel,max_channel));
}
......@@ -408,8 +405,10 @@ void RF24::printDetails(void)
bool RF24::begin(void)
{
debug = false;
//debug = true;
// This initialize the SPI bus with
#if defined(DEBUG)
debug = true;
#endif
// This initialize the SPI bus with
// csn pin as chip select (custom or not)
......@@ -488,7 +487,7 @@ bool RF24::begin(void)
setChannel(76);
// Flush buffers
flush_rx();
//flush_rx();
flush_tx();
powerUp();
......@@ -513,7 +512,7 @@ void RF24::startListening(void)
write_register(RX_ADDR_P0, pipe0_reading_address,addr_width);
}
// Flush buffers
flush_rx();
//flush_rx();
flush_tx();
// Go!
......@@ -559,6 +558,14 @@ void RF24::powerUp(void)
/******************************************************************/
#if defined (FAILURE_HANDLING)
void RF24::errNotify(){
if(debug){ printf("HARDWARE FAIL\n\r"); }
failureDetect = true;
}
#endif
/******************************************************************/
bool RF24::write( const void* buf, uint8_t len, const bool multicast ){
// Begin the write
......@@ -566,9 +573,18 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast ){
//Wait until complete or failed
#if defined (FAILURE_HANDLING)
uint32_t timer = millis();
#endif
// If this hangs, it ain't coming back, no sense in timing out
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { }
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) {
#if defined (FAILURE_HANDLING)
if(millis() - timer > 175){
errNotify();
return 0;
}
#endif
}
bcm2835_gpio_write(ce_pin, LOW);
uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
......@@ -604,7 +620,12 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout )
reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag
if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0
}
#if defined (FAILURE_HANDLING)
if(millis() - timer > (timeout+75) ){
errNotify();
return 0;
}
#endif
}
//Start Writing
......@@ -632,6 +653,10 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast )
//Return 0 so the user can control the retrys and set a timer or failure counter if required
//The radio will auto-clear everything in the FIFO as long as CE remains high
#if defined (FAILURE_HANDLING)
uint32_t timer = millis();
#endif
while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail
if( get_status() & _BV(MAX_RT)){
......@@ -640,7 +665,12 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast )
return 0; //Return 0. The previous payload has been retransmitted
//From the user perspective, if you get a 0, just keep trying to send the same payload
}
#if defined (FAILURE_HANDLING)
if(millis() - timer > 75 ){
errNotify();
return 0;
}
#endif
}
//Start Writing
startFastWrite(buf,len,multicast);
......@@ -683,7 +713,9 @@ void RF24::startWrite( const void* buf, uint8_t len, const bool multicast )
/****************************************************************************/
bool RF24::txStandBy(){
#if defined (FAILURE_HANDLING)
uint32_t timer = millis();
#endif
while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){
if( get_status() & _BV(MAX_RT)){
write_register(STATUS,_BV(MAX_RT) );
......@@ -691,6 +723,12 @@ bool RF24::txStandBy(){
flush_tx(); //Non blocking, flush the data
return 0;
}
#if defined (FAILURE_HANDLING)
if( millis() - timer > 75){
errNotify();
return 0;
}
#endif
}
bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode
......@@ -714,6 +752,12 @@ bool RF24::txStandBy(uint32_t timeout){
return 0;
}
}
#if defined (FAILURE_HANDLING)
if( millis() - start > (timeout+75)){
errNotify();
return 0;
}
#endif
}
bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode
return 1;
......@@ -1079,13 +1123,11 @@ bool RF24::setDataRate(rf24_datarate_e speed)
uint8_t setup = read_register(RF_SETUP) ;
// HIGH and LOW '00' is 1Mbs - our default
wide_band = false ;
setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ;
if( speed == RF24_250KBPS )
{
// Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0
// Making it '10'.
wide_band = false ;
setup |= _BV( RF_DR_LOW ) ;
}
else
......@@ -1094,14 +1136,8 @@ bool RF24::setDataRate(rf24_datarate_e speed)
// Making it '01'
if ( speed == RF24_2MBPS )
{
wide_band = true ;
setup |= _BV(RF_DR_HIGH);
}
else
{
// 1Mbs
wide_band = false ;
}
}
write_register(RF_SETUP,setup);
......@@ -1110,10 +1146,6 @@ bool RF24::setDataRate(rf24_datarate_e speed)
{
result = true;
}
else
{
wide_band = false;
}
return result;
}
......
......@@ -51,6 +51,30 @@ typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e
class RF24
{
public:
/**
* Enable error detection by un-commenting #define FAILURE_HANDLING in RF24_config.h
* If a failure has been detected, it usually indicates a hardware issue. By default the library
* will cease operation when a failure is detected.
* This should allow advanced users to detect and resolve intermittent hardware issues.
*
* In most cases, the radio must be re-enabled via radio.begin(); and the appropriate settings
* applied after a failure occurs, if wanting to re-enable the device immediately.
*
* Usage: (Failure handling must be enabled per above)
* @code
* if(radio.failureDetected){
* radio.begin(); // Attempt to re-configure the radio with defaults
* radio.failureDetected = 0; // Reset the detection value
* radio.openWritingPipe(addresses[1]); // Re-configure pipe addresses
* radio.openReadingPipe(1,addresses[0]);
* report_failure(); // Blink leds, send a message, etc. to indicate failure
* }
* @endcode
**/
#if defined (FAILURE_HANDLING)
bool failureDetect;
#endif
private:
uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */
uint8_t csn_pin; /**< SPI Chip select */
......@@ -87,7 +111,7 @@ protected:
* @return Current value of status register
*/
uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len);
/**
* Read single byte from a register
*
......@@ -95,7 +119,7 @@ protected:
* @return Current value of register @p reg
*/
uint8_t read_register(uint8_t reg);
/**
* Write a chunk of data to a register
*
......@@ -202,6 +226,11 @@ protected:
* are enabled. See the datasheet for details.
*/
void toggle_features(void);
#if defined (FAILURE_HANDLING)
void errNotify(void);
#endif
/**@}*/
public:
......@@ -424,6 +453,20 @@ public:
*/
void enableDynamicPayloads(void);
/**
* Enable dynamic ACKs (single write multicasting) for chosen messages
*
* @note To enable full multicasting or per-pipe multicast, use setAutoAck()
*
* @warning This MUST be called prior to attempting single write NOACK calls
* @code
* radio.enableDynamicAck();
* radio.write(&data,32,1); // Sends a payload with no acknowledgement requested
* radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK
* @endcode
*/
void enableDynamicAck(void);
/**
* Determine whether the hardware is an nRF24L01+ or not.
*
......@@ -513,6 +556,8 @@ public:
*/
void disableCRC( void ) ;
/**@}*/
/**
* @name Deprecated
......@@ -837,20 +882,6 @@ public:
*/
void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len);
/**
* Enable dynamic ACKs (single write multicasting) for chosen messages
*
* @note To enable full multicasting or per-pipe multicast, use setAutoAck()
*
* @warning This MUST be called prior to attempting single write NOACK calls
* @code
* radio.enableDynamicAck();
* radio.write(&data,32,1); // Sends a payload with no acknowledgement requested
* radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK
* @endcode
*/
void enableDynamicAck();
/**
* Determine if an ack payload was received in the most recent call to
* write().
......
......@@ -15,6 +15,11 @@
#ifndef __RF24_CONFIG_H__
#define __RF24_CONFIG_H__
/*** USER DEFINES: ***/
//#define FAILURE_HANDLING
//#define DEBUG
/**********************/
#include <stdint.h>
#include <stdio.h>
#include <time.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment