Radio communication with NRF24

Dependents:   F030

ssRadio.cpp

Committer:
gume
Date:
2017-10-06
Revision:
0:9f5a444886a8

File content as of revision 0:9f5a444886a8:

#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;
}