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
@@ -70,7 +70,13 @@
|
||||
#define MY_CAP_SIGN "-"
|
||||
#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 */
|
||||
|
||||
+63
-1
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MyHw.h
|
||||
*
|
||||
* MySensors hardware abstraction layer
|
||||
*/
|
||||
|
||||
#ifndef MyHw_h
|
||||
#define MyHw_h
|
||||
|
||||
@@ -42,11 +48,67 @@ void hwWriteConfig(int adr, uint8_t value);
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
#ifdef MY_DEBUG
|
||||
void hwDebugPrint(const char *fmt, ... );
|
||||
#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
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <avr/power.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/wdt.h>
|
||||
|
||||
#include <util/atomic.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -92,4 +92,8 @@ enum period_t
|
||||
|
||||
void hwInternalSleep(unsigned long ms);
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,5 +43,17 @@ void hwWriteConfigBlock(void* buf, void* adr, size_t length);
|
||||
void hwWriteConfig(int adr, uint8_t value);
|
||||
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
|
||||
|
||||
@@ -58,4 +58,28 @@ uint8_t hwReadConfig(int adr);
|
||||
#define hwWriteConfig(__adr, __value) ( __value = __value)
|
||||
#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
|
||||
|
||||
@@ -20,11 +20,47 @@
|
||||
#include "MyConfig.h"
|
||||
#include "MyTransport.h"
|
||||
#include "drivers/RF24/RF24.h"
|
||||
#include "drivers/CircularBuffer/CircularBuffer.h"
|
||||
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
#include "drivers/AES/AES.h"
|
||||
#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)
|
||||
AES _aes;
|
||||
uint8_t _dataenc[32] = {0};
|
||||
@@ -32,7 +68,6 @@
|
||||
#endif
|
||||
|
||||
bool transportInit() {
|
||||
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||
//set up AES-key
|
||||
@@ -41,6 +76,9 @@ bool transportInit() {
|
||||
memset(_psk, 0, 16);
|
||||
#endif
|
||||
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
RF24_registerReceiveCallback( transportRxCallback );
|
||||
#endif
|
||||
return RF24_initialize();
|
||||
}
|
||||
|
||||
@@ -71,8 +109,12 @@ bool transportSend(uint8_t recipient, const void* data, uint8_t len) {
|
||||
}
|
||||
|
||||
bool transportAvailable() {
|
||||
bool avail = RF24_isDataAvailable();
|
||||
return avail;
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
(void)RF24_isDataAvailable; // Prevent 'defined but not used' warning
|
||||
return !transportRxQueue.empty();
|
||||
#else
|
||||
return RF24_isDataAvailable();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool transportSanityCheck() {
|
||||
@@ -80,7 +122,18 @@ bool transportSanityCheck() {
|
||||
}
|
||||
|
||||
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)
|
||||
// has to be adjusted, WIP!
|
||||
_aes.set_IV(0);
|
||||
|
||||
Reference in New Issue
Block a user