High Speed Data Transfers/Optimization
Changes: - Enable PTX at radio begin, in standby-I mode - Enable PTX at radio stopListening(); in standy-I mode - Radio startListening - removed delay. What is there to wait for when the radio will just be listening anyway? - Radio powerUp - radio will need to be powered up after configuration - radio will not power down unless instructed to - need to issue radio.txStandBy(); when finished transmitting to engage standby-I mode - Added txStandBy() to drop to standBy-I mode if not switching to RX mode - Radio Write: Sped up - radio previously did not use standby mode effectively, after TX sending of each packet, the radio is powered down introducing a 1.5ms delay in addition to the 130us delay from standby. - No observe-tx code, a simple NOP cmd is sent to get the status info - No millis() timeout, hanging condition should only occur because of faulty code - Simplified whatHappened() code - Flush TX FIFO if MAX_RT met as lib does not support re-transmit instead of flush every time. - Remove ack size monitoring - No powerdown needed, - Added writeClear() function for writes that need the interrupt flags cleared (ack payloads, etc). Same as write() otherwise - Radio startWrite: Sped up - Enable PTX mode is now done in stopListening() code. Enter TX mode by writing CE high. This brings the data transfer capabilities up to a reasonable level - No need to power up, already in Standby-I mode - No need for delayMicroseconds() in write functon. - No need for delay after writing CE high - Does not enter Standby-I mode be default. It is up to the MCU to call the new txStandBy() function to enter standby-I mode from PTX if not switching back to PRX - Radio available: - Now checks the FIFO buffer instead of the interrupt flag (no missed data) - Moved clearing of interrupt flags to Read() - Radio read: - No longer returns any value - use available to check for more data - Radio isAckPayloadAvailable() now checks the FIFO directly - Radio getDynamicPayloadSize() - Per the datasheet, added fix to flush corrupted packets
This commit is contained in:
@@ -103,9 +103,9 @@ 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 );
|
||||
while ( data_len-- )
|
||||
@@ -126,9 +126,9 @@ 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);
|
||||
|
||||
|
||||
csn(LOW);
|
||||
status = SPI.transfer( R_RX_PAYLOAD );
|
||||
while ( data_len-- )
|
||||
@@ -239,7 +239,7 @@ void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty)
|
||||
/****************************************************************************/
|
||||
|
||||
RF24::RF24(uint8_t _cepin, uint8_t _cspin):
|
||||
ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false),
|
||||
ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false),
|
||||
payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false),
|
||||
pipe0_reading_address(0)
|
||||
{
|
||||
@@ -299,7 +299,7 @@ static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN";
|
||||
static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW";
|
||||
static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_MED";
|
||||
static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_HIGH";
|
||||
static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = {
|
||||
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,
|
||||
@@ -366,14 +366,14 @@ void RF24::begin(void)
|
||||
{
|
||||
p_variant = true ;
|
||||
}
|
||||
|
||||
|
||||
// Then set the data rate to the slowest (and most reliable) speed supported by all
|
||||
// hardware.
|
||||
setDataRate( RF24_1MBPS ) ;
|
||||
|
||||
// Initialize CRC and request 2-byte (16bit) CRC
|
||||
setCRCLength( RF24_CRC_16 ) ;
|
||||
|
||||
|
||||
// Disable dynamic payloads, to match dynamic_payloads_enabled setting
|
||||
write_register(DYNPD,0);
|
||||
|
||||
@@ -389,6 +389,11 @@ void RF24::begin(void)
|
||||
// Flush buffers
|
||||
flush_rx();
|
||||
flush_tx();
|
||||
|
||||
//Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp )
|
||||
write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );
|
||||
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -409,8 +414,6 @@ void RF24::startListening(void)
|
||||
// Go!
|
||||
ce(HIGH);
|
||||
|
||||
// wait for the radio to come up (130us actually only needed)
|
||||
delayMicroseconds(130);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -420,6 +423,8 @@ void RF24::stopListening(void)
|
||||
ce(LOW);
|
||||
flush_tx();
|
||||
flush_rx();
|
||||
write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -431,6 +436,7 @@ void RF24::powerDown(void)
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
//Power up now. Radio will not power down unless instructed by MCU for config changes etc.
|
||||
void RF24::powerUp(void)
|
||||
{
|
||||
write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP));
|
||||
@@ -438,85 +444,69 @@ void RF24::powerUp(void)
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
bool RF24::write( const void* buf, uint8_t len )
|
||||
//Similar to the previous write, clears the interrupt flags
|
||||
bool RF24::writeClear( const void* buf, uint8_t len )
|
||||
{
|
||||
bool result = false;
|
||||
//Start Writing
|
||||
startWrite(buf,len);
|
||||
|
||||
// Begin the write
|
||||
startWrite(buf,len);
|
||||
//Wait until complete or failed
|
||||
//ACK payloads that are handled improperly will cause this to hang
|
||||
//If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload
|
||||
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { }
|
||||
|
||||
// ------------
|
||||
// At this point we could return from a non-blocking write, and then call
|
||||
// the rest after an interrupt
|
||||
uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
|
||||
|
||||
// Instead, we are going to block here until we get TX_DS (transmission completed and ack'd)
|
||||
// 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
|
||||
uint8_t observe_tx;
|
||||
uint8_t status;
|
||||
uint32_t sent_at = millis();
|
||||
const uint32_t timeout = 500; //ms to wait for timeout
|
||||
do
|
||||
{
|
||||
status = read_register(OBSERVE_TX,&observe_tx,1);
|
||||
IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX));
|
||||
//Max retries exceeded
|
||||
if( status & _BV(MAX_RT)){
|
||||
//Flush the FIFO since this is a single-packet-write function, and FIFO is NOT cleared when MAX_RT is reached
|
||||
flush_tx();
|
||||
}
|
||||
while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( millis() - 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
|
||||
// ------------
|
||||
|
||||
// Call this when you get an interrupt
|
||||
// The status tells us three things
|
||||
// * The send was successful (TX_DS)
|
||||
// * The send failed, too many retries (MAX_RT)
|
||||
// * 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\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));
|
||||
}
|
||||
|
||||
// Yay, we are done.
|
||||
|
||||
// Power down
|
||||
powerDown();
|
||||
|
||||
// Flush buffers (Is this a relic of past experimentation, and not needed anymore??)
|
||||
flush_tx();
|
||||
|
||||
return result;
|
||||
//TX OK 1 or 0
|
||||
return status & _BV(TX_DS);//tx_ok;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void RF24::startWrite( const void* buf, uint8_t len )
|
||||
//For general use, the flags are not important to clear
|
||||
bool RF24::write( const void* buf, uint8_t len )
|
||||
{
|
||||
// Transmitter power-up
|
||||
write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) );
|
||||
delayMicroseconds(150);
|
||||
//Start Writing
|
||||
startWrite(buf,len);
|
||||
|
||||
// Send the payload
|
||||
write_payload( buf, len );
|
||||
//Wait until complete or failed
|
||||
//ACK payloads that are handled improperly will cause this to hang
|
||||
//If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload
|
||||
uint8_t status;
|
||||
while( ! ( status = get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { }
|
||||
|
||||
// Allons!
|
||||
ce(HIGH);
|
||||
delayMicroseconds(15);
|
||||
ce(LOW);
|
||||
//Max retries exceeded
|
||||
if( status & _BV(MAX_RT)){
|
||||
//Flush the FIFO since this is a single-packet-write function, and FIFO is NOT cleared when MAX_RT is reached
|
||||
flush_tx();
|
||||
}
|
||||
//TX OK 1 or 0
|
||||
return status & _BV(TX_DS);//tx_ok;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high
|
||||
//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay)
|
||||
//Otherwise we enter Standby-II mode, which is still faster than standby mode
|
||||
//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data
|
||||
|
||||
void RF24::startWrite( const void* buf, uint8_t len ){ //TMRh20
|
||||
|
||||
write_payload( buf,len);
|
||||
ce(HIGH);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void RF24::txStandBy(){
|
||||
ce(LOW);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -530,6 +520,7 @@ uint8_t RF24::getDynamicPayloadSize(void)
|
||||
result = SPI.transfer(0xff);
|
||||
csn(HIGH);
|
||||
|
||||
if(result > 32) { flush_rx(); return 0; }
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -544,45 +535,35 @@ bool RF24::available(void)
|
||||
|
||||
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));
|
||||
//Check the FIFO buffer to see if data is waitng to be read
|
||||
|
||||
bool result = ( status & _BV(RX_DR) );
|
||||
if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){
|
||||
|
||||
if (result)
|
||||
{
|
||||
// If the caller wants the pipe number, include that
|
||||
if ( pipe_num )
|
||||
if ( pipe_num ){
|
||||
uint8_t status = get_status();
|
||||
*pipe_num = ( status >> RX_P_NO ) & B111;
|
||||
|
||||
// Clear the status bit
|
||||
|
||||
// ??? Should this REALLY be cleared now? Or wait until we
|
||||
// actually READ the payload?
|
||||
|
||||
write_register(STATUS,_BV(RX_DR) );
|
||||
|
||||
// Handle ack payload receipt
|
||||
if ( status & _BV(TX_DS) )
|
||||
{
|
||||
write_register(STATUS,_BV(TX_DS));
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
bool RF24::read( void* buf, uint8_t len )
|
||||
{
|
||||
void RF24::read( void* buf, uint8_t len ){
|
||||
|
||||
// Fetch the payload
|
||||
read_payload( buf, len );
|
||||
|
||||
// was this the last of the data available?
|
||||
return read_register(FIFO_STATUS) & _BV(RX_EMPTY);
|
||||
//Clear the two possible interrupt flags with one command
|
||||
write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) );
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -736,9 +717,10 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len)
|
||||
|
||||
bool RF24::isAckPayloadAvailable(void)
|
||||
{
|
||||
bool result = ack_payload_available;
|
||||
ack_payload_available = false;
|
||||
return result;
|
||||
if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -905,7 +887,7 @@ rf24_datarate_e RF24::getDataRate( void )
|
||||
{
|
||||
rf24_datarate_e result ;
|
||||
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
|
||||
if ( dr == _BV(RF_DR_LOW) )
|
||||
@@ -931,11 +913,11 @@ 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 uses RAM (evil!)
|
||||
if ( length == RF24_CRC_DISABLED )
|
||||
{
|
||||
// Do nothing, we turned it off above.
|
||||
// Do nothing, we turned it off above.
|
||||
}
|
||||
else if ( length == RF24_CRC_8 )
|
||||
{
|
||||
|
||||
@@ -51,9 +51,10 @@ private:
|
||||
bool p_variant; /* False for RF24L01 and true for RF24L01P */
|
||||
uint8_t payload_size; /**< Fixed size of payloads */
|
||||
bool ack_payload_available; /**< Whether there is an ack payload waiting */
|
||||
bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */
|
||||
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. */
|
||||
bool avail;
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -219,6 +220,11 @@ protected:
|
||||
/**@}*/
|
||||
|
||||
public:
|
||||
|
||||
//TMRh20
|
||||
void txStandBy();
|
||||
bool writeClear( const void* buf, uint8_t len );
|
||||
|
||||
/**
|
||||
* @name Primary public interface
|
||||
*
|
||||
@@ -301,7 +307,7 @@ public:
|
||||
* @param len Maximum number of bytes to read into the buffer
|
||||
* @return True if the payload was delivered successfully false if not
|
||||
*/
|
||||
bool read( void* buf, uint8_t len );
|
||||
void read( void* buf, uint8_t len );
|
||||
|
||||
/**
|
||||
* Open a pipe for writing
|
||||
@@ -351,7 +357,7 @@ public:
|
||||
|
||||
/**@}*/
|
||||
/**
|
||||
* @name Optional Configurators
|
||||
* @name Optional Configurators
|
||||
*
|
||||
* Methods you can use to get or set the configuration of the chip.
|
||||
* None are required. Calling begin() sets up a reasonable set of
|
||||
@@ -406,7 +412,7 @@ public:
|
||||
* @return Payload length of last-received dynamic payload
|
||||
*/
|
||||
uint8_t getDynamicPayloadSize(void);
|
||||
|
||||
|
||||
/**
|
||||
* Enable custom payloads on the acknowledge packets
|
||||
*
|
||||
@@ -486,7 +492,7 @@ public:
|
||||
* @return true if the change was successful
|
||||
*/
|
||||
bool setDataRate(rf24_datarate_e speed);
|
||||
|
||||
|
||||
/**
|
||||
* Fetches the transmission data rate
|
||||
*
|
||||
@@ -518,9 +524,9 @@ public:
|
||||
|
||||
/**@}*/
|
||||
/**
|
||||
* @name Advanced Operation
|
||||
* @name Advanced Operation
|
||||
*
|
||||
* Methods you can use to drive the chip in more advanced ways
|
||||
* Methods you can use to drive the chip in more advanced ways
|
||||
*/
|
||||
/**@{*/
|
||||
|
||||
@@ -642,9 +648,9 @@ public:
|
||||
* 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
|
||||
* @return true if this is a legitimate radio
|
||||
*/
|
||||
bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; }
|
||||
bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; }
|
||||
|
||||
/**@}*/
|
||||
};
|
||||
@@ -653,12 +659,12 @@ public:
|
||||
* @example GettingStarted.pde
|
||||
*
|
||||
* This is an example which corresponds to my "Getting Started" blog post:
|
||||
* <a style="text-align:center" href="http://maniacbug.wordpress.com/2011/11/02/getting-started-rf24/">Getting Started with nRF24L01+ on Arduino</a>.
|
||||
* <a style="text-align:center" href="http://maniacbug.wordpress.com/2011/11/02/getting-started-rf24/">Getting Started with nRF24L01+ on Arduino</a>.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -690,14 +696,14 @@ public:
|
||||
*/
|
||||
|
||||
/**
|
||||
* @example pingpair_maple.pde
|
||||
* @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:
|
||||
* <a href="http://maniacbug.wordpress.com/2011/12/14/nrf24l01-running-on-maple-3/">nRF24L01+ Running on Maple</a>
|
||||
*
|
||||
* 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
|
||||
@@ -759,25 +765,25 @@ public:
|
||||
* @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @section News News
|
||||
*
|
||||
* NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions.
|
||||
*
|
||||
* NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions.
|
||||
* Please <a href="https://github.com/maniacbug/RF24/issues/new">open an issue</a> if you find any problems using it with any version of Arduino.
|
||||
*
|
||||
* NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the
|
||||
* <a href="http://leaflabs.com/store/#Maple-Native">Maple Native</a>,
|
||||
* NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the
|
||||
* <a href="http://leaflabs.com/store/#Maple-Native">Maple Native</a>,
|
||||
* 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 <a href="http://maniacbug.github.com/RF24/">Documentation Main Page</a>
|
||||
@@ -799,11 +805,11 @@ public:
|
||||
*
|
||||
* <img src="http://farm7.staticflickr.com/6044/6307669179_a8d19298a6_m.jpg" width="240" height="160" alt="RF24 Getting Started - Finished Product">
|
||||
*
|
||||
* <a style="text-align:center" href="http://maniacbug.wordpress.com/2011/11/02/getting-started-rf24/">Getting Started with nRF24L01+ on Arduino</a>
|
||||
* <a style="text-align:center" href="http://maniacbug.wordpress.com/2011/11/02/getting-started-rf24/">Getting Started with nRF24L01+ on Arduino</a>
|
||||
*
|
||||
* <img src="http://farm8.staticflickr.com/7159/6645514331_38eb2bdeaa_m.jpg" width="240" height="160" alt="Nordic FOB and nRF24L01+">
|
||||
*
|
||||
* <a style="text-align:center" href="http://maniacbug.wordpress.com/2012/01/08/nordic-fob/">Using the Sparkfun Nordic FOB</a>
|
||||
* <a style="text-align:center" href="http://maniacbug.wordpress.com/2012/01/08/nordic-fob/">Using the Sparkfun Nordic FOB</a>
|
||||
*
|
||||
* <img src="http://farm7.staticflickr.com/6097/6224308836_b9b3b421a3_m.jpg" width="240" height="160" alt="RF Duinode V3 (2V4)">
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user