REKLAMA

nRF24L01.7z

[STM32f103ZET6][C/CooCox] - Sieć układów nRF24L01.

Witam, Mam problem z nawiązaniem komunikacji między dwoma zestawami STM32F103ZET6+nRF24L01. Bazowałem na informacjach z tej strony: "RF24Network". Ściągnąłem odpowiednie pliki z klasami RF24 i RF24Network, oraz klasą SPI(wykorzystywana przez poprzednie klasy), przerobiłem je pod STM32, po czym spróbowałem uruchomić najprostszy możliwy przykład z powyższej stronki, czyli "helloworld_rx" w parze z "helloworld_tx". Oczywiście pomijając zbędne "printfy" itp rzeczy. Tak wygląda mój "main.cpp": #include "stm32f10x.h" #include "system.h" #include "Delay.h" #include "Open103Z_LEDs.h" #include "node_config.h" #include "string.h" #include "nRF24L01.h" int main() { SystemInit(); PeripheralInit(); LEDsInit(LED1 | LED2); Setup(); while (1) { Network.update(); #ifdef _TRANSCEIVER Transmit(); #else Receive(); #endif } } a tak pliki z przykładem z tej strony: "node_config.h": #ifndef __NODE_CONFIGURATION #define __NODE_CONFIGURATION #include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "RF24.h" #include "RF24Network.h" #include "Delay.h" #include "spi.h" #include "string.h" #define _TRANSCEIVER extern RF24 radio; // Network uses that radio extern RF24Network Network; #ifdef _TRANSCEIVER extern void Transmit(); #endif #ifndef _TRANSCEIVER void Receive(); #endif void Setup(); #endif "node_config.cpp" #include "node_config.h" RF24 radio(GPIO_Pin_3, GPIO_Pin_4); // Network uses that radio RF24Network Network(radio); #ifdef _TRANSCEIVER #include "Open103Z_LEDs.h" // Address of our node const uint16_t this_node = 1; // Address of the other node const uint16_t other_node = 0; // How often to send 'hello world to the other unit const unsigned long interval = 200; //ms // When did we last send? unsigned long last_sent; void Transmit() { unsigned long now = Millis(); if (now - last_sent > interval) { last_sent = now; const char* hello = "Hello!"; RF24NetworkHeader header(/*to node*/other_node); //uint8_t a = strlen(hello); bool ok = Network.write(header, hello, strlen(hello)); if (ok) ToggleLED(LED1); } } #endif #ifndef _TRANSCEIVER #include "Open103Z_LEDs.h" const uint16_t this_node = 0; // Address of the other node const uint16_t other_node = 1; void Receive() { // Is there anything ready for us? while ( Network.available() ) { // If so, grab it and print it out RF24NetworkHeader header; static char message; Network.read(header,message,sizeof(message)); uint32_t mess = 0; uint8_t i = 0; for(i = 0; i < 32; i++) { mess += message; message = 0; } if(mess) { ToggleLED(LED1); } } } #endif void Setup() { SPI.begin(); radio.begin(); Network.begin(/*channel*/90, /*node address*/this_node); } W załączniku dorzucam pliki z wykorzystanymi przeze mnie klasami. Do jednego zestawu STM32F103ZET6+nRF24L01 wgrywam program z zdefiniowanym "_TRANSCEIVER", co skutkuje wgraniem funkcji odpowiedzialnych za nadawanie(wszystko w plikach powyżej), a do drugiego zestawu bez tej definicji(czyli pracuje jako odbiornik). Do obu zestawów podłączyłem po diodzie, diody mają zmieniać stan za każdym nadaniem pakietu(w nadajniku) oraz za każdym odebraniem pakietu (w odbiorniku). Za nadanie pakietu uznaje się uzyskanie potwierdzenia ACK. Próbował ktoś już się bawić z taką siecią?? Możecie coś doradzić?? Dziwi mnie też wartość zmiennej "out_pipe"w "RF24Network::write_to_pipe" wynosi aż: 2305884725978394714. Tak ma być??


Pobierz plik - link do postu
  • nRF24L01.7z
    • RF24_config.h
    • RF24Network.cpp
    • RF24.h
    • nRF24L01.h
    • RF24.cpp
    • RF24Network_config.h
    • RF24Network.h


nRF24L01.7z > RF24Network.cpp

/*
Copyright (C) 2011 James Coliz, Jr. &amp; lt; maniacbug@ymail.com &amp; gt;

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 &quot; RF24Network_config.h &quot;
#include &quot; RF24.h &quot;
#include &quot; RF24Network.h &quot;

uint16_t RF24NetworkHeader::next_id = 1;

uint64_t pipe_address(uint16_t node, uint8_t pipe);
bool is_valid_address(uint16_t node);

/******************************************************************/

RF24Network::RF24Network(RF24 &amp; _radio) :
radio(_radio), next_frame(frame_queue)
{
}

/******************************************************************/

void RF24Network::begin(uint8_t _channel, uint16_t _node_address)
{
if (!is_valid_address(_node_address))
return;

node_address = _node_address;

// if (!radio.isValid())
// return;

// Set up the radio the way we want it to look
radio.setChannel(_channel);
radio.setDataRate(RF24_1MBPS);
radio.setCRCLength(RF24_CRC_16);

// Setup our address helper cache
setup_address();

// Open up all listening pipes
int i = 6;
while (i--)
radio.openReadingPipe(i, pipe_address(_node_address, i));
radio.startListening();

// Spew debugging state about the radio
radio.printDetails();
}

/******************************************************************/

