Add nRF24 receive message buffering (#538)
* Add critical section support Allows defining a code block which is executed as a critical section. Supports AVR, ESP8266 & SAMD. * Add receive message buffering When MY_RF24_IRQ_PIN is defined (and connected), a buffer will be used to retrieve & store messages received by nRF24 as fast as possible. This reduces the chance of losing messages due to a busy microcontroller. nRF24 SPI rx message transfer is running from interrupt context when MY_RF24_IRQ_PIN is defined. If this transfer takes too long, serial characters received will get lost. Therefore serial rx is enabled during SPI transfer of nRF messages. Limitations: Only AVR/SAMD & nRF24L01+ combinations can buffer rx messages. nRF24L01+ IRQ pin should be connected to AVR. Tested on W5100- & Serial-gateway. SAMD has not been tested yet! * Fix Doxygen warnings * Fix more Doxygen warnings
This commit is contained in:
committed by
Olivier
parent
4695cc71dd
commit
99973a346e
+18
@@ -429,6 +429,22 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_RF24_IRQ_PIN
|
||||||
|
* @brief Disable RF24 interrupt pin usage by default. Override in sketch if needed.
|
||||||
|
*/
|
||||||
|
//#define MY_RF24_IRQ_PIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_RX_MESSAGE_BUFFER_SIZE
|
||||||
|
* @brief Declare the amount of incoming messages that can be buffered. Requires MY_RADIO_NRF24 and MY_RF24_IRQ_PIN. Override in sketch if needed.
|
||||||
|
*/
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
#ifndef MY_RX_MESSAGE_BUFFER_SIZE
|
||||||
|
#define MY_RX_MESSAGE_BUFFER_SIZE (20)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def MY_RF24_PA_LEVEL
|
* @def MY_RF24_PA_LEVEL
|
||||||
* @brief Default RF24 PA level. Override in sketch if needed.
|
* @brief Default RF24 PA level. Override in sketch if needed.
|
||||||
@@ -694,5 +710,7 @@
|
|||||||
#define MY_REGISTRATION_CONTROLLER
|
#define MY_REGISTRATION_CONTROLLER
|
||||||
#define MY_DEBUG_VERBOSE_RF24
|
#define MY_DEBUG_VERBOSE_RF24
|
||||||
#define MY_TRANSPORT_SANITY_CHECK
|
#define MY_TRANSPORT_SANITY_CHECK
|
||||||
|
#define MY_RF24_IRQ_PIN
|
||||||
|
#define MY_RX_MESSAGE_BUFFER_SIZE
|
||||||
#define MY_NODE_LOCK_FEATURE
|
#define MY_NODE_LOCK_FEATURE
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+17
@@ -255,6 +255,23 @@
|
|||||||
#error Only one forward link driver can be activated
|
#error Only one forward link driver can be activated
|
||||||
#endif
|
#endif
|
||||||
#if defined(MY_RADIO_NRF24)
|
#if defined(MY_RADIO_NRF24)
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
// SoftSPI does not support usingInterrupt()
|
||||||
|
#ifdef MY_SOFTSPI
|
||||||
|
#error RF24 IRQ usage cannot be used with Soft SPI
|
||||||
|
#endif
|
||||||
|
// ESP8266 does not support usingInterrupt()
|
||||||
|
#ifdef ESP8266
|
||||||
|
#error RF24 IRQ usage cannot be used with ESP8266
|
||||||
|
#endif
|
||||||
|
#ifndef SPI_HAS_TRANSACTION
|
||||||
|
#error RF24 IRQ usage requires transactional SPI support
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef MY_RX_MESSAGE_BUFFER_SIZE
|
||||||
|
#error Receive message buffering requires RF24 IRQ usage
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||||
#include "drivers/AES/AES.cpp"
|
#include "drivers/AES/AES.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -70,7 +70,13 @@
|
|||||||
#define MY_CAP_SIGN "-"
|
#define MY_CAP_SIGN "-"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(MY_RX_MESSAGE_BUFFER_SIZE)
|
||||||
|
#define MY_CAP_RXBUF "Q"
|
||||||
|
#else
|
||||||
|
#define MY_CAP_RXBUF "-"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MY_CAPABILITIES MY_CAP_RESET MY_CAP_RADIO MY_CAP_OTA_FW MY_CAP_TYPE MY_CAP_ARCH MY_CAP_SIGN
|
|
||||||
|
#define MY_CAPABILITIES MY_CAP_RESET MY_CAP_RADIO MY_CAP_OTA_FW MY_CAP_TYPE MY_CAP_ARCH MY_CAP_SIGN MY_CAP_RXBUF
|
||||||
|
|
||||||
#endif /* MyGatewayTransportEthernet_h */
|
#endif /* MyGatewayTransportEthernet_h */
|
||||||
|
|||||||
+63
-1
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -17,6 +17,12 @@
|
|||||||
* version 2 as published by the Free Software Foundation.
|
* version 2 as published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file MyHw.h
|
||||||
|
*
|
||||||
|
* MySensors hardware abstraction layer
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef MyHw_h
|
#ifndef MyHw_h
|
||||||
#define MyHw_h
|
#define MyHw_h
|
||||||
|
|
||||||
@@ -42,11 +48,67 @@ void hwWriteConfig(int adr, uint8_t value);
|
|||||||
uint8_t hwReadConfig(int adr);
|
uint8_t hwReadConfig(int adr);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep for a defined time, using minimum power.
|
||||||
|
* @param ms Time to sleep, in [ms].
|
||||||
|
* @return Nonsense, please ignore.
|
||||||
|
*/
|
||||||
int8_t hwSleep(unsigned long ms);
|
int8_t hwSleep(unsigned long ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep for a defined time, using minimum power, or until woken by interrupt.
|
||||||
|
* @param interrupt Interrupt number, which can wake the mcu from sleep.
|
||||||
|
* @param mode Interrupt mode, as passed to attachInterrupt.
|
||||||
|
* @param ms Time to sleep, in [ms].
|
||||||
|
* @return -1 when woken by timer, or interrupt number when woken by interrupt.
|
||||||
|
*/
|
||||||
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
|
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep for a defined time, using minimum power, or until woken by one of the interrupts.
|
||||||
|
* @param interrupt1 Interrupt1 number, which can wake the mcu from sleep.
|
||||||
|
* @param mode1 Interrupt1 mode, as passed to attachInterrupt.
|
||||||
|
* @param interrupt2 Interrupt2 number, which can wake the mcu from sleep.
|
||||||
|
* @param mode2 Interrupt2 mode, as passed to attachInterrupt.
|
||||||
|
* @param ms Time to sleep, in [ms].
|
||||||
|
* @return -1 when woken by timer, or interrupt number when woken by interrupt.
|
||||||
|
*/
|
||||||
int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms);
|
int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms);
|
||||||
|
|
||||||
#ifdef MY_DEBUG
|
#ifdef MY_DEBUG
|
||||||
void hwDebugPrint(const char *fmt, ... );
|
void hwDebugPrint(const char *fmt, ... );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_CRITICAL_SECTION
|
||||||
|
* @brief Creates a block of code that is guaranteed to be executed atomically.
|
||||||
|
* Upon entering the block all interrupts are disabled, and re-enabled upon
|
||||||
|
* exiting the block from any exit path.
|
||||||
|
* A typical example that requires atomic access is a 16 (or more) bit variable
|
||||||
|
* that is shared between the main execution path and an ISR, on an 8-bit
|
||||||
|
* platform (e.g AVR):
|
||||||
|
* @code
|
||||||
|
* volatile uint16_t val = 0;
|
||||||
|
*
|
||||||
|
* void interrupHandler()
|
||||||
|
* {
|
||||||
|
* val = ~val;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void loop()
|
||||||
|
* {
|
||||||
|
* uint16_t copy_val;
|
||||||
|
* MY_CRITICAL_SECTION
|
||||||
|
* {
|
||||||
|
* copy_val = val;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* All code within the MY_CRITICAL_SECTION block will be protected from being
|
||||||
|
* interrupted during execution.
|
||||||
|
*/
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
#define MY_CRITICAL_SECTION
|
||||||
|
#endif /* DOXYGEN */
|
||||||
|
|
||||||
#endif // #ifdef MyHw_h
|
#endif // #ifdef MyHw_h
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include <avr/power.h>
|
#include <avr/power.h>
|
||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
#include <avr/wdt.h>
|
#include <avr/wdt.h>
|
||||||
|
#include <util/atomic.h>
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -92,4 +92,8 @@ enum period_t
|
|||||||
|
|
||||||
void hwInternalSleep(unsigned long ms);
|
void hwInternalSleep(unsigned long ms);
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
#define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||||||
|
#endif /* DOXYGEN */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -43,5 +43,17 @@ void hwWriteConfigBlock(void* buf, void* adr, size_t length);
|
|||||||
void hwWriteConfig(int adr, uint8_t value);
|
void hwWriteConfig(int adr, uint8_t value);
|
||||||
uint8_t hwReadConfig(int adr);
|
uint8_t hwReadConfig(int adr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore interrupt state.
|
||||||
|
* Helper function for MY_CRITICAL_SECTION.
|
||||||
|
*/
|
||||||
|
static __inline__ void __psRestore(const uint32_t *__s)
|
||||||
|
{
|
||||||
|
xt_wsr_ps( *__s );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
#define MY_CRITICAL_SECTION for ( uint32_t __psSaved __attribute__((__cleanup__(__psRestore))) = xt_rsil(15), __ToDo = 1; __ToDo ; __ToDo = 0 )
|
||||||
|
#endif /* DOXYGEN */
|
||||||
|
|
||||||
#endif // #ifdef ARDUINO_ARCH_ESP8266
|
#endif // #ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
|||||||
@@ -58,4 +58,28 @@ uint8_t hwReadConfig(int adr);
|
|||||||
#define hwWriteConfig(__adr, __value) ( __value = __value)
|
#define hwWriteConfig(__adr, __value) ( __value = __value)
|
||||||
#define hwReadConfig(__adr) (0)
|
#define hwReadConfig(__adr) (0)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable all interrupts.
|
||||||
|
* Helper function for MY_CRITICAL_SECTION.
|
||||||
|
*/
|
||||||
|
static __inline__ uint8_t __disableIntsRetVal(void)
|
||||||
|
{
|
||||||
|
__disable_irq();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore priority mask register.
|
||||||
|
* Helper function for MY_CRITICAL_SECTION.
|
||||||
|
*/
|
||||||
|
static __inline__ void __priMaskRestore(const uint32_t *priMask)
|
||||||
|
{
|
||||||
|
__set_PRIMASK(*priMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
#define MY_CRITICAL_SECTION for ( uint32_t __savePriMask __attribute__((__cleanup__(__priMaskRestore))) = __get_PRIMASK(), __ToDo = __disableIntsRetVal(); __ToDo ; __ToDo = 0 )
|
||||||
|
#endif /* DOXYGEN */
|
||||||
|
|
||||||
#endif // #ifdef ARDUINO_ARCH_SAMD
|
#endif // #ifdef ARDUINO_ARCH_SAMD
|
||||||
|
|||||||
@@ -20,11 +20,47 @@
|
|||||||
#include "MyConfig.h"
|
#include "MyConfig.h"
|
||||||
#include "MyTransport.h"
|
#include "MyTransport.h"
|
||||||
#include "drivers/RF24/RF24.h"
|
#include "drivers/RF24/RF24.h"
|
||||||
|
#include "drivers/CircularBuffer/CircularBuffer.h"
|
||||||
|
|
||||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||||
#include "drivers/AES/AES.h"
|
#include "drivers/AES/AES.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
typedef struct _transportQueuedMessage
|
||||||
|
{
|
||||||
|
uint8_t m_len; // Length of the data
|
||||||
|
uint8_t m_data[MAX_MESSAGE_LENGTH]; // The raw data
|
||||||
|
} transportQueuedMessage;
|
||||||
|
|
||||||
|
/** Buffer to store queued messages in. */
|
||||||
|
static transportQueuedMessage transportRxQueueStorage[MY_RX_MESSAGE_BUFFER_SIZE];
|
||||||
|
/** Circular buffer, which uses the transportRxQueueStorage and administers stored messages. */
|
||||||
|
static CircularBuffer<transportQueuedMessage> transportRxQueue(transportRxQueueStorage, MY_RX_MESSAGE_BUFFER_SIZE);
|
||||||
|
|
||||||
|
static volatile uint8_t transportLostMessageCount = 0;
|
||||||
|
|
||||||
|
static void transportRxCallback(void)
|
||||||
|
{
|
||||||
|
// Called for each message received by radio, from interrupt context.
|
||||||
|
// This function _must_ call RF24_readMessage() to de-assert interrupt line!
|
||||||
|
if (!transportRxQueue.full())
|
||||||
|
{
|
||||||
|
transportQueuedMessage* msg = transportRxQueue.getFront();
|
||||||
|
msg->m_len = RF24_readMessage(msg->m_data); // Read payload & clear RX_DR
|
||||||
|
(void)transportRxQueue.pushFront(msg);
|
||||||
|
} else {
|
||||||
|
// Queue is full. Discard message.
|
||||||
|
(void)RF24_readMessage(NULL); // Read payload & clear RX_DR
|
||||||
|
// Keep track of messages lost. Max 255, prevent wrapping.
|
||||||
|
if (transportLostMessageCount < 255)
|
||||||
|
{
|
||||||
|
++transportLostMessageCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||||
AES _aes;
|
AES _aes;
|
||||||
uint8_t _dataenc[32] = {0};
|
uint8_t _dataenc[32] = {0};
|
||||||
@@ -32,7 +68,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool transportInit() {
|
bool transportInit() {
|
||||||
|
|
||||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||||
//set up AES-key
|
//set up AES-key
|
||||||
@@ -41,6 +76,9 @@ bool transportInit() {
|
|||||||
memset(_psk, 0, 16);
|
memset(_psk, 0, 16);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
RF24_registerReceiveCallback( transportRxCallback );
|
||||||
|
#endif
|
||||||
return RF24_initialize();
|
return RF24_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +109,12 @@ bool transportSend(uint8_t recipient, const void* data, uint8_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool transportAvailable() {
|
bool transportAvailable() {
|
||||||
bool avail = RF24_isDataAvailable();
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
return avail;
|
(void)RF24_isDataAvailable; // Prevent 'defined but not used' warning
|
||||||
|
return !transportRxQueue.empty();
|
||||||
|
#else
|
||||||
|
return RF24_isDataAvailable();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool transportSanityCheck() {
|
bool transportSanityCheck() {
|
||||||
@@ -80,7 +122,18 @@ bool transportSanityCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t transportReceive(void* data) {
|
uint8_t transportReceive(void* data) {
|
||||||
uint8_t len = RF24_readMessage(data);
|
uint8_t len = 0;
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
transportQueuedMessage* msg = transportRxQueue.getBack();
|
||||||
|
if (msg)
|
||||||
|
{
|
||||||
|
len = msg->m_len;
|
||||||
|
(void)memcpy(data, msg->m_data, len);
|
||||||
|
(void)transportRxQueue.popBack();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
len = RF24_readMessage(data);
|
||||||
|
#endif
|
||||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||||
// has to be adjusted, WIP!
|
// has to be adjusted, WIP!
|
||||||
_aes.set_IV(0);
|
_aes.set_IV(0);
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
CircularBuffer - An Arduino circular buffering library for arbitrary types.
|
||||||
|
|
||||||
|
Created by Ivo Pullens, Emmission, 2014-2016 -- www.emmission.nl
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file CircularBuffer.h
|
||||||
|
*
|
||||||
|
* Circular buffering for arbitrary types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CircularBuffer_h
|
||||||
|
#define CircularBuffer_h
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The circular buffer class.
|
||||||
|
* Pass the datatype to be stored in the buffer as template parameter.
|
||||||
|
*/
|
||||||
|
template <class T> class CircularBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param buffer Preallocated buffer of at least size records.
|
||||||
|
* @param size Number of records available in the buffer.
|
||||||
|
*/
|
||||||
|
CircularBuffer(T* buffer, const uint8_t size )
|
||||||
|
: m_size(size), m_buff(buffer)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all entries in the circular buffer.
|
||||||
|
*/
|
||||||
|
void clear(void)
|
||||||
|
{
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
m_front = 0;
|
||||||
|
m_fill = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if the circular buffer is empty.
|
||||||
|
* @return True, when empty.
|
||||||
|
*/
|
||||||
|
inline bool empty(void) const
|
||||||
|
{
|
||||||
|
bool empty;
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
empty = !m_fill;
|
||||||
|
}
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if the circular buffer is full.
|
||||||
|
* @return True, when full.
|
||||||
|
*/
|
||||||
|
inline bool full(void) const
|
||||||
|
{
|
||||||
|
bool full;
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
full = m_fill == m_size;
|
||||||
|
}
|
||||||
|
return full;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of records stored in the buffer.
|
||||||
|
* @return number of records.
|
||||||
|
*/
|
||||||
|
inline uint8_t available(void) const
|
||||||
|
{
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
return m_fill;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aquire unused record on front of the buffer, for writing.
|
||||||
|
* After filling the record, it has to be pushed to actually
|
||||||
|
* add it to the buffer.
|
||||||
|
* @return Pointer to record, or NULL when buffer is full.
|
||||||
|
*/
|
||||||
|
T* getFront(void) const
|
||||||
|
{
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
if (!full()) return get(m_front);
|
||||||
|
}
|
||||||
|
return static_cast<T*>(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push record to front of the buffer.
|
||||||
|
* @param record Record to push. If record was aquired previously (using getFront) its
|
||||||
|
* data will not be copied as it is already present in the buffer.
|
||||||
|
* @return True, when record was pushed successfully.
|
||||||
|
*/
|
||||||
|
bool pushFront(T* record)
|
||||||
|
{
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
if (!full())
|
||||||
|
{
|
||||||
|
T* f = get(m_front);
|
||||||
|
if (f != record)
|
||||||
|
*f = *record;
|
||||||
|
m_front = (m_front+1) % m_size;
|
||||||
|
m_fill++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aquire record on back of the buffer, for reading.
|
||||||
|
* After reading the record, it has to be pop'ed to actually
|
||||||
|
* remove it from the buffer.
|
||||||
|
* @return Pointer to record, or NULL when buffer is empty.
|
||||||
|
*/
|
||||||
|
T* getBack(void) const
|
||||||
|
{
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
if (!empty()) return get(back());
|
||||||
|
}
|
||||||
|
return static_cast<T*>(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove record from back of the buffer.
|
||||||
|
* @return True, when record was pop'ed successfully.
|
||||||
|
*/
|
||||||
|
bool popBack(void)
|
||||||
|
{
|
||||||
|
MY_CRITICAL_SECTION
|
||||||
|
{
|
||||||
|
if (!empty())
|
||||||
|
{
|
||||||
|
m_fill--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Internal getter for records.
|
||||||
|
* @param idx Record index in buffer.
|
||||||
|
* @return Ptr to record.
|
||||||
|
*/
|
||||||
|
inline T * get(const uint8_t idx) const
|
||||||
|
{
|
||||||
|
return &(m_buff[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal getter for index of last used record in buffer.
|
||||||
|
* @return Index of last record.
|
||||||
|
*/
|
||||||
|
inline uint8_t back(void) const
|
||||||
|
{
|
||||||
|
return (m_front - m_fill + m_size) % m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t m_size; //!< Total number of records that can be stored in the buffer.
|
||||||
|
T* const m_buff; //!< Ptr to buffer holding all records.
|
||||||
|
volatile uint8_t m_front; //!< Index of front element (not pushed yet).
|
||||||
|
volatile uint8_t m_fill; //!< Amount of records currently pushed.
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CircularBuffer_h
|
||||||
@@ -25,6 +25,10 @@
|
|||||||
LOCAL uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID };
|
LOCAL uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID };
|
||||||
LOCAL uint8_t MY_RF24_NODE_ADDRESS = AUTO;
|
LOCAL uint8_t MY_RF24_NODE_ADDRESS = AUTO;
|
||||||
|
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
LOCAL RF24_receiveCallbackType RF24_receiveCallback = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
LOCAL void RF24_csn(bool level) {
|
LOCAL void RF24_csn(bool level) {
|
||||||
digitalWrite(MY_RF24_CS_PIN, level);
|
digitalWrite(MY_RF24_CS_PIN, level);
|
||||||
}
|
}
|
||||||
@@ -86,6 +90,10 @@ LOCAL uint8_t RF24_getStatus(void) {
|
|||||||
return RF24_spiByteTransfer( NOP );
|
return RF24_spiByteTransfer( NOP );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOCAL uint8_t RF24_getFifoStatus(void) {
|
||||||
|
return RF24_readByteRegister(FIFO_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
LOCAL void RF24_setChannel(uint8_t channel) {
|
LOCAL void RF24_setChannel(uint8_t channel) {
|
||||||
RF24_writeByteRegister(RF_CH, channel);
|
RF24_writeByteRegister(RF_CH, channel);
|
||||||
}
|
}
|
||||||
@@ -253,14 +261,69 @@ LOCAL bool RF24_sanityCheck(void) {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
LOCAL void RF24_irqHandler( void )
|
||||||
|
{
|
||||||
|
if (RF24_receiveCallback)
|
||||||
|
{
|
||||||
|
// Will stay for a while (several 100us) in this interrupt handler. Any interrupts from serial
|
||||||
|
// rx coming in during our stay will not be handled and will cause characters to be lost.
|
||||||
|
// As a workaround we re-enable interrupts to allow nested processing of other interrupts.
|
||||||
|
// Our own handler is disconnected to prevent recursive calling of this handler.
|
||||||
|
#ifdef MY_GATEWAY_SERIAL
|
||||||
|
detachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN));
|
||||||
|
interrupts();
|
||||||
|
#endif
|
||||||
|
// Read FIFO until empty.
|
||||||
|
// Procedure acc. to datasheet (pg. 63):
|
||||||
|
// 1.Read payload, 2.Clear RX_DR IRQ, 3.Read FIFO_status, 4.Repeat when more data available.
|
||||||
|
// Datasheet (ch. 8.5) states, that the nRF de-asserts IRQ after reading STATUS.
|
||||||
|
|
||||||
|
// Start checking if RX-FIFO is not empty, as we might end up here from an interrupt
|
||||||
|
// for a message we've already read.
|
||||||
|
while (!(RF24_getFifoStatus() & _BV(0))) {
|
||||||
|
RF24_receiveCallback(); // Must call RF24_readMessage(), which will clear RX_DR IRQ !
|
||||||
|
}
|
||||||
|
// Restore our interrupt handler.
|
||||||
|
#ifdef MY_GATEWAY_SERIAL
|
||||||
|
noInterrupts();
|
||||||
|
attachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN), RF24_irqHandler, FALLING);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
// clear RX interrupt
|
||||||
|
RF24_setStatus(_BV(RX_DR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOCAL void RF24_registerReceiveCallback( RF24_receiveCallbackType cb )
|
||||||
|
{
|
||||||
|
MY_CRITICAL_SECTION {
|
||||||
|
RF24_receiveCallback = cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LOCAL bool RF24_initialize(void) {
|
LOCAL bool RF24_initialize(void) {
|
||||||
// Initialize pins
|
// Initialize pins
|
||||||
pinMode(MY_RF24_CE_PIN,OUTPUT);
|
pinMode(MY_RF24_CE_PIN,OUTPUT);
|
||||||
pinMode(MY_RF24_CS_PIN,OUTPUT);
|
pinMode(MY_RF24_CS_PIN,OUTPUT);
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
pinMode(MY_RF24_IRQ_PIN,INPUT);
|
||||||
|
#endif
|
||||||
// Initialize SPI
|
// Initialize SPI
|
||||||
_SPI.begin();
|
_SPI.begin();
|
||||||
RF24_ce(LOW);
|
RF24_ce(LOW);
|
||||||
RF24_csn(HIGH);
|
RF24_csn(HIGH);
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
// assure SPI can be used from interrupt context
|
||||||
|
// Note: ESP8266 & SoftSPI currently do not support interrupt usage for SPI,
|
||||||
|
// therefore it is unsafe to use MY_RF24_IRQ_PIN with ESP8266/SoftSPI!
|
||||||
|
_SPI.usingInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN));
|
||||||
|
// attach interrupt
|
||||||
|
attachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN), RF24_irqHandler, FALLING);
|
||||||
|
#else
|
||||||
|
(void)RF24_getFifoStatus; // prevent 'defined but not used' warning
|
||||||
|
#endif
|
||||||
// CRC and power up
|
// CRC and power up
|
||||||
RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) ) ;
|
RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) ) ;
|
||||||
// settle >2ms
|
// settle >2ms
|
||||||
|
|||||||
+17
-1
@@ -62,7 +62,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// RF24 settings
|
// RF24 settings
|
||||||
#define MY_RF24_CONFIGURATION (uint8_t) (RF24_CRC_16 << 2)
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
#define MY_RF24_CONFIGURATION (uint8_t) ((RF24_CRC_16 << 2) | (1 << MASK_TX_DS) | (1 << MASK_MAX_RT))
|
||||||
|
#else
|
||||||
|
#define MY_RF24_CONFIGURATION (uint8_t) (RF24_CRC_16 << 2)
|
||||||
|
#endif
|
||||||
#define MY_RF24_FEATURE (uint8_t)( _BV(EN_DPL) | _BV(EN_ACK_PAY) )
|
#define MY_RF24_FEATURE (uint8_t)( _BV(EN_DPL) | _BV(EN_ACK_PAY) )
|
||||||
#define MY_RF24_RF_SETUP (uint8_t)( ((MY_RF24_DATARATE & 0b10 ) << 4) | ((MY_RF24_DATARATE & 0b01 ) << 3) | (MY_RF24_PA_LEVEL << 1) ) + 1 // +1 for Si24R1
|
#define MY_RF24_RF_SETUP (uint8_t)( ((MY_RF24_DATARATE & 0b10 ) << 4) | ((MY_RF24_DATARATE & 0b01 ) << 3) | (MY_RF24_PA_LEVEL << 1) ) + 1 // +1 for Si24R1
|
||||||
|
|
||||||
@@ -222,6 +226,7 @@ LOCAL uint8_t RF24_RAW_writeByteRegister(uint8_t cmd, uint8_t value);
|
|||||||
LOCAL void RF24_flushRX(void);
|
LOCAL void RF24_flushRX(void);
|
||||||
LOCAL void RF24_flushTX(void);
|
LOCAL void RF24_flushTX(void);
|
||||||
LOCAL uint8_t RF24_getStatus(void);
|
LOCAL uint8_t RF24_getStatus(void);
|
||||||
|
LOCAL uint8_t RF24_getFifoStatus(void);
|
||||||
LOCAL void RF24_openWritingPipe(uint8_t recipient);
|
LOCAL void RF24_openWritingPipe(uint8_t recipient);
|
||||||
LOCAL void RF24_startListening(void);
|
LOCAL void RF24_startListening(void);
|
||||||
LOCAL void RF24_stopListening(void);
|
LOCAL void RF24_stopListening(void);
|
||||||
@@ -248,4 +253,15 @@ LOCAL void RF24_setPipeLSB(uint8_t pipe, uint8_t LSB);
|
|||||||
LOCAL void RF24_setStatus(uint8_t status);
|
LOCAL void RF24_setStatus(uint8_t status);
|
||||||
LOCAL void RF24_enableFeatures(void);
|
LOCAL void RF24_enableFeatures(void);
|
||||||
|
|
||||||
|
#ifdef MY_RF24_IRQ_PIN
|
||||||
|
typedef void (*RF24_receiveCallbackType)(void);
|
||||||
|
/**
|
||||||
|
* Register a callback, which will be called (from interrupt context) for every message received.
|
||||||
|
* @note When a callback is registered, it _must_ retrieve the message from the nRF24
|
||||||
|
* by calling RF24_readMessage(). Otherwise the interrupt will not get deasserted
|
||||||
|
* and message reception will stop.
|
||||||
|
*/
|
||||||
|
LOCAL void RF24_registerReceiveCallback( RF24_receiveCallbackType cb );
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // __RF24_H__
|
#endif // __RF24_H__
|
||||||
@@ -74,6 +74,7 @@ MY_RF24_ENABLE_ENCRYPTION LITERAL1
|
|||||||
MY_RF24_SPI_MAX_SPEED LITERAL1
|
MY_RF24_SPI_MAX_SPEED LITERAL1
|
||||||
MY_RF24_CE_PIN LITERAL1
|
MY_RF24_CE_PIN LITERAL1
|
||||||
MY_RF24_CS_PIN LITERAL1
|
MY_RF24_CS_PIN LITERAL1
|
||||||
|
MY_RF24_IRQ_PIN LITERAL1
|
||||||
MY_RF24_PA_LEVEL LITERAL1
|
MY_RF24_PA_LEVEL LITERAL1
|
||||||
MY_RF24_CHANNEL LITERAL1
|
MY_RF24_CHANNEL LITERAL1
|
||||||
MY_RF24_DATARATE LITERAL1
|
MY_RF24_DATARATE LITERAL1
|
||||||
@@ -127,3 +128,5 @@ MY_NODE_LOCK_FEATURE LITERAL1
|
|||||||
MY_NODE_UNLOCK_PIN LITERAL1
|
MY_NODE_UNLOCK_PIN LITERAL1
|
||||||
MY_NODE_LOCK_COUNTER_MAX LITERAL1
|
MY_NODE_LOCK_COUNTER_MAX LITERAL1
|
||||||
MY_SPIFLASH_SST25TYPE LITERAL1
|
MY_SPIFLASH_SST25TYPE LITERAL1
|
||||||
|
MY_CRITICAL_SECTION LITERAL1
|
||||||
|
MY_RX_MESSAGE_BUFFER_SIZE LITERAL1
|
||||||
Reference in New Issue
Block a user