From bfa493ce3eadf1cc436e24cfe7a66b44bdbd2287 Mon Sep 17 00:00:00 2001 From: Embedded Innovation Date: Thu, 28 Apr 2016 21:10:20 +0200 Subject: [PATCH] Added interrupt handling and reception queueing for nRF24 When nRF24 interrupt is connected to Arduino, handling of reception interrupts can be enabled. This will store all data received in a buffer which will prevent messages from getting lost when the Arduino is too busy to empty the FIFO. --- libraries/MySensors/MyConfig.h | 18 ++++ libraries/MySensors/core/MyTransportNRF24.cpp | 77 +++++++++++++-- libraries/MySensors/drivers/RF24/RF24.cpp | 40 +++++++- libraries/MySensors/drivers/RF24/RF24.h | 6 +- .../QueueTestGateway/QueueTestGateway.ino | 94 +++++++++++++++++++ .../examples/QueueTestNode/QueueTestNode.ino | 52 ++++++++++ libraries/MySensors/keywords.txt | 1 + 7 files changed, 276 insertions(+), 12 deletions(-) create mode 100644 libraries/MySensors/examples/QueueTestGateway/QueueTestGateway.ino create mode 100644 libraries/MySensors/examples/QueueTestNode/QueueTestNode.ino diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 2bc11e1..234e1e3 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -368,6 +368,24 @@ #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_UNUSED 0 +#ifndef MY_RF24_IRQ_PIN + #define MY_RF24_IRQ_PIN MY_RF24_IRQ_PIN_UNUSED +#endif + +/** + * @def MY_RF24_MESSAGE_BUFFER_SIZE + * @brief Declare the amount of messages that can be buffered. Override in sketch if needed. + */ +#ifndef MY_RF24_MESSAGE_BUFFER_SIZE + #define MY_RF24_MESSAGE_BUFFER_SIZE (20) +#endif + + /** * @def MY_RF24_PA_LEVEL * @brief Default RF24 PA level. Override in sketch if needed. diff --git a/libraries/MySensors/core/MyTransportNRF24.cpp b/libraries/MySensors/core/MyTransportNRF24.cpp index 5aa1596..6589a09 100644 --- a/libraries/MySensors/core/MyTransportNRF24.cpp +++ b/libraries/MySensors/core/MyTransportNRF24.cpp @@ -20,17 +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 -#if defined(MY_RF24_ENABLE_ENCRYPTION) - AES _aes; - uint8_t _dataenc[32] = {0}; - uint8_t _psk[16]; +#if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) +typedef struct _queuedMessage +{ + uint8_t m_to; /** Destination address of this message. */ + uint8_t m_len; /** Length of the data. */ + uint8_t m_data[MAX_MESSAGE_LENGTH]; /** The raw data. */ +} queuedMessage; + +/** Buffer to store queued messages in. */ +static queuedMessage queueStorage[MY_RF24_MESSAGE_BUFFER_SIZE]; +/** Circular buffer, which uses the queueStorage and administers stored messages. */ +static CircularBuffer receiveQueue(queueStorage, MY_RF24_MESSAGE_BUFFER_SIZE); + +static volatile uint8_t lostMessageCount = 0; + +static void rxCallback(const uint8_t to) +{ + if (!receiveQueue.full()) + { + queuedMessage* msg = receiveQueue.getFront(); + msg->m_len = readMessage(msg->m_data); + (void)receiveQueue.pushFront(msg); + } else { + // TODO: Message is not really lost, as it is still in the nRF FIFO. But once the FIFO is also full messages will get lost. + ++lostMessageCount; + } +} #endif +#if defined(MY_RF24_ENABLE_ENCRYPTION) + AES _aes; + uint8_t _dataenc[32] = {0}; + uint8_t _psk[16]; +#endif + bool transportInit() { #if defined(MY_RF24_ENABLE_ENCRYPTION) @@ -40,7 +70,10 @@ bool transportInit() { // Make sure it is purged from memory when set memset(_psk, 0, 16); #endif - + + #if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) + registerReceiveCallback( rxCallback ); + #endif return initializeRF24(); } @@ -71,13 +104,39 @@ bool transportSend(uint8_t recipient, const void* data, uint8_t len) { } bool transportAvailable(uint8_t *to) { - bool avail = IsDataAvailable(to); - return avail; + bool avail = false; + #if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) + queuedMessage* msg = receiveQueue.getBack(); + if (msg) + { + avail = true; + *to = msg->m_to; + } else { + // TODO: The nRF FIFO could still hold a message + } + #else + avail = IsDataAvailable(to); + #endif + return avail; } uint8_t transportReceive(void* data) { - uint8_t len = readMessage(data); - #if defined(MY_RF24_ENABLE_ENCRYPTION) + uint8_t len; + #if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) +// debug(PSTR("lost:%d\n"), lostMessageCount); + queuedMessage* msg = receiveQueue.getBack(); + if (msg) + { + len = msg->m_len; + (void)memcpy(data, msg->m_data, len); + (void)receiveQueue.popBack(); + } else { + // TODO: The nRF FIFO could still hold a message + } + #else + len = readMessage(data); + #endif + #if defined(MY_RF24_ENABLE_ENCRYPTION) // has to be adjusted, WIP! _aes.set_IV(0); // decrypt data diff --git a/libraries/MySensors/drivers/RF24/RF24.cpp b/libraries/MySensors/drivers/RF24/RF24.cpp index 1466005..c9fa914 100644 --- a/libraries/MySensors/drivers/RF24/RF24.cpp +++ b/libraries/MySensors/drivers/RF24/RF24.cpp @@ -22,8 +22,13 @@ #include "RF24.h" -uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID }; -uint8_t MY_RF24_NODE_ADDRESS = AUTO; +static uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID }; +static uint8_t MY_RF24_NODE_ADDRESS = AUTO; + +#if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) +typedef void (*receiveCallbackType)(const uint8_t); +static receiveCallbackType receiveCallback = NULL; +#endif void csn(bool level) { digitalWrite(MY_RF24_CS_PIN, level); @@ -204,14 +209,45 @@ uint8_t getNodeID(void) { return MY_RF24_NODE_ADDRESS; } +#if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) +static void irqHandler( void ) +{ + if (receiveCallback) + { + uint8_t to; + (void)IsDataAvailable(&to); + receiveCallback(to); + } else { + // clear RX interrupt + writeByteRegister(RF24_STATUS, _BV(RX_DR) ); + } +} + +void registerReceiveCallback( receiveCallbackType cb ) +{ + noInterrupts(); + receiveCallback = cb; + interrupts(); +} +#endif + bool initializeRF24(void) { // Initialize pins pinMode(MY_RF24_CE_PIN,OUTPUT); pinMode(MY_RF24_CS_PIN,OUTPUT); + #if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) + pinMode(MY_RF24_IRQ_PIN,INPUT); + #endif // Initialize SPI _SPI.begin(); ce(LOW); csn(HIGH); + #if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) + // assure SPI can be used from interrupt context + _SPI.usingInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN)); + // attach interrupt + attachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN), irqHandler, FALLING); + #endif // CRC and power up writeByteRegister(NRF_CONFIG, MY_RF24_CONFIGURATION | _BV(PWR_UP) ) ; // settle >2ms diff --git a/libraries/MySensors/drivers/RF24/RF24.h b/libraries/MySensors/drivers/RF24/RF24.h index dc4ac68..c2adb8a 100644 --- a/libraries/MySensors/drivers/RF24/RF24.h +++ b/libraries/MySensors/drivers/RF24/RF24.h @@ -38,7 +38,11 @@ #define MY_RF24_SPI_DATA_MODE SPI_MODE0 // settings -#define MY_RF24_CONFIGURATION (uint8_t) (RF24_CRC_16 << 1) +#if (MY_RF24_IRQ_PIN != MY_RF24_IRQ_PIN_UNUSED) + #define MY_RF24_CONFIGURATION (uint8_t)((RF24_CRC_16 << 1) | (1 << MASK_TX_DS) | (1 << MASK_MAX_RT)) +#else + #define MY_RF24_CONFIGURATION (uint8_t)((RF24_CRC_16 << 1) | (1 << MASK_RX_DR) | (1 << MASK_TX_DS) | (1 << MASK_MAX_RT)) +#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 diff --git a/libraries/MySensors/examples/QueueTestGateway/QueueTestGateway.ino b/libraries/MySensors/examples/QueueTestGateway/QueueTestGateway.ino new file mode 100644 index 0000000..c8ed43f --- /dev/null +++ b/libraries/MySensors/examples/QueueTestGateway/QueueTestGateway.ino @@ -0,0 +1,94 @@ + /** + * 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 + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2015 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * 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. + * + ******************************* + * + * DESCRIPTION + * The ArduinoGateway prints data received from sensors on the serial link. + * The gateway accepts input on seral which will be sent out on radio network. + * + * The GW code is designed for Arduino Nano 328p / 16MHz + * + * Wire connections (OPTIONAL): + * - Inclusion button should be connected between digital pin 3 and GND + * - RX/TX/ERR leds need to be connected between +5V (anode) and digital pin 6/5/4 with resistor 270-330R in a series + * + * LEDs (OPTIONAL): + * - To use the feature, uncomment MY_LEDS_BLINKING_FEATURE in MyConfig.h + * - RX (green) - blink fast on radio message recieved. In inclusion mode will blink fast only on presentation recieved + * - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly + * - ERR (red) - fast blink on error during transmission error or recieve crc error + * + */ + +#define MY_RADIO_NRF24 +#define MY_RF24_CHANNEL (100) +#define MY_RF24_DATARATE (RF24_1MBPS) +#define MY_GATEWAY_SERIAL +#define MY_LEDS_BLINKING_FEATURE +#define MY_DEFAULT_LED_BLINK_PERIOD 50 +#define MY_WITH_LEDS_BLINKING_INVERSE +#define MY_RF24_IRQ_PIN (2) + +#define MY_DEFAULT_ERR_LED_PIN A0 // Error led pin +#define MY_DEFAULT_RX_LED_PIN A1 // Receive led pin +#define MY_DEFAULT_TX_LED_PIN A2 // the PCB, on board LED + +//#define MY_DEBUG +//#define MY_DEBUG_VERBOSE_RF24 + +#include +#include + +void setup() { +} + +void presentation() { +} + + +void receive(const MyMessage &message) +{ + static uint16_t prevCount = 0; + uint16_t count = message.getUInt(); + if ((count-1) != prevCount) + { + Serial.print(F("Mismatch: prev=")); + Serial.print(prevCount); + Serial.print(F(" new=")); + Serial.println(count); + } + prevCount = count; +} + +const uint8_t maxCount = 100; + +void loop() { + static uint8_t count = 0; + ++count; + if (count++ > maxCount) + { + count = 0; + delay(1000); + } +} + + + + + diff --git a/libraries/MySensors/examples/QueueTestNode/QueueTestNode.ino b/libraries/MySensors/examples/QueueTestNode/QueueTestNode.ino new file mode 100644 index 0000000..47dfe22 --- /dev/null +++ b/libraries/MySensors/examples/QueueTestNode/QueueTestNode.ino @@ -0,0 +1,52 @@ +#include +#include + +#define MY_RADIO_NRF24 +#define MY_RF24_CHANNEL (100) +#define MY_NODE_ID (200) +#define MY_RF24_CE_PIN (9) +#define MY_RF24_CS_PIN (10) +//#define MY_RF24_IRQ_PIN (2) +#define MY_RF24_DATARATE (RF24_1MBPS) +#define MY_DEBUG +//#define MY_DEBUG_VERBOSE_RF24 + +#include +#include + +#undef debug +#ifndef NODEBUG +#define debug(x) Serial.print(x) +#define debugln(x) Serial.println(x) +#else +#define debug(x) +#define debugln(x) +#endif + +void presentation() +{ + sendSketchInfo("Test", "1.0"); + debug("ID: "); debugln(getNodeId()); + + present(0, S_CUSTOM); +} + +void receive(const MyMessage &message) +{ + // debug("Message "); debugln(message.type); +} + +void setup() +{ + debugln(F("---- Starting sensor ----")); +} + +void loop() +{ + static uint16_t count = 0; + send( MyMessage(0, V_VAR1).set(count) ); + ++count; + + delay(100); +} + diff --git a/libraries/MySensors/keywords.txt b/libraries/MySensors/keywords.txt index 7b18486..f679ead 100644 --- a/libraries/MySensors/keywords.txt +++ b/libraries/MySensors/keywords.txt @@ -66,6 +66,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