void RF24Network::update(void)
{
// if there is data ready
uint8_t pipe_num;
while (/*radio.isValid() &amp; &amp; */ radio.available( &amp; pipe_num))
{
// Dump the payloads until we've gotten everything
bool done = false;
while (!done)
{
// Fetch the payload, and see if this was the last one.
done = radio.read(frame_buffer, sizeof(frame_buffer));

// Read the beginning of the frame as the header
const RF24NetworkHeader &amp; header =
*reinterpret_cast &amp; lt; RF24NetworkHeader* &amp; gt; (frame_buffer);


// Throw it away if it's not a valid address
if (!is_valid_address(header.to_node))
continue;

// Is this for us?
if (header.to_node == node_address)
// Add it to the buffer of frames for us
enqueue();
else
// Relay it
write(header.to_node);

// NOT NEEDED anymore. Now all reading pipes are open to start.
#if 0
// If this was for us, from one of our children, but on our listening
// pipe, it could mean that we are not listening to them. If so, open up
// and listen to their talking pipe

if ( header.to_node == node_address &amp; &amp; pipe_num == 0 &amp; &amp; is_descendant(header.from_node) )
{
uint8_t pipe = pipe_to_descendant(header.from_node);
radio.openReadingPipe(pipe,pipe_address(node_address,pipe));

// Also need to open pipe 1 so the system can get the full 5-byte address of the pipe.
radio.openReadingPipe(1,pipe_address(node_address,1));
}
#endif
}
}
}

/******************************************************************/

bool RF24Network::enqueue(void)
{
bool result = false;

// Copy the current frame into the frame queue
if (next_frame &amp; lt; frame_queue + sizeof(frame_queue))
{
memcpy(next_frame, frame_buffer, frame_size);
next_frame += frame_size;

result = true; }
else
{
}

return result;
}

/******************************************************************/

bool RF24Network::available(void)
{
// Are there frames on the queue for us?
return (next_frame &amp; gt; frame_queue);
}

/******************************************************************/

uint16_t RF24Network::parent() const
{
if (node_address == 0)
return -1;
else
return parent_node;
}

/******************************************************************/

void RF24Network::peek(RF24NetworkHeader &amp; header)
{
if (available())
{
// Copy the next available frame from the queue into the provided buffer
memcpy( &amp; header, next_frame - frame_size, sizeof(RF24NetworkHeader));
}
}

/******************************************************************/

size_t RF24Network::read(RF24NetworkHeader &amp; header, void* message,
size_t maxlen)
{
size_t bufsize = 0;

if (available())
{
// Move the pointer back one in the queue
next_frame -= frame_size;
uint8_t* frame = next_frame;

memcpy( &amp; header, frame, sizeof(RF24NetworkHeader));

if (maxlen &amp; gt; 0)
{
// How much buffer size should we actually copy?
bufsize = min(maxlen, frame_size - sizeof(RF24NetworkHeader));

// Copy the next available frame from the queue into the provided buffer
memcpy(message, frame + sizeof(RF24NetworkHeader), bufsize);
}
}

return bufsize;
}

/******************************************************************/

bool RF24Network::write(RF24NetworkHeader &amp; header, const void* message,
size_t len)
{
// Fill out the header
header.from_node = node_address;

// Build the full frame to send
memcpy(frame_buffer, &amp; header, sizeof(RF24NetworkHeader));
if (len)
memcpy(frame_buffer + sizeof(RF24NetworkHeader), message,
min(frame_size - sizeof(RF24NetworkHeader), len));
if (len)
{

}

// If the user is trying to send it to himself
if (header.to_node == node_address)
// Just queue it in the received queue
return enqueue();
else
// Otherwise send it out over the air
return write(header.to_node);
}

/******************************************************************/

bool RF24Network::write(uint16_t to_node)
{
bool ok = false;

// Throw it away if it's not a valid address
if (!is_valid_address(to_node))
return false;

// First, stop listening so we can talk.
//radio.stopListening();

// Where do we send this? By default, to our parent
uint16_t send_node = parent_node;
// On which pipe
uint8_t send_pipe = parent_pipe;

// If the node is a direct child,
if (is_direct_child(to_node))
{
// Send directly
send_node = to_node;

// To its listening pipe
send_pipe = 0;
}
// If the node is a child of a child
// talk on our child's listening pipe,
// and let the direct child relay it.
else if (is_descendant(to_node))
{
send_node = direct_child_route_to(to_node);
send_pipe = 0;
}

// First, stop listening so we can talk
radio.stopListening();

// Put the frame on the pipe
int retries = 3;
do
{
ok = write_to_pipe(send_node, send_pipe);
} while (!ok &amp; &amp; retries--);

// NOT NEEDED anymore. Now all reading pipes are open to start.
#if 0
// If we are talking on our talking pipe, it's possible that no one is listening.
// If this fails, try sending it on our parent's listening pipe. That will wake
// it up, and next time it will listen to us.

if ( !ok &amp; &amp; send_node == parent_node )
ok = write_to_pipe( parent_node, 0 );
#endif

// Now, continue listening
radio.startListening();

return ok;
}

/******************************************************************/

bool RF24Network::write_to_pipe(uint16_t node, uint8_t pipe)
{
bool ok = false;

uint64_t out_pipe = pipe_address(node, pipe);

// Open the correct pipe for writing.
radio.openWritingPipe(out_pipe);

// Retry a few times
short attempts = 5;
do
{
ok = radio.write(frame_buffer, frame_size);
} while (!ok &amp; &amp; --attempts);

return ok;
}

/******************************************************************/

const char* RF24NetworkHeader::toString(void) const
{
static char buffer[45];
snprintf(buffer, sizeof(buffer), PSTR( &quot; id %04x from 0%o to 0%o type %c &quot; ),
id, from_node, to_node, type);
return buffer;
}

/******************************************************************/

bool RF24Network::is_direct_child(uint16_t node)
{
bool result = false;

// A direct child of ours has the same low numbers as us, and only
// one higher number.
//
// e.g. node 0234 is a direct child of 034, and node 01234 is a
// descendant but not a direct child

// First, is it even a descendant?
if (is_descendant(node))
{
// Does it only have ONE more level than us?
uint16_t child_node_mask = (~node_mask) &amp; lt; &amp; lt; 3;
result = (node &amp; child_node_mask) == 0;
}

return result;
}

