nRF24L01无线模块半双工通信调试小记

要做无线控制传输,最终选择便宜入门的nRF24L01(+)模块。

一开始把一个模块接了usb转spi的模块5v,结果被烧坏了,无奈又下单。

系统是树莓派2/3,接nRF24L01有7个管脚是必须要接的,如下:

1

VCC

官方规范说3~5v,但最好老实用3.3V
2GND接地
3CE始能管脚,这个根据自己板上定义,选择IO管脚即可
4CSN片选,接CS0
5SCKSPI时钟
6MOSI输入输出
7MISO输入输出
8IRQ中断悬空

 看了半天技术规范,然后参考了开源的RF24的代码。要注意收发模块的各功能参数配置要一致,如速率,信道,校验等,否则无法通信。

编译后运行,总算有收发信息了,但问题是为什么断断续续的,不是一边没收到,就是一边没有回复,卡顿半天,就这还无线通信?整了几天都没整明白。网络上找解答,看到有说是周围干扰如wifi信息引起的,通过变换信道,问题依旧,最后无奈只能分析链路过程,确认是哪边block住的,然后调试代码。最后问题定位是SPI读写timing和CE高电平的维持时间有关。规范上说ce高电平维持130us即可,但实际根据不同平台系统硬件可能需要调节。当然使用高的信道也是优先的选项。

我的实例中最终ce持续时间设了8ms才保持无block。

如果大虾也做过nrf24l01的项目,欢迎留言评论。最后上代码,例子是transmitter发送信息等待回复,receiver接收信息并原样回复。

RF24.h

/*
 Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>

 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.

 03/17/2013 : Charles-Henri Hallard (http://hallard.me)
              Modified to use with Arduipi board http://hallard.me/arduipi
              Modified to use the great bcm2835 library for I/O and SPI

 */

/**
 * @file RF24.h
 *
 * Class declaration for RF24 and helper enums
 */

#ifndef __RF24_H__
#define __RF24_H__
#include <nRF24L01.h>
#include <bcm2835.h>
#define DEBUG 0
/// \brief bcm2835SPISpeed
/// Specifies the divider used to generate the SPI clock from the system clock.
/// Figures below give the clock speed instead of clock divider.
#define BCM2835_SPI_SPEED_64MHZ BCM2835_SPI_CLOCK_DIVIDER_4
#define BCM2835_SPI_SPEED_32MHZ BCM2835_SPI_CLOCK_DIVIDER_8
#define BCM2835_SPI_SPEED_16MHZ BCM2835_SPI_CLOCK_DIVIDER_16
#define BCM2835_SPI_SPEED_8MHZ BCM2835_SPI_CLOCK_DIVIDER_32
#define BCM2835_SPI_SPEED_4MHZ BCM2835_SPI_CLOCK_DIVIDER_64
#define BCM2835_SPI_SPEED_2MHZ BCM2835_SPI_CLOCK_DIVIDER_128
#define BCM2835_SPI_SPEED_1MHZ BCM2835_SPI_CLOCK_DIVIDER_256
#define BCM2835_SPI_SPEED_512KHZ BCM2835_SPI_CLOCK_DIVIDER_512
#define BCM2835_SPI_SPEED_256KHZ BCM2835_SPI_CLOCK_DIVIDER_1024
#define BCM2835_SPI_SPEED_128KHZ BCM2835_SPI_CLOCK_DIVIDER_2048
#define BCM2835_SPI_SPEED_64KHZ BCM2835_SPI_CLOCK_DIVIDER_4096
#define BCM2835_SPI_SPEED_32KHZ BCM2835_SPI_CLOCK_DIVIDER_8192
#define BCM2835_SPI_SPEED_16KHZ BCM2835_SPI_CLOCK_DIVIDER_16384
#define BCM2835_SPI_SPEED_8KHZ BCM2835_SPI_CLOCK_DIVIDER_32768

#define max(a, b) (a > b ? a : b)
#define min(a, b) (a < b ? a : b)
#define _BV(x) (1 << (x))
#define pgm_read_word(p) (*(p))
#define pgm_read_byte(p) (*(p))
/**
 * 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;

/**
 * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver
 */

class RF24
{
public:
  /**
   * Enable error detection by un-commenting #define FAILURE_HANDLING in RF24_config.h
   * If a failure has been detected, it usually indicates a hardware issue. By default the library
   * will cease operation when a failure is detected.
   * This should allow advanced users to detect and resolve intermittent hardware issues.
   *
   * In most cases, the radio must be re-enabled via radio.begin(); and the appropriate settings
   * applied after a failure occurs, if wanting to re-enable the device immediately.
   *
   * Usage: (Failure handling must be enabled per above)
   *  @code
   *  if(radio.failureDetected){
   *    radio.begin(); 					     // Attempt to re-configure the radio with defaults
   *    radio.failureDetected = 0;		     // Reset the detection value
   *	radio.openWritingPipe(addresses[1]); // Re-configure pipe addresses
   *   radio.openReadingPipe(1,addresses[0]);
   *    report_failure();               	 // Blink leds, send a message, etc. to indicate failure
   *  }
   * @endcode
   **/
#if defined(FAILURE_HANDLING)
  bool failureDetect;
#endif
private:
  uint8_t ce_pin;     /**< "Chip Enable" pin, activates the RX or TX role */
  uint8_t csn_pin;    /**< SPI Chip select */
  uint16_t spi_speed; /**< SPI Bus Speed */
  uint8_t com_channel; /**< Comunication channel*/
  uint32_t cehighus;  /**< CE high delay in microsecond*/
  bool wide_band;       /* 2Mbs data rate in use? */
  bool p_variant;       /* False for RF24L01 and true for RF24L01P */
  uint8_t payload_size; /**< Fixed size of payloads */

  bool dynamic_payloads_enabled;    /**< Whether dynamic payloads are enabled. */
  uint8_t pipe0_reading_address[5]; /**< Last address set on pipe 0 for reading. */
  uint8_t addr_width;

  uint8_t spi_rxbuff[32 + 1]; // SPI receive buffer (payload max 32 bytes)
  uint8_t spi_txbuff[32 + 1]; // SPI transmit buffer (payload max 32 bytes + 1 byte for the command)

protected:
  /**
   * @name Low-level internal interface.
   *
   *  Protected methods that address the chip directly.  Regular users cannot
   *  ever call these.  They are documented for completeness and for developers who
   *  may want to extend this class.
   */
  /**@{*/

  /**
   * Read a chunk of data in from a register
   *
   * @param reg Which register. Use constants from nRF24L01.h
   * @param buf Where to put the data
   * @param len How many bytes of data to transfer
   * @return Current value of status register
   */
  uint8_t read_register(uint8_t reg, uint8_t *buf, uint8_t len);

  /**
   * Read single byte from a register
   *
   * @param reg Which register. Use constants from nRF24L01.h
   * @return Current value of register @p reg
   */
  uint8_t read_register(uint8_t reg);

  /**
   * Write a chunk of data to a register
   *
   * @param reg Which register. Use constants from nRF24L01.h
   * @param buf Where to get the data
   * @param len How many bytes of data to transfer
   * @return Current value of status register
   */
  uint8_t write_register(uint8_t reg, const uint8_t *buf, uint8_t len);

  /**
   * Write a single byte to a register
   *
   * @param reg Which register. Use constants from nRF24L01.h
   * @param value The new value to write
   * @return Current value of status register
   */
  uint8_t write_register(uint8_t reg, uint8_t value);

  /**
   * Write the transmit payload
   *
   * The size of data written is the fixed payload size, see getPayloadSize()
   *
   * @param buf Where to get the data
   * @param len Number of bytes to be sent
   * @return Current value of status register
   */
  uint8_t write_payload(const void *buf, uint8_t len, const uint8_t writeType);

  /**
   * Read the receive payload
   *
   * The size of data read is the fixed payload size, see getPayloadSize()
   *
   * @param buf Where to put the data
   * @param len Maximum number of bytes to read
   * @return Current value of status register
   */
  uint8_t read_payload(void *buf, uint8_t len);

  /**
   * Empty the receive buffer
   *
   * @return Current value of status register
   */
  uint8_t flush_rx(void);

