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/RF24.cpp b/RF24.cpp index 6ad87e4c..5f6c41d9 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -1,36 +1,28 @@ /* - 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_DIV2); + SPI.setClockDivider(SPI_CLOCK_DIV4); +#endif digitalWrite(csn_pin,mode); } @@ -91,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)\n\r"),reg,value)); + IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); csn(LOW); status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); @@ -103,7 +95,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, const uint8_t writeType) { uint8_t status; @@ -111,11 +103,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); - status = SPI.transfer( W_TX_PAYLOAD ); + status = SPI.transfer( writeType ); while ( data_len-- ) SPI.transfer(*current++); while ( blank_len-- ) @@ -191,7 +183,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, @@ -205,7 +197,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 @@ -214,21 +206,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--) { @@ -241,14 +233,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(false), p_variant(false), + payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), + pipe0_reading_address(0) { } @@ -259,14 +252,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); } /****************************************************************************/ @@ -278,6 +280,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 = "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()); @@ -294,13 +329,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" } ; - - 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("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()])); } /****************************************************************************/ @@ -312,17 +344,7 @@ 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) SPI.begin(); - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); ce(LOW); csn(HIGH); @@ -338,7 +360,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 ) ; @@ -367,7 +389,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(); @@ -381,11 +405,15 @@ 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); +#if 0 // Flush buffers flush_rx(); + flush_tx(); +#endif // Go! ce(HIGH); @@ -399,6 +427,8 @@ void RF24::startListening(void) void RF24::stopListening(void) { ce(LOW); + flush_tx(); + flush_rx(); } /****************************************************************************/ @@ -413,16 +443,17 @@ 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, const bool multicast ) { bool result = false; // Begin the write - startWrite(buf,len); + startWrite( buf, len, multicast ); // ------------ // At this point we could return from a non-blocking write, and then call @@ -432,19 +463,20 @@ 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. - // 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 + // 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 = 500; //ms to wait for timeout + uint32_t sent_at = micros(); + const uint16_t timeout = getMaxTimeout() ; //us to wait for timeout + + // Monitor the send do { 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 @@ -457,8 +489,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("%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")); @@ -471,31 +503,22 @@ bool RF24::write( const void* buf, uint8_t len ) IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); } - // Yay, we are done. - - // Power down - powerDown(); - - // Flush buffers (Is this a relic of past experimentation, and not needed anymore??) - flush_tx(); - return result; } /****************************************************************************/ -void RF24::startWrite( const void* buf, uint8_t len ) +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) ); - delay(2); - // Send the payload - write_payload( buf, len ); + // 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); - delayMicroseconds(15); - delay(2); + delayMicroseconds(10); ce(LOW); } @@ -588,26 +611,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. @@ -618,21 +643,28 @@ 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]))); } } /****************************************************************************/ +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); @@ -656,7 +688,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 // @@ -675,17 +707,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("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 @@ -702,7 +734,8 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) csn(LOW); 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++); @@ -775,27 +808,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 ) ; @@ -808,23 +841,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 ; @@ -882,27 +914,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 ; } @@ -911,23 +941,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 ) ; } @@ -958,10 +986,28 @@ void RF24::disableCRC( void ) } /****************************************************************************/ + void RF24::setRetries(uint8_t delay, uint8_t count) { write_register(SETUP_RETR,(delay&0xf)<> 4))) * (retries & 0x0f)) ; + + return to ; +} + // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index e5751585..b7d3c7a9 100644 --- a/RF24.h +++ b/RF24.h @@ -1,19 +1,43 @@ /* - 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. */ +/** + * @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; /** @@ -109,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, const uint8_t writeType); /** * Read the receive payload @@ -172,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 @@ -185,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 @@ -254,9 +278,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. */ - bool write( const void* buf, uint8_t len ); + bool write( const void* buf, uint8_t len, const bool multicast=false ); /** * Test whether there are bytes available to be read @@ -320,6 +346,9 @@ 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. Pipe 1 + * is the unicast pipe address. + * * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits * * @param number Which pipe# to open, 0-5. @@ -327,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 @@ -345,6 +382,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 * @@ -352,6 +399,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 * @@ -391,6 +445,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); @@ -439,7 +495,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. */ @@ -546,9 +602,9 @@ class RF24 * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not + * @param multicast true or false. True, buffer will be multicast; ignoring retry/timeout */ - void startWrite( const void* buf, uint8_t len ); + void startWrite( const void* buf, uint8_t len, const bool multicast=false ); /** * Write an ack payload for the specified pipe @@ -556,6 +612,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. * @@ -615,9 +673,39 @@ class RF24 */ bool testRPD(void) ; + + /** + * Calculate the maximum timeout in us based on current hardware + * configuration. + * + * @return us of maximum timeout; accounting for retries + */ + uint16_t getMaxTimeout(void) ; + /**@}*/ }; +/** + * @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 * @@ -637,6 +725,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 * @@ -691,12 +794,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 @@ -711,6 +828,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/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 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/pingpair.pde b/examples/pingpair/pingpair.pde index 05254c4d..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" // @@ -91,11 +90,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 +119,16 @@ 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_MAX ) ; + radio.enableDynamicPayloads() ; + radio.setAutoAck( true ) ; + radio.powerUp() ; radio.startListening(); // @@ -153,13 +161,14 @@ 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 > 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 { @@ -191,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_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_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__ 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/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 ); 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/scanner/scanner.pde b/examples/scanner/scanner.pde index f8beaf9b..87ae1580 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,78 @@ 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 ) ; + 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 <= 300; amp++ ) { + // Alternate data rates + 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 ; + } + + // 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 ) ; + + // Reset the scanned value since its already beend displayed + values[channel] = 0 ; + } } - printf("\n\r"); } // vim:ai:cin:sts=2 sw=2 ft=cpp 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/keywords.txt b/keywords.txt index d0bd5570..79987eb3 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,13 +1,26 @@ 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 + startWrite KEYWORD2 available KEYWORD2 read KEYWORD2 openWritingPipe KEYWORD2 - openReadingPipe KEYWORD2 \ No newline at end of file + openReadingPipe KEYWORD2 + closeReadingPipe KEYWORD2 + enableDynamicPayloads KEYWORD2 + enableAckPayload KEYWORD2 + setAutoAck KEYWORD2 + setCRCLength KEYWORD2 + getCRCLength KEYWORD2 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 diff --git a/tests/pingpair_blocking/pingpair_blocking.pde b/tests/pingpair_blocking/pingpair_blocking.pde index 817310de..504b8c82 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" @@ -69,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 */ @@ -134,7 +133,6 @@ void setup(void) // radio.begin(); - // // Open pipes to other nodes for communication // @@ -187,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 ); diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index c16db2b2..403c1953 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" @@ -85,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 */ @@ -229,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); } @@ -262,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 ); @@ -271,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 );