Radio communication with NRF24
ssRadio.cpp@0:9f5a444886a8, 2017-10-06 (annotated)
- Committer:
- gume
- Date:
- Fri Oct 06 20:17:36 2017 +0000
- Revision:
- 0:9f5a444886a8
Initial release
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
gume | 0:9f5a444886a8 | 1 | #include "ssRadio.h" |
gume | 0:9f5a444886a8 | 2 | //#include "printf.h" |
gume | 0:9f5a444886a8 | 3 | |
gume | 0:9f5a444886a8 | 4 | SSRadio::SSRadio(SPI *spi, NodeConfig *config) : rf24(spi, config->cePin, config->csnPin) { |
gume | 0:9f5a444886a8 | 5 | |
gume | 0:9f5a444886a8 | 6 | this->config = config; |
gume | 0:9f5a444886a8 | 7 | |
gume | 0:9f5a444886a8 | 8 | this->myAddress = config->nodeId; |
gume | 0:9f5a444886a8 | 9 | this->gatewayAddress = ((uint64_t)config->netPrefix << 16) + config->gwId; |
gume | 0:9f5a444886a8 | 10 | |
gume | 0:9f5a444886a8 | 11 | this->sendSlot = -1; |
gume | 0:9f5a444886a8 | 12 | this->freeSlot = -1; |
gume | 0:9f5a444886a8 | 13 | this->slot_ms = 4; |
gume | 0:9f5a444886a8 | 14 | |
gume | 0:9f5a444886a8 | 15 | this->onConnect = NULL; |
gume | 0:9f5a444886a8 | 16 | this->onDisconnect = NULL; |
gume | 0:9f5a444886a8 | 17 | this->onReceiveData = NULL; |
gume | 0:9f5a444886a8 | 18 | } |
gume | 0:9f5a444886a8 | 19 | |
gume | 0:9f5a444886a8 | 20 | void SSRadio::init() { |
gume | 0:9f5a444886a8 | 21 | |
gume | 0:9f5a444886a8 | 22 | rf24.begin(); |
gume | 0:9f5a444886a8 | 23 | rf24.enableDynamicAck(); |
gume | 0:9f5a444886a8 | 24 | rf24.enableDynamicPayloads(); |
gume | 0:9f5a444886a8 | 25 | rf24.setCRCLength(RF24_CRC_16); |
gume | 0:9f5a444886a8 | 26 | rf24.openReadingPipe(0, config->syncAddress); |
gume | 0:9f5a444886a8 | 27 | rf24.openReadingPipe(1, ((uint64_t)config->netPrefix << 16) + config->nodeId); |
gume | 0:9f5a444886a8 | 28 | rf24.setAutoAck(0, false); // Disable autoACK on broadcast |
gume | 0:9f5a444886a8 | 29 | rf24.setAutoAck(1, true); // Ensure autoACK is enabled on data/control |
gume | 0:9f5a444886a8 | 30 | rf24.setChannel(config->channel); |
gume | 0:9f5a444886a8 | 31 | rf24.setPALevel(config->paLevel); |
gume | 0:9f5a444886a8 | 32 | rf24.setDataRate(config->dataRate); |
gume | 0:9f5a444886a8 | 33 | |
gume | 0:9f5a444886a8 | 34 | txCounter = 0; |
gume | 0:9f5a444886a8 | 35 | rxCounter = 0; |
gume | 0:9f5a444886a8 | 36 | |
gume | 0:9f5a444886a8 | 37 | txQueueLength = 0; |
gume | 0:9f5a444886a8 | 38 | rxQueueLength = 0; |
gume | 0:9f5a444886a8 | 39 | |
gume | 0:9f5a444886a8 | 40 | rf24.startListening(); |
gume | 0:9f5a444886a8 | 41 | // IF_SERIAL_DEBUG(rf24.printDetails()); |
gume | 0:9f5a444886a8 | 42 | } |
gume | 0:9f5a444886a8 | 43 | |
gume | 0:9f5a444886a8 | 44 | bool SSRadio::isRunning() { |
gume | 0:9f5a444886a8 | 45 | return rf24.isChipConnected(); |
gume | 0:9f5a444886a8 | 46 | } |
gume | 0:9f5a444886a8 | 47 | |
gume | 0:9f5a444886a8 | 48 | void SSRadio::loop() { |
gume | 0:9f5a444886a8 | 49 | |
gume | 0:9f5a444886a8 | 50 | // Checking incoming frames |
gume | 0:9f5a444886a8 | 51 | uint8_t pipenum; |
gume | 0:9f5a444886a8 | 52 | if (rf24.available(&pipenum)) { |
gume | 0:9f5a444886a8 | 53 | IF_SERIAL_DEBUG(Serial.println(F("Incoming packet."))); |
gume | 0:9f5a444886a8 | 54 | uint8_t ps = rf24.getDynamicPayloadSize(); |
gume | 0:9f5a444886a8 | 55 | if (ps > 0) { |
gume | 0:9f5a444886a8 | 56 | rf24.read(packetRx, ps); |
gume | 0:9f5a444886a8 | 57 | if (pipenum == 0) { |
gume | 0:9f5a444886a8 | 58 | receiveSyncFrame(packetRx, ps); |
gume | 0:9f5a444886a8 | 59 | } else { |
gume | 0:9f5a444886a8 | 60 | receiveDataFrame(packetRx, ps); |
gume | 0:9f5a444886a8 | 61 | } |
gume | 0:9f5a444886a8 | 62 | } |
gume | 0:9f5a444886a8 | 63 | } |
gume | 0:9f5a444886a8 | 64 | |
gume | 0:9f5a444886a8 | 65 | long now = us_ticker_read() / 1000; |
gume | 0:9f5a444886a8 | 66 | // Check the queues |
gume | 0:9f5a444886a8 | 67 | bool sent = false; |
gume | 0:9f5a444886a8 | 68 | if (txQueueLength > 0) { |
gume | 0:9f5a444886a8 | 69 | |
gume | 0:9f5a444886a8 | 70 | if (sendSlot > -1) { |
gume | 0:9f5a444886a8 | 71 | // There is a timeslot now for the transmission |
gume | 0:9f5a444886a8 | 72 | if ((now > lastSync + sendSlot * slot_ms) && (now < lastSync + (sendSlot + 1) * slot_ms)) { |
gume | 0:9f5a444886a8 | 73 | instantData(txQueue + 2, txQueue[1], txQueue[0]); |
gume | 0:9f5a444886a8 | 74 | //Serial.println("sendSlot"); |
gume | 0:9f5a444886a8 | 75 | sent = true; |
gume | 0:9f5a444886a8 | 76 | } |
gume | 0:9f5a444886a8 | 77 | } |
gume | 0:9f5a444886a8 | 78 | else if (freeSlot > 0) { |
gume | 0:9f5a444886a8 | 79 | // When freeSlot is not 0 (nobody is scheduled), then send in the free slot |
gume | 0:9f5a444886a8 | 80 | if ((now >= lastSync + freeSlot * slot_ms) && (now <= lastSync + (freeSlot + 1) * slot_ms)) { |
gume | 0:9f5a444886a8 | 81 | instantData(txQueue + 2, txQueue[1], txQueue[0]); |
gume | 0:9f5a444886a8 | 82 | //Serial.println("freeSlot"); |
gume | 0:9f5a444886a8 | 83 | sent = true; |
gume | 0:9f5a444886a8 | 84 | } |
gume | 0:9f5a444886a8 | 85 | } |
gume | 0:9f5a444886a8 | 86 | else { |
gume | 0:9f5a444886a8 | 87 | // No sync or no others, send immediately |
gume | 0:9f5a444886a8 | 88 | instantData(txQueue + 2, txQueue[1], txQueue[0]); |
gume | 0:9f5a444886a8 | 89 | //Serial.println("Other"); |
gume | 0:9f5a444886a8 | 90 | sent = true; |
gume | 0:9f5a444886a8 | 91 | } |
gume | 0:9f5a444886a8 | 92 | |
gume | 0:9f5a444886a8 | 93 | if (sent) { |
gume | 0:9f5a444886a8 | 94 | // Move in the queue; |
gume | 0:9f5a444886a8 | 95 | txQueueLength--; |
gume | 0:9f5a444886a8 | 96 | if (txQueueLength > 0) |
gume | 0:9f5a444886a8 | 97 | memcpy(txQueue, txQueue + 30, txQueueLength * 30); |
gume | 0:9f5a444886a8 | 98 | } |
gume | 0:9f5a444886a8 | 99 | } |
gume | 0:9f5a444886a8 | 100 | |
gume | 0:9f5a444886a8 | 101 | // Scheduled tasks |
gume | 0:9f5a444886a8 | 102 | if (now > lastSync + 1000) { |
gume | 0:9f5a444886a8 | 103 | // last Sync was really long ago |
gume | 0:9f5a444886a8 | 104 | sendSlot = -1; |
gume | 0:9f5a444886a8 | 105 | freeSlot = -1; |
gume | 0:9f5a444886a8 | 106 | } |
gume | 0:9f5a444886a8 | 107 | } |
gume | 0:9f5a444886a8 | 108 | |
gume | 0:9f5a444886a8 | 109 | void SSRadio::receiveSyncFrame(uint8_t *frame, uint8_t size) { |
gume | 0:9f5a444886a8 | 110 | |
gume | 0:9f5a444886a8 | 111 | lastSync = us_ticker_read() / 1000; |
gume | 0:9f5a444886a8 | 112 | |
gume | 0:9f5a444886a8 | 113 | // Find self address to get the sending slot |
gume | 0:9f5a444886a8 | 114 | uint16_t *nodes = (uint16_t*) frame; |
gume | 0:9f5a444886a8 | 115 | sendSlot = -1; |
gume | 0:9f5a444886a8 | 116 | //Serial.println("Sync"); |
gume | 0:9f5a444886a8 | 117 | //Serial.println(size); |
gume | 0:9f5a444886a8 | 118 | uint8_t i = 0; |
gume | 0:9f5a444886a8 | 119 | while ((i < size / 2) && (nodes[i] != 0)) { |
gume | 0:9f5a444886a8 | 120 | //Serial.println(nodes[i]); |
gume | 0:9f5a444886a8 | 121 | if (nodes[i] == myAddress) { |
gume | 0:9f5a444886a8 | 122 | sendSlot = i; |
gume | 0:9f5a444886a8 | 123 | } |
gume | 0:9f5a444886a8 | 124 | i++; |
gume | 0:9f5a444886a8 | 125 | } |
gume | 0:9f5a444886a8 | 126 | if (nodes[i] == 0) { |
gume | 0:9f5a444886a8 | 127 | freeSlot = i; |
gume | 0:9f5a444886a8 | 128 | } |
gume | 0:9f5a444886a8 | 129 | } |
gume | 0:9f5a444886a8 | 130 | |
gume | 0:9f5a444886a8 | 131 | void SSRadio::receiveDataFrame(uint8_t *frame, uint8_t size) { |
gume | 0:9f5a444886a8 | 132 | |
gume | 0:9f5a444886a8 | 133 | Packet *p = (Packet*) frame; |
gume | 0:9f5a444886a8 | 134 | IF_SERIAL_DEBUG(Serial.println(F("Data frame received."))); |
gume | 0:9f5a444886a8 | 135 | if (onReceiveData) { |
gume | 0:9f5a444886a8 | 136 | IF_SERIAL_DEBUG(Serial.println(F("Calling onReceiveData."))); |
gume | 0:9f5a444886a8 | 137 | (*onReceiveData)(p->fields.payload, p->fields.type, size - 4); |
gume | 0:9f5a444886a8 | 138 | } |
gume | 0:9f5a444886a8 | 139 | } |
gume | 0:9f5a444886a8 | 140 | |
gume | 0:9f5a444886a8 | 141 | bool SSRadio::sendData(uint8_t *data, uint16_t type, uint8_t size) { |
gume | 0:9f5a444886a8 | 142 | |
gume | 0:9f5a444886a8 | 143 | if (txQueueLength == QUEUELEN) return false; // Queue is full |
gume | 0:9f5a444886a8 | 144 | |
gume | 0:9f5a444886a8 | 145 | uint8_t *frame = txQueue + txQueueLength * 30; |
gume | 0:9f5a444886a8 | 146 | frame[0] = size; |
gume | 0:9f5a444886a8 | 147 | frame[1] = type & 0xff; // !!! SHOULD be FIXED for extended types |
gume | 0:9f5a444886a8 | 148 | memcpy(frame + 2, data, size); |
gume | 0:9f5a444886a8 | 149 | |
gume | 0:9f5a444886a8 | 150 | txQueueLength ++; |
gume | 0:9f5a444886a8 | 151 | |
gume | 0:9f5a444886a8 | 152 | return true; |
gume | 0:9f5a444886a8 | 153 | } |
gume | 0:9f5a444886a8 | 154 | |
gume | 0:9f5a444886a8 | 155 | bool SSRadio::instantData(uint8_t *data, uint16_t type, uint8_t size) { |
gume | 0:9f5a444886a8 | 156 | |
gume | 0:9f5a444886a8 | 157 | Packet p; |
gume | 0:9f5a444886a8 | 158 | p.fields.address = myAddress; |
gume | 0:9f5a444886a8 | 159 | p.fields.type = type & 0xff; |
gume | 0:9f5a444886a8 | 160 | p.fields.counter = txCounter ++; |
gume | 0:9f5a444886a8 | 161 | memcpy(p.fields.payload, data, size); |
gume | 0:9f5a444886a8 | 162 | |
gume | 0:9f5a444886a8 | 163 | IF_SERIAL_DEBUG(Serial.println(F("Sending data."))); |
gume | 0:9f5a444886a8 | 164 | |
gume | 0:9f5a444886a8 | 165 | rf24.stopListening(); |
gume | 0:9f5a444886a8 | 166 | rf24.setRetries(0, 5); |
gume | 0:9f5a444886a8 | 167 | rf24.openWritingPipe(gatewayAddress); |
gume | 0:9f5a444886a8 | 168 | bool ok = rf24.write(p.raw, size + 4); |
gume | 0:9f5a444886a8 | 169 | rf24.startListening(); |
gume | 0:9f5a444886a8 | 170 | |
gume | 0:9f5a444886a8 | 171 | if (ok) IF_SERIAL_DEBUG(Serial.println("Success.")); |
gume | 0:9f5a444886a8 | 172 | else IF_SERIAL_DEBUG(Serial.println("Fail!")); |
gume | 0:9f5a444886a8 | 173 | |
gume | 0:9f5a444886a8 | 174 | return ok; |
gume | 0:9f5a444886a8 | 175 | } |
gume | 0:9f5a444886a8 | 176 | |
gume | 0:9f5a444886a8 | 177 | void SSRadio::setOnReceiveData(void (*f)(uint8_t *data, uint16_t type, uint8_t len)) { |
gume | 0:9f5a444886a8 | 178 | onReceiveData = f; |
gume | 0:9f5a444886a8 | 179 | } |
gume | 0:9f5a444886a8 | 180 |