  /**
   * Retrieve the current status of the chip
   *
   * @return Current value of status register
   */
  uint8_t get_status(void);

  /**
   * Decode and print the given status to stdout
   *
   * @param status Status value to print
   *
   * @warning Does nothing if stdout is not defined.  See fdevopen in stdio.h
   */
  void print_status(uint8_t status);

  /**
   * Decode and print the given 'observe_tx' value to stdout
   *
   * @param value The observe_tx value to print
   *
   * @warning Does nothing if stdout is not defined.  See fdevopen in stdio.h
   */
  void print_observe_tx(uint8_t value);

  /**
   * Print the name and value of an 8-bit register to stdout
   *
   * Optionally it can print some quantity of successive
   * registers on the same line.  This is useful for printing a group
   * of related registers on one line.
   *
   * @param name Name of the register
   * @param reg Which register. Use constants from nRF24L01.h
   * @param qty How many successive registers to print
   */
  void print_byte_register(const char *name, uint8_t reg, uint8_t qty = 1);

  /**
   * Print the name and value of a 40-bit address register to stdout
   *
   * Optionally it can print some quantity of successive
   * registers on the same line.  This is useful for printing a group
   * of related registers on one line.
   *
   * @param name Name of the register
   * @param reg Which register. Use constants from nRF24L01.h
   * @param qty How many successive registers to print
   */
  void print_address_register(const char *name, uint8_t reg, uint8_t qty = 1);

  /**
   * Turn on or off the special features of the chip
   *
   * The chip has certain 'features' which are only available when the 'features'
   * are enabled.  See the datasheet for details.
   */
  void toggle_features(void);

#if defined(FAILURE_HANDLING)
  void errNotify(void);
#endif

  /**@}*/

public:
  unsigned int bcm2835_millis(void);
  /**
   * @name Primary public interface
   *
   *  These are the main methods you need to operate the chip
   */
  /**@{*/

  /**
   * Constructor
   *
   * Creates a new instance of this driver.  Before using, you create an instance
   * and send in the unique pins that this chip is connected to.
   *
   * @param _cepin The pin attached to Chip Enable on the RF module
   * @param _cspin The pin attached to Chip Select
   * @param spispeed SPI speed
   * @param _channel for comunication
   * @param _cehighus
   */
  RF24(uint8_t _cepin, uint8_t _cspin);
  RF24(uint8_t _cepin, uint8_t _cspin, uint32_t spispeed, uint8_t _channel,uint32_t _cehighus);

  /**
   * Begin operation of the chip
   *
   * Call this in setup(), before calling any other methods.
   */
  bool begin(void);

  /**
   * Start listening on the pipes opened for reading.
   *
   * Be sure to call openReadingPipe() first.  Do not call write() while
   * in this mode, without first calling stopListening().  Call
   * isAvailable() to check for incoming traffic, and read() to get it.
   */
  void startListening(void);

  /**
   * Stop listening for incoming messages
   *
   * Do this before calling write().
   */
  void stopListening(void);

  /**
   * Write to the open writing pipe
   *
   * Be sure to call openWritingPipe() first to set the destination
   * of where to write to.
   *
   * This blocks until the message is successfully acknowledged by
   * the receiver or the timeout/retransmit maxima are reached.  In
   * the current configuration, the max delay here is 60ms.
   *
   * The maximum size of data written is the fixed payload size, see
   * getPayloadSize().  However, you can write less, and the remainder
   * will just be filled with zeroes.
   *
   * @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
   */
  bool write(const void *buf, uint8_t len);

  /**
   * Test whether there are bytes available to be read
   *
   * @return True if there is a payload available, false if none is
   */
  bool available(void);

  /**
   * Read the payload
   *
   * Return the last payload received
   *
   * The size of data read is the fixed payload size, see getPayloadSize()
   *
   * @note I specifically chose 'void*' as a data type to make it easier
   * for beginners to use.  No casting needed.
   *
   * @param buf Pointer to a buffer where the data should be written
   * @param len Maximum number of bytes to read into the buffer
   * @return True if the payload was delivered successfully false if not
   */
  void read(void *buf, uint8_t len);

  /**
   * New: Open a pipe for writing
   *
   * Only one pipe can be open at once, but you can change the pipe
   * you'll write to. Call stopListening() first.
   *
   * Addresses are assigned via a byte array, default is 5 byte address length
   *
   * Usage is exactly the same as before, except for declaring the array
   *
   * @code
   *   uint8_t addresses[][6] = {"1Node","2Node"};
   *   openWritingPipe(addresses[0]);
   * @endcode
   * @see setAddressWidth
   *
   * @param address The address of the pipe to open. Coordinate these pipe
   * addresses amongst nodes on the network.
   */

  void openWritingPipe(const uint8_t *address);

  /**
   * Open a pipe for reading
   *
   * Up to 6 pipes can be open for reading at once.  Open all the
   * reading pipes, and then call startListening().
   *
   * @see openWritingPipe
   * @see setAddressWidth
   *
   * @warning Pipes 1-5 should share the same address, except the first byte.
   * Only the first byte in the array should be unique, e.g.
   * @code
   *   uint8_t addresses[][6] = {"1Node","2Node"};
   *   openReadingPipe(1,addresses[0]);
   *   openReadingPipe(2,addresses[1]);
   * @endcode
   *
   * @warning Pipe 0 is also used by the writing pipe.  So if you open
   * pipe 0 for reading, and then startListening(), it will overwrite the
   * writing pipe.  Ergo, do an openWritingPipe() again before write().
   *
   * @param number Which pipe# to open, 0-5.
   * @param address The 24, 32 or 40 bit address of the pipe to open.
   */

  void openReadingPipe(uint8_t number, const uint8_t *address);

  /**
   * Empty the transmit buffer
   *
   * @return Current value of status register
   */
  uint8_t flush_tx(void);

  /**@}*/
  /**
   * @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
   *  defaults.
   */
  /**@{*/
  /**
   * Set the number and delay of retries upon failed submit
   *
   * @param delay How long to wait between each retry, in multiples of 250us,
   * max is 15.  0 means 250us, 15 means 4000us.
   * @param count How many retries before giving up, max 15
   */
  void setRetries(uint8_t delay, uint8_t count);

  /**
   * Set RF communication channel
   *
   * @param channel Which RF channel to communicate on, 0-127
   */
  void setChannel(uint8_t channel);

  /**
   * Set Static Payload Size
   *
   * This implementation uses a pre-stablished fixed payload size for all
   * transmissions.  If this method is never called, the driver will always
   * transmit the maximum payload size (32 bytes), no matter how much
   * was sent to write().
   *
   * @todo Implement variable-sized payloads feature
   *
   * @param size The number of bytes in the payload
   */
  void setPayloadSize(uint8_t size);

  /**
   * Get Static Payload Size
   *
   * @see setPayloadSize()
   *
   * @return The number of bytes in the payload
   */
  uint8_t getPayloadSize(void);

  /**
   * Get Dynamic Payload Size
   *
   * For dynamic payloads, this pulls the size of the payload off
   * the chip
   *
   * @return Payload length of last-received dynamic payload
   */
  uint8_t getDynamicPayloadSize(void);

  /**
   * Enable custom payloads on the acknowledge packets
   *
   * Ack payloads are a handy way to return data back to senders without
   * manually changing the radio modes on both units.
   *
   * @see examples/pingpair_pl/pingpair_pl.pde
   */
  void enableAckPayload(void);

  /**
   * Enable dynamically-sized payloads
   *
   * This way you don't always have to send large packets just to send them
   * once in a while.  This enables dynamic payloads on ALL pipes.
   *
   * @see examples/pingpair_pl/pingpair_dyn.pde
   */
  void enableDynamicPayloads(void);

  /**
   * Enable dynamic ACKs (single write multicasting) for chosen messages
   *
   * @note To enable full multicasting or per-pipe multicast, use setAutoAck()
   *
   * @warning This MUST be called prior to attempting single write NOACK calls
   * @code
   * radio.enableDynamicAck();
   * radio.write(&data,32,1);  // Sends a payload with no acknowledgement requested
   * radio.write(&data,32,0);  // Sends a payload using auto-retry/autoACK
   * @endcode
   */
  void enableDynamicAck(void);

