Radio communication with NRF24

Dependents:   F030

Files at this revision

API Documentation at this revision

Comitter:
gume
Date:
Fri Oct 06 20:17:36 2017 +0000
Commit message:
Initial release

Changed in this revision

NodeConfig.cpp Show annotated file Show diff for this revision Revisions of this file
NodeConfig.h Show annotated file Show diff for this revision Revisions of this file
ssRadio.cpp Show annotated file Show diff for this revision Revisions of this file
ssRadio.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NodeConfig.cpp	Fri Oct 06 20:17:36 2017 +0000
@@ -0,0 +1,53 @@
+#include "NodeConfig.h"
+
+#if defined(__AVR_ATmega328P__) 
+#include <EEPROM.h>
+#include "Entropy.h"
+#endif
+
+uint16_t NodeConfig::getNodeId() {
+
+  uint16_t id;
+  
+  return id;
+}
+
+NodeConfig::NodeConfig(PinName cePin, PinName csnPin) {
+
+    this->cePin = cePin;
+    this->csnPin = csnPin;
+    
+    this->netPrefix = DEFAULT_NETWORK_PREFIX;
+    this->syncAddress = DEFAULT_SYNC_ADDRESS;
+    this->channel = DEFAULT_RADIO_CHANNEL;
+    this->dataRate = DEFAULT_DATA_RATE;
+    this->paLevel = DEFAULT_PA_LEVEL;
+    this->nodeId = getNodeId();
+    this->gwId = DEFAULT_GW_ID;
+}
+
+void NodeConfig::setChannel(uint8_t channel) {
+    this->channel = channel;
+}
+
+void NodeConfig::setSpeed(rf24_datarate_e dataRate) {
+    this->dataRate = dataRate;
+}
+
+void NodeConfig::setNetPrefix(uint32_t netPrefix) {
+  this->netPrefix = netPrefix;
+}
+
+void NodeConfig::setNodeId(uint16_t nodeId) {
+  this->nodeId = nodeId;
+}
+
+void NodeConfig::setGwId(uint16_t gwId) {
+  this->gwId = gwId;
+}
+
+void NodeConfig::setSyncAddress(uint64_t syncAddress) {
+    this->syncAddress = syncAddress;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NodeConfig.h	Fri Oct 06 20:17:36 2017 +0000
@@ -0,0 +1,47 @@
+#ifndef __NODECONFIG_H_
+#define __NODECONFIG_H_
+
+//#include <Arduino.h>
+#include "RF24.h"
+
+
+#define DEFAULT_NETWORK_PREFIX 0x424D45L    // All network node has a common 3 byte address prefix
+#define DEFAULT_NODE_ADDRESS 0x424D455555L
+#define DEFAULT_SYNC_ADDRESS 0x3333333333L
+#define DEFAULT_GW_ID 0x0000
+#define DEFAULT_RADIO_CHANNEL 96
+#define DEFAULT_PA_LEVEL RF24_PA_MAX
+#define DEFAULT_DATA_RATE RF24_2MBPS
+
+class SSRadio;
+
+class NodeConfig {
+friend class SSRadio;
+
+private:
+    uint32_t netPrefix;
+    uint16_t nodeId;
+    uint16_t gwId;
+    uint64_t syncAddress;
+
+    uint8_t channel;            // Radio channel
+    rf24_pa_dbm_e paLevel;      // PA level
+    rf24_datarate_e dataRate;   // The transmission speed
+
+    PinName cePin;         // CE pin
+    PinName csnPin;        // CS pin
+
+public:
+    NodeConfig(PinName _cepin, PinName _cspin);
+
+    void setNodeId(uint16_t nodeId);
+    void setGwId(uint16_t gwId);
+    void setNetPrefix(uint32_t netPrefix);
+    void setSyncAddress(uint64_t syncAddress);
+    void setChannel(uint8_t channel);
+    void setSpeed(rf24_datarate_e dataRate);
+
+    uint16_t getNodeId();   // Returns ID from EEPROM or UinqID. If no ID in EEPROM, then create one
+};
+
+#endif // NODECONFIG_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ssRadio.cpp	Fri Oct 06 20:17:36 2017 +0000
@@ -0,0 +1,180 @@
+#include "ssRadio.h"
+//#include "printf.h"
+
+SSRadio::SSRadio(SPI *spi, NodeConfig *config) : rf24(spi, config->cePin, config->csnPin) {
+    
+    this->config = config;
+    
+    this->myAddress = config->nodeId;
+    this->gatewayAddress = ((uint64_t)config->netPrefix << 16) + config->gwId;
+    
+    this->sendSlot = -1;
+    this->freeSlot = -1;
+    this->slot_ms = 4;
+    
+    this->onConnect = NULL;
+    this->onDisconnect = NULL;
+    this->onReceiveData = NULL;
+}
+
+void SSRadio::init() {
+
+    rf24.begin();
+    rf24.enableDynamicAck();
+    rf24.enableDynamicPayloads();
+    rf24.setCRCLength(RF24_CRC_16);
+    rf24.openReadingPipe(0, config->syncAddress);
+    rf24.openReadingPipe(1, ((uint64_t)config->netPrefix << 16) + config->nodeId);
+    rf24.setAutoAck(0, false);   // Disable autoACK on broadcast
+    rf24.setAutoAck(1, true);    // Ensure autoACK is enabled on data/control
+    rf24.setChannel(config->channel);
+    rf24.setPALevel(config->paLevel);
+    rf24.setDataRate(config->dataRate);
+
+    txCounter = 0;
+    rxCounter = 0;
+    
+    txQueueLength = 0;
+    rxQueueLength = 0;
+    
+    rf24.startListening();  
+  // IF_SERIAL_DEBUG(rf24.printDetails());  
+}
+    
+bool SSRadio::isRunning() {
+    return rf24.isChipConnected();
+}
+    
+void SSRadio::loop() {
+    
+    // Checking incoming frames
+    uint8_t pipenum;
+    if (rf24.available(&pipenum)) {
+        IF_SERIAL_DEBUG(Serial.println(F("Incoming packet.")));
+        uint8_t ps = rf24.getDynamicPayloadSize();
+        if (ps > 0) {
+            rf24.read(packetRx, ps);
+            if (pipenum == 0) {
+                receiveSyncFrame(packetRx, ps);
+            } else {
+                receiveDataFrame(packetRx, ps);
+            }
+        }
+    }
+    
+    long now = us_ticker_read() / 1000;
+    // Check the queues
+    bool sent = false;
+    if (txQueueLength > 0) {
+                
+        if (sendSlot > -1) {
+            // There is a timeslot now for the transmission
+            if ((now > lastSync + sendSlot * slot_ms) && (now < lastSync + (sendSlot + 1) * slot_ms)) {
+                instantData(txQueue + 2, txQueue[1], txQueue[0]);
+        //Serial.println("sendSlot");
+                sent = true;
+            }
+        }
+        else if (freeSlot > 0) {
+            // When freeSlot is not 0 (nobody is scheduled), then send in the free slot
+            if ((now >= lastSync + freeSlot * slot_ms) && (now <= lastSync + (freeSlot + 1) * slot_ms)) {
+                instantData(txQueue + 2, txQueue[1], txQueue[0]);
+        //Serial.println("freeSlot");
+                sent = true;
+            }
+        }
+        else {
+      // No sync or no others, send immediately
+            instantData(txQueue + 2, txQueue[1], txQueue[0]);
+      //Serial.println("Other");      
+            sent = true;
+        }
+        
+        if (sent) {
+            // Move in the queue;
+            txQueueLength--;
+            if (txQueueLength > 0)
+                memcpy(txQueue, txQueue + 30, txQueueLength * 30);
+        }
+    }
+    
+    // Scheduled tasks
+    if (now > lastSync + 1000) {
+        // last Sync was really long ago
+        sendSlot = -1;
+        freeSlot = -1;
+    }
+}
+
+void SSRadio::receiveSyncFrame(uint8_t *frame, uint8_t size) {
+    
+    lastSync = us_ticker_read() / 1000;
+    
+    // Find self address to get the sending slot
+    uint16_t *nodes = (uint16_t*) frame;
+    sendSlot = -1;
+    //Serial.println("Sync");
+    //Serial.println(size);
+    uint8_t i = 0;
+    while ((i < size / 2) && (nodes[i] != 0)) {
+        //Serial.println(nodes[i]);
+        if (nodes[i] == myAddress) {
+            sendSlot = i;
+        }
+        i++;
+    }
+    if (nodes[i] == 0) {
+        freeSlot = i;
+    }
+}
+
+void SSRadio::receiveDataFrame(uint8_t *frame, uint8_t size) {
+    
+  Packet *p = (Packet*) frame;
+  IF_SERIAL_DEBUG(Serial.println(F("Data frame received.")));
+  if (onReceiveData) {
+    IF_SERIAL_DEBUG(Serial.println(F("Calling onReceiveData.")));
+    (*onReceiveData)(p->fields.payload, p->fields.type, size - 4);
+  }
+}
+
+bool SSRadio::sendData(uint8_t *data, uint16_t type, uint8_t size) {
+    
+    if (txQueueLength == QUEUELEN) return false; // Queue is full
+    
+    uint8_t *frame = txQueue + txQueueLength * 30;
+    frame[0] = size;
+    frame[1] = type & 0xff; // !!! SHOULD be FIXED for extended types
+    memcpy(frame + 2, data, size);
+    
+    txQueueLength ++;
+    
+    return true;
+}
+
+bool SSRadio::instantData(uint8_t *data, uint16_t type, uint8_t size) {
+
+  Packet p;
+  p.fields.address = myAddress;
+  p.fields.type = type & 0xff;
+  p.fields.counter = txCounter ++;
+  memcpy(p.fields.payload, data, size);
+
+    IF_SERIAL_DEBUG(Serial.println(F("Sending data.")));
+    
+  rf24.stopListening();
+  rf24.setRetries(0, 5);
+  rf24.openWritingPipe(gatewayAddress);
+  bool ok = rf24.write(p.raw, size + 4);
+  rf24.startListening();
+
+  if (ok) IF_SERIAL_DEBUG(Serial.println("Success."));
+  else IF_SERIAL_DEBUG(Serial.println("Fail!"));
+  
+    return ok;
+}
+
+void SSRadio::setOnReceiveData(void (*f)(uint8_t *data, uint16_t type, uint8_t len)) {
+  onReceiveData = f;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ssRadio.h	Fri Oct 06 20:17:36 2017 +0000
@@ -0,0 +1,75 @@
+#ifndef __SSRADIO_H__
+#define __SSRADIO_H__
+
+//#define SERIAL_DEBUG 1
+
+#include "RF24.h"
+#include "NodeConfig.h"
+
+#define QUEUELEN 3
+
+typedef union _Packet {
+  struct __attribute__((packed)) {
+    uint16_t address;     // Source address
+    uint8_t type;         // Type. MSB signals extended type (+1 byte). Not implemented yet.
+    uint8_t counter;      // Packet counter
+    uint8_t payload[28]; 
+  } fields;
+  uint8_t raw[32];
+} Packet;
+
+class SSRadio {
+
+protected:
+    NodeConfig *config;
+    RF24 rf24;
+    
+    void (*onReceiveData)(uint8_t *data, uint16_t type, uint8_t len);
+    void (*onConnect)();
+    void (*onDisconnect)();
+
+    uint16_t txCounter;
+    uint16_t rxCounter;
+    
+    uint8_t packetRx[32];
+    long lastSync;  // millis when the last Sync was received
+    int8_t sendSlot;    // Transmission slot, when synced
+    int8_t freeSlot;    // Free (for everyone) slot, when synced
+    uint8_t slot_ms;    // Slot size in ms
+    
+    void receiveSyncFrame(uint8_t *frame, uint8_t size);
+    void receiveDataFrame(uint8_t *frame, uint8_t size);
+    
+    uint16_t myAddress;
+    uint64_t gatewayAddress;
+    
+    uint8_t txQueue[QUEUELEN * 30]; // 1 length + 1 type + 28 payload
+    uint8_t rxQueue[QUEUELEN * 30];
+    uint8_t txQueueLength;
+    uint8_t rxQueueLength;
+    
+public:
+    SSRadio(SPI *spi, NodeConfig *nc);
+    
+    void init();
+    void loop();
+    
+    void useIRQ(uint16_t irqPin);
+    
+    bool isRunning();   // RF24 is connected or not
+    bool isConnected(); // There is a GW nearby
+    bool isScheduled(); // Sending is scheduled by the GW
+    bool isAvailableData(); // Available data
+    
+    bool sendData(uint8_t *data, uint16_t type, uint8_t len); // Send data to GW
+    bool instantData(uint8_t *data, uint16_t type, uint8_t len); // Send data to GW immediately
+    bool receiveData(uint8_t *data, uint16_t &type, uint8_t &len); // Receive data from GW
+    void setSlotSize(uint8_t slot_ms);
+    
+    // Callbacks
+    void setOnReceiveData(void (*onReceiveData)(uint8_t *data, uint16_t type, uint8_t len));
+    void setOnConnect(void (*onConnect)());
+    void setOnDisconnect(void (*onDisconnect)());
+};
+
+#endif // __SSRADIO_H__