mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-25 17:42:48 +00:00
the mountain of changes needed to kinda make tx work compiles.
This commit is contained in:
parent
074ac33b8a
commit
fce31560c6
@ -47,6 +47,9 @@ Needed to be fully functional at least at the same level of the ESP32 boards. At
|
|||||||
|
|
||||||
## Items to be 'feature complete'
|
## Items to be 'feature complete'
|
||||||
|
|
||||||
|
- figure out what the correct current limit should be for the sx1262, currently we just use the default 100
|
||||||
|
- use SX126x::startReceiveDutyCycleAuto to save power by sleeping and briefly waking to check for preamble bits. Change xmit rules to have more preamble bits.
|
||||||
|
- put sx1262 in sleepmode when processor gets shutdown (or rebooted), ideally even for critical faults (to keep power draw low). repurpose deepsleep state for this.
|
||||||
- good power management tips: https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/optimizing-power-on-nrf52-designs
|
- good power management tips: https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/optimizing-power-on-nrf52-designs
|
||||||
- call PMU set_ADC_CONV(0) during sleep, to stop reading PMU adcs and decrease current draw
|
- call PMU set_ADC_CONV(0) during sleep, to stop reading PMU adcs and decrease current draw
|
||||||
- do final power measurements
|
- do final power measurements
|
||||||
|
@ -7,11 +7,7 @@
|
|||||||
|
|
||||||
#ifdef RF95_IRQ_GPIO
|
#ifdef RF95_IRQ_GPIO
|
||||||
|
|
||||||
/// A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need
|
CustomRF95::CustomRF95() : RH_RF95(NSS_GPIO, RF95_IRQ_GPIO) {}
|
||||||
#define MAX_RHPACKETLEN 251
|
|
||||||
static uint8_t radiobuf[MAX_RHPACKETLEN];
|
|
||||||
|
|
||||||
CustomRF95::CustomRF95() : RH_RF95(NSS_GPIO, RF95_IRQ_GPIO), txQueue(MAX_TX_QUEUE) {}
|
|
||||||
|
|
||||||
bool CustomRF95::canSleep()
|
bool CustomRF95::canSleep()
|
||||||
{
|
{
|
||||||
@ -52,13 +48,11 @@ ErrorCode CustomRF95::send(MeshPacket *p)
|
|||||||
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
|
// 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,
|
// 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.
|
// we almost certainly guarantee no one outside will like the packet we are sending.
|
||||||
if (_mode == RHModeIdle || (_mode == RHModeRx && !isReceiving())) {
|
if (_mode == RHModeIdle || !isReceiving()) {
|
||||||
// if the radio is idle, we can send right away
|
// 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,
|
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());
|
txGood(), rxGood(), rxBad());
|
||||||
|
|
||||||
waitPacketSent(); // Make sure we dont interrupt an outgoing message
|
|
||||||
|
|
||||||
if (!waitCAD())
|
if (!waitCAD())
|
||||||
return false; // Check channel activity
|
return false; // Check channel activity
|
||||||
|
|
||||||
@ -159,29 +153,20 @@ void CustomRF95::handleIdleISR()
|
|||||||
/// This routine might be called either from user space or ISR
|
/// This routine might be called either from user space or ISR
|
||||||
void CustomRF95::startSend(MeshPacket *txp)
|
void CustomRF95::startSend(MeshPacket *txp)
|
||||||
{
|
{
|
||||||
assert(!sendingPacket);
|
size_t numbytes = beginSending(txp);
|
||||||
|
|
||||||
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
|
||||||
assert(txp->has_payload);
|
|
||||||
|
|
||||||
lastTxStart = millis();
|
|
||||||
|
|
||||||
size_t numbytes = pb_encode_to_bytes(radiobuf, sizeof(radiobuf), SubPacket_fields, &txp->payload);
|
|
||||||
|
|
||||||
sendingPacket = txp;
|
|
||||||
|
|
||||||
setHeaderTo(txp->to);
|
setHeaderTo(txp->to);
|
||||||
setHeaderId(txp->id);
|
setHeaderId(txp->id);
|
||||||
|
|
||||||
// if the sender nodenum is zero, that means uninitialized
|
// if the sender nodenum is zero, that means uninitialized
|
||||||
assert(txp->from);
|
|
||||||
setHeaderFrom(txp->from); // We must do this before each send, because we might have just changed our nodenum
|
setHeaderFrom(txp->from); // We must do this before each send, because we might have just changed our nodenum
|
||||||
|
|
||||||
assert(numbytes <= 251); // Make sure we don't overflow the tiny max packet size
|
assert(numbytes <= 251); // Make sure we don't overflow the tiny max packet size
|
||||||
|
|
||||||
// uint32_t start = millis(); // FIXME, store this in the class
|
// uint32_t start = millis(); // FIXME, store this in the class
|
||||||
|
|
||||||
int res = RH_RF95::send(radiobuf, numbytes);
|
// This legacy implementation doesn't use our inserted packet header
|
||||||
|
int res = RH_RF95::send(radiobuf + sizeof(PacketHeader), numbytes - sizeof(PacketHeader));
|
||||||
assert(res);
|
assert(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +13,6 @@ class CustomRF95 : public RH_RF95, public RadioInterface
|
|||||||
{
|
{
|
||||||
friend class MeshRadio; // for debugging we let that class touch pool
|
friend class MeshRadio; // for debugging we let that class touch pool
|
||||||
|
|
||||||
PointerQueue<MeshPacket> txQueue;
|
|
||||||
|
|
||||||
uint32_t lastTxStart = 0L;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** pool is the pool we will alloc our rx packets from
|
/** pool is the pool we will alloc our rx packets from
|
||||||
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
|
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
|
|
||||||
RadioInterface::RadioInterface() {}
|
RadioInterface::RadioInterface() : txQueue(MAX_TX_QUEUE)
|
||||||
|
{
|
||||||
|
assert(sizeof(PacketHeader) == 4); // make sure the compiler did what we expected
|
||||||
|
}
|
||||||
|
|
||||||
ErrorCode SimRadio::send(MeshPacket *p)
|
ErrorCode SimRadio::send(MeshPacket *p)
|
||||||
{
|
{
|
||||||
@ -21,3 +24,32 @@ void RadioInterface::deliverToReceiver(MeshPacket *p)
|
|||||||
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
|
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of payload bytes to send
|
||||||
|
*/
|
||||||
|
size_t RadioInterface::beginSending(MeshPacket *p)
|
||||||
|
{
|
||||||
|
assert(!sendingPacket);
|
||||||
|
|
||||||
|
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
||||||
|
assert(p->has_payload);
|
||||||
|
|
||||||
|
lastTxStart = millis();
|
||||||
|
|
||||||
|
PacketHeader *h = (PacketHeader *)radiobuf;
|
||||||
|
|
||||||
|
h->from = p->from;
|
||||||
|
h->to = p->to;
|
||||||
|
h->flags = 0;
|
||||||
|
h->id = p->id;
|
||||||
|
|
||||||
|
// if the sender nodenum is zero, that means uninitialized
|
||||||
|
assert(h->from);
|
||||||
|
|
||||||
|
size_t numbytes = pb_encode_to_bytes(radiobuf + sizeof(PacketHeader), sizeof(radiobuf), SubPacket_fields, &p->payload) + sizeof(PacketHeader);
|
||||||
|
|
||||||
|
assert(numbytes <= MAX_RHPACKETLEN);
|
||||||
|
|
||||||
|
sendingPacket = p;
|
||||||
|
return numbytes;
|
||||||
|
}
|
@ -8,6 +8,18 @@
|
|||||||
|
|
||||||
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
||||||
|
|
||||||
|
#define MAX_RHPACKETLEN 256
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This structure has to exactly match the wire layout when sent over the radio link. Used to keep compatibility
|
||||||
|
* wtih the old radiohead implementation.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t to, from, id, flags;
|
||||||
|
} PacketHeader;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic operations all radio chipsets must implement.
|
* Basic operations all radio chipsets must implement.
|
||||||
*
|
*
|
||||||
@ -20,6 +32,13 @@ class RadioInterface
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
MeshPacket *sendingPacket = NULL; // The packet we are currently sending
|
MeshPacket *sendingPacket = NULL; // The packet we are currently sending
|
||||||
|
PointerQueue<MeshPacket> txQueue;
|
||||||
|
uint32_t lastTxStart = 0L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need
|
||||||
|
* */
|
||||||
|
uint8_t radiobuf[MAX_RHPACKETLEN];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueue a received packet for the registered receiver
|
* Enqueue a received packet for the registered receiver
|
||||||
@ -82,6 +101,14 @@ class RadioInterface
|
|||||||
/// Make sure the Driver is properly configured before calling init().
|
/// Make sure the Driver is properly configured before calling init().
|
||||||
/// \return true if initialisation succeeded.
|
/// \return true if initialisation succeeded.
|
||||||
virtual bool reconfigure() = 0;
|
virtual bool reconfigure() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/***
|
||||||
|
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of bytes to send (including the PacketHeader & payload).
|
||||||
|
*
|
||||||
|
* Used as the first step of
|
||||||
|
*/
|
||||||
|
size_t beginSending(MeshPacket *p);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SimRadio : public RadioInterface
|
class SimRadio : public RadioInterface
|
||||||
|
@ -8,8 +8,26 @@ RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq
|
|||||||
SPIClass &spi, PhysicalLayer *_iface)
|
SPIClass &spi, PhysicalLayer *_iface)
|
||||||
: module(cs, irq, rst, busy, spi, spiSettings), iface(*_iface)
|
: 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...
|
* Convert our modemConfig enum into wf, sf, etc...
|
||||||
*/
|
*/
|
||||||
@ -41,9 +59,144 @@ void RadioLibInterface::applyModemConfig()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)
|
ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||||
{
|
{
|
||||||
return ERR_NONE;
|
// 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, -1,
|
||||||
|
-1, -1);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RadioLibInterface::loop()
|
||||||
|
{
|
||||||
|
PendingISR wasPending = pending; // atomic read
|
||||||
|
if (wasPending) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// FIXME - RH_RF95::setModeRx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RadioLibInterface::handleTransmitInterrupt()
|
||||||
|
{
|
||||||
|
assert(sendingPacket); // Were we sending?
|
||||||
|
|
||||||
|
// FIXME - check result code from ISR
|
||||||
|
|
||||||
|
// We are done sending that packet, release it
|
||||||
|
packetPool.release(sendingPacket);
|
||||||
|
sendingPacket = NULL;
|
||||||
|
// DEBUG_MSG("Done with send\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RadioLibInterface::handleReceiveInterrupt()
|
||||||
|
{
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// After doing standard behavior, check to see if a new packet arrived or one was sent and start a new send or receive as
|
||||||
|
// necessary
|
||||||
|
void CustomRF95::handleInterrupt()
|
||||||
|
{
|
||||||
|
RH_RF95::handleInterrupt();
|
||||||
|
enableInterrupt(); // Let ISR run again
|
||||||
|
|
||||||
|
if (_mode == RHModeIdle) // We are now done sending or receiving
|
||||||
|
{
|
||||||
|
|
||||||
|
// If we just finished receiving a packet, forward it into a queue
|
||||||
|
if (_rxBufValid) {
|
||||||
|
// We received a packet
|
||||||
|
|
||||||
|
// Skip the 4 headers that are at the beginning of the rxBuf
|
||||||
|
size_t payloadLen = _bufLen - RH_RF95_HEADER_LEN;
|
||||||
|
uint8_t *payload = _buf + RH_RF95_HEADER_LEN;
|
||||||
|
|
||||||
|
// FIXME - throws exception if called in ISR context: frequencyError() - probably the floating point math
|
||||||
|
int32_t freqerr = -1, snr = lastSNR();
|
||||||
|
// DEBUG_MSG("Received packet from mesh src=0x%x,dest=0x%x,id=%d,len=%d rxGood=%d,rxBad=%d,freqErr=%d,snr=%d\n",
|
||||||
|
// srcaddr, destaddr, id, rxlen, rf95.rxGood(), rf95.rxBad(), freqerr, snr);
|
||||||
|
|
||||||
|
MeshPacket *mp = packetPool.allocZeroed();
|
||||||
|
|
||||||
|
SubPacket *p = &mp->payload;
|
||||||
|
|
||||||
|
mp->from = _rxHeaderFrom;
|
||||||
|
mp->to = _rxHeaderTo;
|
||||||
|
mp->id = _rxHeaderId;
|
||||||
|
|
||||||
|
//_rxHeaderId = _buf[2];
|
||||||
|
//_rxHeaderFlags = _buf[3];
|
||||||
|
|
||||||
|
// If we already have an entry in the DB for this nodenum, goahead and hide the snr/freqerr info there.
|
||||||
|
// Note: we can't create it at this point, because it might be a bogus User node allocation. But odds are we will
|
||||||
|
// already have a record we can hide this debugging info in.
|
||||||
|
NodeInfo *info = nodeDB.getNode(mp->from);
|
||||||
|
if (info) {
|
||||||
|
info->snr = snr;
|
||||||
|
info->frequency_error = freqerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pb_decode_from_bytes(payload, payloadLen, SubPacket_fields, p)) {
|
||||||
|
packetPool.release(mp);
|
||||||
|
} else {
|
||||||
|
// parsing was successful, queue for our recipient
|
||||||
|
mp->has_payload = true;
|
||||||
|
|
||||||
|
deliverToReceiver(mp);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearRxBuf(); // This message accepted and cleared
|
||||||
|
}
|
||||||
|
|
||||||
|
handleIdleISR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** start an immediate transmit */
|
||||||
|
void RadioLibInterface::startSend(MeshPacket *txp)
|
||||||
|
{
|
||||||
|
size_t numbytes = beginSending(txp);
|
||||||
|
|
||||||
|
int res = iface.startTransmit(radiobuf, numbytes);
|
||||||
|
assert(res);
|
||||||
|
|
||||||
|
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||||
|
enableInterrupt(isrTxLevel0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,39 +206,178 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
|||||||
// include the library
|
// include the library
|
||||||
|
|
||||||
|
|
||||||
|
// save transmission state between loops
|
||||||
|
int transmissionState = ERR_NONE;
|
||||||
|
|
||||||
void loop() {
|
void setup() {
|
||||||
Serial.print(F("[SX1262] Transmitting packet ... "));
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// initialize SX1262 with default settings
|
||||||
|
Serial.print(F("[SX1262] Initializing ... "));
|
||||||
|
// carrier frequency: 434.0 MHz
|
||||||
|
// bandwidth: 125.0 kHz
|
||||||
|
// spreading factor: 9
|
||||||
|
// coding rate: 7
|
||||||
|
// sync word: 0x12 (private network)
|
||||||
|
// output power: 14 dBm
|
||||||
|
// current limit: 60 mA
|
||||||
|
// preamble length: 8 symbols
|
||||||
|
// TCXO voltage: 1.6 V (set to 0 to not use TCXO)
|
||||||
|
// regulator: DC-DC (set to true to use LDO)
|
||||||
|
// CRC: enabled
|
||||||
|
int state = lora.begin();
|
||||||
|
if (state == ERR_NONE) {
|
||||||
|
Serial.println(F("success!"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("failed, code "));
|
||||||
|
Serial.println(state);
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the function that will be called
|
||||||
|
// when packet transmission is finished
|
||||||
|
lora.setDio1Action(setFlag);
|
||||||
|
|
||||||
|
// start transmitting the first packet
|
||||||
|
Serial.print(F("[SX1262] Sending first packet ... "));
|
||||||
|
|
||||||
// you can transmit C-string or Arduino string up to
|
// you can transmit C-string or Arduino string up to
|
||||||
// 256 characters long
|
// 256 characters long
|
||||||
// NOTE: transmit() is a blocking method!
|
transmissionState = lora.startTransmit("Hello World!");
|
||||||
// See example SX126x_Transmit_Interrupt for details
|
|
||||||
// on non-blocking transmission method.
|
|
||||||
int state = lora.transmit("Hello World!");
|
|
||||||
|
|
||||||
// you can also transmit byte array up to 256 bytes long
|
// you can also transmit byte array up to 256 bytes long
|
||||||
|
|
||||||
byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF};
|
byte byteArr[] = {0x01, 0x23, 0x45, 0x67,
|
||||||
int state = lora.transmit(byteArr, 8);
|
0x89, 0xAB, 0xCD, 0xEF};
|
||||||
|
state = lora.startTransmit(byteArr, 8);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// flag to indicate that a packet was sent
|
||||||
|
volatile bool transmittedFlag = false;
|
||||||
|
|
||||||
|
// disable interrupt when it's not needed
|
||||||
|
volatile bool enableInterrupt = true;
|
||||||
|
|
||||||
|
// this function is called when a complete packet
|
||||||
|
// is transmitted by the module
|
||||||
|
// IMPORTANT: this function MUST be 'void' type
|
||||||
|
// and MUST NOT have any arguments!
|
||||||
|
void setFlag(void)
|
||||||
|
{
|
||||||
|
// check if the interrupt is enabled
|
||||||
|
if (!enableInterrupt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we sent a packet, set the flag
|
||||||
|
transmittedFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// check if the previous transmission finished
|
||||||
|
if (transmittedFlag) {
|
||||||
|
// disable the interrupt service routine while
|
||||||
|
// processing the data
|
||||||
|
enableInterrupt = false;
|
||||||
|
|
||||||
|
// reset flag
|
||||||
|
transmittedFlag = false;
|
||||||
|
|
||||||
|
if (transmissionState == ERR_NONE) {
|
||||||
|
// packet was successfully sent
|
||||||
|
Serial.println(F("transmission finished!"));
|
||||||
|
|
||||||
|
// NOTE: when using interrupt-driven transmit method,
|
||||||
|
// it is not possible to automatically measure
|
||||||
|
// transmission data rate using getDataRate()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Serial.print(F("failed, code "));
|
||||||
|
Serial.println(transmissionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait a second before transmitting again
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// send another one
|
||||||
|
Serial.print(F("[SX1262] Sending another packet ... "));
|
||||||
|
|
||||||
|
// you can transmit C-string or Arduino string up to
|
||||||
|
// 256 characters long
|
||||||
|
transmissionState = lora.startTransmit("Hello World!");
|
||||||
|
|
||||||
|
// you can also transmit byte array up to 256 bytes long
|
||||||
|
|
||||||
|
byte byteArr[] = {0x01, 0x23, 0x45, 0x67,
|
||||||
|
0x89, 0xAB, 0xCD, 0xEF};
|
||||||
|
int state = lora.startTransmit(byteArr, 8);
|
||||||
|
|
||||||
|
|
||||||
|
// we're ready to send more packets,
|
||||||
|
// enable interrupt service routine
|
||||||
|
enableInterrupt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function is called when a complete packet
|
||||||
|
// is received by the module
|
||||||
|
// IMPORTANT: this function MUST be 'void' type
|
||||||
|
// and MUST NOT have any arguments!
|
||||||
|
void setFlag(void)
|
||||||
|
{
|
||||||
|
// check if the interrupt is enabled
|
||||||
|
if (!enableInterrupt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we got a packet, set the flag
|
||||||
|
receivedFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// check if the flag is set
|
||||||
|
if (receivedFlag) {
|
||||||
|
// disable the interrupt service routine while
|
||||||
|
// processing the data
|
||||||
|
enableInterrupt = false;
|
||||||
|
|
||||||
|
// reset flag
|
||||||
|
receivedFlag = false;
|
||||||
|
|
||||||
|
// you can read received data as an Arduino String
|
||||||
|
String str;
|
||||||
|
int state = lora.readData(str);
|
||||||
|
|
||||||
|
// you can also read received data as byte array
|
||||||
|
|
||||||
|
byte byteArr[8];
|
||||||
|
int state = lora.readData(byteArr, 8);
|
||||||
|
|
||||||
|
|
||||||
if (state == ERR_NONE) {
|
if (state == ERR_NONE) {
|
||||||
// the packet was successfully transmitted
|
// packet was successfully received
|
||||||
Serial.println(F("success!"));
|
Serial.println(F("[SX1262] Received packet!"));
|
||||||
|
|
||||||
// print measured data rate
|
// print data of the packet
|
||||||
Serial.print(F("[SX1262] Datarate:\t"));
|
Serial.print(F("[SX1262] Data:\t\t"));
|
||||||
Serial.print(lora.getDataRate());
|
Serial.println(str);
|
||||||
Serial.println(F(" bps"));
|
|
||||||
|
|
||||||
} else if (state == ERR_PACKET_TOO_LONG) {
|
// print RSSI (Received Signal Strength Indicator)
|
||||||
// the supplied packet was longer than 256 bytes
|
Serial.print(F("[SX1262] RSSI:\t\t"));
|
||||||
Serial.println(F("too long!"));
|
Serial.print(lora.getRSSI());
|
||||||
|
Serial.println(F(" dBm"));
|
||||||
|
|
||||||
} else if (state == ERR_TX_TIMEOUT) {
|
// print SNR (Signal-to-Noise Ratio)
|
||||||
// timeout occured while transmitting packet
|
Serial.print(F("[SX1262] SNR:\t\t"));
|
||||||
Serial.println(F("timeout!"));
|
Serial.print(lora.getSNR());
|
||||||
|
Serial.println(F(" dB"));
|
||||||
|
|
||||||
|
} else if (state == ERR_CRC_MISMATCH) {
|
||||||
|
// packet was received, but is malformed
|
||||||
|
Serial.println(F("CRC error!"));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// some other error occurred
|
// some other error occurred
|
||||||
@ -93,7 +385,12 @@ if (state == ERR_NONE) {
|
|||||||
Serial.println(state);
|
Serial.println(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for a second before transmitting again
|
// put module back to listen mode
|
||||||
delay(1000);
|
lora.startReceive();
|
||||||
|
|
||||||
|
// we're ready to receive more packets,
|
||||||
|
// enable interrupt service routine
|
||||||
|
enableInterrupt = true;
|
||||||
}
|
}
|
||||||
*/
|
}
|
||||||
|
*/
|
@ -4,8 +4,30 @@
|
|||||||
|
|
||||||
#include <RadioLib.h>
|
#include <RadioLib.h>
|
||||||
|
|
||||||
|
// ESP32 has special rules about ISR code
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#define INTERRUPT_ATTR IRAM_ATTR
|
||||||
|
#else
|
||||||
|
#define INTERRUPT_ATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
class RadioLibInterface : public RadioInterface
|
class RadioLibInterface : public RadioInterface
|
||||||
{
|
{
|
||||||
|
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What sort of interrupt do we expect our helper thread to now handle */
|
||||||
|
volatile PendingISR pending;
|
||||||
|
|
||||||
|
/** Our ISR code currently needs this to find our active instance
|
||||||
|
*/
|
||||||
|
static RadioLibInterface *instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw ISR handler that just calls our polymorphic method
|
||||||
|
*/
|
||||||
|
static void isrRxLevel0(), isrTxLevel0();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float bw = 125;
|
float bw = 125;
|
||||||
uint8_t sf = 9;
|
uint8_t sf = 9;
|
||||||
@ -27,6 +49,16 @@ class RadioLibInterface : public RadioInterface
|
|||||||
*/
|
*/
|
||||||
PhysicalLayer &iface;
|
PhysicalLayer &iface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Glue functions called from ISR land
|
||||||
|
*/
|
||||||
|
virtual void disableInterrupt() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable a particular ISR callback glue function
|
||||||
|
*/
|
||||||
|
virtual void enableInterrupt(void (*)()) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi,
|
RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi,
|
||||||
PhysicalLayer *iface);
|
PhysicalLayer *iface);
|
||||||
@ -51,9 +83,20 @@ class RadioLibInterface : public RadioInterface
|
|||||||
/// \return true if initialisation succeeded.
|
/// \return true if initialisation succeeded.
|
||||||
virtual bool init() { return true; }
|
virtual bool init() { return true; }
|
||||||
|
|
||||||
|
virtual void loop(); // Idle processing
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Convert our modemConfig enum into wf, sf, etc...
|
* Convert our modemConfig enum into wf, sf, etc...
|
||||||
*/
|
*/
|
||||||
void applyModemConfig();
|
void applyModemConfig();
|
||||||
|
|
||||||
|
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||||
|
virtual bool canSendImmediately() = 0;
|
||||||
|
|
||||||
|
/** start an immediate transmit */
|
||||||
|
void startSend(MeshPacket *txp);
|
||||||
|
|
||||||
|
void handleTransmitInterrupt();
|
||||||
|
void handleReceiveInterrupt();
|
||||||
};
|
};
|
@ -27,6 +27,9 @@ bool SX1262Interface::init()
|
|||||||
int res = lora.begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO);
|
int res = lora.begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO);
|
||||||
DEBUG_MSG("LORA init result %d\n", res);
|
DEBUG_MSG("LORA init result %d\n", res);
|
||||||
|
|
||||||
|
if (res != ERR_NONE)
|
||||||
|
res = lora.setCRC(SX126X_LORA_CRC_ON);
|
||||||
|
|
||||||
return res == ERR_NONE;
|
return res == ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,3 +72,19 @@ bool SX1262Interface::reconfigure()
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||||
|
bool SX1262Interface::canSendImmediately()
|
||||||
|
{
|
||||||
|
return true; // FIXME
|
||||||
|
#if 0
|
||||||
|
// 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 (_mode == RHModeIdle || isReceiving()) {
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
@ -18,4 +18,18 @@ class SX1262Interface : public RadioLibInterface
|
|||||||
/// Make sure the Driver is properly configured before calling init().
|
/// Make sure the Driver is properly configured before calling init().
|
||||||
/// \return true if initialisation succeeded.
|
/// \return true if initialisation succeeded.
|
||||||
virtual bool reconfigure();
|
virtual bool reconfigure();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Glue functions called from ISR land
|
||||||
|
*/
|
||||||
|
virtual void INTERRUPT_ATTR disableInterrupt() { lora.clearDio1Action(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable a particular ISR callback glue function
|
||||||
|
*/
|
||||||
|
virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); }
|
||||||
|
|
||||||
|
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||||
|
virtual bool canSendImmediately();
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user