  /**
   * Determine whether the hardware is an nRF24L01+ or not.
   *
   * @return true if the hardware is nRF24L01+ (or compatible) and false
   * if its not.
   */
  bool isPVariant(void);

  /**
   * Enable or disable auto-acknowlede packets
   *
   * This is enabled by default, so it's only needed if you want to turn
   * it off for some reason.
   *
   * @param enable Whether to enable (true) or disable (false) auto-acks
   */
  void setAutoAck(bool enable);

  /**
   * Enable or disable auto-acknowlede packets on a per pipeline basis.
   *
   * AA is enabled by default, so it's only needed if you want to turn
   * it off/on for some reason on a per pipeline basis.
   *
   * @param pipe Which pipeline to modify
   * @param enable Whether to enable (true) or disable (false) auto-acks
   */
  void setAutoAck(uint8_t pipe, bool enable);

  /**
   * Set Power Amplifier (PA) level to one of four levels.
   * 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.
   *
   * @param level Desired PA level.
   */
  void setPALevel(uint8_t level);

  /**
   * Fetches the current PA level.
   *
   * @return Returns a value from the rf24_pa_dbm_e enum describing
   * the current PA setting. Please remember, all values represented
   * by the enum mnemonics are negative dBm. See setPALevel for
   * return value descriptions.
   */
  uint8_t getPALevel(void);

  /**
   * Set the transmission data rate
   *
   * @warning setting RF24_250KBPS will fail for non-plus units
   *
   * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps
   * @return true if the change was successful
   */
  bool setDataRate(rf24_datarate_e speed);

  /**
   * Fetches the transmission data rate
   *
   * @return Returns the hardware's currently configured datarate. The value
   * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the
   * rf24_datarate_e enum.
   */
  rf24_datarate_e getDataRate(void);

  /**
   * Set the CRC length
   *
   * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit
   */
  void setCRCLength(rf24_crclength_e length);

  /**
   * Get the CRC length
   *
   * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit
   */
  rf24_crclength_e getCRCLength(void);

  /**
   * Disable CRC validation
   *
   */
  void disableCRC(void);

  /**@}*/
  /**
   * @name Deprecated
   *
   *  Methods provided for backwards compabibility.
   */
  /**@{*/

  /**
   * Open a pipe for writing
   *
   * Only one pipe can be open at once, but you can change the pipe
   * you'll listen to.  Do not call this while actively listening.
   * Remember to stopListening() first.
   *
   * Addresses are 40-bit hex values, e.g.:
   *
   * @code
   *   openWritingPipe(0xF0F0F0F0F0);
   * @endcode
   *
   * @param address The 40-bit address of the pipe to open.  This can be
   * any value whatsoever, as long as you are the only one writing to it
   * and only one other radio is listening to it.  Coordinate these pipe
   * addresses amongst nodes on the network.
   */
  void openWritingPipe(uint64_t address);

  /**
   * Open a pipe for reading
   *
   * Up to 6 pipes can be open for reading at once.  Open all the
   * reading pipes, and then call startListening().
   *
   * @see openWritingPipe
   *
   * @warning Pipes 1-5 should share the first 32 bits.
   * Only the least significant byte should be unique, e.g.
   * @code
   *   openReadingPipe(1,0xF0F0F0F0AA);
   *   openReadingPipe(2,0xF0F0F0F066);
   * @endcode
   *
   * @warning Pipe 0 is also used by the writing pipe.  So if you open
   * pipe 0 for reading, and then startListening(), it will overwrite the
   * writing pipe.  Ergo, do an openWritingPipe() again before write().
   *
   * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits
   *
   * @param number Which pipe# to open, 0-5.
   * @param address The 40-bit address of the pipe to open.
   */
  void openReadingPipe(uint8_t number, uint64_t address);

  /**@}*/
  /**
   * @name Advanced Operation
   *
   *  Methods you can use to drive the chip in more advanced ways
   */
  /**@{*/

  /**
   * Print a giant block of debugging information to stdout
   *
   * @warning Does nothing if stdout is not defined.  See fdevopen in stdio.h
   */
  void printDetails(void);

  /**
   * Enter low-power mode
   *
   * To return to normal power mode, either write() some data or
   * startListening, or powerUp().
   */
  void powerDown(void);

  /**
   * Leave low-power mode - making radio more responsive
   *
   * To return to low power mode, call powerDown().
   */
  void powerUp(void);

  /**
   * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write.
   *
   * @note enableDynamicAck() must be called to enable this feature
   *
   * Can be used with enableAckPayload() to request a response
   * @see enableDynamicAck()
   * @see setAutoAck()
   * @see write()
   *
   * @param buf Pointer to the data to be sent
   * @param len Number of bytes to be sent
   * @param multicast Request ACK (0), NOACK (1)
   */
  bool write(const void *buf, uint8_t len, const bool multicast);

  /**
   * @note Optimization: New Command   *
   * This will not block until the 3 FIFO buffers are filled with data.
   * Once the FIFOs are full, writeFast will simply wait for success or
   * timeout, and return 1 or 0 respectively. From a user perspective, just
   * keep trying to send the same data. The library will keep auto retrying
   * the current payload using the built in functionality.
   * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto
   * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO
   * to clear by issuing txStandBy() or ensure appropriate time between transmissions.
   *
   * ONLY max retry interrupt flags will be cleared when writeFast is called
   *
   * @code
   * Example (Partial blocking):
   *
   *			radio.writeFast(&buf,32);  // Writes 1 payload to the buffers
   *			txStandBy();     		   // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail.
   *
   *			radio.writeFast(&buf,32);  // Writes 1 payload to the buffers
   *			txStandBy(1000);		   // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0.
   * @endcode
   *
   * @see txStandBy()
   * @see write()
   * @see writeBlocking()
   *
   * @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
   */
  bool writeFast(const void *buf, uint8_t len);

  /**
   * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write.
   *
   * @note enableDynamicAck() must be called to enable this feature
   * @see enableDynamicAck()
   * @see setAutoAck()
   *
   * @param buf Pointer to the data to be sent
   * @param len Number of bytes to be sent
   * @param multicast Request ACK (0) or NOACK (1)
   */
  bool writeFast(const void *buf, uint8_t len, const bool multicast);

  /**
   * @note Optimization: New Command
   * This function extends the auto-retry mechanism to any specified duration.
   * It will not block until the 3 FIFO buffers are filled with data.
   * If so the library will auto retry until a new payload is written
   * or the user specified timeout period is reached.
   * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto
   * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO
   * to clear by issuing txStandBy() or ensure appropriate time between transmissions.
   *
   * ONLY max retry interrupt flags will be cleared when writeBlocking is called
   * @code
   * Example (Full blocking):
   *
   *			radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers
   *			txStandBy(1000);     			   //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed.
   *					  				   		   //Blocks only until user timeout or success. Data flushed on fail.
   * @endcode
   * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis().
   * @see txStandBy()
   * @see write()
   * @see writeFast()
   *
   * @param buf Pointer to the data to be sent
   * @param len Number of bytes to be sent
   * @param timeout User defined timeout in milliseconds.
   * @return True if the payload was loaded into the buffer successfully false if not
   */
  bool writeBlocking(const void *buf, uint8_t len, uint32_t timeout);

  /**
   * @note Optimization: New Command
   * This function should be called as soon as transmission is finished to
   * drop the radio back to STANDBY-I mode. If not issued, the radio will
   * remain in STANDBY-II mode which, per the data sheet, is not a recommended
   * operating mode.
   *
   * @note When transmitting data in rapid succession, it is still recommended by
   * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is
   * time enough between sends for the FIFOs to empty.
   *
   * Relies on built-in auto retry functionality.
   *
   * @code
   * Example (Partial blocking):
   *
   *			radio.writeFast(&buf,32);
   *			radio.writeFast(&buf,32);
   *			radio.writeFast(&buf,32);  //Fills the FIFO buffers up
   *			bool ok = txStandBy();     //Returns 0 if failed. 1 if success.
   *					  				   //Blocks only until MAX_RT timeout or success. Data flushed on fail.
   * @endcode
   * @see txStandBy(unsigned long timeout)
   * @return True if transmission is successful
   *
   */
  bool txStandBy();