/******************************************************************/

bool RF24Network::is_descendant(uint16_t node)
{
return (node &amp; node_mask) == node_address;
}

/******************************************************************/

void RF24Network::setup_address(void)
{
// First, establish the node_mask
uint16_t node_mask_check = 0xFFFF;
while (node_address &amp; node_mask_check)
node_mask_check &amp; lt; &amp; lt; = 3;

node_mask = ~node_mask_check;

// parent mask is the next level down
uint16_t parent_mask = node_mask &amp; gt; &amp; gt; 3;

// parent node is the part IN the mask
parent_node = node_address &amp; parent_mask;

// parent pipe is the part OUT of the mask
uint16_t i = node_address;
uint16_t m = parent_mask;
while (m)
{
i &amp; gt; &amp; gt; = 3;
m &amp; gt; &amp; gt; = 3;
}
parent_pipe = i;

#ifdef SERIAL_DEBUG
printf_P(PSTR( &quot; setup_address node=0%o mask=0%o parent=0%o pipe=0%o\n\r &quot; ),node_address,node_mask,parent_node,parent_pipe);
#endif
}

/******************************************************************/

uint16_t RF24Network::direct_child_route_to(uint16_t node)
{
// Presumes that this is in fact a child!!

uint16_t child_mask = (node_mask &amp; lt; &amp; lt; 3) | 0B111;
return node &amp; child_mask;
}

/******************************************************************/

uint8_t RF24Network::pipe_to_descendant(uint16_t node)
{
uint16_t i = node;
uint16_t m = node_mask;

while (m)
{
i &amp; gt; &amp; gt; = 3;
m &amp; gt; &amp; gt; = 3;
}

return i &amp; 0B111;
}

/******************************************************************/

bool is_valid_address(uint16_t node)
{
bool result = true;

while (node)
{
uint8_t digit = node &amp; 0B111;
if (digit &amp; lt; 1 || digit &amp; gt; 5)
{
result = false;
printf_P(PSTR( &quot; *** WARNING *** Invalid address 0%o\n\r &quot; ), node);
break;
}
node &amp; gt; &amp; gt; = 3;
}

return result;
}

/******************************************************************/

uint64_t pipe_address(uint16_t node, uint8_t pipe)
{
static uint8_t pipe_segment[] =
{ 0x3c, 0x5a, 0x69, 0x96, 0xa5, 0xc3 };

uint64_t result;
uint8_t* out = reinterpret_cast &amp; lt; uint8_t* &amp; gt; ( &amp; result);

out[0] = pipe_segment[pipe];

uint8_t w;
short i = 4;
short shift = 12;
while (i--)
{
w = (node &amp; gt; &amp; gt; shift) &amp; 0xF;
w |= ~w &amp; lt; &amp; lt; 4;
out[i + 1] = w;

shift -= 4;
}

return result;
}

// vim:ai:cin:sts=2 sw=2 ft=cpp


nRF24L01.7z > RF24.h

//RF24.h

/*
Copyright (C) 2011 J. Coliz &amp; lt; maniacbug@ymail.com &amp; gt;

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_H__
#define __RF24_H__

#include &amp; lt; RF24_config.h &amp; gt;
#include &quot; Delay.h &quot;
#include &quot; spi.h &quot;
#include &quot; stm32f10x.h &quot;
#include &quot; stm32f10x_gpio.h &quot;
#include &quot; spi.h &quot;
#define min(x,y) (x &amp; lt; y) ? x : y
#define HIGH 1
#define LOW 0


typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ;

typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e;

typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e;

class RF24
{
private:
uint8_t ce_pin;
uint8_t csn_pin;
bool wide_band; /* 2Mbs data rate in use? */
bool p_variant; /* False for RF24L01 and true for RF24L01P */
uint8_t payload_size;
bool ack_payload_available;
bool dynamic_payloads_enabled;
uint8_t ack_payload_length;
uint64_t pipe0_reading_address;
protected:
void csn(int mode);

void ce(int level);

uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len);

public: uint8_t read_register(uint8_t reg);

protected: uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len);

uint8_t write_register(uint8_t reg, uint8_t value);

uint8_t write_payload(const void* buf, uint8_t len);

uint8_t read_payload(void* buf, uint8_t len);

uint8_t flush_rx(void);

uint8_t flush_tx(void);

uint8_t get_status(void);

void print_status(uint8_t status);

void print_observe_tx(uint8_t value);

void print_byte_register(prog_char* name, uint8_t reg, uint8_t qty = 1);

void print_address_register(prog_char* name, uint8_t reg, uint8_t qty = 1);

void toggle_features(void);
public:
RF24(uint16_t _cepin, uint16_t _cspin);

void begin(void);

void startListening(void);

void stopListening(void);

bool write( const void* buf, uint8_t len );

bool available(void);

bool read( void* buf, uint8_t len );

void openWritingPipe(uint64_t address);

void openReadingPipe(uint8_t number, uint64_t address);

void setRetries(uint8_t delay, uint8_t count);

void setChannel(uint8_t channel);

void setPayloadSize(uint8_t size);

uint8_t getPayloadSize(void);

uint8_t getDynamicPayloadSize(void);

void enableAckPayload(void);

void enableDynamicPayloads(void);

bool isPVariant(void) ;

void setAutoAck(bool enable);

void setAutoAck( uint8_t pipe, bool enable ) ;

void setPALevel( rf24_pa_dbm_e level ) ;

rf24_pa_dbm_e getPALevel( void ) ;

bool setDataRate(rf24_datarate_e speed);

rf24_datarate_e getDataRate( void ) ;

void setCRCLength(rf24_crclength_e length);

rf24_crclength_e getCRCLength(void);

void disableCRC( void ) ;

void printDetails(void);

void powerDown(void);

void powerUp(void) ;

bool available(uint8_t* pipe_num);

