From b76284700d86adb9c928b4631e33bfe288367174 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Tue, 19 Jul 2011 13:37:19 -0500 Subject: [PATCH 01/26] Broken out SPI bus configuratino to its own method. SPI access is not prefixed with this new private method to ensure proper operation when multiple SPI devices are in use. --- RF24.cpp | 28 ++++++++++++++++++++++------ RF24.h | 8 ++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 7ad80569..e5afebd7 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -28,9 +28,6 @@ void RF24::csn(int mode) { - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); digitalWrite(csn_pin,mode); } @@ -43,11 +40,21 @@ void RF24::ce(int level) /******************************************************************/ +void RF24::configSPIBus(void) +{ + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV2); +} + +/******************************************************************/ + uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) { uint8_t status; csn(LOW); + configSPIBus() ; status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); while ( len-- ) *buf++ = SPI.transfer(0xff); @@ -62,6 +69,7 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) uint8_t RF24::read_register(uint8_t reg) { csn(LOW); + configSPIBus() ; SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); uint8_t result = SPI.transfer(0xff); @@ -76,6 +84,7 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) uint8_t status; csn(LOW); + configSPIBus() ; status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); while ( len-- ) SPI.transfer(*buf++); @@ -94,6 +103,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\n\r"),reg,value)); csn(LOW); + configSPIBus() ; status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); SPI.transfer(value); csn(HIGH); @@ -110,6 +120,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) const uint8_t* current = (const uint8_t*)buf; csn(LOW); + configSPIBus() ; status = SPI.transfer( W_TX_PAYLOAD ); uint8_t data_len = min(len,payload_size); uint8_t blank_len = payload_size - data_len; @@ -131,6 +142,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) uint8_t* current = (uint8_t*)buf; csn(LOW); + configSPIBus() ; status = SPI.transfer( R_RX_PAYLOAD ); uint8_t data_len = min(len,payload_size); uint8_t blank_len = payload_size - data_len; @@ -150,6 +162,7 @@ uint8_t RF24::flush_rx(void) uint8_t status; csn(LOW); + configSPIBus() ; status = SPI.transfer( FLUSH_RX ); csn(HIGH); @@ -163,6 +176,7 @@ uint8_t RF24::flush_tx(void) uint8_t status; csn(LOW); + configSPIBus() ; status = SPI.transfer( FLUSH_TX ); csn(HIGH); @@ -176,6 +190,7 @@ uint8_t RF24::get_status(void) uint8_t status; csn(LOW); + configSPIBus() ; status = SPI.transfer( NOP ); csn(HIGH); @@ -324,10 +339,8 @@ void RF24::begin(void) // We'll use a divider of 2 which will work up to // MCU speeds of 20Mhz. // CLK:BUS 8Mhz:4Mhz, 16Mhz:8Mhz, or 20Mhz:10Mhz (max) + configSPIBus() ; SPI.begin(); - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); ce(LOW); csn(HIGH); @@ -481,6 +494,7 @@ uint8_t RF24::read_payload_length(void) uint8_t result = 0; csn(LOW); + configSPIBus() ; SPI.transfer( R_RX_PL_WID ); result = SPI.transfer(0xff); csn(HIGH); @@ -597,6 +611,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) void RF24::toggle_features(void) { csn(LOW); + configSPIBus() ; SPI.transfer( ACTIVATE ); SPI.transfer( 0x73 ); csn(HIGH); @@ -636,6 +651,7 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) const uint8_t* current = (const uint8_t*)buf; csn(LOW); + configSPIBus() ; SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); uint8_t data_len = min(len,32); while ( data_len-- ) diff --git a/RF24.h b/RF24.h index 81f10ba5..17a77997 100644 --- a/RF24.h +++ b/RF24.h @@ -60,6 +60,14 @@ class RF24 */ void ce(int level) ; + /** + * + * Setup the SPI bus. This centralizes its configuration. Use insures proper + * SPI operation whereby multiple SPI devices, having a different SPI configuration, + * are in use. + */ + void configSPIBus(void) ; + /** * Read a chunk of data in from a register * From 60dc4e01bca9da640ba80d2fa4ee854cf18c4572 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Tue, 16 Aug 2011 11:07:31 -0500 Subject: [PATCH 02/26] Hopefully fix a radio write bug. --- RF24.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index e5afebd7..dd0f958a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -42,9 +42,16 @@ void RF24::ce(int level) void RF24::configSPIBus(void) { + // Minimum ideal SPI bus speed is 2x data rate + // If we assume 2Mbs data rate and 16Mhz clock, a + // divider of 4 is the minimum we want. + // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz + // We'll use a divider of 2 which will work up to + // MCU speeds of 20Mhz. + // CLK:BUS 8Mhz:4Mhz, 16Mhz:8Mhz, or 20Mhz:10Mhz (max) SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); + SPI.setClockDivider(SPI_CLOCK_DIV4); } /******************************************************************/ @@ -332,15 +339,8 @@ void RF24::begin(void) pinMode(csn_pin,OUTPUT); // Initialize SPI bus - // Minimum ideal SPI bus speed is 2x data rate - // If we assume 2Mbs data rate and 16Mhz clock, a - // divider of 4 is the minimum we want. - // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz - // We'll use a divider of 2 which will work up to - // MCU speeds of 20Mhz. - // CLK:BUS 8Mhz:4Mhz, 16Mhz:8Mhz, or 20Mhz:10Mhz (max) - configSPIBus() ; SPI.begin(); + configSPIBus() ; ce(LOW); csn(HIGH); @@ -440,23 +440,28 @@ boolean RF24::write( const void* buf, uint8_t len ) ce(HIGH); // IN the end, the send should be blocking. It comes back in 60ms worst case, or much faster - // if I tighted up the retry logic. (Default settings will be 1500us. - // Monitor the send - uint8_t observe_tx; + // if I tighted up the retry logic. (Default settings will be 1500us). The imposed delay + // works out to be 255*ARD*ARC. This defaults to 255*1500us*15 retries = 5,737.5ms, + // or 5.7 seconds. Seems like this should be tweaked - that's a really long time. uint8_t status; + // uint8_t observe_tx; uint8_t retries = 255; + uint32_t sent_at = millis(); + const uint32_t timeout = 1500; //ms to wait for timeout + do { - status = read_register(OBSERVE_TX,&observe_tx,1); + status = read_register(STATUS); + // status = read_register(OBSERVE_TX,&observe_tx,1); IF_SERIAL_DEBUG(Serial.print(status,HEX)); - IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); + Serial.print( status, HEX ) ; if ( ! retries-- ) { - IF_SERIAL_DEBUG(printf("ABORTED: too many retries\n\r")); + IF_SERIAL_DEBUG(printf_P(PSTR("ABORTED: too many retries\n\r"))); break; } } - while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) ); + while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( millis() - sent_at < timeout ) ); if ( status & _BV(TX_DS) ) result = true; @@ -635,7 +640,7 @@ void RF24::enableAckPayload(void) write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); } - IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); + IF_SERIAL_DEBUG(printf_P(PSTR(("FEATURE=%i\n\r"),read_register(FEATURE)))); // // Enable dynamic payload on pipe 0 From 6a897800dc272cac195637204cdc2ed205b9ed3f Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Wed, 17 Aug 2011 10:31:58 -0500 Subject: [PATCH 03/26] Changed SIP bus speed to use a divider of 4 for enhanced compatibility. --- RF24.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index dd0f958a..16388f94 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -46,9 +46,6 @@ void RF24::configSPIBus(void) // If we assume 2Mbs data rate and 16Mhz clock, a // divider of 4 is the minimum we want. // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz - // We'll use a divider of 2 which will work up to - // MCU speeds of 20Mhz. - // CLK:BUS 8Mhz:4Mhz, 16Mhz:8Mhz, or 20Mhz:10Mhz (max) SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV4); From 356e3e773b2a57266bee969ad5b5a6962a14a8f7 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Wed, 17 Aug 2011 11:02:58 -0500 Subject: [PATCH 04/26] Merge clean up. --- RF24.h | 1 - 1 file changed, 1 deletion(-) diff --git a/RF24.h b/RF24.h index 5e3215a6..29c7ecaa 100644 --- a/RF24.h +++ b/RF24.h @@ -204,7 +204,6 @@ class RF24 */ void toggle_features(void); /**@}*/ ->>>>>>> 5344c5fe8cefaafa1a267267e7edefec631603d9 public: /** From 9fec262316b1ebe31388b096d928658acc92c98b Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Wed, 17 Aug 2011 11:26:49 -0500 Subject: [PATCH 05/26] Added some additional debug options (commented out) for testing. More merge cleanup (printDetails). --- RF24.cpp | 2 ++ examples/pingpair/pingpair.pde | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 9a4e5a43..e3dcaca7 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -316,10 +316,12 @@ void RF24::printDetails(void) const char * rf24_datarate_e_str[] = { "1MBPS", "2MBPS", "250KBPS" }; const char * rf24_model_e_str[] = { "nRF24L01", "nRF24L01+" } ; const char * rf24_crclength_e_str[] = { "Disabled", "8 bits", "16 bits" } ; + const char * rf24_pa_dbm_e_str[] = { "PA_MIN", "PA_LOW", "LA_MED", "PA_HIGH"} ; printf_P(PSTR("Data Rate\t = %s\n\r"),rf24_datarate_e_str[getDataRate()]); printf_P(PSTR("Model\t\t = %s\n\r"),rf24_model_e_str[isPVariant()]); printf_P(PSTR("CRC Length\t = %s\n\r"),rf24_crclength_e_str[getCRCLength()]); + printf_P(PSTR("PA Power\t = %s\n\r"),rf24_pa_dbm_e_str[getPALevel()]); } /****************************************************************************/ diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 05254c4d..3152b60b 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -27,6 +27,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 RF24 radio(8,9); +// RF24 radio(22,23); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter @@ -91,11 +92,11 @@ void setup(void) radio.begin(); // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); + // radio.setRetries(15,15); // optionally, reduce the payload size. seems to // improve reliability - radio.setPayloadSize(8); + // radio.setPayloadSize(8); // // Open pipes to other nodes for communication @@ -120,7 +121,15 @@ void setup(void) // // Start listening // - + // if( radio.setDataRate( RF24_250KBPS ) ) { + // printf( "Data rate 250KBPS set!\n\r" ) ; + // } else { + // printf( "Data rate 250KBPS set FAILED!!\n\r" ) ; + // } + // radio.setDataRate( RF24_2MBPS ) ; + // radio.setPALevel( RF24_PA_MIN ) ; + // radio.enableDynamicPayloads() ; + // radio.setAutoAck( true ) ; radio.startListening(); // @@ -153,7 +162,7 @@ void loop(void) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) + if (millis() - started_waiting_at > 250 ) timeout = true; // Describe the results From 4ccce0f9ed868de6e53c755a41f34016524dd8b7 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Wed, 17 Aug 2011 12:02:51 -0500 Subject: [PATCH 06/26] Yet more merge cleanup. Changes default channel to be US/Internationally legal while ensuring no spectrum bleed. --- RF24.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index e3dcaca7..a816c0a8 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -379,7 +379,9 @@ void RF24::begin(void) write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Set up default configuration. Callers can always change it later. - setChannel(100); + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); // Flush buffers flush_rx(); From 1607ec7e1ff476c48cda33f28d070fbbf3779ef6 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sat, 16 Mar 2013 09:53:00 -0500 Subject: [PATCH 07/26] Performance optimizations. Radio no longer automatically powers down. Radio is now allowed to enter standby. This allows for the removal of needless delays within the driver. Additional timings optimizations. If applications desire additional power savings, they should manually power down the radio as needed. Multicast write support added. ::write timeout monitoring logic changed to emcompass worst timeouts for retries/timeouts. Should never trigger anyways...but better safe. Fixes write retries timeout bug. Proper hardware delay added to powerUp. Was previously part of startWrite; which needlessly added overhead to every write/startWrite. Allows for query of hardware channel. PA documentation fixes. --- RF24_config.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 RF24_config.h diff --git a/RF24_config.h b/RF24_config.h new file mode 100644 index 00000000..fc7397fb --- /dev/null +++ b/RF24_config.h @@ -0,0 +1,65 @@ + +/* + Copyright (C) 2011 J. Coliz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ + +#if ARDUINO < 100 +#include +#else +#include +#endif + +#include + +// Stuff that is normally provided by Arduino +#ifdef ARDUINO +#include +#else +#include +#include +#include +extern HardwareSPI SPI; +#define _BV(x) (1<<(x)) +#endif + +#undef SERIAL_DEBUG +#ifdef SERIAL_DEBUG +#define IF_SERIAL_DEBUG(x) ({x;}) +#else +#define IF_SERIAL_DEBUG(x) +#endif + +// Avoid spurious warnings +#if 1 +#if ! defined( NATIVE ) && defined( ARDUINO ) +#undef PROGMEM +#define PROGMEM __attribute__(( section(".progmem.data") )) +#undef PSTR +#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) +#endif +#endif + +// Progmem is Arduino-specific +#ifdef ARDUINO +#include +#define PRIPSTR "%S" +#else +typedef char const char; +typedef uint16_t prog_uint16_t; +#define PSTR(x) (x) +#define printf_P printf +#define strlen_P strlen +#define PROGMEM +#define pgm_read_word(p) (*(p)) +#define PRIPSTR "%s" +#endif + +#endif // __RF24_CONFIG_H__ +// vim:ai:cin:sts=2 sw=2 ft=cpp From 974268bc76584531c108ce4349d07dc632ab418a Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sat, 16 Mar 2013 10:00:27 -0500 Subject: [PATCH 08/26] Previous commit message applies here. --- RF24.cpp | 380 ++++++++++++++++++++++++++++------------------------- RF24.h | 152 +++++++++++++++++---- nRF24L01.h | 4 +- 3 files changed, 332 insertions(+), 204 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index a816c0a8..24d2f13d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -1,33 +1,29 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz + Portions Copyright (C) 2011 Greg Copeland This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. */ -#include -#include -#include "nRF24L01.h" #include "RF24.h" -#undef SERIAL_DEBUG -#ifdef SERIAL_DEBUG -#define IF_SERIAL_DEBUG(x) ({x;}) -#else -#define IF_SERIAL_DEBUG(x) -#endif - -// Avoid spurious warnings -#undef PROGMEM -#define PROGMEM __attribute__(( section(".progmem.data") )) -#undef PSTR -#define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) /****************************************************************************/ void RF24::csn(int mode) { + // Minimum ideal SPI bus speed is 2x data rate + // If we assume 2Mbs data rate and 16Mhz clock, a + // divider of 4 is the minimum we want. + // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz +#ifdef ARDUINO + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + // SPI.setClockDivider(SPI_CLOCK_DIV4); + SPI.setClockDivider(SPI_CLOCK_DIV16); +#endif digitalWrite(csn_pin,mode); } @@ -40,25 +36,11 @@ void RF24::ce(int level) /****************************************************************************/ -void RF24::configSPIBus(void) -{ - // Minimum ideal SPI bus speed is 2x data rate - // If we assume 2Mbs data rate and 16Mhz clock, a - // divider of 4 is the minimum we want. - // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV4); -} - -/****************************************************************************/ - uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) { uint8_t status; csn(LOW); - configSPIBus() ; status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); while ( len-- ) *buf++ = SPI.transfer(0xff); @@ -73,7 +55,6 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) uint8_t RF24::read_register(uint8_t reg) { csn(LOW); - configSPIBus() ; SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); uint8_t result = SPI.transfer(0xff); @@ -88,7 +69,6 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) uint8_t status; csn(LOW); - configSPIBus() ; status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); while ( len-- ) SPI.transfer(*buf++); @@ -104,10 +84,9 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) { uint8_t status; - IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\n\r"),reg,value)); + IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); csn(LOW); - configSPIBus() ; status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); SPI.transfer(value); csn(HIGH); @@ -117,7 +96,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) /****************************************************************************/ -uint8_t RF24::write_payload(const void* buf, uint8_t len) +uint8_t RF24::write_payload(const void* buf, uint8_t len, uint8_t writeType) { uint8_t status; @@ -125,12 +104,11 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) uint8_t data_len = min(len,payload_size); uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - + //printf("[Writing %u bytes %u blanks]",data_len,blank_len); - + csn(LOW); - configSPIBus() ; - status = SPI.transfer( W_TX_PAYLOAD ); + status = SPI.transfer( writeType ); while ( data_len-- ) SPI.transfer(*current++); while ( blank_len-- ) @@ -153,7 +131,6 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) //printf("[Reading %u bytes %u blanks]",data_len,blank_len); csn(LOW); - configSPIBus() ; status = SPI.transfer( R_RX_PAYLOAD ); while ( data_len-- ) *current++ = SPI.transfer(0xff); @@ -171,8 +148,7 @@ uint8_t RF24::flush_rx(void) uint8_t status; csn(LOW); - configSPIBus() ; - status = SPI.transfer( FLUSH_RX ); + status = SPI.transfer( FLUSH_RX ); csn(HIGH); return status; @@ -185,8 +161,7 @@ uint8_t RF24::flush_tx(void) uint8_t status; csn(LOW); - configSPIBus() ; - status = SPI.transfer( FLUSH_TX ); + status = SPI.transfer( FLUSH_TX ); csn(HIGH); return status; @@ -199,7 +174,6 @@ uint8_t RF24::get_status(void) uint8_t status; csn(LOW); - configSPIBus() ; status = SPI.transfer( NOP ); csn(HIGH); @@ -210,7 +184,7 @@ uint8_t RF24::get_status(void) void RF24::print_status(uint8_t status) { - printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), + printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), status, (status & _BV(RX_DR))?1:0, (status & _BV(TX_DS))?1:0, @@ -224,7 +198,7 @@ void RF24::print_status(uint8_t status) void RF24::print_observe_tx(uint8_t value) { - printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r"), + printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), value, (value >> PLOS_CNT) & B1111, (value >> ARC_CNT) & B1111 @@ -233,21 +207,21 @@ void RF24::print_observe_tx(uint8_t value) /****************************************************************************/ -void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) +void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR("%S\t%c ="),name,extra_tab); + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); while (qty--) printf_P(PSTR(" 0x%02x"),read_register(reg++)); - printf_P(PSTR("\n\r")); + printf_P(PSTR("\r\n")); } /****************************************************************************/ -void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) +void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR("%S\t%c ="),name,extra_tab); + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); while (qty--) { @@ -260,14 +234,15 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) printf_P(PSTR("%02x"),*bufptr); } - printf_P(PSTR("\n\r")); + printf_P(PSTR("\r\n")); } /****************************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), payload_size(32), - ack_payload_available(false), dynamic_payloads_enabled(false) + ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), + payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), + pipe0_reading_address(0) { } @@ -278,14 +253,23 @@ 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. - write_register(RF_CH,min(channel,127)); + const uint8_t max_channel = 127; + write_register(RF_CH,min(channel,max_channel)); +} + +/****************************************************************************/ + +uint8_t RF24::getChannel( void ) +{ + return read_register( RF_CH ); } /****************************************************************************/ void RF24::setPayloadSize(uint8_t size) { - payload_size = min(size,32); + const uint8_t max_payload_size = 32; + payload_size = min(size,max_payload_size); } /****************************************************************************/ @@ -297,6 +281,39 @@ uint8_t RF24::getPayloadSize(void) /****************************************************************************/ +static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; +static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; +static const char rf24_datarate_e_str_2[] PROGMEM = "250KBPS"; +static const char * const rf24_datarate_e_str_P[] PROGMEM = { + rf24_datarate_e_str_0, + rf24_datarate_e_str_1, + rf24_datarate_e_str_2, +}; +static const char rf24_model_e_str_0[] PROGMEM = "nRF24L01"; +static const char rf24_model_e_str_1[] PROGMEM = "nRF24L01+"; +static const char * const rf24_model_e_str_P[] PROGMEM = { + rf24_model_e_str_0, + rf24_model_e_str_1, +}; +static const char rf24_crclength_e_str_0[] PROGMEM = "Disabled"; +static const char rf24_crclength_e_str_1[] PROGMEM = "8 bits"; +static const char rf24_crclength_e_str_2[] PROGMEM = "16 bits" ; +static const char * const rf24_crclength_e_str_P[] PROGMEM = { + rf24_crclength_e_str_0, + rf24_crclength_e_str_1, + rf24_crclength_e_str_2, +}; +static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_MED"; +static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_HIGH"; +static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { + rf24_pa_dbm_e_str_0, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + void RF24::printDetails(void) { print_status(get_status()); @@ -313,15 +330,10 @@ void RF24::printDetails(void) print_byte_register(PSTR("CONFIG"),CONFIG); print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); - const char * rf24_datarate_e_str[] = { "1MBPS", "2MBPS", "250KBPS" }; - const char * rf24_model_e_str[] = { "nRF24L01", "nRF24L01+" } ; - const char * rf24_crclength_e_str[] = { "Disabled", "8 bits", "16 bits" } ; - const char * rf24_pa_dbm_e_str[] = { "PA_MIN", "PA_LOW", "LA_MED", "PA_HIGH"} ; - - printf_P(PSTR("Data Rate\t = %s\n\r"),rf24_datarate_e_str[getDataRate()]); - printf_P(PSTR("Model\t\t = %s\n\r"),rf24_model_e_str[isPVariant()]); - printf_P(PSTR("CRC Length\t = %s\n\r"),rf24_crclength_e_str[getCRCLength()]); - printf_P(PSTR("PA Power\t = %s\n\r"),rf24_pa_dbm_e_str[getPALevel()]); + printf_P(PSTR("Data Rate\t = %S\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); + printf_P(PSTR("Model\t\t = %S\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); + printf_P(PSTR("CRC Length\t = %S\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); + printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); } /****************************************************************************/ @@ -334,7 +346,6 @@ void RF24::begin(void) // Initialize SPI bus SPI.begin(); - configSPIBus() ; ce(LOW); csn(HIGH); @@ -350,7 +361,7 @@ void RF24::begin(void) // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet // sizes must never be used. See documentation for a more complete explanation. - write_register(SETUP_RETR,(B0100 << ARD) | (B1111 << ARC)); + write_register(SETUP_RETR,(B0101 << ARD) | (B1111 << ARC)); // Restore our default PA level setPALevel( RF24_PA_MAX ) ; @@ -395,11 +406,13 @@ void RF24::startListening(void) write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - // Restore the pipe0 adddress - write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address) + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); // Flush buffers flush_rx(); + flush_tx(); // Go! ce(HIGH); @@ -413,6 +426,8 @@ void RF24::startListening(void) void RF24::stopListening(void) { ce(LOW); + flush_tx(); + flush_rx(); } /****************************************************************************/ @@ -427,16 +442,18 @@ void RF24::powerDown(void) void RF24::powerUp(void) { write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); + delayMicroseconds(150); } -/****************************************************************************/ +/******************************************************************/ -bool RF24::write( const void* buf, uint8_t len ) +bool RF24::write( const void* buf, uint8_t len, bool multicast ) { bool result = false; - // Begin the write - startWrite(buf,len); + // Begin the write - unicast or multicast + uint8_t writeType = multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ; + startWrite(buf,len, writeType ); // ------------ // At this point we could return from a non-blocking write, and then call @@ -446,22 +463,19 @@ bool RF24::write( const void* buf, uint8_t len ) // or MAX_RT (maximum retries, transmission failed). Also, we'll timeout in case the radio // is flaky and we get neither. - // For the timeout to ever be triggered means we've hit a serious programming error and/or - // hardware failure. It should never happen. Regardless, we'll dynamically adapt with - // ARD/ARC programming changes. This computes actual hardware AA timeout + 1ms. - const uint8_t ardarc = read_register( SETUP_RETR ) ; - const uint32_t timeout = ((((((ardarc & 0xF0) >> ARD) + 1) * 250 ) * \ - ((ardarc & 0x0F) >> ARC)) / 1000) + 1 ; - IF_SERIAL_DEBUG(Serial.print("timeout")); - IF_SERIAL_DEBUG(Serial.print(timeout)); - - // Monitor the send + // IN the end, the send should be blocking. It comes back in 1500ms worst case, or much faster + // if I tighted up the retry logic. (Default settings will be 1500ms. + // FIXME: timeout is hardware configuration dependent. This is wrong. + uint8_t observe_tx; uint8_t status; uint32_t sent_at = millis(); + const uint32_t timeout = 1500; //ms to wait for timeout + + // Monitor the send do { - read_register(STATUS, &status, 1 ) ; - IF_SERIAL_DEBUG(Serial.print(status,HEX)); + status = read_register(OBSERVE_TX,&observe_tx,1); + IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); } while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( millis() - sent_at < timeout ) ); @@ -476,8 +490,8 @@ bool RF24::write( const void* buf, uint8_t len ) // * There is an ack packet waiting (RX_DR) bool tx_ok, tx_fail; whatHappened(tx_ok,tx_fail,ack_payload_available); - // printf( "ok: %d, fail: %d, ack: %d\n\r", tx_ok, tx_fail, ack_payload_available ) ; - // printf("%u%u%u\n\r",tx_ok,tx_fail,ack_payload_available); + + //printf("%u%u%u\r\n",tx_ok,tx_fail,ack_payload_available); result = tx_ok; IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); @@ -490,33 +504,40 @@ bool RF24::write( const void* buf, uint8_t len ) IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); } - // Yay, we are done. - + // Powering down is an application requirement, not a driver requirement. + // Radio will automatically enter standby mode. + // There exists no need to flush the buffer. In fact, doing so makes it + // difficult to implement some hardware features down the road. +#if 0 // Power down powerDown(); // Flush buffers (Is this a relic of past experimentation, and not needed anymore??) flush_tx(); +#endif return result; } - - /****************************************************************************/ -void RF24::startWrite( const void* buf, uint8_t len ) +void RF24::startWrite( const void* buf, uint8_t len, const uint8_t writeType ) { // Transmitter power-up write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); - delay(2); +#if 0 + delayMicroseconds(150); +#endif // Send the payload - write_payload( buf, len ); + write_payload( buf, len, writeType ); // Allons! ce(HIGH); - delayMicroseconds(15); - delay(2); +#if 0 + delayMicroseconds(15); // Can this be 10uS?? +#else + delayMicroseconds(10); // Can this be 10uS?? +#endif ce(LOW); } @@ -527,7 +548,6 @@ uint8_t RF24::getDynamicPayloadSize(void) uint8_t result = 0; csn(LOW); - configSPIBus() ; SPI.transfer( R_RX_PL_WID ); result = SPI.transfer(0xff); csn(HIGH); @@ -610,26 +630,28 @@ void RF24::openWritingPipe(uint64_t value) write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); write_register(TX_ADDR, reinterpret_cast(&value), 5); - write_register(RX_PW_P0,min(payload_size,32)); + + const uint8_t max_payload_size = 32; + write_register(RX_PW_P0,min(payload_size,max_payload_size)); } /****************************************************************************/ -void RF24::openReadingPipe(uint8_t child, uint64_t address) +static const uint8_t child_pipe[] PROGMEM = { - const uint8_t child_pipe[] = - { - RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 - }; - const uint8_t child_payload_size[] = - { - RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 - }; - const uint8_t child_pipe_enable[] = - { - ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 - }; + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 +}; +static const uint8_t child_payload_size[] PROGMEM = +{ + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 +}; +static const uint8_t child_pipe_enable[] PROGMEM = +{ + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 +}; +void RF24::openReadingPipe(uint8_t child, uint64_t address) +{ // If this is pipe 0, cache the address. This is needed because // openWritingPipe() will overwrite the pipe 0 address, so // startListening() will have to restore it. @@ -640,16 +662,16 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) { // For pipes 2-5, only write the LSB if ( child < 2 ) - write_register(child_pipe[child], reinterpret_cast(&address), 5); + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); else - write_register(child_pipe[child], reinterpret_cast(&address), 1); + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); - write_register(child_payload_size[child],payload_size); + write_register(pgm_read_byte(&child_payload_size[child]),payload_size); // Note it would be more efficient to set all of the bits for all open // pipes at once. However, I thought it would make the calling code // more simple to do it this way. - write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(child_pipe_enable[child])); + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); } } @@ -658,7 +680,6 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) void RF24::toggle_features(void) { csn(LOW); - configSPIBus() ; SPI.transfer( ACTIVATE ); SPI.transfer( 0x73 ); csn(HIGH); @@ -679,7 +700,7 @@ void RF24::enableDynamicPayloads(void) write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); } - IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); // Enable dynamic payload on all pipes // @@ -698,17 +719,17 @@ void RF24::enableAckPayload(void) // enable ack payload and dynamic payload features // - write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); // If it didn't work, the features are not enabled if ( ! read_register(FEATURE) ) { // So enable them and try again toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); } - IF_SERIAL_DEBUG(printf_P(PSTR(("FEATURE=%i\n\r"),read_register(FEATURE)))); + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); // // Enable dynamic payload on pipes 0 & 1 @@ -724,9 +745,9 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) const uint8_t* current = reinterpret_cast(buf); csn(LOW); - configSPIBus() ; SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); - uint8_t data_len = min(len,32); + const uint8_t max_payload_size = 32; + uint8_t data_len = min(len,max_payload_size); while ( data_len-- ) SPI.transfer(*current++); @@ -799,27 +820,27 @@ void RF24::setPALevel(rf24_pa_dbm_e level) uint8_t setup = read_register(RF_SETUP) ; setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - switch( level ) + // switch uses RAM (evil!) + if ( level == RF24_PA_MAX ) { - case RF24_PA_MAX: setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - break ; - - case RF24_PA_HIGH: + } + else if ( level == RF24_PA_HIGH ) + { setup |= _BV(RF_PWR_HIGH) ; - break ; - - case RF24_PA_LOW: - setup |= _BV(RF_PWR_LOW) ; - break ; - - case RF24_PA_MIN: - break ; - - case RF24_PA_ERROR: + } + else if ( level == RF24_PA_LOW ) + { + setup |= _BV(RF_PWR_LOW); + } + else if ( level == RF24_PA_MIN ) + { + // nothing + } + else if ( level == RF24_PA_ERROR ) + { // On error, go to maximum PA setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - break ; } write_register( RF_SETUP, setup ) ; @@ -832,23 +853,22 @@ rf24_pa_dbm_e RF24::getPALevel(void) rf24_pa_dbm_e result = RF24_PA_ERROR ; uint8_t power = read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - switch( power ) + // switch uses RAM (evil!) + if ( power == (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ) { - case (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)): result = RF24_PA_MAX ; - break ; - - case _BV(RF_PWR_HIGH): + } + else if ( power == _BV(RF_PWR_HIGH) ) + { result = RF24_PA_HIGH ; - break ; - - case _BV(RF_PWR_LOW): + } + else if ( power == _BV(RF_PWR_LOW) ) + { result = RF24_PA_LOW ; - break ; - - default: + } + else + { result = RF24_PA_MIN ; - break ; } return result ; @@ -906,27 +926,25 @@ bool RF24::setDataRate(rf24_datarate_e speed) rf24_datarate_e RF24::getDataRate( void ) { rf24_datarate_e result ; - uint8_t setup = read_register(RF_SETUP) ; - + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + // switch uses RAM (evil!) // Order matters in our case below - switch( setup & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ) + if ( dr == _BV(RF_DR_LOW) ) { - case _BV(RF_DR_LOW): // '10' = 250KBPS result = RF24_250KBPS ; - break ; - - case _BV(RF_DR_HIGH): + } + else if ( dr == _BV(RF_DR_HIGH) ) + { // '01' = 2MBPS result = RF24_2MBPS ; - break ; - - default: + } + else + { // '00' = 1MBPS result = RF24_1MBPS ; - break ; } - return result ; } @@ -935,23 +953,21 @@ rf24_datarate_e RF24::getDataRate( void ) void RF24::setCRCLength(rf24_crclength_e length) { uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - - switch (length) + + // switch uses RAM (evil!) + if ( length == RF24_CRC_DISABLED ) { - case RF24_CRC_DISABLED: - break; - - case RF24_CRC_8: - config |= _BV(EN_CRC); - break; - - case RF24_CRC_16: - default: - config |= _BV(EN_CRC); - config |= _BV( CRCO ); - break; + // Do nothing, we turned it off above. + } + else if ( length == RF24_CRC_8 ) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV( CRCO ); } - write_register( CONFIG, config ) ; } @@ -982,10 +998,18 @@ void RF24::disableCRC( void ) } /****************************************************************************/ + void RF24::setRetries(uint8_t delay, uint8_t count) { write_register(SETUP_RETR,(delay&0xf)< + Copyright (C) 2011 J. Coliz + Portions Copyright (C) 2011 Greg Copeland This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. */ +/** + * @file RF24.h + * + * Class declaration for RF24 and helper enums + */ + #ifndef __RF24_H__ #define __RF24_H__ -#include -#include +#include +#include +/** + * Power Amplifier level. + * + * For use with setPALevel() + */ typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; + +/** + * Data rate. How fast data moves through the air. + * + * For use with setDataRate() + */ typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +/** + * CRC Length. How big (if any) of a CRC is included. + * + * For use with setCRCLength() + */ typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; /** @@ -46,6 +70,11 @@ class RF24 /** * Set chip select pin * + * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data + * and best of all, we make use of the radio's FIFO buffers. A lower speed + * means we're less likely to effectively leverage our FIFOs and pay a higher + * AVR runtime cost as toll. + * * @param mode HIGH to take this unit off the SPI bus, LOW to put it on */ void csn(int mode); @@ -58,20 +87,6 @@ class RF24 */ void ce(int level); - /** - * - * Setup the SPI bus. This centralizes its configuration. Use insures proper - * SPI operation whereby multiple SPI devices, having a different SPI configuration, - * are in use. - * - * Running SPI bus at PI_CLOCK_DIV4 so we don't waste time transferring data - * and best of all, we make use of the radio's FIFO buffers. A lower speed - * means we're less likely to effectively leverage our FIFOs and pay a higher - * AVR runtime cost as toll. - * - */ - void configSPIBus(void) ; - /** * Read a chunk of data in from a register * @@ -118,7 +133,7 @@ class RF24 * @param len Number of bytes to be sent * @return Current value of status register */ - uint8_t write_payload(const void* buf, uint8_t len); + uint8_t write_payload(const void* buf, uint8_t len, uint8_t writeType=W_TX_PAYLOAD); /** * Read the receive payload @@ -181,7 +196,7 @@ class RF24 * @param reg Which register. Use constants from nRF24L01.h * @param qty How many successive registers to print */ - void print_byte_register(prog_char* name, uint8_t reg, uint8_t qty = 1); + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); /** * Print the name and value of a 40-bit address register to stdout @@ -194,7 +209,7 @@ class RF24 * @param reg Which register. Use constants from nRF24L01.h * @param qty How many successive registers to print */ - void print_address_register(prog_char* name, uint8_t reg, uint8_t qty = 1); + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); /** * Turn on or off the special features of the chip @@ -265,7 +280,7 @@ class RF24 * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not */ - bool write( const void* buf, uint8_t len ); + bool write( const void* buf, uint8_t len, bool multicast=false ); /** * Test whether there are bytes available to be read @@ -354,6 +369,16 @@ class RF24 */ void setRetries(uint8_t delay, uint8_t count); + /**@{*/ + /** + * Get delay and count values of the radio + * + * @param high and low nibbles of delay and count as currently configured on + * the radio. Valid ranges for both nibbles are 0x00-0x0f. The delay nibble + * translates as 0=250us, 15=4000us, in bit multiples of 250us. + */ + uint8_t getRetries( void ) ; + /** * Set RF communication channel * @@ -361,6 +386,13 @@ class RF24 */ void setChannel(uint8_t channel); + /** + * Get RF communication channel + * + * @param channel To which RF channel radio is current tuned, 0-127 + */ + uint8_t getChannel(void); + /** * Set Static Payload Size * @@ -448,7 +480,7 @@ class RF24 * Relative mnemonics have been used to allow for future PA level * changes. According to 6.5 of the nRF24L01+ specification sheet, * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, - * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. + * RF24_PA_HIGH=-6dBM, and RF24_PA_MAX=0dBm. * * @param level Desired PA level. */ @@ -557,7 +589,7 @@ class RF24 * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not */ - void startWrite( const void* buf, uint8_t len ); + void startWrite( const void* buf, uint8_t len, uint8_t writeType=W_TX_PAYLOAD); /** * Write an ack payload for the specified pipe @@ -627,6 +659,27 @@ class RF24 /**@}*/ }; +/** + * @example GettingStarted.pde + * + * This is an example which corresponds to my "Getting Started" blog post: + * Getting Started with nRF24L01+ on Arduino. + * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + /** * @example led_remote.pde * @@ -646,6 +699,21 @@ class RF24 * The ping node can then see how long the whole cycle took. */ +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + /** * @example starping.pde * @@ -700,12 +768,26 @@ class RF24 /** * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver * - * Design Goals: This library is designed to be... + * @section Goals Design Goals + * + * This library is designed to be... * @li Maximally compliant with the intended operation of the chip * @li Easy for beginners to use * @li Consumed with a public interface that's similiar to other Arduino standard libraries - * @li Built against the standard SPI library. * + * @section News News + * + * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. + * Please open an issue if you find any problems using it with any version of Arduino. + * + * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the + * Maple Native, + * and should work with any Maple board. See the pingpair_maple example. + * Note that only the pingpair_maple example has been tested on Maple, although + * the others can certainly be adapted. + * + * @section Useful Useful References + * * Please refer to: * * @li Documentation Main Page @@ -720,6 +802,26 @@ class RF24 * @section More More Information * * @subpage FAQ + * + * @section Projects Projects + * + * Stuff I have built with RF24 + * + * RF24 Getting Started - Finished Product + * + * Getting Started with nRF24L01+ on Arduino + * + * Nordic FOB and nRF24L01+ + * + * Using the Sparkfun Nordic FOB + * + * RF Duinode V3 (2V4) + * + * Low-Power Wireless Sensor Node + * + * nRF24L01+ connected to Leaf Labs Maple Native + * + * nRF24L01+ Running on Maple */ #endif // __RF24_H__ diff --git a/nRF24L01.h b/nRF24L01.h index 2012ce6e..9943c3af 100644 --- a/nRF24L01.h +++ b/nRF24L01.h @@ -1,5 +1,6 @@ /* Copyright (c) 2007 Stefan Engelke + Portions Copyright (C) 2011 Greg Copeland Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -116,7 +117,8 @@ #define LNA_HCURR 0 /* P model memory Map */ -#define RPD 0x09 +#define RPD 0x09 +#define W_TX_PAYLOAD_NO_ACK 0xB0 /* P model bit Mnemonics */ #define RF_DR_LOW 5 From 6db1d40ced48d0eb432c8c5e829f272fb9c9cdca Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sat, 16 Mar 2013 11:32:59 -0500 Subject: [PATCH 09/26] Fixed write hardware timeout monitoring. Timeout is now dynamically calculated based on current hardware configuration. There is room for optimization via caching. --- RF24.cpp | 17 +++++++++++++---- RF24.h | 10 ++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 24d2f13d..85f2e2c4 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -463,13 +463,12 @@ bool RF24::write( const void* buf, uint8_t len, bool multicast ) // or MAX_RT (maximum retries, transmission failed). Also, we'll timeout in case the radio // is flaky and we get neither. - // IN the end, the send should be blocking. It comes back in 1500ms worst case, or much faster - // if I tighted up the retry logic. (Default settings will be 1500ms. - // FIXME: timeout is hardware configuration dependent. This is wrong. + // IN the end, the send should be blocking. It comes back in 60ms worst case. + // Generally much faster. uint8_t observe_tx; uint8_t status; uint32_t sent_at = millis(); - const uint32_t timeout = 1500; //ms to wait for timeout + const uint16_t timeout = getMaxTimeout() ; //ms to wait for timeout // Monitor the send do @@ -1011,5 +1010,15 @@ uint8_t RF24::getRetries( void ) return read_register( SETUP_RETR ) ; } +/****************************************************************************/ + +uint16_t RF24::getMaxTimeout( void ) +{ + uint8_t retries = getRetries() ; + uint16_t to = ((250 + (250 * ((retries & 0xf0) >> 4))) * (retries & 0x0f)) / 1000 ; + + return to ; +} + // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 00dedb7d..7bd6628b 100644 --- a/RF24.h +++ b/RF24.h @@ -278,6 +278,7 @@ class RF24 * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent + * @param multicast Buffer will be multicast; ignoring retry/timeoute * @return True if the payload was delivered successfully false if not */ bool write( const void* buf, uint8_t len, bool multicast=false ); @@ -656,6 +657,15 @@ class RF24 */ bool testRPD(void) ; + + /** + * Calculate the maximum timeout in ms based on current hardware + * configuration. + * + * @return ms of maximum timeout; accounting for retries + */ + uint16_t getMaxTimeout(void) ; + /**@}*/ }; From 65a291edc62b1708b0f95c01226d5643c94f1bc4 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sat, 16 Mar 2013 11:37:02 -0500 Subject: [PATCH 10/26] Fixed spelling. --- RF24.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.h b/RF24.h index 7bd6628b..3912d1c3 100644 --- a/RF24.h +++ b/RF24.h @@ -278,7 +278,7 @@ class RF24 * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent - * @param multicast Buffer will be multicast; ignoring retry/timeoute + * @param multicast Buffer will be multicast; ignoring retry/timeout * @return True if the payload was delivered successfully false if not */ bool write( const void* buf, uint8_t len, bool multicast=false ); From e9788ccc6b10f5beb5cf068f60fec8604f423e6a Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sat, 16 Mar 2013 12:10:26 -0500 Subject: [PATCH 11/26] Changed timeout calculation from ms to us as usual math resulted in loss of resolution and the possibility of a premature timeout. The write monitoring loop now uses us rather than ms to be more compatible with the us result returned from getMaxTimeout. --- RF24.cpp | 10 +++++----- RF24.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 85f2e2c4..ec52093e 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -467,8 +467,8 @@ bool RF24::write( const void* buf, uint8_t len, bool multicast ) // Generally much faster. uint8_t observe_tx; uint8_t status; - uint32_t sent_at = millis(); - const uint16_t timeout = getMaxTimeout() ; //ms to wait for timeout + uint32_t sent_at = micros(); + const uint16_t timeout = getMaxTimeout() ; //us to wait for timeout // Monitor the send do @@ -476,7 +476,7 @@ bool RF24::write( const void* buf, uint8_t len, bool multicast ) status = read_register(OBSERVE_TX,&observe_tx,1); IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); } - while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( millis() - sent_at < timeout ) ); + while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( micros() - sent_at < timeout ) ); // The part above is what you could recreate with your own interrupt handler, // and then call this when you got an interrupt @@ -1015,8 +1015,8 @@ uint8_t RF24::getRetries( void ) uint16_t RF24::getMaxTimeout( void ) { uint8_t retries = getRetries() ; - uint16_t to = ((250 + (250 * ((retries & 0xf0) >> 4))) * (retries & 0x0f)) / 1000 ; - + uint16_t to = ((250 + (250 * ((retries & 0xf0) >> 4))) * (retries & 0x0f)) ; + return to ; } diff --git a/RF24.h b/RF24.h index 3912d1c3..0af2d1a6 100644 --- a/RF24.h +++ b/RF24.h @@ -659,10 +659,10 @@ class RF24 /** - * Calculate the maximum timeout in ms based on current hardware + * Calculate the maximum timeout in us based on current hardware * configuration. * - * @return ms of maximum timeout; accounting for retries + * @return us of maximum timeout; accounting for retries */ uint16_t getMaxTimeout(void) ; From d95d703f39906e29f4c6939ac3b4688b5a9d5b7d Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sun, 17 Mar 2013 09:45:45 -0500 Subject: [PATCH 12/26] Normalized write and startWrite interfaces for multicast. Now both take a bool for multicasting. Documentation updates. --- RF24.cpp | 29 +++++++++++------------------ RF24.h | 12 +++++++++--- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index ec52093e..7a98fd01 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -305,8 +305,8 @@ static const char * const rf24_crclength_e_str_P[] PROGMEM = { }; static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; -static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_MED"; -static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_HIGH"; +static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_MAX"; static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { rf24_pa_dbm_e_str_0, rf24_pa_dbm_e_str_1, @@ -447,13 +447,12 @@ void RF24::powerUp(void) /******************************************************************/ -bool RF24::write( const void* buf, uint8_t len, bool multicast ) +bool RF24::write( const void* buf, uint8_t len, const bool multicast ) { bool result = false; - // Begin the write - unicast or multicast - uint8_t writeType = multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ; - startWrite(buf,len, writeType ); + // Begin the write + startWrite( buf, len, multicast ); // ------------ // At this point we could return from a non-blocking write, and then call @@ -519,24 +518,18 @@ bool RF24::write( const void* buf, uint8_t len, bool multicast ) } /****************************************************************************/ -void RF24::startWrite( const void* buf, uint8_t len, const uint8_t writeType ) +void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ) { // Transmitter power-up write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); -#if 0 - delayMicroseconds(150); -#endif - // Send the payload - write_payload( buf, len, writeType ); + // Send the payload - Unicast (W_TX_PAYLOAD) or multicast (W_TX_PAYLOAD_NO_ACK) + write_payload( buf, len, + multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ) ; // Allons! ce(HIGH); -#if 0 - delayMicroseconds(15); // Can this be 10uS?? -#else - delayMicroseconds(10); // Can this be 10uS?? -#endif + delayMicroseconds(10); ce(LOW); } @@ -1016,7 +1009,7 @@ uint16_t RF24::getMaxTimeout( void ) { uint8_t retries = getRetries() ; uint16_t to = ((250 + (250 * ((retries & 0xf0) >> 4))) * (retries & 0x0f)) ; - + return to ; } diff --git a/RF24.h b/RF24.h index 0af2d1a6..e7391562 100644 --- a/RF24.h +++ b/RF24.h @@ -56,6 +56,9 @@ class RF24 bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ +#if 0 + uint64_t multicast_address; /** Address used for multicast */ +#endif protected: /** @@ -278,10 +281,11 @@ class RF24 * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent - * @param multicast Buffer will be multicast; ignoring retry/timeout + * @param multicast true or false. True, buffer will be multicast; ignoring retry/timeout * @return True if the payload was delivered successfully false if not + * for multicast payloads, true only means it was transmitted. */ - bool write( const void* buf, uint8_t len, bool multicast=false ); + bool write( const void* buf, uint8_t len, const bool multicast=false ); /** * Test whether there are bytes available to be read @@ -588,9 +592,11 @@ class RF24 * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent + * @param multicast true or false. True, buffer will be multicast; ignoring retry/timeout * @return True if the payload was delivered successfully false if not + * for multicast payloads, true only means it was transmitted. */ - void startWrite( const void* buf, uint8_t len, uint8_t writeType=W_TX_PAYLOAD); + void startWrite( const void* buf, uint8_t len, const bool multicast=false ); /** * Write an ack payload for the specified pipe From 0e184ab02dbaf3316a803066293a7f6f013a2a66 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sun, 17 Mar 2013 10:27:44 -0500 Subject: [PATCH 13/26] Updated documentation for multicast warnings. --- RF24.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RF24.h b/RF24.h index e7391562..15161e3d 100644 --- a/RF24.h +++ b/RF24.h @@ -437,6 +437,8 @@ class RF24 * Ack payloads are a handy way to return data back to senders without * manually changing the radio modes on both units. * + * @warning Do note, multicast payloads will not trigger ack payloads. + * * @see examples/pingpair_pl/pingpair_pl.pde */ void enableAckPayload(void); @@ -604,6 +606,8 @@ class RF24 * The next time a message is received on @p pipe, the data in @p buf will * be sent back in the acknowledgement. * + * @warning Do note, multicast payloads will not trigger ack payloads. + * * @warning According to the data sheet, only three of these can be pending * at any time. I have not tested this. * From 03d22b58849c1d1830e5a0353171147c1ccd5e4b Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sun, 17 Mar 2013 10:30:08 -0500 Subject: [PATCH 14/26] More doc updates. --- RF24.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/RF24.h b/RF24.h index 15161e3d..6ef80dd5 100644 --- a/RF24.h +++ b/RF24.h @@ -595,8 +595,6 @@ class RF24 * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent * @param multicast true or false. True, buffer will be multicast; ignoring retry/timeout - * @return True if the payload was delivered successfully false if not - * for multicast payloads, true only means it was transmitted. */ void startWrite( const void* buf, uint8_t len, const bool multicast=false ); From 53d8a02aecc9804e5548cc0d581c28a4e56fa1a7 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sun, 17 Mar 2013 12:42:28 -0500 Subject: [PATCH 15/26] Minor cleanup. Documentation change for multicast. --- RF24.cpp | 18 +++--------------- RF24.h | 7 +++---- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 7a98fd01..6e4bff62 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -96,7 +96,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) /****************************************************************************/ -uint8_t RF24::write_payload(const void* buf, uint8_t len, uint8_t writeType) +uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeType) { uint8_t status; @@ -240,9 +240,9 @@ 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(true), p_variant(false), + ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false), payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), - pipe0_reading_address(0) + pipe0_reading_address(0xe7e7e7e7e7) { } @@ -502,18 +502,6 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast ) IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); } - // Powering down is an application requirement, not a driver requirement. - // Radio will automatically enter standby mode. - // There exists no need to flush the buffer. In fact, doing so makes it - // difficult to implement some hardware features down the road. -#if 0 - // Power down - powerDown(); - - // Flush buffers (Is this a relic of past experimentation, and not needed anymore??) - flush_tx(); -#endif - return result; } /****************************************************************************/ diff --git a/RF24.h b/RF24.h index 6ef80dd5..ccce53e8 100644 --- a/RF24.h +++ b/RF24.h @@ -56,9 +56,6 @@ class RF24 bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ -#if 0 - uint64_t multicast_address; /** Address used for multicast */ -#endif protected: /** @@ -136,7 +133,7 @@ class RF24 * @param len Number of bytes to be sent * @return Current value of status register */ - uint8_t write_payload(const void* buf, uint8_t len, uint8_t writeType=W_TX_PAYLOAD); + uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); /** * Read the receive payload @@ -349,6 +346,8 @@ class RF24 * pipe 0 for reading, and then startListening(), it will overwrite the * writing pipe. Ergo, do an openWritingPipe() again before write(). * + * @warning Pipe 0 is also used as the multicast address pipe. + * * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits * * @param number Which pipe# to open, 0-5. From f066fc2691169405b8a24e958404a56c0cc61dfd Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sun, 17 Mar 2013 14:44:23 -0500 Subject: [PATCH 16/26] Docs. --- RF24.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RF24.h b/RF24.h index ccce53e8..2d1d0411 100644 --- a/RF24.h +++ b/RF24.h @@ -346,7 +346,8 @@ class RF24 * pipe 0 for reading, and then startListening(), it will overwrite the * writing pipe. Ergo, do an openWritingPipe() again before write(). * - * @warning Pipe 0 is also used as the multicast address pipe. + * @warning Pipe 0 is also used as the multicast address pipe. Pipe 1 + * is the unicast pipe address. * * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits * From 204062520bf88b710d092215aabf63e5a08c09d3 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sun, 17 Mar 2013 17:35:52 -0500 Subject: [PATCH 17/26] Example changes. Pingpair now reports a more accurate round drip without artificial delay. Examples use more options. Added a new multicast examples based on pingpair_dyn; pingpair_multi_dyn. --- RF24.cpp | 4 +- examples/pingpair/pingpair.pde | 30 +-- examples/pingpair/printf.h | 2 +- examples/pingpair_dyn/pingpair_dyn.pde | 18 +- examples/pingpair_dyn/printf.h | 2 - examples/pingpair_multi_dyn/Jamfile | 206 ++++++++++++++ .../pingpair_multi_dyn/pingpair_multi_dyn.pde | 253 ++++++++++++++++++ examples/pingpair_multi_dyn/printf.h | 31 +++ 8 files changed, 515 insertions(+), 31 deletions(-) create mode 100644 examples/pingpair_multi_dyn/Jamfile create mode 100644 examples/pingpair_multi_dyn/pingpair_multi_dyn.pde create mode 100644 examples/pingpair_multi_dyn/printf.h diff --git a/RF24.cpp b/RF24.cpp index 6e4bff62..0402d761 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -242,7 +242,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), payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), - pipe0_reading_address(0xe7e7e7e7e7) + pipe0_reading_address(0) { } @@ -305,7 +305,7 @@ static const char * const rf24_crclength_e_str_P[] PROGMEM = { }; static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; -static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_HIGH"; +static const char rf24_pa_dbm_e_str_2[] PROGMEM = "PA_HIGH"; static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_MAX"; static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { rf24_pa_dbm_e_str_0, diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 3152b60b..449f5377 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -16,8 +16,7 @@ */ #include -#include "nRF24L01.h" -#include "RF24.h" +#include #include "printf.h" // @@ -27,7 +26,6 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 RF24 radio(8,9); -// RF24 radio(22,23); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter @@ -122,14 +120,15 @@ void setup(void) // Start listening // // if( radio.setDataRate( RF24_250KBPS ) ) { - // printf( "Data rate 250KBPS set!\n\r" ) ; + // printf( "Data rate 250KBPS set!\n\r" ) ; // } else { - // printf( "Data rate 250KBPS set FAILED!!\n\r" ) ; + // printf( "Data rate 250KBPS set FAILED!!\n\r" ) ; // } // radio.setDataRate( RF24_2MBPS ) ; - // radio.setPALevel( RF24_PA_MIN ) ; - // radio.enableDynamicPayloads() ; - // radio.setAutoAck( true ) ; + // radio.setPALevel( RF24_PA_MAX ) ; + radio.enableDynamicPayloads() ; + radio.setAutoAck( true ) ; + radio.powerUp() ; radio.startListening(); // @@ -162,13 +161,14 @@ void loop(void) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) + if (millis() - started_waiting_at > 1+(radio.getMaxTimeout()/1000) ) timeout = true; // Describe the results if ( timeout ) { printf("Failed, response timed out.\n\r"); + printf("Timeout duration: %d\n\r", (1+radio.getMaxTimeout()/1000) ) ; } else { @@ -200,21 +200,15 @@ void loop(void) { // Fetch the payload, and see if this was the last one. done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - delay(20); } // First, stop listening so we can talk radio.stopListening(); - // Send the final one back. + // Send the final one back. This way, we don't delay + // the reply while we wait on serial i/o. radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); + printf("Sent response %lu\n\r", got_time); // Now, resume listening so we catch the next packets. radio.startListening(); diff --git a/examples/pingpair/printf.h b/examples/pingpair/printf.h index df6c46ae..05dd0888 100644 --- a/examples/pingpair/printf.h +++ b/examples/pingpair/printf.h @@ -16,7 +16,7 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" +#include int serial_putc( char c, FILE * ) { diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde index 5d3be9e0..78e8b320 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -13,7 +13,6 @@ */ #include -#include "nRF24L01.h" #include "RF24.h" #include "printf.h" @@ -23,7 +22,8 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +//RF24 radio(8,9); +RF24 radio(22,23); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter @@ -34,7 +34,7 @@ const int role_pin = 7; // // Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; +const uint64_t pipes[2] = { 0xEEFDFDFDECLL, 0xEEFDFDF0DFLL }; // // Role management @@ -59,9 +59,9 @@ role_e role; // Payload // -const int min_payload_size = 4; +const int min_payload_size = 1; const int max_payload_size = 32; -const int payload_size_increments_by = 2; +const int payload_size_increments_by = 1; int next_payload_size = min_payload_size; char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char @@ -99,10 +99,12 @@ void setup(void) radio.begin(); // enable dynamic payloads + radio.setCRCLength( RF24_CRC_16 ) ; radio.enableDynamicPayloads(); // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); + radio.setAutoAck( true ) ; + radio.setPALevel( RF24_PA_HIGH ) ; // // Open pipes to other nodes for communication @@ -153,7 +155,7 @@ void loop(void) // Take the time, and send it. This will block until complete printf("Now sending length %i...",next_payload_size); - radio.write( send_payload, next_payload_size ); + radio.write( send_payload, next_payload_size, false ); // Now, continue listening radio.startListening(); @@ -162,7 +164,7 @@ void loop(void) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 500 ) + if (millis() - started_waiting_at > 1 + radio.getMaxTimeout()/1000 ) timeout = true; // Describe the results diff --git a/examples/pingpair_dyn/printf.h b/examples/pingpair_dyn/printf.h index df6c46ae..1b853db6 100644 --- a/examples/pingpair_dyn/printf.h +++ b/examples/pingpair_dyn/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair_multi_dyn/Jamfile b/examples/pingpair_multi_dyn/Jamfile new file mode 100644 index 00000000..901f8da8 --- /dev/null +++ b/examples/pingpair_multi_dyn/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/pingpair_multi_dyn/pingpair_multi_dyn.pde b/examples/pingpair_multi_dyn/pingpair_multi_dyn.pde new file mode 100644 index 00000000..8809023d --- /dev/null +++ b/examples/pingpair_multi_dyn/pingpair_multi_dyn.pde @@ -0,0 +1,253 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +/** + * Example using Dynamic Payloads + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +#include +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +RF24 radio(8,9); + +// Use multicast? +// sets the multicast behavior this unit in hardware. Connect to GND to use unicast +// Leave open (default) to use multicast. +const int multicast_pin = 6 ; + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 7; +bool multicast = true ; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xEEFAFDFDEELL, 0xEEFDFAF50DFLL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +// +// Payload +// + +const int min_payload_size = 1; +const int max_payload_size = 32; +const int payload_size_increments_by = 1; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + +void setup(void) +{ + // + // Multicast + // + pinMode(multicast_pin, INPUT); + digitalWrite(multicast_pin,HIGH); + delay( 20 ) ; + + // read multicast role, LOW for unicast + if( digitalRead( multicast_pin ) ) + multicast = true ; + else + multicast = false ; + + + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay( 20 ); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_multi_dyn/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("MULTICAST: %s\r\n",(multicast?"true (unreliable)":"false (reliable)")); + // + // Setup and configure rf radio + // + + radio.begin(); + + // enable dynamic payloads + radio.enableDynamicPayloads(); + radio.setCRCLength( RF24_CRC_16 ) ; + + // optionally, increase the delay between retries & # of retries + radio.setRetries( 15, 5 ) ; + radio.setAutoAck( true ) ; + //radio.setPALevel( RF24_PA_LOW ) ; + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + radio.powerUp() ; + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + printf("Now sending length %i...",next_payload_size); + radio.write( send_payload, next_payload_size, multicast ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 1 + radio.getMaxTimeout()/1000 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + uint8_t len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got response size=%i value=%s\n\r",len,receive_payload); + } + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; + + // Try again 1s later + delay(250); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + uint8_t len; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + len = radio.getDynamicPayloadSize(); + done = radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got payload size=%i value=%s\n\r",len,receive_payload); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( receive_payload, len, multicast ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_multi_dyn/printf.h b/examples/pingpair_multi_dyn/printf.h new file mode 100644 index 00000000..1b853db6 --- /dev/null +++ b/examples/pingpair_multi_dyn/printf.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From ac63bec6166135c538885aeeef907fc0494fbccc Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Mon, 18 Mar 2013 09:15:06 -0500 Subject: [PATCH 18/26] Removed redundant headers as its already included, and required, by RF24.h. Fixed some radio power up/down issues in code which relied on side effect behavior of radio powering down after every TX. Applications now take responsibility for power management. --- examples/led_remote/led_remote.pde | 1 - examples/pingpair_irq/pingpair_irq.pde | 3 ++- examples/pingpair_irq/printf.h | 1 - examples/pingpair_sleepy/pingpair_sleepy.pde | 2 +- tests/pingpair_blocking/pingpair_blocking.pde | 1 - tests/pingpair_test/pingpair_test.pde | 1 - 6 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index 45f971d0..22388453 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -26,7 +26,6 @@ */ #include -#include "nRF24L01.h" #include "RF24.h" #include "printf.h" diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index bf565cb6..58eec332 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -144,10 +144,11 @@ void loop(void) // Take the time, and send it. unsigned long time = millis(); printf("Now sending %lu\n\r",time); + radio.powerUp() ; radio.startWrite( &time, sizeof(unsigned long) ); // Try again soon - delay(2000); + delay(1000); } // diff --git a/examples/pingpair_irq/printf.h b/examples/pingpair_irq/printf.h index df6c46ae..ef29df73 100644 --- a/examples/pingpair_irq/printf.h +++ b/examples/pingpair_irq/printf.h @@ -16,7 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" int serial_putc( char c, FILE * ) { diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index dea0d3c8..0acf623d 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -23,7 +23,6 @@ #include #include #include -#include "nRF24L01.h" #include "RF24.h" #include "printf.h" @@ -283,6 +282,7 @@ void do_sleep(void) sleep_mode(); // System sleeps here sleep_disable(); // System continues execution here when watchdog timed out + radio.powerUp() ; } // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/tests/pingpair_blocking/pingpair_blocking.pde b/tests/pingpair_blocking/pingpair_blocking.pde index 817310de..2a2f080b 100644 --- a/tests/pingpair_blocking/pingpair_blocking.pde +++ b/tests/pingpair_blocking/pingpair_blocking.pde @@ -7,7 +7,6 @@ */ #include -#include "nRF24L01.h" #include "RF24.h" #include "printf.h" diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index c16db2b2..64197147 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -19,7 +19,6 @@ */ #include -#include "nRF24L01.h" #include "RF24.h" #include "printf.h" From 52e196b17e3b4ed693ddd4877ba3f50177713195 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Mon, 18 Mar 2013 09:20:09 -0500 Subject: [PATCH 19/26] Removed redundant arduino < 100 header include. --- examples/pingpair_sleepy/printf.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/pingpair_sleepy/printf.h b/examples/pingpair_sleepy/printf.h index df6c46ae..1b853db6 100644 --- a/examples/pingpair_sleepy/printf.h +++ b/examples/pingpair_sleepy/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); From 2c1317f44e69287a9f8ef8cb4fad1bcc32300a30 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Mon, 18 Mar 2013 10:19:36 -0500 Subject: [PATCH 20/26] More cleanup. --- examples/scanner/printf.h | 2 -- examples/starping/printf.h | 2 -- tests/pingpair_test/pingpair_test.pde | 7 ++++--- tests/pingpair_test/printf.h | 2 -- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/scanner/printf.h b/examples/scanner/printf.h index df6c46ae..1b853db6 100644 --- a/examples/scanner/printf.h +++ b/examples/scanner/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/starping/printf.h b/examples/starping/printf.h index df6c46ae..1b853db6 100644 --- a/examples/starping/printf.h +++ b/examples/starping/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 64197147..403c1953 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -84,7 +84,7 @@ bool notified; //*< Have we notified the user we're done? */ const int num_needed = 10; //*< How many success/failures until we're done? */ int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ -const int interval = 100; //*< ms to wait between sends */ +int interval = 100; //*< ms to wait between sends */ char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ @@ -228,14 +228,13 @@ void setup(void) // // Dump the configuration of the rf unit for debugging // - radio.printDetails(); // // Attach interrupt handler to interrupt #0 (using pin 2) // on BOTH the sender and receiver // - + delay(40) ; attachInterrupt(0, check_radio, FALLING); } @@ -261,6 +260,7 @@ void loop(void) radio.stopListening(); // Send it. This will block until complete + radio.powerUp() ; printf("\n\rNow sending length %i...",next_payload_size); radio.startWrite( send_payload, next_payload_size ); @@ -270,6 +270,7 @@ void loop(void) next_payload_size = min_payload_size; // Try again soon + interval = 1 + (radio.getMaxTimeout()/1000) ; delay(interval); // Timeout if we have not received anything back ever diff --git a/tests/pingpair_test/printf.h b/tests/pingpair_test/printf.h index df6c46ae..1b853db6 100644 --- a/tests/pingpair_test/printf.h +++ b/tests/pingpair_test/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); From 5558e4549d8cbcb7990cae902a1064ad40294d4f Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Thu, 21 Mar 2013 12:56:28 -0500 Subject: [PATCH 21/26] Cleanup, fork documentations to README.md, and use of dynamic timeout in tests. --- README.md | 13 +++++++++++-- keywords.txt | 3 ++- tests/pingpair_blocking/pingpair_blocking.pde | 9 ++++----- tests/pingpair_blocking/printf.h | 2 -- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c0e71c09..9d30e6cd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Arduino driver for nRF24L01 2.4GHz Wireless Transceiver +# Arduino driver for nRF24L01(+) 2.4GHz Wireless Transceiver Design Goals: This library is designed to be... @@ -7,6 +7,13 @@ Design Goals: This library is designed to be... * Consumed with a public interface that's similiar to other Arduino standard libraries * Built against the standard SPI library. +* Modifications to the RF24 library in this fork is backward compatible. A single + enhancement which may cause issue, is code which relies on the driver to power down the + radio, as a side effect. The radio is no longer powered down after each transmit. Rather, + the application must take responsibility for power management. Normally this is + achieved by use of powerDown and powerUp. If you wish to maximize power efficiency, + you must call powerDown after transmit (write, startWrite). + Please refer to: * [Documentation Main Page](http://maniacbug.github.com/RF24) @@ -16,5 +23,7 @@ Please refer to: * [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or -the SPI hardware will go into 'slave' mode. +the SPI hardware will go into 'slave' mode. This is because the 'SS', or slave select, pin on the arduino +controls if the arduino is the slave. For RF24 use, the arduino is the master and the RF24 is the slave. + diff --git a/keywords.txt b/keywords.txt index d0bd5570..2887a913 100644 --- a/keywords.txt +++ b/keywords.txt @@ -7,7 +7,8 @@ startListening KEYWORD2 stopListening KEYWORD2 write KEYWORD2 + startWrite KEYWORD2 available KEYWORD2 read KEYWORD2 openWritingPipe KEYWORD2 - openReadingPipe KEYWORD2 \ No newline at end of file + openReadingPipe KEYWORD2 diff --git a/tests/pingpair_blocking/pingpair_blocking.pde b/tests/pingpair_blocking/pingpair_blocking.pde index 2a2f080b..504b8c82 100644 --- a/tests/pingpair_blocking/pingpair_blocking.pde +++ b/tests/pingpair_blocking/pingpair_blocking.pde @@ -68,7 +68,7 @@ bool notified; //*< Have we notified the user we're done? */ const int num_needed = 10; //*< How many success/failures until we're done? */ int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ -const int interval = 100; //*< ms to wait between sends */ +int interval = 100; //*< ms to wait between sends */ char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ @@ -133,7 +133,6 @@ void setup(void) // radio.begin(); - // // Open pipes to other nodes for communication // @@ -186,11 +185,11 @@ void loop(void) // Now, continue listening radio.startListening(); - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); + // Wait here until we get a response, or timeout + unsigned long started_waiting_at = micros(); bool timeout = false; while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) + if (micros() - started_waiting_at > radio.getMaxTimeout() ) timeout = true; // Describe the results diff --git a/tests/pingpair_blocking/printf.h b/tests/pingpair_blocking/printf.h index df6c46ae..1b853db6 100644 --- a/tests/pingpair_blocking/printf.h +++ b/tests/pingpair_blocking/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); From 828add79a5375479cd29a7433c598b8ce56ee60b Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Fri, 22 Mar 2013 09:23:37 -0500 Subject: [PATCH 22/26] Added closeReadingPipe method and extended keywords with common method calls. --- RF24.cpp | 7 +++++++ RF24.h | 8 ++++++++ keywords.txt | 14 +++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 0402d761..398ce723 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -657,6 +657,13 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) /****************************************************************************/ +void RF24::closeReadingPipe( uint8_t pipe ) +{ + write_register(EN_RXADDR,read_register(EN_RXADDR) & ~_BV(pgm_read_byte(&child_pipe_enable[pipe]))); +} + +/****************************************************************************/ + void RF24::toggle_features(void) { csn(LOW); diff --git a/RF24.h b/RF24.h index 2d1d0411..b7d3c7a9 100644 --- a/RF24.h +++ b/RF24.h @@ -356,6 +356,14 @@ class RF24 */ void openReadingPipe(uint8_t number, uint64_t address); + + /** + * Close a pipe after it has been previously opened. + * Can be safely called without having previously opened a pipe. + * @param pipe Which pipe # to close, 0-5. + */ + void closeReadingPipe( uint8_t pipe ) ; + /**@}*/ /** * @name Optional Configurators diff --git a/keywords.txt b/keywords.txt index 2887a913..79987eb3 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,9 +1,15 @@ RF24 KEYWORD1 begin KEYWORD2 + setDataRate KEYWORD2 + getDataRate KEYWORD2 + powerUp KEYWORD2 + powerDown KEYWORD2 + whatHappened KEYWORD2 + writeAckPayload KEYWORD2 setChannel KEYWORD2 setPayloadSize KEYWORD2 getPayloadSize KEYWORD2 - print_details KEYWORD2 + printDetails KEYWORD2 startListening KEYWORD2 stopListening KEYWORD2 write KEYWORD2 @@ -12,3 +18,9 @@ read KEYWORD2 openWritingPipe KEYWORD2 openReadingPipe KEYWORD2 + closeReadingPipe KEYWORD2 + enableDynamicPayloads KEYWORD2 + enableAckPayload KEYWORD2 + setAutoAck KEYWORD2 + setCRCLength KEYWORD2 + getCRCLength KEYWORD2 From 0bba2d0b084924b80f2e4cbe0e1ad8d2c48e5533 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Wed, 3 Apr 2013 15:21:29 -0500 Subject: [PATCH 23/26] Removed redundant rx/tx buffer flushes. If an application needs these flushed, it should do it. Otherwise, properly behaving applications should need this. Also, by not flushing it opens the door for additional retransmission features down the road. --- RF24.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index 398ce723..3bbe9558 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -410,9 +410,11 @@ void RF24::startListening(void) if (pipe0_reading_address) write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); +#if 0 // Flush buffers flush_rx(); flush_tx(); +#endif // Go! ce(HIGH); From 7a60b5a58e75a3390f4209115431fcdab208a618 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Thu, 4 Apr 2013 17:15:36 -0500 Subject: [PATCH 24/26] Updated scanner to be a bit more sensitive as well as much easier to read. --- examples/scanner/scanner.pde | 107 +++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.pde index f8beaf9b..33cffa22 100644 --- a/examples/scanner/scanner.pde +++ b/examples/scanner/scanner.pde @@ -1,6 +1,7 @@ /* Copyright (C) 2011 James Coliz, Jr. + Copyright (c) 2012 Greg Copeland This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -19,10 +20,12 @@ */ #include -#include "nRF24L01.h" #include "RF24.h" #include "printf.h" +// Only display active frequencies +static const bool activeOnly = true ; + // // Hardware configuration // @@ -37,6 +40,7 @@ RF24 radio(8,9); const short num_channels = 128; short values[num_channels]; +uint8_t signalMeter[55] ; // // Setup @@ -48,7 +52,7 @@ void setup(void) // Print preamble // - Serial.begin(57600); + Serial.begin(115200); printf_begin(); printf("\n\rRF24/examples/scanner/\n\r"); @@ -57,69 +61,72 @@ void setup(void) // radio.begin(); + radio.powerUp() ; radio.setAutoAck(false); // Get into standby mode - radio.startListening(); - radio.stopListening(); - - // Print out header, high then low digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",i>>4); - ++i; - } - printf("\n\r"); - i = 0; - while ( i < num_channels ) - { - printf("%x",i&0xf); - ++i; - } - printf("\n\r"); + radio.openReadingPipe( 0, 0xFFFFFFFFFFULL ) ; + // radio.setDataRate( RF24_250KBPS ) ; // may fallback to 1Mbps + radio.setDataRate( RF24_1MBPS ) ; // may fallback to 1Mbps + radio.startListening() ; + radio.stopListening() ; } // // Loop // - -const short num_reps = 100; - void loop(void) { // Clear measurement values - memset(values,0,num_channels); - - // Scan all channels num_reps times - int rep_counter = num_reps; - while (rep_counter--) - { - int i = num_channels; - while (i--) - { - // Select this channel - radio.setChannel(i); - - // Listen for a little - radio.startListening(); - delayMicroseconds(128); - radio.stopListening(); - - // Did we get a carrier? - if ( radio.testCarrier() ) - ++values[i]; + memset( values, 0x00, num_channels ) ; + memset( signalMeter, 0x00, 55 ) ; + printf( "Scanning all available frequencies..." ) ; + + // Repeatedly scan multiple channels + for( int channel=0 ; channel < num_channels; channel++ ) { + radio.setChannel( channel ) ; + + // Amplify the signal based on carrier bandwidth + int ampFactor ; + for( int amp=0; amp < 201; amp++ ) { + // Alternate data rates + ampFactor = amp%2 ; + switch( ampFactor ) { + case 0: + radio.setDataRate( RF24_250KBPS ) ; + break ; + + default: + radio.setDataRate( RF24_2MBPS ) ; + break ; + } + + // Listen for carrier + ampFactor++ ; + radio.startListening() ; + delayMicroseconds( 6 - ampFactor ) ; + radio.stopListening() ; + + // Was carrier detected? If so, signal level based on bandwidth + if( radio.testRPD() ) { + values[channel] += ampFactor ; + } } } - // Print out channel measurements, clamped to a single hex digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",min(0xf,values[i]&0xf)); - ++i; + // Now display our results + printf( "Scan completed.\r\n" ) ; + for( int channel=0 ; channel < num_channels; channel++ ) { + if( !activeOnly || (activeOnly && values[channel] > 0) ) { + memset( signalMeter, '*', min( values[channel], 54 ) ) ; + signalMeter[min(values[channel], 54)] = 0x00 ; + printf( "%03d (%4dMhz): %02d - %s\r\n", + channel, + 2400+channel, + values[channel], + signalMeter ) ; + } } - printf("\n\r"); } // vim:ai:cin:sts=2 sw=2 ft=cpp From 951b593f59862c10d31f57b7a5ca609cf0815408 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Fri, 5 Apr 2013 09:10:17 -0500 Subject: [PATCH 25/26] Bug fix. Added back 1MBPS RPD check. --- examples/scanner/scanner.pde | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.pde index 33cffa22..87ae1580 100644 --- a/examples/scanner/scanner.pde +++ b/examples/scanner/scanner.pde @@ -79,7 +79,6 @@ void loop(void) { // Clear measurement values memset( values, 0x00, num_channels ) ; - memset( signalMeter, 0x00, 55 ) ; printf( "Scanning all available frequencies..." ) ; // Repeatedly scan multiple channels @@ -88,14 +87,18 @@ void loop(void) // Amplify the signal based on carrier bandwidth int ampFactor ; - for( int amp=0; amp < 201; amp++ ) { + for( int amp=0; amp <= 300; amp++ ) { // Alternate data rates - ampFactor = amp%2 ; + ampFactor = amp%3 ; switch( ampFactor ) { case 0: radio.setDataRate( RF24_250KBPS ) ; break ; + case 1: + radio.setDataRate( RF24_1MBPS ) ; + break ; + default: radio.setDataRate( RF24_2MBPS ) ; break ; @@ -125,6 +128,9 @@ void loop(void) 2400+channel, values[channel], signalMeter ) ; + + // Reset the scanned value since its already beend displayed + values[channel] = 0 ; } } } From 950bbd7067d522bcad90493e333f1ffc946432aa Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Mon, 15 Apr 2013 14:33:13 -0500 Subject: [PATCH 26/26] Discovered old debugging SPI bus clock setting. This horribly slows things down. Its now where it needs to be. Dang. --- RF24.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 3bbe9558..5f6c41d9 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -21,8 +21,7 @@ void RF24::csn(int mode) #ifdef ARDUINO SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - // SPI.setClockDivider(SPI_CLOCK_DIV4); - SPI.setClockDivider(SPI_CLOCK_DIV16); + SPI.setClockDivider(SPI_CLOCK_DIV4); #endif digitalWrite(csn_pin,mode); }