diff --git a/RF24.cpp b/RF24.cpp index 5f6c41d9..6d1e224d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -83,7 +83,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) { uint8_t status; - IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); + on_write_register(reg, value); csn(LOW); status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); @@ -104,7 +104,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeTyp 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); + on_write_payload(data_len, blank_len); csn(LOW); status = SPI.transfer( writeType ); @@ -127,7 +127,7 @@ uint8_t RF24::read_payload(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("[Reading %u bytes %u blanks]",data_len,blank_len); + on_read_payload(data_len, blank_len); csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); @@ -181,63 +181,6 @@ 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\r\n"), - status, - (status & _BV(RX_DR))?1:0, - (status & _BV(TX_DS))?1:0, - (status & _BV(MAX_RT))?1:0, - ((status >> RX_P_NO) & B111), - (status & _BV(TX_FULL))?1:0 - ); -} - -/****************************************************************************/ - -void RF24::print_observe_tx(uint8_t value) -{ - printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), - value, - (value >> PLOS_CNT) & B1111, - (value >> ARC_CNT) & B1111 - ); -} - -/****************************************************************************/ - -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(PRIPSTR"\t%c ="),name,extra_tab); - while (qty--) - printf_P(PSTR(" 0x%02x"),read_register(reg++)); - printf_P(PSTR("\r\n")); -} - -/****************************************************************************/ - -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(PRIPSTR"\t%c ="),name,extra_tab); - - while (qty--) - { - uint8_t buffer[5]; - read_register(reg++,buffer,sizeof buffer); - - printf_P(PSTR(" 0x")); - uint8_t* bufptr = buffer + sizeof buffer; - while( --bufptr >= buffer ) - printf_P(PSTR("%02x"),*bufptr); - } - - printf_P(PSTR("\r\n")); -} - -/****************************************************************************/ - 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), @@ -280,63 +223,6 @@ 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 = "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, - 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()); - - print_address_register(PSTR("RX_ADDR_P0-1"),RX_ADDR_P0,2); - print_byte_register(PSTR("RX_ADDR_P2-5"),RX_ADDR_P2,4); - print_address_register(PSTR("TX_ADDR"),TX_ADDR); - - print_byte_register(PSTR("RX_PW_P0-6"),RX_PW_P0,6); - print_byte_register(PSTR("EN_AA"),EN_AA); - print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); - print_byte_register(PSTR("RF_CH"),RF_CH); - print_byte_register(PSTR("RF_SETUP"),RF_SETUP); - print_byte_register(PSTR("CONFIG"),CONFIG); - print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); - - 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()])); -} - -/****************************************************************************/ - void RF24::begin(void) { // Initialize pins @@ -386,7 +272,7 @@ void RF24::begin(void) // Reset current status // Notice reset and flush is the last thing we do - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + write_register(STATUS_,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Set up default configuration. Callers can always change it later. // This channel should be universally safe and not bleed over into adjacent @@ -403,7 +289,7 @@ void RF24::begin(void) 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) ); + write_register(STATUS_, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Restore the pipe0 adddress, if exists if (pipe0_reading_address) @@ -465,7 +351,7 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast ) // IN the end, the send should be blocking. It comes back in 60ms worst case. // Generally much faster. - uint8_t observe_tx; + uint8_t obs; uint8_t status; uint32_t sent_at = micros(); const uint16_t timeout = getMaxTimeout() ; //us to wait for timeout @@ -473,8 +359,8 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast ) // Monitor the send do { - status = read_register(OBSERVE_TX,&observe_tx,1); - IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); + status = read_register(OBSERVE_TX, &obs, 1); + observe_tx(obs); } while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( micros() - sent_at < timeout ) ); @@ -490,17 +376,13 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast ) bool tx_ok, tx_fail; whatHappened(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")); // Handle the ack packet if ( ack_payload_available ) { ack_payload_length = getDynamicPayloadSize(); - IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); - IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); + on_ack(ack_payload_length); } return result; @@ -549,9 +431,6 @@ bool RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); - // Too noisy, enable if you really want lots o data!! - //IF_SERIAL_DEBUG(print_status(status)); - bool result = ( status & _BV(RX_DR) ); if (result) @@ -565,12 +444,12 @@ bool RF24::available(uint8_t* pipe_num) // ??? Should this REALLY be cleared now? Or wait until we // actually READ the payload? - write_register(STATUS,_BV(RX_DR) ); + write_register(STATUS_,_BV(RX_DR) ); // Handle ack payload receipt if ( status & _BV(TX_DS) ) { - write_register(STATUS,_BV(TX_DS)); + write_register(STATUS_,_BV(TX_DS)); } } @@ -594,7 +473,9 @@ void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) { // Read the status & reset the status in one easy call // Or is that such a good idea? - uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + uint8_t status = write_register(STATUS_,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + on_status(status); // Report to the user what happened tx_ok = status & _BV(TX_DS); @@ -688,8 +569,6 @@ void RF24::enableDynamicPayloads(void) write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); } - IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); - // Enable dynamic payload on all pipes // // Not sure the use case of only having dynamic payload on certain @@ -717,8 +596,6 @@ void RF24::enableAckPayload(void) write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); } - IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); - // // Enable dynamic payload on pipes 0 & 1 // diff --git a/RF24.h b/RF24.h index b7d3c7a9..a67f3c78 100644 --- a/RF24.h +++ b/RF24.h @@ -56,8 +56,15 @@ 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. */ - protected: + /* debugging interface: does nothing by default */ + virtual void on_write_register(uint8_t reg, uint8_t value) {} + virtual void observe_tx(uint8_t tx) {} + virtual void on_ack(uint8_t ack_len) {} + virtual void on_status(uint8_t status) {} + virtual void on_write_payload(uint8_t data_len, uint8_t blank_len) {} + virtual void on_read_payload(uint8_t data_len, uint8_t blank_len) {} + /** * @name Low-level internal interface. * @@ -167,50 +174,6 @@ class RF24 */ uint8_t get_status(void); - /** - * Decode and print the given status to stdout - * - * @param status Status value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_status(uint8_t status); - - /** - * Decode and print the given 'observe_tx' value to stdout - * - * @param value The observe_tx value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_observe_tx(uint8_t value); - - /** - * Print the name and value of an 8-bit register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - 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 - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); - /** * Turn on or off the special features of the chip * @@ -561,9 +524,9 @@ class RF24 /** * Print a giant block of debugging information to stdout * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + * @warning Does nothing by default */ - void printDetails(void); + virtual void printDetails(void) {} /** * Enter low-power mode @@ -682,9 +645,81 @@ class RF24 */ uint16_t getMaxTimeout(void) ; + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + /**@}*/ }; +class RF24Debug: public RF24 +{ +private: + Print &_out; + + void print_name(const char *name); + + void print_hex_byte(uint8_t b); + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status); + + /** + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + 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 + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(const char *name, uint8_t reg, uint8_t qty = 1); + +protected: + void on_write_register(uint8_t reg, uint8_t value); + void observe_tx(uint8_t tx); + void on_ack(uint8_t ack_len); + void on_status(uint8_t status); + void on_write_payload(uint8_t data_len, uint8_t blank_len); + void on_read_payload(uint8_t data_len, uint8_t blank_len); + +public: + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); + + RF24Debug(uint8_t _cepin, uint8_t _cspin, Print &out = Serial): RF24(_cepin, _cspin), _out(out) {} +}; + /** * @example GettingStarted.pde * diff --git a/RF24Debug.cpp b/RF24Debug.cpp new file mode 100644 index 00000000..8ff91667 --- /dev/null +++ b/RF24Debug.cpp @@ -0,0 +1,186 @@ +#include "RF24_config.h" +#include "RF24.h" + +#if defined(__AVR_ATtinyX4__) || defined(__AVR_ATtinyX5__) +# define FLASH_PTR(x) ((fstr_t *)x) +#else +# define FLASH_PTR(x) ((const __FlashStringHelper *)x) +#endif + +void RF24Debug::print_name(const char *name) +{ + _out.print(FLASH_PTR(name)); + if (strlen_P(name) < 8) + _out.print('\t'); + _out.print(F("\t =")); +} + +void RF24Debug::print_hex_byte(uint8_t b) +{ + _out.print(F(" 0x")); + if (b < 16) + _out.print('0'); + _out.print(b, HEX); +} + +void RF24Debug::print_byte_register(const char *name, uint8_t reg, uint8_t qty) +{ + print_name(name); + while (qty--) + print_hex_byte(read_register(reg++)); + _out.println(); +} + +void RF24Debug::print_address_register(const char *name, uint8_t reg, uint8_t qty) +{ + print_name(name); + + while (qty--) + { + uint8_t buffer[5]; + read_register(reg++,buffer,sizeof buffer); + + _out.print(F(" 0x")); + uint8_t* bufptr = buffer + sizeof buffer; + while( --bufptr >= buffer ) { + uint8_t b = *bufptr; + if (b < 10) + _out.print('0'); + _out.print(b, HEX); + } + } + _out.println(); +} + +void RF24Debug::print_status(uint8_t status) +{ + print_name(PSTR("STATUS")); + print_hex_byte(status); + _out.print(F(" RX_DR=")); + _out.print((status & _BV(RX_DR))? 1: 0); + _out.print(F(" TX_DS=")); + _out.print((status & _BV(TX_DS))? 1: 0); + _out.print(F(" MAX_RT=")); + _out.print((status & _BV(MAX_RT))? 1: 0); + _out.print(F(" RX_P_NO=")); + _out.print((status >> RX_P_NO) & B111); + _out.print(F(" TX_FULL=")); + _out.println((status & _BV(TX_FULL))? 1: 0); +} + +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 = "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, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + +void RF24Debug::printDetails(void) +{ + print_status(get_status()); + + print_address_register(PSTR("RX_ADDR_P0-1"),RX_ADDR_P0,2); + print_byte_register(PSTR("RX_ADDR_P2-5"),RX_ADDR_P2,4); + print_address_register(PSTR("TX_ADDR"),TX_ADDR); + + print_byte_register(PSTR("RX_PW_P0-6"),RX_PW_P0,6); + print_byte_register(PSTR("EN_AA"),EN_AA); + print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); + print_byte_register(PSTR("RF_CH"),RF_CH); + print_byte_register(PSTR("RF_SETUP"),RF_SETUP); + print_byte_register(PSTR("CONFIG"),CONFIG); + print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); + +#if defined(ESP8266) + _out.print(F("Data Rate\t = ")); + _out.println(FLASH_PTR(pgm_read_dword(&rf24_datarate_e_str_P[getDataRate()]))); + _out.print(F("Model\t\t = ")); + _out.println(FLASH_PTR(pgm_read_dword(&rf24_model_e_str_P[isPVariant()]))); + _out.print(F("CRC Length\t = ")); + _out.println(FLASH_PTR(pgm_read_dword(&rf24_crclength_e_str_P[getCRCLength()]))); + _out.print(F("PA Power\t = ")); + _out.println(FLASH_PTR(pgm_read_dword(&rf24_pa_dbm_e_str_P[getPALevel()]))); +#else + _out.print(F("Data Rate\t = ")); + _out.println(FLASH_PTR(pgm_read_word(&rf24_datarate_e_str_P[getDataRate()]))); + _out.print(F("Model\t\t = ")); + _out.println(FLASH_PTR(pgm_read_word(&rf24_model_e_str_P[isPVariant()]))); + _out.print(F("CRC Length\t = ")); + _out.println(FLASH_PTR(pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()]))); + _out.print(F("PA Power\t = ")); + _out.println(FLASH_PTR(pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()]))); +#endif +} + +void RF24Debug::on_write_register(uint8_t reg, uint8_t value) +{ + _out.print(F("write_register(")); + print_hex_byte(reg); + _out.print(','); + print_hex_byte(value); + _out.println(')'); +} + +void RF24Debug::observe_tx(uint8_t value) +{ + _out.print(F("OBSERVE_TX")); + print_hex_byte(value); + _out.print(F(": PLOS_CNT=")); + _out.print((value >> PLOS_CNT) & B1111, HEX); + _out.print(F(" ARC_CNT=")); + _out.println((value >> ARC_CNT) & B1111, HEX); +} + +void RF24Debug::on_status(uint8_t status) +{ + print_status(status); + _out.println(status & _BV(TX_DS)? F("...OK."): F("...Failed")); +} + +void RF24Debug::on_ack(uint8_t ack_len) +{ + _out.print(F("got ack ")); + _out.println(ack_len); +} + +void RF24Debug::on_write_payload(uint8_t data_len, uint8_t blank_len) +{ + _out.print(F("Writing ")); + _out.print(data_len, DEC); + _out.print(F(" bytes ")); + _out.println(blank_len, DEC); +} + +void RF24Debug::on_read_payload(uint8_t data_len, uint8_t blank_len) +{ + _out.print(F("Reading ")); + _out.print(data_len); + _out.print(F(" bytes ")); + _out.println(blank_len); +} diff --git a/RF24_config.h b/RF24_config.h index fc7397fb..d997fd88 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -29,28 +29,21 @@ 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 +#if defined(ENERGIA) +#define strlen_P strlen +#define pgm_read_byte(p) (*(p)) +#define pgm_read_word(p) (*(p)) +#define _BV(x) (1 << (x)) +#define PROGMEM +#define PSTR(s) (s) +#define __FlashStringHelper char +#elif defined(ARDUINO) // Progmem is Arduino-specific -#ifdef ARDUINO #include -#define PRIPSTR "%S" + #else +// RPi maybe? typedef char const char; typedef uint16_t prog_uint16_t; #define PSTR(x) (x) @@ -58,7 +51,6 @@ typedef uint16_t prog_uint16_t; #define strlen_P strlen #define PROGMEM #define pgm_read_word(p) (*(p)) -#define PRIPSTR "%s" #endif #endif // __RF24_CONFIG_H__ diff --git a/examples/GettingStarted/GettingStarted.pde b/examples/GettingStarted/GettingStarted.pde new file mode 100644 index 00000000..bf1851ac --- /dev/null +++ b/examples/GettingStarted/GettingStarted.pde @@ -0,0 +1,227 @@ +/* + 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. + */ + +/** + * Example for Getting Started with nRF24L01+ radios. + * + * This 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. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(9,10); + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. +// + +// 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 = role_pong_back; + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // optionally, reduce the payload size. seems to + // improve reliability + //radio.setPayloadSize(8); + + // + // 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.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) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (ok) + printf("ok..."); + else + printf("failed.\n\r"); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // 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 + unsigned long got_time; + bool done = false; + while (!done) + { + // 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. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } + + // + // Change roles + // + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + // Become the primary transmitter (ping out) + role = role_ping_out; + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + // Become the primary receiver (pong back) + role = role_pong_back; + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/GettingStarted/Jamfile b/examples/GettingStarted/Jamfile new file mode 100644 index 00000000..9a5f2c47 --- /dev/null +++ b/examples/GettingStarted/Jamfile @@ -0,0 +1,210 @@ +# (1) Project Information + +PROJECT_LIBS = SPI RF24 ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= arduino ; +UPLOAD_SPEED ?= 57600 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. + +if $(OS) = MACOSX +{ + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN ?= /usr/bin ; + AVR_INCLUDE ?= /usr/lib/avr/include ; + AVR_ETC = /etc ; +} + +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; + +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; + +# Search everywhere for headers + +HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +# +# Targets +# + +# 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) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Grab everything from the current dir +PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; + +# Main output executable +MAIN = $(PWD:B).elf ; + +Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +Hex $(MAIN:B).hex : $(MAIN) ; + +# Upload targets +for _p in $(PORTS) +{ + Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; +} diff --git a/examples/GettingStarted/printf.h b/examples/GettingStarted/printf.h new file mode 100644 index 00000000..6336f855 --- /dev/null +++ b/examples/GettingStarted/printf.h @@ -0,0 +1,49 @@ +/* + 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__ + +#include + +#ifdef ENERGIA +#undef putchar +extern "C" { + int putchar(int c) + { + Serial.write((uint8_t)c); + return c; + } +} + +void printf_begin(void) +{ +} + +#else +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} +#endif + +#endif // __PRINTF_H__ diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.ino similarity index 87% rename from examples/pingpair/pingpair.pde rename to examples/pingpair/pingpair.ino index 449f5377..4f25dd33 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.ino @@ -20,16 +20,36 @@ #include "printf.h" // -// Hardware configuration +// Hardware configuration: first MSP430, then ATMega // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); +#if defined(ENERGIA) +#if defined(__MSP430FR5739__) +# define CE P1_2 +# define CS P1_3 +# define ROLE P2_5 +#elif defined(__MSP430G2553__) +# define CE P2_1 +# define CS P2_0 +# define ROLE P2_2 +//#elif defined(__LM4F120H5QR__) +//# define CE PA_6 +//# define CS PB_5 +//# define ROLE PA_5 +#endif +# define BAUD 9600 +#else +# define CE 9 +# define CS 10 +# define ROLE 7 +# define BAUD 57600 +#endif + +RF24 radio(CE, CS); // 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; +const int role_pin = ROLE; // // Topology @@ -65,7 +85,7 @@ void setup(void) // set up the role pin pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); + digitalWrite(role_pin, HIGH); delay(20); // Just to get a solid reading on the role pin // read the address pin, establish our role @@ -78,9 +98,9 @@ void setup(void) // Print preamble // - Serial.begin(57600); + Serial.begin(BAUD); printf_begin(); - printf("\n\rRF24/examples/pingpair/\n\r"); + printf("RF24/examples/pingpair/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); // @@ -161,7 +181,8 @@ void loop(void) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 1+(radio.getMaxTimeout()/1000) ) +// if (millis() - started_waiting_at > 1+(radio.getMaxTimeout()/1000) ) + if (millis() - started_waiting_at > 250 ) timeout = true; // Describe the results @@ -180,8 +201,8 @@ void loop(void) printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); } - // Try again 1s later - delay(1000); + // Try again 5s later + delay(5000); } // diff --git a/examples/pingpair/printf.h b/examples/pingpair/printf.h index 05dd0888..6336f855 100644 --- a/examples/pingpair/printf.h +++ b/examples/pingpair/printf.h @@ -18,6 +18,21 @@ #include +#ifdef ENERGIA +#undef putchar +extern "C" { + int putchar(int c) + { + Serial.write((uint8_t)c); + return c; + } +} + +void printf_begin(void) +{ +} + +#else int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -27,7 +42,8 @@ int serial_putc( char c, FILE * ) void printf_begin(void) { - fdevopen( &serial_putc, 0 ); + fdevopen( &serial_putc, 0 ); } +#endif #endif // __PRINTF_H__ diff --git a/examples/tinypingpair/tinypingpair.ino b/examples/tinypingpair/tinypingpair.ino new file mode 100644 index 00000000..1160230c --- /dev/null +++ b/examples/tinypingpair/tinypingpair.ino @@ -0,0 +1,233 @@ +/* + 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. + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class. 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. + */ +#include +#include "nRF24L01.h" +#include "RF24.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24Debug radio(2,3); + +// 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; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// 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; + +void setup(void) +{ + // + // Role + // + Serial.begin(9600); + Serial.println("hello world"); + + // 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 + + role = role_ping_out; + +/* + // read the address pin, establish our role + if ( ! digitalRead(role_pin) ) { + role = role_ping_out; + Serial.println("ping out"); + } else { + role = role_pong_back; + Serial.println("pong back"); + } +*/ + + // + // Print preamble + // + + + // + // Setup and configure rf radio + // + + radio.begin(); + + // optionally, increase the delay between retries & # of retries +// radio.setRetries(15,15); + + // optionally, reduce the payload size. seems to + // improve reliability +// radio.setPayloadSize(8); + + // + // 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.enableDynamicPayloads() ; + radio.setAutoAck( true ) ; + 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) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + Serial.print("Sending "); + Serial.println(time); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (ok) { + Serial.println("ok"); + } else { + Serial.println("failed"); + } + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + Serial.println("Failed, response timed out"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + Serial.print("Got response "); + Serial.print(got_time); + Serial.print(", round-trip delay: "); + Serial.println(millis() - got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // 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 + unsigned long got_time; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + Serial.print("Got payload "); + Serial.println(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. + radio.write( &got_time, sizeof(unsigned long) ); + Serial.println("Sent response"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/nRF24L01.h b/nRF24L01.h index 9943c3af..9247fa91 100644 --- a/nRF24L01.h +++ b/nRF24L01.h @@ -31,7 +31,7 @@ #define SETUP_RETR 0x04 #define RF_CH 0x05 #define RF_SETUP 0x06 -#define STATUS 0x07 +#define STATUS_ 0x07 #define OBSERVE_TX 0x08 #define CD 0x09 #define RX_ADDR_P0 0x0A