  /**
   * @note Optimization: New Command
   *
   * This function allows extended blocking and auto-retries per a user defined timeout
   * @code
   *	Fully Blocking Example:
   *
   *			radio.writeFast(&buf,32);
   *			radio.writeFast(&buf,32);
   *			radio.writeFast(&buf,32);   //Fills the FIFO buffers up
   *			bool ok = txStandBy(1000);  //Returns 0 if failed after 1 second of retries. 1 if success.
   *					  				    //Blocks only until user defined timeout or success. Data flushed on fail.
   * @endcode
   * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis().
   * @param timeout Number of milliseconds to retry failed payloads
   * @return True if transmission is successful
   *
   */
  bool txStandBy(uint32_t timeout);

  /**
   * Test whether there are bytes available to be read
   *
   * Use this version to discover on which pipe the message
   * arrived.
   *
   * @param[out] pipe_num Which pipe has the payload available
   * @return True if there is a payload available, false if none is
   */
  bool available(uint8_t *pipe_num);

  /**
   * Non-blocking write to the open writing pipe used for buffered writes
   *
   * @note Optimization: This function now leaves the CE pin high, so the radio
   * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued.
   * This allows the chip to be used to its full potential in TX mode.
   * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto
   * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO
   * to clear by issuing txStandBy() or ensure appropriate time between transmissions.
   *
   * @see write()
   * @see writeFast()
   * @see startWrite()
   * @see writeBlocking()
   *
   * For single noAck writes see:
   * @see enableDynamicAck()
   * @see setAutoAck()
   *
   * @param buf Pointer to the data to be sent
   * @param len Number of bytes to be sent
   * @param multicast Request ACK (0) or NOACK (1)
   * @return True if the payload was delivered successfully false if not
   */
  void startFastWrite(const void *buf, uint8_t len, const bool multicast);

  /**
   * Non-blocking write to the open writing pipe
   *
   * Just like write(), but it returns immediately. To find out what happened
   * to the send, catch the IRQ and then call whatHappened().
   *
   * @note Optimization: This function again behaves as it did previously for backwards-compatibility.
   * with user code. The library uses startFastWrite() internally.
   * This is mainly used for single-payload transactions.
   *
   * @see write()
   * @see writeFast()
   * @see startFastWrite()
   * @see whatHappened()
   *
   * For single noAck writes see:
   * @see enableDynamicAck()
   * @see setAutoAck()
   *
   * @param buf Pointer to the data to be sent
   * @param len Number of bytes to be sent
   * @param multicast Request ACK (0) or NOACK (1)
   *
   */
  void startWrite(const void *buf, uint8_t len, const bool multicast);

  /**
   * Optimization: New Command
   *
   * This function is mainly used internally to take advantage of the auto payload
   * re-use functionality of the chip, but can be beneficial to users as well.
   *
   * The function will instruct the radio to re-use the data in the FIFO buffers,
   * and instructs the radio to re-send once the timeout limit has been reached.
   * Used by writeFast and writeBlocking to initiate retries when a TX failure
   * occurs. Retries are automatically initiated except with the standard write().
   * This way, data is not flushed from the buffer until switching between modes.
   *
   * @note This is to be used AFTER auto-retry fails if wanting to resend
   * using the built-in payload reuse features.
   * After issuing reUseTX(), it will keep reending the same payload forever or until
   * a payload is written to the FIFO, or a flush_tx command is given.
   */
  void reUseTX();

  /**
   * Write an ack payload for the specified pipe
   *
   * The next time a message is received on @p pipe, the data in @p buf will
   * be sent back in the acknowledgement.
   *
   * @warning According to the data sheet, only three of these can be pending
   * at any time.  I have not tested this.
   *
   * @param pipe Which pipe# (typically 1-5) will get this response.
   * @param buf Pointer to data that is sent
   * @param len Length of the data to send, up to 32 bytes max.  Not affected
   * by the static payload set by setPayloadSize().
   */
  void writeAckPayload(uint8_t pipe, const void *buf, uint8_t len);

  /**
   * Determine if an ack payload was received in the most recent call to
   * write().
   *
   * Call read() to retrieve the ack payload.
   *
   * @warning Calling this function clears the internal flag which indicates
   * a payload is available.  If it returns true, you must read the packet
   * out as the very next interaction with the radio, or the results are
   * undefined.
   *
   * @return True if an ack payload is available.
   */
  bool isAckPayloadAvailable(void);

  /**
   * Call this when you get an interrupt to find out why
   *
   * Tells you what caused the interrupt, and clears the state of
   * interrupts.
   *
   * @param[out] tx_ok The send was successful (TX_DS)
   * @param[out] tx_fail The send failed, too many retries (MAX_RT)
   * @param[out] rx_ready There is a message waiting to be read (RX_DS)
   */
  void whatHappened(bool &tx_ok, bool &tx_fail, bool &rx_ready);

  /**
   * Test whether there was a carrier on the line for the
   * previous listening period.
   *
   * Useful to check for interference on the current channel.
   *
   * @return true if was carrier, false if not
   */
  bool testCarrier(void);

  /**
   * Test whether a signal (carrier or otherwise) greater than
   * or equal to -64dBm is present on the channel. Valid only
   * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier().
   *
   * Useful to check for interference on the current channel and
   * channel hopping strategies.
   *
   * @return true if signal => -64dBm, false if not
   */
  bool testRPD(void);

  /**
   * Test whether this is a real radio, or a mock shim for
   * debugging.  Setting either pin to 0xff is the way to
   * indicate that this is not a real radio.
   *
   * @return true if this is a legitimate radio
   */
  bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; }

  /**
   * The radio will generate interrupt signals when a transmission is complete,
   * a transmission fails, or a payload is received. This allows users to mask
   * those interrupts to prevent them from generating a signal on the interrupt
   * pin.
   *
   * @code
   * 	Mask all interrupts except the receive interrupt:
   *
   *		radio.maskIRQ(1,1,0);
   * @endcode
   *
   * @param tx_ok  Mask transmission complete interrupts
   * @param tx_fail  Mask transmit failure interrupts
   * @param rx_ready Mask payload received interrupts
   */
  void maskIRQ(bool tx_ok, bool tx_fail, bool rx_ready);

  /**
   * Set the address width from 3 to 5 bytes (24, 32 or 40 bit)
   *
   * @param a_width The address width to use: 3,4 or 5
   */

  void setAddressWidth(uint8_t a_width);

  /**@}*/
};

/**
 * @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>.
 *
 * 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
 *
 * This is an example of how to use the RF24 class to control a remote
 * bank of LED's using buttons on a remote control.
 *
 * Every time the buttons change on the remote, the entire state of
 * buttons is send to the led board, which displays the state.
 */

/**
 * @example pingpair.pde
 *
 * This is an example of how to use the RF24 class.  Write this sketch to two
 * different nodes, connect the role_pin to ground on one.  The ping node sends
 * the current time to the pong node, which responds by sending the value back.
 * The ping node can then see how long the whole cycle took.
 */

/**
 * @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
 * took.
 */

/**
 * @example starping.pde
 *
 * This sketch is a more complex example of using the RF24 library for Arduino.
 * Deploy this on up to six nodes.  Set one as the 'pong receiver' by tying the
 * role_pin low, and the others will be 'ping transmit' units.  The ping units
 * unit will send out the value of millis() once a second.  The pong unit will
 * respond back with a copy of the value.  Each ping unit can get that response
 * back, and determine how long the whole cycle took.
 *
 * This example requires a bit more complexity to determine which unit is which.
 * The pong receiver is identified by having its role_pin tied to ground.
 * The ping senders are further differentiated by a byte in eeprom.
 */

/**
 * @example pingpair_pl.pde
 *
 * This is an example of how to do two-way communication without changing
 * transmit/receive modes.  Here, a payload is set to the transmitter within
 * the Ack packet of each transmission.  Note that the payload is set BEFORE
 * the sender's message arrives.
 */

/**
 * @example pingpair_irq.pde
 *
 * This is an example of how to user interrupts to interact with the radio.
 * It builds on the pingpair_pl example, and uses ack payloads.
 */