void startWrite( const void* buf, uint8_t len );

void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len);

bool isAckPayloadAvailable(void);

void whatHappened(bool &amp; tx_ok,bool &amp; tx_fail,bool &amp; rx_ready);

bool testCarrier(void);

bool testRPD(void) ;

};

#endif // __RF24_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp


//Generated on Sun Jan 15 2012 22:21:58 for RF24 by doxygen 1.7.4


nRF24L01.7z > nRF24L01.h

//nRF24L01.h
/*
Copyright (c) 2007 Stefan Engelke &amp; lt; mbox@stefanengelke.de &amp; gt;
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the &quot; Software &quot; ), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED &quot; AS IS &quot; , WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/* Memory Map */
#define CONFIG 0x00
#define EN_AA 0x01
#define EN_RXADDR 0x02
#define SETUP_AW 0x03
#define SETUP_RETR 0x04
#define RF_CH 0x05
#define RF_SETUP 0x06
#define STATUS 0x07
#define OBSERVE_TX 0x08
#define CD 0x09
#define RX_ADDR_P0 0x0A
#define RX_ADDR_P1 0x0B
#define RX_ADDR_P2 0x0C
#define RX_ADDR_P3 0x0D
#define RX_ADDR_P4 0x0E
#define RX_ADDR_P5 0x0F
#define TX_ADDR 0x10
#define RX_PW_P0 0x11
#define RX_PW_P1 0x12
#define RX_PW_P2 0x13
#define RX_PW_P3 0x14
#define RX_PW_P4 0x15
#define RX_PW_P5 0x16
#define FIFO_STATUS 0x17
#define DYNPD 0x1C
#define FEATURE 0x1D

/* Bit Mnemonics */
#define MASK_RX_DR 6
#define MASK_TX_DS 5
#define MASK_MAX_RT 4
#define EN_CRC 3
#define CRCO 2
#define PWR_UP 1
#define PRIM_RX 0
#define ENAA_P5 5
#define ENAA_P4 4
#define ENAA_P3 3
#define ENAA_P2 2
#define ENAA_P1 1
#define ENAA_P0 0
#define ERX_P5 5
#define ERX_P4 4
#define ERX_P3 3
#define ERX_P2 2
#define ERX_P1 1
#define ERX_P0 0
#define AW 0
#define ARD 4
#define ARC 0
#define PLL_LOCK 4
#define RF_DR 3
#define RF_PWR 6
#define RX_DR 6
#define TX_DS 5
#define MAX_RT 4
#define RX_P_NO 1
#define TX_FULL 0
#define PLOS_CNT 4
#define ARC_CNT 0
#define TX_REUSE 6
#define FIFO_FULL 5
#define TX_EMPTY 4
#define RX_FULL 1
#define RX_EMPTY 0
#define DPL_P5 5
#define DPL_P4 4
#define DPL_P3 3
#define DPL_P2 2
#define DPL_P1 1
#define DPL_P0 0
#define EN_DPL 2
#define EN_ACK_PAY 1
#define EN_DYN_ACK 0

/* Instruction Mnemonics */
#define R_REGISTER 0x00
#define W_REGISTER 0x20
#define REGISTER_MASK 0x1F
#define ACTIVATE 0x50
#define R_RX_PL_WID 0x60
#define R_RX_PAYLOAD 0x61
#define W_TX_PAYLOAD 0xA0
#define W_ACK_PAYLOAD 0xA8
#define FLUSH_TX 0xE1
#define FLUSH_RX 0xE2
#define REUSE_TX_PL 0xE3
#define NOP 0xFF

/* Non-P omissions */
#define LNA_HCURR 0

/* P model memory Map */
#define RPD 0x09

/* P model bit Mnemonics */
#define RF_DR_LOW 5
#define RF_DR_HIGH 3
#define RF_PWR_LOW 1
#define RF_PWR_HIGH 2

//Generated on Sun Jan 15 2012 22:21:58 for RF24 by doxygen 1.7.4


nRF24L01.7z > RF24_config.h

//RF24_config.h

