From 99973a346eafe2fb74c46be4bdba7412d3446344 Mon Sep 17 00:00:00 2001 From: Embedded Innovation Date: Tue, 2 Aug 2016 22:20:25 +0200 Subject: [PATCH] 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 --- MyConfig.h | 18 +++ MySensors.h | 17 ++ core/MyCapabilities.h | 8 +- core/MyHw.h | 64 +++++++- core/MyHwATMega328.h | 6 +- core/MyHwESP8266.h | 12 ++ core/MyHwSAMD.h | 24 +++ core/MyTransportNRF24.cpp | 61 +++++++- drivers/CircularBuffer/CircularBuffer.h | 196 ++++++++++++++++++++++++ drivers/RF24/RF24.cpp | 63 ++++++++ drivers/RF24/RF24.h | 18 ++- keywords.txt | 5 +- 12 files changed, 483 insertions(+), 9 deletions(-) create mode 100644 drivers/CircularBuffer/CircularBuffer.h diff --git a/MyConfig.h b/MyConfig.h index 3432dbe..5d04238 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -429,6 +429,22 @@ #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 * @brief Default RF24 PA level. Override in sketch if needed. @@ -694,5 +710,7 @@ #define MY_REGISTRATION_CONTROLLER #define MY_DEBUG_VERBOSE_RF24 #define MY_TRANSPORT_SANITY_CHECK +#define MY_RF24_IRQ_PIN +#define MY_RX_MESSAGE_BUFFER_SIZE #define MY_NODE_LOCK_FEATURE #endif diff --git a/MySensors.h b/MySensors.h index aa5241c..8b589a8 100644 --- a/MySensors.h +++ b/MySensors.h @@ -255,6 +255,23 @@ #error Only one forward link driver can be activated #endif #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) #include "drivers/AES/AES.cpp" #endif diff --git a/core/MyCapabilities.h b/core/MyCapabilities.h index 2ec468d..735ec32 100644 --- a/core/MyCapabilities.h +++ b/core/MyCapabilities.h @@ -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 */ diff --git a/core/MyHw.h b/core/MyHw.h index 1224f66..f2b29c3 100644 --- a/core/MyHw.h +++ b/core/MyHw.h @@ -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 diff --git a/core/MyHwATMega328.h b/core/MyHwATMega328.h index 8eb0060..157ecb1 100644 --- a/core/MyHwATMega328.h +++ b/core/MyHwATMega328.h @@ -27,7 +27,7 @@ #include #include #include - +#include #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 diff --git a/core/MyHwESP8266.h b/core/MyHwESP8266.h index a9693ff..1495e69 100644 --- a/core/MyHwESP8266.h +++ b/core/MyHwESP8266.h @@ -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 diff --git a/core/MyHwSAMD.h b/core/MyHwSAMD.h index fcc5a9d..dcb783c 100644 --- a/core/MyHwSAMD.h +++ b/core/MyHwSAMD.h @@ -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 diff --git a/core/MyTransportNRF24.cpp b/core/MyTransportNRF24.cpp index e38ecf4..6d7f407 100644 --- a/core/MyTransportNRF24.cpp +++ b/core/MyTransportNRF24.cpp @@ -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 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); diff --git a/drivers/CircularBuffer/CircularBuffer.h b/drivers/CircularBuffer/CircularBuffer.h new file mode 100644 index 0000000..c3362d9 --- /dev/null +++ b/drivers/CircularBuffer/CircularBuffer.h @@ -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 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(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(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 diff --git a/drivers/RF24/RF24.cpp b/drivers/RF24/RF24.cpp index 5113790..d263c1f 100644 --- a/drivers/RF24/RF24.cpp +++ b/drivers/RF24/RF24.cpp @@ -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_NODE_ADDRESS = AUTO; +#ifdef MY_RF24_IRQ_PIN + LOCAL RF24_receiveCallbackType RF24_receiveCallback = NULL; +#endif + LOCAL void RF24_csn(bool level) { digitalWrite(MY_RF24_CS_PIN, level); } @@ -86,6 +90,10 @@ LOCAL uint8_t RF24_getStatus(void) { return RF24_spiByteTransfer( NOP ); } +LOCAL uint8_t RF24_getFifoStatus(void) { + return RF24_readByteRegister(FIFO_STATUS); +} + LOCAL void RF24_setChannel(uint8_t channel) { RF24_writeByteRegister(RF_CH, channel); } @@ -253,14 +261,69 @@ LOCAL bool RF24_sanityCheck(void) { 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) { // Initialize pins pinMode(MY_RF24_CE_PIN,OUTPUT); pinMode(MY_RF24_CS_PIN,OUTPUT); + #ifdef MY_RF24_IRQ_PIN + pinMode(MY_RF24_IRQ_PIN,INPUT); + #endif // Initialize SPI _SPI.begin(); RF24_ce(LOW); 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 RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) ) ; // settle >2ms diff --git a/drivers/RF24/RF24.h b/drivers/RF24/RF24.h index e0f81ba..765a434 100644 --- a/drivers/RF24/RF24.h +++ b/drivers/RF24/RF24.h @@ -62,7 +62,11 @@ #endif // 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_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_flushTX(void); LOCAL uint8_t RF24_getStatus(void); +LOCAL uint8_t RF24_getFifoStatus(void); LOCAL void RF24_openWritingPipe(uint8_t recipient); LOCAL void RF24_startListening(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_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__ \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index da92188..808abf6 100644 --- a/keywords.txt +++ b/keywords.txt @@ -74,6 +74,7 @@ MY_RF24_ENABLE_ENCRYPTION LITERAL1 MY_RF24_SPI_MAX_SPEED LITERAL1 MY_RF24_CE_PIN LITERAL1 MY_RF24_CS_PIN LITERAL1 +MY_RF24_IRQ_PIN LITERAL1 MY_RF24_PA_LEVEL LITERAL1 MY_RF24_CHANNEL LITERAL1 MY_RF24_DATARATE LITERAL1 @@ -126,4 +127,6 @@ MY_SMART_SLEEP_WAIT_DURATION LITERAL1 MY_NODE_LOCK_FEATURE LITERAL1 MY_NODE_UNLOCK_PIN LITERAL1 MY_NODE_LOCK_COUNTER_MAX LITERAL1 -MY_SPIFLASH_SST25TYPE LITERAL1 \ No newline at end of file +MY_SPIFLASH_SST25TYPE LITERAL1 +MY_CRITICAL_SECTION LITERAL1 +MY_RX_MESSAGE_BUFFER_SIZE LITERAL1 \ No newline at end of file