/**
 * @example pingpair_sleepy.pde
 *
 * This is an example of how to use the RF24 class to create a battery-
 * efficient system.  It is just like the pingpair.pde example, but the
 * ping node powers down the radio and sleeps the MCU after every
 * ping/pong cycle.
 */

/**
 * @example scanner.pde
 *
 * Example to detect interference on the various channels available.
 * This is a good diagnostic tool to check whether you're picking a
 * good channel for your application.
 *
 * Inspired by cpixip.
 * See http://arduino.cc/forum/index.php/topic,54795.0.html
 */

/**
 * @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.
 * 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>,
 * 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>
 * @li <a href="http://maniacbug.github.com/RF24/classRF24.html">RF24 Class Documentation</a>
 * @li <a href="https://github.com/maniacbug/RF24/">Source Code</a>
 * @li <a href="https://github.com/maniacbug/RF24/archives/master">Downloads Page</a>
 * @li <a href="http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf">Chip Datasheet</a>
 *
 * 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.
 *
 * @section More More Information
 *
 * @subpage FAQ
 *
 * @section Projects Projects
 *
 * Stuff I have built with RF24
 *
 * <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>
 *
 * <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>
 *
 * <img src="http://farm7.staticflickr.com/6097/6224308836_b9b3b421a3_m.jpg" width="240" height="160" alt="RF Duinode V3 (2V4)">
 *
 * <a href="http://maniacbug.wordpress.com/2011/10/19/sensor-node/">Low-Power Wireless Sensor Node</a>
 *
 * <img src="http://farm8.staticflickr.com/7012/6489477865_b56edb629b_m.jpg" width="240" height="161" alt="nRF24L01+ connected to Leaf Labs Maple Native">
 *
 * <a href="http://maniacbug.wordpress.com/2011/12/14/nrf24l01-running-on-maple-3/">nRF24L01+ Running on Maple</a>
 */

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

RF24.cpp

/*
 Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>

 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.



03/17/2013 : Charles-Henri Hallard (http://hallard.me)
             Modified to use with Arduipi board http://hallard.me/arduipi
             Changed to use modified bcm2835 library

TMRh20 2014: Updated to work with optimized RF24 and RF24 Network Arduino libs.
*/

#include <RF24.h>
#include <nRF24L01.h>
#include <cstdio>
#include <cstring>
#include <sys/time.h>
/****************************************************************************/
uint8_t RF24::read_register(uint8_t reg, uint8_t *buf, uint8_t len)
{
  uint8_t status;
  uint8_t *prx = spi_rxbuff;
  uint8_t *ptx = spi_txbuff;
  uint8_t size = len + 1; // Add register value to transmit buffer

  *ptx++ = (R_REGISTER | (REGISTER_MASK & reg));
  while (len--)
  {
    *ptx++ = NOP; // Dummy operation, just for reading
  }
  bcm2835_spi_transfernb((char *)spi_txbuff, (char *)spi_rxbuff, size);

  status = *prx++; // status is 1st byte of receive buffer

  // decrement before to skip status byte
  while (--size)
  {
    *buf++ = *prx++;
  }

  return status;
}

/****************************************************************************/
uint8_t RF24::read_register(uint8_t reg)
{
  uint8_t result;

  uint8_t *prx = spi_rxbuff;
  uint8_t *ptx = spi_txbuff;

  *ptx++ = (R_REGISTER | (REGISTER_MASK & reg));
  *ptx++ = NOP; // Dummy operation, just for reading

  bcm2835_spi_transfernb((char *)spi_txbuff, (char *)spi_rxbuff, 2);

  result = *++prx; // result is 2nd byte of receive buffer

  return result;
}

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

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

  uint8_t *prx = spi_rxbuff;
  uint8_t *ptx = spi_txbuff;

  *ptx++ = (W_REGISTER | (REGISTER_MASK & reg));
  *ptx = value;

  bcm2835_spi_transfernb((char *)spi_txbuff, (char *)spi_rxbuff, 2);

  status = *prx++; // status is 1st byte of receive buffer

  return status;
}

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

uint8_t RF24::write_register(uint8_t reg, const uint8_t *buf, uint8_t len)
{
  uint8_t status;
  uint8_t *prx = spi_rxbuff;
  uint8_t *ptx = spi_txbuff;
  uint8_t size = len + 1; // Add register value to transmit buffer

  *ptx++ = (W_REGISTER | (REGISTER_MASK & reg));
  while (len--)
    *ptx++ = *buf++;

  bcm2835_spi_transfernb((char *)spi_txbuff, (char *)spi_rxbuff, size);

  status = *prx; // status is 1st byte of receive buffer

  return status;
}

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

uint8_t RF24::write_payload(const void *buf, uint8_t len, const uint8_t writeType)
{
  uint8_t status;
  uint8_t *prx = spi_rxbuff;
  uint8_t *ptx = spi_txbuff;
  uint8_t size;

  const uint8_t *current = reinterpret_cast<const uint8_t *>(buf);

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

  size = data_len + blank_len + 1; // Add register value to transmit buffer

  if (DEBUG)
    printf("[Writing %u bytes %u blanks]", data_len, blank_len);

  *ptx++ = W_TX_PAYLOAD;
  while (data_len--)
    *ptx++ = *current++;
  while (blank_len--)
    *ptx++ = 0;

  bcm2835_spi_transfernb((char *)spi_txbuff, (char *)spi_rxbuff, size);

  status = *prx; // status is 1st byte of receive buffer

  return status;
}

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

uint8_t RF24::read_payload(void *buf, uint8_t len)
{
  uint8_t status;
  uint8_t *prx = spi_rxbuff;
  uint8_t *ptx = spi_txbuff;
  uint8_t size;

  uint8_t *current = reinterpret_cast<uint8_t *>(buf);

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

  size = data_len + blank_len + 1; // Add register value to transmit buffer

  if (DEBUG)
    printf("[Reading %u bytes %u blanks]", data_len, blank_len);

  *ptx++ = R_RX_PAYLOAD;
  while (size--)
    *ptx++ = NOP;

  // Size has been lost during while, re affect
  size = data_len + blank_len + 1; // Add register value to transmit buffer

  bcm2835_spi_transfernb((char *)spi_txbuff, (char *)spi_rxbuff, size);

  // 1st byte is status
  status = *prx++;

  // Decrement before to skip 1st status byte
  while (--size)
    *current++ = *prx++;

  return status;
}

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

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

  status = bcm2835_spi_transfer(FLUSH_RX);

  return status;
}

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

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

  status = bcm2835_spi_transfer(FLUSH_TX);

  return status;
}

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

uint8_t RF24::get_status(void)
{
  return bcm2835_spi_transfer(NOP);
}

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

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

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

void RF24::print_observe_tx(uint8_t value)
{
  printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n",
         value,
         (value >> PLOS_CNT) & 0b1111,
         (value >> ARC_CNT) & 0b1111);
}

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

void RF24::print_byte_register(const char *name, uint8_t reg, uint8_t qty)
{
  char extra_tab = strlen(name) < 8 ? '\t' : 0;
  printf("%s\t%c =", name, extra_tab);
  while (qty--)
    printf(" 0x%02x", read_register(reg++));
  printf("\n");
}

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

void RF24::print_address_register(const char *name, uint8_t reg, uint8_t qty)
{
  char extra_tab = strlen(name) < 8 ? '\t' : 0;
  printf("%s\t%c =", name, extra_tab);

  while (qty--)
  {
    uint8_t buffer[addr_width];
    read_register(reg++, buffer, sizeof buffer);

    printf(" 0x");
    uint8_t *bufptr = buffer + sizeof buffer;
    while (--bufptr >= buffer)
      printf("%02x", *bufptr);
  }

  printf("\r\n");
}

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

RF24::RF24(
    uint8_t _cepin, 
  uint8_t _cspin, 
  uint32_t _spi_speed, 
  uint8_t _channel, 
  uint32_t _cehighus) : ce_pin(_cepin), 
  csn_pin(_cspin), spi_speed(_spi_speed), 
  com_channel(_channel), cehighus(_cehighus),
  p_variant(false),payload_size(32), 
  dynamic_payloads_enabled(false), addr_width(5) //,pipe0_reading_address(0)
{
}

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