/*
Copyright (C) 2011 J. Coliz &amp; lt; maniacbug@ymail.com &amp; gt;

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__

#include &amp; lt; stddef.h &amp; gt;

#include &quot; stm32f10x_spi.h &quot;
#include &amp; lt; stdint.h &amp; gt;
#include &amp; lt; stdio.h &amp; gt;
#include &amp; lt; string.h &amp; gt;
#include &quot; spi.h &quot;
extern SPIClass SPI;
#define _BV(x) (1 &amp; lt; &amp; lt; (x))

typedef char const prog_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 &quot; %s &quot;

#endif
// __RF24_CONFIG_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp

//Generated on Sun Jan 15 2012 22:21:58 for RF24 by doxygen 1.7.4


nRF24L01.7z > RF24Network_config.h

//RF24Network_config.h


/*
Copyright (C) 2011 James Coliz, Jr. &amp; lt; maniacbug@ymail.com &amp; gt;

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__

#include &amp; lt; stddef.h &amp; gt;


#include &amp; lt; stdint.h &amp; gt;
#include &amp; lt; stdio.h &amp; gt;
#include &amp; lt; string.h &amp; gt;
#include &quot; spi.h &quot;
extern SPIClass SPI;
#define _BV(x) (1 &amp; lt; &amp; lt; (x))

typedef char const prog_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 &quot; %s &quot;

#endif // __RF24_CONFIG_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp

//Generated on Wed Dec 21 2011 21:28:16 for RF24Network by doxygen 1.7.3


nRF24L01.7z > RF24Network.h

//RF24Network.h
/*
Copyright (C) 2011 James Coliz, Jr. &amp; lt; maniacbug@ymail.com &amp; gt;

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 __RF24NETWORK_H__
#define __RF24NETWORK_H__

#include &amp; lt; stddef.h &amp; gt;
#include &amp; lt; stdint.h &amp; gt;
#include &amp; lt; string.h &amp; gt;
#include &quot; RF24.h &quot;

class RF24;



struct RF24NetworkHeader
{
uint16_t from_node;
uint16_t to_node;
uint16_t id;
unsigned char type;
unsigned char reserved;
static uint16_t next_id;
RF24NetworkHeader()
{
}

RF24NetworkHeader(uint16_t _to, unsigned char _type = 0)
: to_node(_to), id(next_id++), type(_type &amp; 0x7f)
{}

const char* toString(void)
const;
};

class RF24Network
{
public:
RF24Network( RF24 &amp; _radio );

void begin(uint8_t _channel, uint16_t _node_address );

void update(void);

bool available(void);

void peek(RF24NetworkHeader &amp; header);

uint16_t parent() const;

size_t read(RF24NetworkHeader &amp; header, void* message, size_t maxlen);

bool write(RF24NetworkHeader &amp; header,const void* message, size_t len);

protected:
void open_pipes(void);
uint16_t find_node( uint16_t current_node, uint16_t target_node );
bool write(uint16_t);
bool write_to_pipe( uint16_t node, uint8_t pipe );
bool enqueue(void);

bool is_direct_child( uint16_t node );
bool is_descendant( uint16_t node );
uint16_t direct_child_route_to( uint16_t node );
uint8_t pipe_to_descendant( uint16_t node );
void setup_address(void);

private:
RF24 &amp; radio;
uint16_t node_address;
uint16_t num_nodes;
const static short frame_size = 32;
uint8_t frame_buffer[frame_size];
uint8_t frame_queue[5*frame_size];
uint8_t* next_frame;
uint16_t parent_node;
uint8_t parent_pipe;
uint16_t node_mask;
};

#endif // __RF24NETWORK_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp

//Generated on Wed Dec 21 2011 21:28:16 for RF24Network by doxygen 1.7.3


nRF24L01.7z > RF24.cpp

/*
Copyright (C) 2011 J. Coliz &amp; lt; maniacbug@ymail.com &amp; gt;

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 &quot; nRF24L01.h &quot;
#include &quot; RF24_config.h &quot;
#include &quot; RF24.h &quot;

/****************************************************************************/

void RF24::csn(int mode)
{
// Minimum ideal SPI bus speed is 2x data rate
// If we assume 2Mbs data rate and 16Mhz clock, a
// divider of 4 is the minimum we want.
// CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz
#ifdef ARDUINO
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV4);
#endif
GPIO_WriteBit(GPIOA, csn_pin, (BitAction) mode);
}

/****************************************************************************/

void RF24::ce(int level)
{
GPIO_WriteBit(GPIOA, ce_pin, (BitAction) level);
}

/****************************************************************************/

uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len)
{
uint8_t status;

csn(LOW);
status = SPI.transfer(R_REGISTER | (REGISTER_MASK &amp; reg));
while (len--)
*buf++ = SPI.transfer(0xff);

csn(HIGH);

return status;
}

/****************************************************************************/

uint8_t RF24::read_register(uint8_t reg)
{
csn(LOW);
SPI.transfer(R_REGISTER | (REGISTER_MASK &amp; reg));
uint8_t result = SPI.transfer(0xff);

csn(HIGH);
return result;
}

/****************************************************************************/

uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len)
{
uint8_t status;

csn(LOW);
status = SPI.transfer(W_REGISTER | (REGISTER_MASK &amp; reg));
while (len--)
SPI.transfer(*buf++);

csn(HIGH);

return status;
}

/****************************************************************************/

uint8_t RF24::write_register(uint8_t reg, uint8_t value)
{
uint8_t status;

csn(LOW);
status = SPI.transfer(W_REGISTER | (REGISTER_MASK &amp; reg));
SPI.transfer(value);
csn(HIGH);

return status;
}

/****************************************************************************/

uint8_t RF24::write_payload(const void* buf, uint8_t len)
{
uint8_t status;

const uint8_t* current = reinterpret_cast &amp; lt; const uint8_t* &amp; gt; (buf);

uint8_t data_len = min(len,payload_size);
uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len;

//printf( &quot; [Writing %u bytes %u blanks] &quot; ,data_len,blank_len);

csn(LOW);
status = SPI.transfer(W_TX_PAYLOAD);
while (data_len--)
SPI.transfer(*current++);
while (blank_len--)
SPI.transfer(0);
csn(HIGH);

return status;
}

/****************************************************************************/
uint8_t table[100] = {0};
uint8_t i = 0;
uint8_t RF24::read_payload(void* buf, uint8_t len)
{
uint8_t status;
uint8_t* current = reinterpret_cast &amp; lt; uint8_t* &amp; gt; (buf);

uint8_t data_len = min(len,payload_size);
uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len;

//printf( &quot; [Reading %u bytes %u blanks] &quot; ,data_len,blank_len);

csn(LOW);
status = SPI.transfer(R_RX_PAYLOAD);
while (data_len--)
{
*current++ = SPI.transfer(0xff);
//table[i++] = SPI.transfer(0xff);
}
while (blank_len--)
SPI.transfer(0xff);
csn(HIGH);

return status;
}

/****************************************************************************/

uint8_t RF24::flush_rx(void)
{
uint8_t status;

csn(LOW);
status = SPI.transfer(FLUSH_RX);
csn(HIGH);

return status;
}

/****************************************************************************/

uint8_t RF24::flush_tx(void)
{
uint8_t status;

csn(LOW);
status = SPI.transfer(FLUSH_TX);
csn(HIGH);

return status;
}

/****************************************************************************/

uint8_t RF24::get_status(void)
{
uint8_t status;

csn(LOW);
status = SPI.transfer(NOP);
csn(HIGH);

return status;
}

/****************************************************************************/

