firmware/src/rf95/RadioLibInterface.cpp

217 lines
6.8 KiB
C++
Raw Normal View History

#include "RadioLibInterface.h"
2020-04-30 19:37:58 +00:00
#include "MeshTypes.h"
#include "mesh-pb-constants.h"
2020-04-30 23:36:59 +00:00
#include <NodeDB.h> // FIXME, this class shouldn't need to look into nodedb
#include <configuration.h>
2020-04-30 19:37:58 +00:00
#include <pb_decode.h>
#include <pb_encode.h>
// FIXME, we default to 4MHz SPI, SPI mode 0, check if the datasheet says it can really do that
2020-04-29 23:06:23 +00:00
static SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
SPIClass &spi, PhysicalLayer *_iface)
2020-05-01 00:43:29 +00:00
: module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
{
assert(!instance); // We assume only one for now
instance = this;
}
void INTERRUPT_ATTR RadioLibInterface::isrRxLevel0()
{
instance->pending = ISR_RX;
instance->disableInterrupt();
}
void INTERRUPT_ATTR RadioLibInterface::isrTxLevel0()
{
instance->pending = ISR_TX;
instance->disableInterrupt();
}
/** Our ISR code currently needs this to find our active instance
*/
RadioLibInterface *RadioLibInterface::instance;
/**
* Convert our modemConfig enum into wf, sf, etc...
*/
void RadioLibInterface::applyModemConfig()
{
switch (modemConfig) {
2020-05-01 04:42:11 +00:00
case Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range
bw = 125;
cr = 5;
sf = 7;
break;
2020-05-01 04:42:11 +00:00
case Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range
bw = 500;
cr = 5;
sf = 7;
break;
2020-05-01 04:42:11 +00:00
case Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range
bw = 31.25;
cr = 8;
sf = 9;
break;
2020-05-01 04:42:11 +00:00
case Bw125Cr48Sf4096:
bw = 125;
cr = 8;
sf = 12;
break;
default:
assert(0); // Unknown enum
}
}
/// Send a packet (possibly by enquing in a private fifo). This routine will
/// later free() the packet to pool. This routine is not allowed to stall because it is called from
/// bluetooth comms code. If the txmit queue is empty it might return an error
ErrorCode RadioLibInterface::send(MeshPacket *p)
{
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
// To do otherwise would be doubly bad because not only would we drop the packet that was on the way in,
// we almost certainly guarantee no one outside will like the packet we are sending.
if (canSendImmediately()) {
// if the radio is idle, we can send right away
DEBUG_MSG("immediate send on mesh fr=0x%x,to=0x%x,id=%d\n (txGood=%d,rxGood=%d,rxBad=%d)\n", p->from, p->to, p->id,
txGood, rxGood, rxBad);
startSend(p);
return ERRNO_OK;
} else {
DEBUG_MSG("enqueuing packet for send from=0x%x, to=0x%x\n", p->from, p->to);
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
if (res != ERRNO_OK) // we weren't able to queue it, so we must drop it to prevent leaks
packetPool.release(p);
return res;
}
}
bool RadioLibInterface::canSleep()
{
bool res = txQueue.isEmpty();
if (!res) // only print debug messages if we are vetoing sleep
DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", txQueue.isEmpty());
return res;
}
void RadioLibInterface::loop()
{
2020-05-01 05:53:21 +00:00
PendingISR wasPending;
while ((wasPending = pending) != 0) { // atomic read
pending = ISR_NONE; // If the flag was set, it is _guaranteed_ the ISR won't be running, because it masked itself
if (wasPending == ISR_TX)
handleTransmitInterrupt();
else if (wasPending == ISR_RX)
handleReceiveInterrupt();
else
assert(0);
2020-04-30 19:37:58 +00:00
startNextWork();
}
}
void RadioLibInterface::startNextWork()
{
// First send any outgoing packets we have ready
MeshPacket *txp = txQueue.dequeuePtr(0);
if (txp)
startSend(txp);
else {
// Nothing to send, let's switch back to receive mode
startReceive();
}
}
void RadioLibInterface::handleTransmitInterrupt()
{
2020-05-01 05:53:21 +00:00
// DEBUG_MSG("handling lora TX interrupt\n");
assert(sendingPacket); // Were we sending? - FIXME, this was null coming out of light sleep due to RF95 ISR!
2020-04-30 20:50:40 +00:00
completeSending();
}
2020-04-30 20:50:40 +00:00
void RadioLibInterface::completeSending()
{
if (sendingPacket) {
txGood++;
2020-05-01 05:53:21 +00:00
DEBUG_MSG("Completed sending to=0x%x, id=%u\n", sendingPacket->to, sendingPacket->id);
2020-04-30 20:50:40 +00:00
// We are done sending that packet, release it
packetPool.release(sendingPacket);
sendingPacket = NULL;
// DEBUG_MSG("Done with send\n");
}
}
void RadioLibInterface::handleReceiveInterrupt()
{
2020-04-30 20:50:40 +00:00
assert(isReceiving);
isReceiving = false;
2020-04-30 19:37:58 +00:00
// read the number of actually received bytes
2020-05-01 00:43:29 +00:00
size_t length = iface->getPacketLength();
2020-05-01 00:43:29 +00:00
int state = iface->readData(radiobuf, length);
2020-04-30 19:37:58 +00:00
if (state != ERR_NONE) {
DEBUG_MSG("ignoring received packet due to error=%d\n", state);
rxBad++;
2020-04-30 19:37:58 +00:00
} else {
// Skip the 4 headers that are at the beginning of the rxBuf
int32_t payloadLen = length - sizeof(PacketHeader);
const uint8_t *payload = radiobuf + sizeof(PacketHeader);
2020-04-30 23:36:59 +00:00
// check for short packets
if (payloadLen < 0) {
DEBUG_MSG("ignoring received packet too short\n");
rxBad++;
2020-04-30 19:37:58 +00:00
} else {
2020-04-30 23:36:59 +00:00
const PacketHeader *h = (PacketHeader *)radiobuf;
uint8_t ourAddr = nodeDB.getNodeNum();
if (h->to != 255 && h->to != ourAddr) {
DEBUG_MSG("ignoring packet not sent to us\n");
} else {
MeshPacket *mp = packetPool.allocZeroed();
SubPacket *p = &mp->payload;
mp->from = h->from;
mp->to = h->to;
mp->id = h->id;
2020-05-01 02:58:10 +00:00
addReceiveMetadata(mp);
2020-04-30 23:36:59 +00:00
if (!pb_decode_from_bytes(payload, payloadLen, SubPacket_fields, p)) {
DEBUG_MSG("Invalid protobufs in received mesh packet, discarding.\n");
packetPool.release(mp);
// rxBad++; not really a hw error
} else {
// parsing was successful, queue for our recipient
mp->has_payload = true;
2020-05-01 02:58:10 +00:00
rxGood++;
2020-05-01 05:53:21 +00:00
DEBUG_MSG("Lora RX interrupt from=0x%x, id=%u\n", mp->from, mp->id);
2020-04-30 23:36:59 +00:00
deliverToReceiver(mp);
}
}
}
}
}
/** start an immediate transmit */
void RadioLibInterface::startSend(MeshPacket *txp)
{
size_t numbytes = beginSending(txp);
2020-05-01 00:43:29 +00:00
int res = iface->startTransmit(radiobuf, numbytes);
2020-04-30 17:00:40 +00:00
assert(res == ERR_NONE);
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrTxLevel0);
}