void RF24::setChannel(uint8_t channel)
{
  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[] = "1MBPS";
static const char rf24_datarate_e_str_1[] = "2MBPS";
static const char rf24_datarate_e_str_2[] = "250KBPS";
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[] = "nRF24L01";
static const char rf24_model_e_str_1[] = "nRF24L01+";
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[] = "Disabled";
static const char rf24_crclength_e_str_1[] = "8 bits";
static const char rf24_crclength_e_str_2[] = "16 bits";
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[] = "PA_MIN";
static const char rf24_pa_dbm_e_str_1[] = "PA_LOW";
static const char rf24_pa_dbm_e_str_2[] = "PA_HIGH";
static const char rf24_pa_dbm_e_str_3[] = "PA_MAX";
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,
};

static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)";
static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)";
static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)";
static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven";
static const char *const rf24_csn_e_str_P[] = {
    rf24_csn_e_str_0,
    rf24_csn_e_str_1,
    rf24_csn_e_str_2,
    rf24_csn_e_str_3,
};

// Display NRF24L01 details
void RF24::printDetails(void)
{
  printf("================ SPI Configuration ================\n");

  if (csn_pin < BCM2835_SPI_CS_NONE)
  {
    printf("CSN Pin  \t = %s\n", rf24_csn_e_str_P[csn_pin]);
  }
  else
  {
    printf("CSN Pin  \t = Custom GPIO%d%s\n", csn_pin,
           csn_pin == RPI_V2_GPIO_P1_26 ? " (CE1) Software Driven" : "");
  }

  printf("CE Pin  \t = Custom GPIO%d\n", ce_pin);

  // SPI Bus Speed
  printf("Clock Speed\t = ");
  switch (spi_speed)
  {
  case BCM2835_SPI_SPEED_64MHZ:
    printf("64 Mhz");
    break;
  case BCM2835_SPI_SPEED_32MHZ:
    printf("32 Mhz");
    break;
  case BCM2835_SPI_SPEED_16MHZ:
    printf("16 Mhz");
    break;
  case BCM2835_SPI_SPEED_8MHZ:
    printf("8 Mhz");
    break;
  case BCM2835_SPI_SPEED_4MHZ:
    printf("4 Mhz");
    break;
  case BCM2835_SPI_SPEED_2MHZ:
    printf("2 Mhz");
    break;
  case BCM2835_SPI_SPEED_1MHZ:
    printf("1 Mhz");
    break;
  case BCM2835_SPI_SPEED_512KHZ:
    printf("512 KHz");
    break;
  case BCM2835_SPI_SPEED_256KHZ:
    printf("256 KHz");
    break;
  case BCM2835_SPI_SPEED_128KHZ:
    printf("128 KHz");
    break;
  case BCM2835_SPI_SPEED_64KHZ:
    printf("64 KHz");
    break;
  case BCM2835_SPI_SPEED_32KHZ:
    printf("32 KHz");
    break;
  case BCM2835_SPI_SPEED_16KHZ:
    printf("16 KHz");
    break;
  case BCM2835_SPI_SPEED_8KHZ:
    printf("8 KHz");
    break;
  default:
    printf("Probably Bad !!!");
    break;
  }
  printf("\n");

  printf("================ NRF Configuration ================\n");

  print_status(get_status());

  print_address_register("RX_ADDR_P0-1", RX_ADDR_P0, 2);
  print_byte_register("RX_ADDR_P2-5", RX_ADDR_P2, 4);
  print_address_register("TX_ADDR", TX_ADDR);

  print_byte_register("RX_PW_P0-6", RX_PW_P0, 6);
  print_byte_register("EN_AA", EN_AA);
  print_byte_register("EN_RXADDR", EN_RXADDR);
  print_byte_register("RF_CH", RF_CH);
  print_byte_register("RF_SETUP", RF_SETUP);
  print_byte_register("CONFIG", CONFIG);
  print_byte_register("DYNPD/FEATURE", DYNPD, 2);

  printf("Data Rate\t = %s\r\n", rf24_datarate_e_str_P[getDataRate()]);
  printf("Model\t\t = %s\r\n", rf24_model_e_str_P[isPVariant()]);
  printf("CRC Length\t = %s\r\n", rf24_crclength_e_str_P[getCRCLength()]);
  printf("PA Power\t = %s\r\n", rf24_pa_dbm_e_str_P[getPALevel()]);
}

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

bool RF24::begin(void)
{

  // This initialize the SPI bus with
  // csn pin as chip select (custom or not)

  // Init BCM2835 chipset for talking with us
  if (!bcm2835_init())
  {
    return false;
  }

  switch (csn_pin)
  { // Ensure valid hardware CS pin
  case 0:
    break;
  case 1:
    break;
  case 8:
    csn_pin = 0;
    break;
  case 7:
    csn_pin = 1;
    break;
  default:
    csn_pin = 0;
    break;
  }

  bcm2835_spi_begin();

  // used to drive custom I/O to trigger my logic analyser
  // bcm2835_gpio_fsel(GPIO_CTRL_PIN , BCM2835_GPIO_FSEL_OUTP);

  // start the SPI library:
  // Note the NRF24 wants mode 0, MSB first and default to 1 Mbps
  bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);
  bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);

  // Set SPI bus Speed
  bcm2835_spi_setClockDivider(spi_speed);

  // Choose hardware CSN pin
  bcm2835_spi_chipSelect(csn_pin);

  // Initialise the CE pin of NRF24 (chip enable) after the CSN pin, so that
  // The input mode is not changed if using one of the hardware CE pins
  bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP);
  bcm2835_gpio_write(ce_pin, LOW);

  // wait 100ms
  delay(100);

  // 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.
  // printf("write_register(%02X, %02X)\n", SETUP_RETR, (0b0100 << ARD) | (0b1111 << ARC));
  setRetries(5, 15);

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

  // Initialize CRC and request 2-byte (16bit) CRC
  setCRCLength(RF24_CRC_16);

  toggle_features();
  write_register(FEATURE, 0);
  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(com_channel);

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

  powerUp();

  // 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 )
  // PTX should use only 22uA of power
  write_register(CONFIG, (read_register(CONFIG)) & ~_BV(PRIM_RX));

  return true;
}

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

void RF24::startListening(void)
{
  powerUp();
  write_register(CONFIG, read_register(CONFIG) | _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[0] > 0)
  {
    write_register(RX_ADDR_P0, pipe0_reading_address, addr_width);
  }
  // Flush buffers
  // flush_rx();
  flush_tx();

  // Go!
  bcm2835_gpio_write(ce_pin, HIGH);

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

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

void RF24::stopListening(void)
{
  bcm2835_gpio_write(ce_pin, LOW);
  flush_tx();
  flush_rx();
  delayMicroseconds(150);
  write_register(CONFIG, (read_register(CONFIG)) & ~_BV(PRIM_RX));
  delayMicroseconds(150);
}

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

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

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

void RF24::powerUp(void)
{
  bool up = read_register(CONFIG) & _BV(PWR_UP);

  if (!up)
  {
    write_register(CONFIG, (read_register(CONFIG) | _BV(PWR_UP)));
    delay(5);
  }
}

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

#if defined(FAILURE_HANDLING)
void RF24::errNotify()
{
  if (DEBUG)
  {
    printf("HARDWARE FAIL\n\r");
  }
  failureDetect = true;
}
#endif
/******************************************************************/

bool RF24::write(const void *buf, uint8_t len, const bool multicast)
{

  // Begin the write
  startFastWrite(buf, len, multicast);

// Wait until complete or failed
#if defined(FAILURE_HANDLING)
  uint32_t timer = bcm2835_millis();
#endif
  // If this hangs, it ain't coming back, no sense in timing out
  while (!(get_status() & (_BV(TX_DS) | _BV(MAX_RT))))
  {
#if defined(FAILURE_HANDLING)
    if (bcm2835_millis() - timer > 175)
    {
      errNotify();
      return 0;
    }
#endif
  }
  bcm2835_gpio_write(ce_pin, LOW);
  uint8_t status = write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));

  // Max retries exceeded
  if (status & _BV(MAX_RT))
  {
    flush_tx(); // Only going to be 1 packet int the FIFO at a time using this method, so just flush
    return 0;
  }
  // TX OK 1 or 0
  return 1;
}