void RF24::print_status(uint8_t status)
{
printf_P(
PSTR(
&quot; STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n &quot; ),
status, (status &amp; _BV(RX_DR)) ? 1 : 0,
(status &amp; _BV(TX_DS)) ? 1 : 0, (status &amp; _BV(MAX_RT)) ? 1 : 0,
((status &amp; gt; &amp; gt; RX_P_NO) &amp; 0b111), (status &amp; _BV(TX_FULL)) ? 1 : 0);
}

/****************************************************************************/

void RF24::print_observe_tx(uint8_t value)
{
printf_P(PSTR( &quot; OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n &quot; ), value,
(value &amp; gt; &amp; gt; PLOS_CNT) &amp; 0b1111, (value &amp; gt; &amp; gt; ARC_CNT) &amp; 0b1111);
}

/****************************************************************************/

void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty)
{
// char extra_tab = strlen_P(name) &amp; lt; 8 ? '\t' : 0;
// //printf_P(PSTR(PRIPSTR &quot; \t%c = &quot; ),name,extra_tab);
// while (qty--)
// printf_P(PSTR( &quot; 0x%02x &quot; ), read_register(reg++));
// printf_P(PSTR( &quot; \r\n &quot; ));
}

/****************************************************************************/

void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty)
{
// char extra_tab = strlen_P(name) &amp; lt; 8 ? '\t' : 0;
// //printf_P(PSTR(PRIPSTR &quot; \t%c = &quot; ),name,extra_tab);
//
// while (qty--)
// {
// uint8_t buffer[5];
// read_register(reg++, buffer, sizeof buffer);
//
// printf_P(PSTR( &quot; 0x &quot; ));
// uint8_t* bufptr = buffer + sizeof buffer;
// while (--bufptr &amp; gt; = buffer)
// printf_P(PSTR( &quot; %02x &quot; ), *bufptr);
// }
//
// printf_P(PSTR( &quot; \r\n &quot; ));
}

/****************************************************************************/

RF24::RF24(uint16_t _cepin, uint16_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), pipe0_reading_address(0)
{
}

/****************************************************************************/

void RF24::setChannel(uint8_t channel)
{
// TODO: This method could take advantage of the 'wide_band' calculation
// done in setChannel() to require certain channel spacing.

const uint8_t max_channel = 127;
write_register(RF_CH, min(channel,max_channel));
}

/****************************************************************************/

void RF24::setPayloadSize(uint8_t size)
{
const uint8_t max_payload_size = 32;
payload_size = min(size,max_payload_size);
}

/****************************************************************************/

uint8_t RF24::getPayloadSize(void)
{
return payload_size;
}

/****************************************************************************/

static const char rf24_datarate_e_str_0[] = &quot; 1MBPS &quot; ;
static const char rf24_datarate_e_str_1[] = &quot; 2MBPS &quot; ;
static const char rf24_datarate_e_str_2[] = &quot; 250KBPS &quot; ;
static const char * const rf24_datarate_e_str_P[] =
{ rf24_datarate_e_str_0, rf24_datarate_e_str_1, rf24_datarate_e_str_2, };
static const char rf24_model_e_str_0[] = &quot; nRF24L01 &quot; ;
static const char rf24_model_e_str_1[] = &quot; nRF24L01+ &quot; ;
static const char * const rf24_model_e_str_P[] =
{ rf24_model_e_str_0, rf24_model_e_str_1, };
static const char rf24_crclength_e_str_0[] = &quot; Disabled &quot; ;
static const char rf24_crclength_e_str_1[] = &quot; 8 bits &quot; ;
static const char rf24_crclength_e_str_2[] = &quot; 16 bits &quot; ;
static const char * const rf24_crclength_e_str_P[] =
{ rf24_crclength_e_str_0, rf24_crclength_e_str_1, rf24_crclength_e_str_2, };
static const char rf24_pa_dbm_e_str_0[] = &quot; PA_MIN &quot; ;
static const char rf24_pa_dbm_e_str_1[] = &quot; PA_LOW &quot; ;
static const char rf24_pa_dbm_e_str_2[] = &quot; LA_MED &quot; ;
static const char rf24_pa_dbm_e_str_3[] = &quot; PA_HIGH &quot; ;
static const char * const rf24_pa_dbm_e_str_P[] =
{ 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( &quot; RX_ADDR_P0-1 &quot; ), RX_ADDR_P0, 2);
// print_byte_register(PSTR( &quot; RX_ADDR_P2-5 &quot; ), RX_ADDR_P2, 4);
// print_address_register(PSTR( &quot; TX_ADDR &quot; ), TX_ADDR);
//
// print_byte_register(PSTR( &quot; RX_PW_P0-6 &quot; ), RX_PW_P0, 6);
// print_byte_register(PSTR( &quot; EN_AA &quot; ), EN_AA);
// print_byte_register(PSTR( &quot; EN_RXADDR &quot; ), EN_RXADDR);
// print_byte_register(PSTR( &quot; RF_CH &quot; ), RF_CH);
// print_byte_register(PSTR( &quot; RF_SETUP &quot; ), RF_SETUP);
// print_byte_register(PSTR( &quot; CONFIG &quot; ), CONFIG);
// print_byte_register(PSTR( &quot; DYNPD/FEATURE &quot; ), DYNPD, 2);

// printf_P(PSTR( &quot; Data Rate\t = %S\r\n &quot; ),
// pgm_read_word( &amp; rf24_datarate_e_str_P[getDataRate()]));
// printf_P(PSTR( &quot; Model\t\t = %S\r\n &quot; ),
// pgm_read_word( &amp; rf24_model_e_str_P[isPVariant()]));
// printf_P(PSTR( &quot; CRC Length\t = %S\r\n &quot; ),
// pgm_read_word( &amp; rf24_crclength_e_str_P[getCRCLength()]));
// printf_P(PSTR( &quot; PA Power\t = %S\r\n &quot; ),
// pgm_read_word( &amp; rf24_pa_dbm_e_str_P[getPALevel()]));
}

/****************************************************************************/