bool RF24::write(const void *buf, uint8_t len)
{
  return write(buf, len, 0);
}

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

// For general use, the interrupt flags are not important to clear
bool RF24::writeBlocking(const void *buf, uint8_t len, uint32_t timeout)
{
  // Block until the FIFO is NOT full.
  // Keep track of the MAX retries and set auto-retry if seeing failures
  // This way the FIFO will fill up and allow blocking until packets go through
  // The radio will auto-clear everything in the FIFO as long as CE remains high

  uint32_t timer = bcm2835_millis(); // Get the time that the payload transmission started

  while ((get_status() & (_BV(TX_FULL))))
  { // Blocking only if FIFO is full. This will loop and block until TX is successful or timeout

    if (get_status() & _BV(MAX_RT))
    {            // If MAX Retries have been reached
      reUseTX(); // Set re-transmit and clear the MAX_RT interrupt flag
      if (bcm2835_millis() - timer > timeout)
      {
        return 0;
      } // If this payload has exceeded the user-defined timeout, exit and return 0
    }
#if defined(FAILURE_HANDLING)
    if (bcm2835_millis() - timer > (timeout + 75))
    {
      errNotify();
      return 0;
    }
#endif
  }

  // Start Writing
  startFastWrite(buf, len, 0); // Write the payload if a buffer is clear

  return 1; // Return 1 to indicate successful transmission
}

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

void RF24::reUseTX()
{
  write_register(STATUS, _BV(MAX_RT)); // Clear max retry flag
  // spiTrans( REUSE_TX_PL );
  bcm2835_spi_transfer(REUSE_TX_PL);
  bcm2835_gpio_write(ce_pin, LOW); // Re-Transfer packet
  bcm2835_gpio_write(ce_pin, HIGH);
}

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

bool RF24::writeFast(const void *buf, uint8_t len, const bool multicast)
{
  // Block until the FIFO is NOT full.
  // Keep track of the MAX retries and set auto-retry if seeing failures
  // Return 0 so the user can control the retrys and set a timer or failure counter if required
  // The radio will auto-clear everything in the FIFO as long as CE remains high

#if defined(FAILURE_HANDLING)
  uint32_t timer = bcm2835_millis();
#endif

  while ((get_status() & (_BV(TX_FULL))))
  { // Blocking only if FIFO is full. This will loop and block until TX is successful or fail

    if (get_status() & _BV(MAX_RT))
    {
      // reUseTX();										  //Set re-transmit
      write_register(STATUS, _BV(MAX_RT)); // Clear max retry flag
      return 0;                            // Return 0. The previous payload has been retransmitted
                                           // From the user perspective, if you get a 0, just keep trying to send the same payload
    }
#if defined(FAILURE_HANDLING)
    if (bcm2835_millis() - timer > 75)
    {
      errNotify();
      return 0;
    }
#endif
  }
  // Start Writing
  startFastWrite(buf, len, multicast);

  return 1;
}

bool RF24::writeFast(const void *buf, uint8_t len)
{
  return writeFast(buf, len, 0);
}

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

// 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::startFastWrite(const void *buf, uint8_t len, const bool multicast)
{ // TMRh20

  // write_payload( buf,len);
  write_payload(buf, len, multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD);
  bcm2835_gpio_write(ce_pin, HIGH);
  bcm2835_delayMicroseconds(cehighus);
}

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

void RF24::startWrite(const void *buf, uint8_t len, const bool multicast)
{

  // Send the payload

  write_payload(buf, len, multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD);
  bcm2835_gpio_write(ce_pin, HIGH);
  delayMicroseconds(10);
  bcm2835_gpio_write(ce_pin, LOW);
}

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

bool RF24::txStandBy()
{
#if defined(FAILURE_HANDLING)
  uint32_t timer = bcm2835_millis();
#endif
  while (!(read_register(FIFO_STATUS) & _BV(TX_EMPTY)))
  {
    if (get_status() & _BV(MAX_RT))
    {
      write_register(STATUS, _BV(MAX_RT));
      bcm2835_gpio_write(ce_pin, LOW);
      flush_tx(); // Non blocking, flush the data
      return 0;
    }
#if defined(FAILURE_HANDLING)
    if (bcm2835_millis() - timer > 75)
    {
      errNotify();
      return 0;
    }
#endif
  }

  bcm2835_gpio_write(ce_pin, LOW); // Set STANDBY-I mode
  return 1;
}

unsigned int RF24::bcm2835_millis(void)
{
  struct timeval now;
  unsigned long long ms;

  gettimeofday(&now, NULL);

  ms = (now.tv_sec * 1000000 + now.tv_usec) / 1000;

  return ((uint32_t)ms);
}
/****************************************************************************/

bool RF24::txStandBy(uint32_t timeout)
{

  uint32_t start = bcm2835_millis();

  while (!(read_register(FIFO_STATUS) & _BV(TX_EMPTY)))
  {
    if (get_status() & _BV(MAX_RT))
    {
      write_register(STATUS, _BV(MAX_RT));
      bcm2835_gpio_write(ce_pin, LOW); // Set re-transmit
      bcm2835_gpio_write(ce_pin, HIGH);
      if (bcm2835_millis() - start >= timeout)
      {
        bcm2835_gpio_write(ce_pin, LOW);
        flush_tx();
        return 0;
      }
    }
#if defined(FAILURE_HANDLING)
    if (bcm2835_millis() - start > (timeout + 75))
    {
      errNotify();
      return 0;
    }
#endif
  }
  bcm2835_gpio_write(ce_pin, LOW); // Set STANDBY-I mode
  return 1;
}
/****************************************************************************/

void RF24::maskIRQ(bool tx, bool fail, bool rx)
{

  write_register(CONFIG, (read_register(CONFIG)) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR);
}

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

uint8_t RF24::getDynamicPayloadSize(void)
{
  spi_txbuff[0] = R_RX_PL_WID;
  spi_rxbuff[1] = 0xff;

  bcm2835_spi_transfernb((char *)spi_txbuff, (char *)spi_rxbuff, 2);

  if (spi_rxbuff[1] > 32)
  {
    flush_rx();
    return 0;
  }

  return spi_rxbuff[1];
}

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

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

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

bool RF24::available(uint8_t *pipe_num)
{
  // Check the FIFO buffer to see if data is waitng to be read
  if (!(read_register(FIFO_STATUS) & _BV(RX_EMPTY)))
  {

    // If the caller wants the pipe number, include that
    if (pipe_num)
    {
      uint8_t status = get_status();
      *pipe_num = (status >> RX_P_NO) & 0b111;
    }
    return 1;
  }

  return 0;
}

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

void RF24::read(void *buf, uint8_t len)
{
  // Fetch the payload
  read_payload(buf, len);

  // Clear the two possible interrupt flags with one command
  write_register(STATUS, _BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS));
}

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

void RF24::whatHappened(bool &tx_ok, bool &tx_fail, bool &rx_ready)
{
  // Read the status & reset the status in one easy call
  // Or is that such a good idea?
  uint8_t status = write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));

  // Report to the user what happened
  tx_ok = status & _BV(TX_DS);
  tx_fail = status & _BV(MAX_RT);
  rx_ready = status & _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<uint8_t *>(&value), addr_width);
  write_register(TX_ADDR, reinterpret_cast<uint8_t *>(&value), addr_width);

  write_register(RX_PW_P0, payload_size);
}

/****************************************************************************/
void RF24::openWritingPipe(const uint8_t *address)
{
  // 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, address, addr_width);
  write_register(TX_ADDR, address, addr_width);

  // const uint8_t max_payload_size = 32;
  // write_register(RX_PW_P0,min(payload_size,max_payload_size));
  write_register(RX_PW_P0, 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)
  {
    memcpy(pipe0_reading_address, &address, addr_width);
  }

  if (child <= 6)
  {
    // For pipes 2-5, only write the LSB
    if (child < 2)
      write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast<const uint8_t *>(&address), addr_width);
    else
      write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast<const uint8_t *>(&address), 1);

    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(pgm_read_byte(&child_pipe_enable[child])));
  }
}

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