void RF24::begin(void)
{
// Initialize pins
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = ce_pin | csn_pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &amp; GPIO_InitStructure);

// Initialize SPI bus
SPI.begin();

ce(LOW);
csn(HIGH);

// Must allow the radio time to settle else configuration bits will not necessarily stick.
// This is actually only required following power up but some settling time also appears to
// be required after resets too. For full coverage, we'll always assume the worst.
// Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped.
// Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure.
// WARNING: Delay is based on P-variant whereby non-P *may* require different timing.
Delay_Ms(5);

// 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, (0b0100 &amp; lt; &amp; lt; ARD) | (0b1111 &amp; lt; &amp; lt; ARC));

// Restore our default PA level
setPALevel (RF24_PA_MAX);

// Determine if this is a p or non-p RF24 module and then
// reset our data rate back to default value. This works
// because a non-P variant won't allow the data rate to
// be set to 250Kbps.
if (setDataRate (RF24_250KBPS))
{
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);

// 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));

// Set up default configuration. Callers can always change it later.
// This channel should be universally safe and not bleed over into adjacent
// spectrum.
setChannel(76);

// Flush buffers
flush_rx();
flush_tx();
}

/****************************************************************************/

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, if exists
if (pipe0_reading_address)
write_register(RX_ADDR_P0,
reinterpret_cast &amp; lt; const uint8_t* &amp; gt; ( &amp; pipe0_reading_address), 5);

// Flush buffers
flush_rx();
flush_tx();

// Go!
ce(HIGH);

// wait for the radio to come up (130us actually only needed)
Delay_us(130);
}

/****************************************************************************/

void RF24::stopListening(void)
{
ce(LOW);
flush_tx();
flush_rx();
}

/****************************************************************************/

void RF24::powerDown(void)
{
write_register(CONFIG, read_register(CONFIG) &amp; ~_BV(PWR_UP));
}

/****************************************************************************/

void RF24::powerUp(void)
{
write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP));
}

/******************************************************************/

bool RF24::write(const void* buf, uint8_t len)
{
bool result = false;

// Begin the write
startWrite(buf, len);

// ------------
// At this point we could return from a non-blocking write, and then call
// the rest after an interrupt

// 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, &amp; observe_tx, 1);
} while (!(status &amp; (_BV(TX_DS) | _BV(MAX_RT)))
&amp; &amp; (Millis() - sent_at &amp; lt; 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( &quot; %u%u%u\r\n &quot; ,tx_ok,tx_fail,ack_payload_available);

result = tx_ok;

// Handle the ack packet
if (ack_payload_available)
{
ack_payload_length = getDynamicPayloadSize();
}

// 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)
{
// Transmitter power-up
write_register(CONFIG,
(read_register(CONFIG) | _BV(PWR_UP)) &amp; ~_BV(PRIM_RX));
Delay_us(150);

// Send the payload
write_payload(buf, len);

// Allons!
ce(HIGH);
Delay_us(15);
ce(LOW);
}

/****************************************************************************/

uint8_t RF24::getDynamicPayloadSize(void)
{
uint8_t result = 0;

csn(LOW);
SPI.transfer(R_RX_PL_WID);
result = SPI.transfer(0xff);
csn(HIGH);

return result;
}

/****************************************************************************/

bool RF24::available(void)
{
return available(NULL);
}

/****************************************************************************/

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 &amp; _BV(RX_DR));

if (result)
{
// If the caller wants the pipe number, include that
if (pipe_num)
*pipe_num = (status &amp; gt; &amp; gt; RX_P_NO) &amp; 0b111;

// 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 &amp; _BV(TX_DS))
{
write_register(STATUS, _BV(TX_DS));
}
}

return result;
}

/****************************************************************************/

bool 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) &amp; _BV(RX_EMPTY);
}

/****************************************************************************/

void RF24::whatHappened(bool &amp; tx_ok, bool &amp; tx_fail, bool &amp; rx_ready)
{
// Read the status &amp; 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));

// Report to the user what happened
tx_ok = status &amp; _BV(TX_DS);
tx_fail = status &amp; _BV(MAX_RT);
rx_ready = status &amp; _BV(RX_DR);
}

/****************************************************************************/

void RF24::openWritingPipe(uint64_t value)
{
// Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+)
// expects it LSB first too, so we're good.

write_register(RX_ADDR_P0, reinterpret_cast &amp; lt; uint8_t* &amp; gt; ( &amp; value), 5);
write_register(TX_ADDR, reinterpret_cast &amp; lt; uint8_t* &amp; gt; ( &amp; value), 5);

const uint8_t max_payload_size = 32;
write_register(RX_PW_P0, min(payload_size,max_payload_size));
}

/****************************************************************************/

static const uint8_t child_pipe[] =
{ 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[] =
{ 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[] =
{ 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.
if (child == 0)
pipe0_reading_address = address;

if (child &amp; lt; = 6)
{
// For pipes 2-5, only write the LSB
if (child &amp; lt; 2)
write_register(child_pipe[child], //old version of this line: &quot; write_register(pgm_read_byte( &amp; child_pipe[child]), &quot;
reinterpret_cast &amp; lt; const uint8_t* &amp; gt; ( &amp; address), 5);
else
write_register(child_pipe[child], //old version of this line: &quot; write_register(pgm_read_byte( &amp; child_pipe[child]), &quot;
reinterpret_cast &amp; lt; const uint8_t* &amp; gt; ( &amp; address), 1);

write_register(child_payload_size[child], payload_size); //old version of this line: &quot; write_register(pgm_read_byte( &amp; child_pipe[child]),, payload_size); &quot;

// 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])); //old version of this line: &quot; | _BV(pgm_read_byte( &amp; child_pipe_enable[child]))); &quot;
}
}

/****************************************************************************/

void RF24::toggle_features(void)
{
csn(LOW);
SPI.transfer(ACTIVATE);
SPI.transfer(0x73);
csn(HIGH);
}

/****************************************************************************/

void RF24::enableDynamicPayloads(void)
{
// Enable dynamic payload throughout the system
write_register(FEATURE, read_register(FEATURE) | _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_DPL));
}

// Enable dynamic payload on all pipes
//
// Not sure the use case of only having dynamic payload on certain
// pipes, so the library does not support it.
write_register(DYNPD,
read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3)
| _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0));