void RF24::setAddressWidth(uint8_t a_width)
{

  if (a_width -= 2)
  {
    write_register(SETUP_AW, a_width % 4);
    addr_width = (a_width % 4) + 2;
  }
}

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

void RF24::openReadingPipe(uint8_t child, const uint8_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)
  {
    memcpy(pipe0_reading_address, address, addr_width);
  }
  if (child <= 6)
  {
    // For pipes 2-5, only write the LSB
    if (child < 2)
    {
      write_register(pgm_read_byte(&child_pipe[child]), address, addr_width);
    }
    else
    {
      write_register(pgm_read_byte(&child_pipe[child]), address, 1);
    }
    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(pgm_read_byte(&child_pipe_enable[child])));
  }
}

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

void RF24::toggle_features(void)
{
  bcm2835_spi_transfer(ACTIVATE);
  bcm2835_spi_transfer(0x73);
}

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

void RF24::enableDynamicPayloads(void)
{

  // So enable them and try again
  toggle_features();
  write_register(FEATURE, read_register(FEATURE) | _BV(EN_DPL));

  if (DEBUG)
    printf("FEATURE=%i\r\n", read_register(FEATURE));

  // Enable dynamic payload on all pipes
  //
  // Not sure the use case of only having dynamic payload on certain
  // 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
  //

  // So enable them and try again
  toggle_features();
  write_register(FEATURE, read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL));

  if (DEBUG)
    printf("FEATURE=%i\r\n", read_register(FEATURE));

  //
  // Enable dynamic payload on pipes 0 & 1
  //
  dynamic_payloads_enabled = true;
  write_register(DYNPD, read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0));
}

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

void RF24::enableDynamicAck(void)
{
  //
  // enable dynamic ack features
  //
  toggle_features();
  write_register(FEATURE, read_register(FEATURE) | _BV(EN_DYN_ACK));

  if (DEBUG)
  {
    printf("FEATURE=%i\r\n", read_register(FEATURE));
  }
}

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

void RF24::writeAckPayload(uint8_t pipe, const void *buf, uint8_t len)
{

  uint8_t *ptx = spi_txbuff;
  uint8_t size;

  const uint8_t *current = reinterpret_cast<const uint8_t *>(buf);

  uint8_t data_len = min(len, payload_size);

  size = data_len + 1; // Add register value to transmit buffer

  if (DEBUG)
  {
    printf("[Writing %u bytes]", data_len);
  }
  *ptx++ = W_ACK_PAYLOAD | (pipe & 0b111);
  while (data_len--)
  {
    *ptx++ = *current++;
  }
  bcm2835_spi_transfern((char *)spi_txbuff, size);
}

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

bool RF24::isAckPayloadAvailable(void)
{
  return !read_register(FIFO_STATUS) & _BV(RX_EMPTY);
}

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

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 <= 6)
  {
    uint8_t en_aa = read_register(EN_AA);
    if (enable)
    {
      en_aa |= _BV(pipe);
    }
    else
    {
      en_aa &= ~_BV(pipe);
    }
    write_register(EN_AA, en_aa);
  }
}

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

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

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

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

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

void RF24::setPALevel(uint8_t level)
{
  uint8_t setup = read_register(RF_SETUP) & 0b11111000;

  if (level > 3)
  {                                 // If invalid level, go to max PA
    level = (RF24_PA_MAX << 1) + 1; // +1 to support the SI24R1 chip extra bit
  }
  else
  {
    level = (level << 1) + 1; // Else set level as requested
  }

  write_register(RF_SETUP, setup |= level); // Write it to the chip
}

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

uint8_t RF24::getPALevel(void)
{
  return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1;
}

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

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
  setup &= ~(_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'.
    setup |= _BV(RF_DR_LOW);
  }
  else
  {
    // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1
    // Making it '01'
    if (speed == RF24_2MBPS)
    {
      setup |= _BV(RF_DR_HIGH);
    }
  }
  write_register(RF_SETUP, setup);

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

  return result;
}

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

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))
  {
    // '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) & ~(_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) & (_BV(CRCO) | _BV(EN_CRC));

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

  return result;
}

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

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

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

pingpair.cpp

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/time.h>
#include <string.h>
#include <cstdlib>
#include <inttypes.h>
#include <sstream>
#include <string>
#include <csignal>
#include <time.h>
#include <sys/types.h>
#include <errno.h>
#include <RF24.h>
using namespace std;

/**
 * @brief Construct RF24
 * @param CE pin
 * @param CS pin
 * @param SPI speed
 * @param channel
 * @param CE high delay in microseconds((tuning this in different scenarios or hardware and os))
 */
RF24 radio(RPI_V2_GPIO_P1_11, BCM2835_SPI_CS0, BCM2835_SPI_CLOCK_DIVIDER_16, 27,8000);

void SignalHandler(int sig)
{
    switch (sig)
    {
    case SIGINT:
        cout << "Interrupt signal (" << sig << ") received." << endl;
        exit(sig);
        break;
    default:
        break;
    }
}

const int min_payload_size = 4;
const int max_payload_size = 32;
const int payload_size_increments_by = 1;
int next_payload_size = max_payload_size;

char receive_payload[max_payload_size + 1]; // +1 to allow room for a terminating NULL char

const bool role_ping_out = 1, role_pong_back = 0;

// Radio pipe addresses for the 2 nodes to communicate.
const uint64_t pipes[2] = {0xE7E7E7E7E7LL, 0xE7E7E7E7E7LL};

static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";

// static char send_payload[] = "";

void loop(bool role)
{
    uint64_t sid = 0;
    // forever loop
    while (1)
    {
        bcm2835_delay(10);
        if (role == role_ping_out)
        {
            // The payload will always be the same, what will change is how much of it we send.

            // sprintf(send_payload, "%" PRIu64 "", sid++);
            //  First, stop listening so we can talk.
            radio.stopListening();
            // Take the time, and send it.  This will block until complete
            bool ret = radio.write(send_payload, next_payload_size, 0);
            // printf("Now sending value=%s, result=%d ",send_payload, ret);
            //  Now, continue listening
            radio.startListening();

            // Wait here until we get a response, or timeout
            unsigned long started_waiting_at = radio.bcm2835_millis();
            bool timeout = false;
            while (!radio.available() && !timeout)
                if (radio.bcm2835_millis() - started_waiting_at > 3000)
                    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
        }

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

                while (radio.available())
                {
                    // Fetch the payload, and see if this was the last one.
                    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 payload size=%i value=%s\n\r", len, receive_payload);
                }

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

                // Send the final one back.
                bool ret = radio.write(receive_payload, len, 0);
                printf("Sent response value=%s, result: %d.\n\r", receive_payload, ret);

                // Now, resume listening so we catch the next packets.
                radio.startListening();
            }
        }
    }
}

int main(int argc, char *argv[])
{
    radio.begin();
    radio.enableDynamicPayloads();
    radio.setRetries(8, 15);
    radio.printDetails();

    /********* Role chooser ***********/
    printf("\n ************ Role Setup ***********\n");
    int input = atoi(argv[1]);
    bool role = 0;
    if (argc == 2)
    {
        if (input == 0)
        {
            cout << "Role: Pong Back, awaiting transmission " << endl
                 << endl;
        }
        else
        {
            cout << "Role: Ping Out, starting transmission " << endl
                 << endl;
            role = role_ping_out;
        }
    }

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

    if (role == role_ping_out)
    {
        radio.openWritingPipe(pipes[0]);
        radio.openReadingPipe(1, pipes[1]);
    }
    else
    {
        radio.openWritingPipe(pipes[1]);
        radio.openReadingPipe(1, pipes[0]);
        radio.startListening();
    }

    loop(role);

    bcm2835_spi_end();
    bcm2835_close();
    return 0;
}

编译:

g++ -std=c++11 pingpair.cpp -o nrf24 /opt/bcm2835-1.71/lib/libbcm2835.a \
	-I . -I /opt/bcm2835-1.71/include

运行:

#receiver:

sudo ./nrf24 0

#transmitter:

sudo ./nrf24 1

以上。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值