dynamic_payloads_enabled = true;
}

/****************************************************************************/

void RF24::enableAckPayload(void)
{
//
// enable ack payload and dynamic payload features
//

write_register(FEATURE,
read_register(FEATURE) | _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));
}

//
// Enable dynamic payload on pipes 0 &amp; 1
//

write_register(DYNPD, read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0));
}

/****************************************************************************/

void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len)
{
const uint8_t* current = reinterpret_cast &amp; lt; const uint8_t* &amp; gt; (buf);

csn(LOW);
SPI.transfer(W_ACK_PAYLOAD | (pipe &amp; 0b111));
const uint8_t max_payload_size = 32;
uint8_t data_len = min(len,max_payload_size);
while (data_len--)
SPI.transfer(*current++);

csn(HIGH);
}

/****************************************************************************/

bool RF24::isAckPayloadAvailable(void)
{
bool result = ack_payload_available;
ack_payload_available = false;
return result;
}

/****************************************************************************/

bool RF24::isPVariant(void)
{
return p_variant;
}

/****************************************************************************/

void RF24::setAutoAck(bool enable)
{
if (enable)
write_register(EN_AA, 0b111111);
else
write_register(EN_AA, 0);
}

/****************************************************************************/

void RF24::setAutoAck(uint8_t pipe, bool enable)
{
if (pipe &amp; lt; = 6)
{
uint8_t en_aa = read_register(EN_AA);
if (enable)
{
en_aa |= _BV(pipe);
}
else
{
en_aa &amp; = ~_BV(pipe);
}
write_register(EN_AA, en_aa);
}
}

/****************************************************************************/

bool RF24::testCarrier(void)
{
return (read_register(CD) &amp; 1);
}

/****************************************************************************/

bool RF24::testRPD(void)
{
return (read_register(RPD) &amp; 1);
}

/****************************************************************************/

void RF24::setPALevel(rf24_pa_dbm_e level)
{
uint8_t setup = read_register(RF_SETUP);
setup &amp; = ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH));

// switch uses RAM (evil!)
if (level == RF24_PA_MAX)
{
setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH));
}
else if (level == RF24_PA_HIGH)
{
setup |= _BV(RF_PWR_HIGH);
}
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));
}

write_register(RF_SETUP, setup);
}

/****************************************************************************/

rf24_pa_dbm_e RF24::getPALevel(void)
{
rf24_pa_dbm_e result = RF24_PA_ERROR;
uint8_t power = read_register(RF_SETUP)
&amp; (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH));

// switch uses RAM (evil!)
if (power == (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)))
{
result = RF24_PA_MAX;
}
else if (power == _BV(RF_PWR_HIGH))
{
result = RF24_PA_HIGH;
}
else if (power == _BV(RF_PWR_LOW))
{
result = RF24_PA_LOW;
}
else
{
result = RF24_PA_MIN;
}

return result;
}

/****************************************************************************/

bool RF24::setDataRate(rf24_datarate_e speed)
{
bool result = false;
uint8_t setup = read_register(RF_SETUP);

// HIGH and LOW '00' is 1Mbs - our default
wide_band = false;
setup &amp; = ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH));
if (speed == RF24_250KBPS)
{
// Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0
// Making it '10'.
wide_band = false;
setup |= _BV(RF_DR_LOW);
}
else
{
// Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1
// Making it '01'
if (speed == RF24_2MBPS)
{
wide_band = true;
setup |= _BV(RF_DR_HIGH);
}
else
{
// 1Mbs
wide_band = false;
}
}
write_register(RF_SETUP, setup);

// Verify our result
if (read_register(RF_SETUP) == setup)
{
result = true;
}
else
{
wide_band = false;
}

return result;
}

/****************************************************************************/

rf24_datarate_e RF24::getDataRate(void)
{
rf24_datarate_e result;
uint8_t dr = read_register(RF_SETUP) &amp; (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH));

// switch uses RAM (evil!)
// Order matters in our case below
if (dr == _BV(RF_DR_LOW))
{
// '10' = 250KBPS
result = RF24_250KBPS;
}
else if (dr == _BV(RF_DR_HIGH))
{
// '01' = 2MBPS
result = RF24_2MBPS;
}
else
{
// '00' = 1MBPS
result = RF24_1MBPS;
}
return result;
}

/****************************************************************************/

void RF24::setCRCLength(rf24_crclength_e length)
{
uint8_t config = read_register(CONFIG) &amp; ~(_BV(CRCO) | _BV(EN_CRC));

// switch uses RAM (evil!)
if (length == RF24_CRC_DISABLED)
{
// 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);
}

/****************************************************************************/

rf24_crclength_e RF24::getCRCLength(void)
{
rf24_crclength_e result = RF24_CRC_DISABLED;
uint8_t config = read_register(CONFIG) &amp; (_BV(CRCO) | _BV(EN_CRC));

if (config &amp; _BV(EN_CRC))
{
if (config &amp; _BV(CRCO))
result = RF24_CRC_16;
else
result = RF24_CRC_8;
}

return result;
}

/****************************************************************************/

void RF24::disableCRC(void)
{
uint8_t disable = read_register(CONFIG) &amp; ~_BV(EN_CRC);
write_register(CONFIG, disable);
}

/****************************************************************************/
void RF24::setRetries(uint8_t delay, uint8_t count)
{
write_register(SETUP_RETR, (delay &amp; 0xf) &amp; lt; &amp; lt; ARD | (count &amp; 0xf) &amp; lt; &amp; lt; ARC);
}

// vim:ai:cin:sts=2 sw=2 ft=cpp