Remove dependency on arduino-LoRa library, use RadioLib only (#42)

- Remove dependency on arduino-LoRa library, use RadioLib only
- Run radio TX on the same task where RX is running
- Refactor for better readability
platformio 1.0.2
sh123 2022-12-03 15:43:37 +02:00 zatwierdzone przez GitHub
rodzic aba3fd1645
commit 8a25bf13cb
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
8 zmienionych plików z 192 dodań i 371 usunięć

Wyświetl plik

@ -24,9 +24,7 @@ Modules, which are used by users and known to work
# Dependencies # Dependencies
Install via libraries: Install via libraries:
- Arduino ESP32 library: https://github.com/espressif/arduino-esp32 - Arduino ESP32 library: https://github.com/espressif/arduino-esp32
- LoRa library - RadioLib library: https://github.com/jgromes/RadioLib
- RadioLib library (⚠use github master version): https://github.com/jgromes/RadioLib
- or LoRa Arduino library: https://github.com/sandeepmistry/arduino-LoRa
- Arduino Timer library: https://github.com/contrem/arduino-timer - Arduino Timer library: https://github.com/contrem/arduino-timer
- CircularBuffer library: https://github.com/rlogiacco/CircularBuffer - CircularBuffer library: https://github.com/rlogiacco/CircularBuffer
- DebugLog library: https://github.com/hideakitai/DebugLog - DebugLog library: https://github.com/hideakitai/DebugLog

Wyświetl plik

@ -1,6 +1,3 @@
// comment out for arduino-Lora usage
#define USE_RADIOLIB
// Uncomment for SX126X module usage // Uncomment for SX126X module usage
#define USE_SX126X #define USE_SX126X
@ -50,10 +47,8 @@
// CAD and ISR usage selection // CAD and ISR usage selection
#ifdef USE_SX126X #ifdef USE_SX126X
#define CFG_LORA_USE_ISR false // reading in ISR does not work on sx126x
#define CFG_LORA_USE_CAD true // do not transmit if channel is busy #define CFG_LORA_USE_CAD true // do not transmit if channel is busy
#else #else
#define CFG_LORA_USE_ISR true // true - read incoming data in ISR, false - do not read in ISR
#define CFG_LORA_USE_CAD true // set to true to utilize carrier detection #define CFG_LORA_USE_CAD true // set to true to utilize carrier detection
#endif #endif

Wyświetl plik

@ -48,7 +48,6 @@ void initializeConfig(LoraPrs::Config &cfg) {
cfg.LoraPinB = CFG_LORA_PIN_B; // (sx127x - dio1, sx126x/sx128x - busy) cfg.LoraPinB = CFG_LORA_PIN_B; // (sx127x - dio1, sx126x/sx128x - busy)
cfg.LoraPinSwitchRx = CFG_LORA_PIN_RXEN; // (sx127x - unused, sx126x - RXEN pin number) cfg.LoraPinSwitchRx = CFG_LORA_PIN_RXEN; // (sx127x - unused, sx126x - RXEN pin number)
cfg.LoraPinSwitchTx = CFG_LORA_PIN_TXEN; // (sx127x - unused, sx126x - TXEN pin number) cfg.LoraPinSwitchTx = CFG_LORA_PIN_TXEN; // (sx127x - unused, sx126x - TXEN pin number)
cfg.LoraUseIsr = CFG_LORA_USE_ISR; // set to true for incoming packet ISR usage (stream mode, e.g. speech)
cfg.LoraUseCad = CFG_LORA_USE_CAD; // carrier detect cfg.LoraUseCad = CFG_LORA_USE_CAD; // carrier detect
// aprs configuration // aprs configuration

Wyświetl plik

@ -2,10 +2,6 @@
namespace Kiss { namespace Kiss {
CircularBuffer<uint8_t, Processor::CfgSerialToRigQueueSize> Processor::serialToRigQueue_;
CircularBuffer<uint8_t, Processor::CfgRigToSerialQueueSize> Processor::rigToSerialQueue_;
CircularBuffer<uint8_t, Processor::CfgRigToSerialQueueSize> Processor::rigToSerialQueueIndex_;
Processor::Processor() Processor::Processor()
: disableKiss_(false) : disableKiss_(false)
, isRawIdle_(true) , isRawIdle_(true)
@ -52,7 +48,7 @@ void Processor::sendRigToSerial(Cmd cmd, const byte *packet, int packetLength) {
} }
} }
void ICACHE_RAM_ATTR Processor::queueRigToSerialIsr(Cmd cmd, const byte *packet, int packetLength) { void Processor::queueRigToSerial(Cmd cmd, const byte *packet, int packetLength) {
if (!rigToSerialQueueIndex_.unshift(packetLength)) { if (!rigToSerialQueueIndex_.unshift(packetLength)) {
LOG_WARN("Rig to serial queue is full!"); LOG_WARN("Rig to serial queue is full!");
return; return;
@ -299,7 +295,6 @@ bool Processor::receiveByteKiss(byte rxByte)
} }
bool Processor::receiveByte(byte rxByte) { bool Processor::receiveByte(byte rxByte) {
if (disableKiss_) { if (disableKiss_) {
return receiveByteRaw(rxByte); return receiveByteRaw(rxByte);
} }

Wyświetl plik

@ -55,8 +55,7 @@ public:
Processor(); Processor();
void sendRigToSerial(Cmd cmd, const byte *packet, int packetLength); void sendRigToSerial(Cmd cmd, const byte *packet, int packetLength);
static void ICACHE_RAM_ATTR queueRigToSerialIsr(Cmd cmd, const byte *packet, int packetLength); void queueRigToSerial(Cmd cmd, const byte *packet, int packetLength);
void queueSerialToRig(Cmd cmd, const byte *packet, int packetLength); void queueSerialToRig(Cmd cmd, const byte *packet, int packetLength);
bool processRigToSerial(); bool processRigToSerial();
@ -93,9 +92,9 @@ private:
DataType dataType_; DataType dataType_;
std::vector<byte> cmdBuffer_; std::vector<byte> cmdBuffer_;
static CircularBuffer<uint8_t, CfgSerialToRigQueueSize> serialToRigQueue_; CircularBuffer<uint8_t, CfgSerialToRigQueueSize> serialToRigQueue_;
static CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueue_; CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueue_;
static CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueueIndex_; CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueueIndex_;
}; };
} // Kiss } // Kiss

Wyświetl plik

@ -29,7 +29,6 @@ struct Config
byte LoraPinB; // (sx127x - dio1, sx126x/sx128x - busy) byte LoraPinB; // (sx127x - dio1, sx126x/sx128x - busy)
byte LoraPinSwitchRx; // (sx127x - unused, sx126x - RXEN pin number) byte LoraPinSwitchRx; // (sx127x - unused, sx126x - RXEN pin number)
byte LoraPinSwitchTx; // (sx127x - unused, sx126x - TXEN pin number) byte LoraPinSwitchTx; // (sx127x - unused, sx126x - TXEN pin number)
bool LoraUseIsr; // true to use interrupts, false for fallback polling, e.g. if Dio0 is not connected
bool LoraUseCad; // use carrier detect before transmitting bool LoraUseCad; // use carrier detect before transmitting
// usb // usb

Wyświetl plik

@ -2,38 +2,29 @@
namespace LoraPrs { namespace LoraPrs {
byte Service::rxBuf_[CfgMaxPacketSize]; TaskHandle_t Service::rigTaskHandle_;
volatile bool Service::rigIsRxActive_ = false;
#ifdef USE_RADIOLIB bool Service::rigIsRxIsrEnabled_ = true;
#pragma message("Using RadioLib")
TaskHandle_t Service::rxTaskHandle_;
volatile bool Service::loraDataAvailable_ = false;
bool Service::interruptEnabled_ = true;
std::shared_ptr<MODULE_NAME> Service::radio_;
#else
#pragma message("Using arduino-LoRa")
#endif
Service::Service() Service::Service()
: Kiss::Processor() : Kiss::Processor()
, csmaP_(CfgCsmaPersistence) , csmaP_(CfgCsmaPersistence)
, csmaSlotTime_(CfgCsmaSlotTimeMs) , csmaSlotTime_(CfgCsmaSlotTimeMs)
, csmaSlotTimePrev_(0) , csmaSlotTimePrev_(0)
, rigCurrentTxPacketSize_(0)
, serialBt_() , serialBt_()
, serialBLE_() , serialBLE_()
, kissServer_(new WiFiServer(CfgKissPort)) , kissServer_(new WiFiServer(CfgKissPort))
, isKissConn_(false) , isKissClientConnected(false)
{ {
#ifdef USE_RADIOLIB rigIsRxIsrEnabled_ = true;
interruptEnabled_ = true; rigIsRxActive_ = false;
loraDataAvailable_ = false;
#endif
} }
void Service::setup(const Config &conf) void Service::setup(const Config &conf)
{ {
config_ = conf; config_ = conf;
previousBeaconMs_ = 0; beaconLastTimestampMs_ = 0;
disableKiss_ = conf.EnableTextPackets; disableKiss_ = conf.EnableTextPackets;
LOG_SET_OPTION(false, false, true); // disable file, line, enable func LOG_SET_OPTION(false, false, true); // disable file, line, enable func
@ -54,8 +45,8 @@ void Service::setup(const Config &conf)
} }
// APRS-IS loging callsign validity // APRS-IS loging callsign validity
ownCallsign_ = AX25::Callsign(config_.AprsLogin); aprsMyCallsign_ = AX25::Callsign(config_.AprsLogin);
if (!ownCallsign_.IsValid()) { if (!aprsMyCallsign_.IsValid()) {
LOG_ERROR("Own callsign", config_.AprsLogin, "is not valid"); LOG_ERROR("Own callsign", config_.AprsLogin, "is not valid");
} }
@ -68,15 +59,11 @@ void Service::setup(const Config &conf)
aprsLoginCommand_ += String("\n"); aprsLoginCommand_ += String("\n");
// peripherals, LoRa // peripherals, LoRa
setupLora(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf, setupRig(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf,
config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, config_.LoraCrc, config_.LoraExplicit); config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, config_.LoraCrc, config_.LoraExplicit);
#ifdef USE_RADIOLIB // start radio task
if (!config_.LoraUseIsr) { xTaskCreate(rigTask, "rigTask", 4096, this, 5, &rigTaskHandle_);
LOG_INFO("Reading data on separate task");
xTaskCreate(processIncomingDataTask, "processIncomingDataTask", 10000, NULL, 1, &rxTaskHandle_);
}
#endif
// peripherls, WiFi // peripherls, WiFi
if (needsWifi()) { if (needsWifi()) {
@ -102,11 +89,6 @@ void Service::setup(const Config &conf)
void Service::printConfig() { void Service::printConfig() {
LOG_INFO("Current mode:", config_.IsClientMode ? "NORMAL" : "APRS-IS iGate"); LOG_INFO("Current mode:", config_.IsClientMode ? "NORMAL" : "APRS-IS iGate");
#ifdef USE_RADIOLIB
LOG_INFO("Built with RadioLib library");
#else
LOG_INFO("Built with arduino-LoRa library");
#endif
LOG_INFO(disableKiss_ ? "Using TNC2 text mode" : "Using TNC KISS and AX.25 mode"); LOG_INFO(disableKiss_ ? "Using TNC2 text mode" : "Using TNC KISS and AX.25 mode");
LOG_INFO("UsbSerialEnable:", config_.UsbSerialEnable ? "yes" : "no"); LOG_INFO("UsbSerialEnable:", config_.UsbSerialEnable ? "yes" : "no");
if (!config_.IsClientMode) { if (!config_.IsClientMode) {
@ -184,20 +166,20 @@ bool Service::reconnectAprsis()
{ {
LOG_INFO("APRSIS connecting to", config_.AprsHost); LOG_INFO("APRSIS connecting to", config_.AprsHost);
if (!aprsisConn_.connect(config_.AprsHost.c_str(), config_.AprsPort)) { if (!aprsisConnection_.connect(config_.AprsHost.c_str(), config_.AprsPort)) {
LOG_ERROR("Failed to connect to", config_.AprsHost, ":", config_.AprsPort); LOG_ERROR("Failed to connect to", config_.AprsHost, ":", config_.AprsPort);
return false; return false;
} }
LOG_INFO("APRSIS connected"); LOG_INFO("APRSIS connected");
aprsisConn_.print(aprsLoginCommand_); aprsisConnection_.print(aprsLoginCommand_);
LOG_INFO("APRSIS logged in"); LOG_INFO("APRSIS logged in");
return true; return true;
} }
void Service::setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit) void Service::setupRig(long loraFreq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit)
{ {
isImplicitHeaderMode_ = !isExplicit; rigIsImplicitMode_ = !isExplicit;
isImplicitHeaderMode_ = sf == 6; // must be implicit for SF6 rigIsImplicitMode_ = sf == 6; // must be implicit for SF6
int loraSpeed = (int)(sf * (4.0 / cr) / (pow(2.0, sf) / bw)); int loraSpeed = (int)(sf * (4.0 / cr) / (pow(2.0, sf) / bw));
LOG_INFO("Initializing LoRa"); LOG_INFO("Initializing LoRa");
@ -208,7 +190,7 @@ void Service::setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int syn
LOG_INFO("Power:", pwr, "dBm"); LOG_INFO("Power:", pwr, "dBm");
LOG_INFO("Sync:", "0x" + String(sync, HEX)); LOG_INFO("Sync:", "0x" + String(sync, HEX));
LOG_INFO("CRC:", crcBytes); LOG_INFO("CRC:", crcBytes);
LOG_INFO("Header:", isImplicitHeaderMode_ ? "implicit" : "explicit"); LOG_INFO("Header:", rigIsImplicitMode_ ? "implicit" : "explicit");
LOG_INFO("Speed:", loraSpeed, "bps"); LOG_INFO("Speed:", loraSpeed, "bps");
LOG_INFO("TOA (compressed):", 37.0 / ((double)loraSpeed / 8.0), "sec"); LOG_INFO("TOA (compressed):", 37.0 / ((double)loraSpeed / 8.0), "sec");
LOG_INFO("TOA (uncompressed):", 64.0 / ((double)loraSpeed / 8.0), "sec"); LOG_INFO("TOA (uncompressed):", 64.0 / ((double)loraSpeed / 8.0), "sec");
@ -234,74 +216,35 @@ void Service::setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int syn
break; break;
} }
LOG_INFO("Min level:", -174 + 10 * log10(bw) + 6 + snrLimit, "dBm"); LOG_INFO("Min level:", -174 + 10 * log10(bw) + 6 + snrLimit, "dBm");
rig_ = std::make_shared<MODULE_NAME>(new Module(config_.LoraPinSs, config_.LoraPinA, config_.LoraPinRst, config_.LoraPinB));
#ifdef USE_RADIOLIB int state = rig_->begin((float)loraFreq / 1e6, (float)bw / 1e3, sf, cr, sync, pwr);
radio_ = std::make_shared<MODULE_NAME>(new Module(config_.LoraPinSs, config_.LoraPinA, config_.LoraPinRst, config_.LoraPinB));
int state = radio_->begin((float)loraFreq / 1e6, (float)bw / 1e3, sf, cr, sync, pwr);
if (state != RADIOLIB_ERR_NONE) { if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("Radio start error:", state); LOG_ERROR("Radio start error:", state);
} }
radio_->setCRC(crcBytes); rig_->setCRC(crcBytes);
#ifdef USE_SX126X #ifdef USE_SX126X
#pragma message("Using SX126X") #pragma message("Using SX126X")
LOG_INFO("Using SX126X module"); LOG_INFO("Using SX126X module");
radio_->setRfSwitchPins(config_.LoraPinSwitchRx, config_.LoraPinSwitchTx); rig_->setRfSwitchPins(config_.LoraPinSwitchRx, config_.LoraPinSwitchTx);
radio_->clearDio1Action(); rig_->clearDio1Action();
if (config_.LoraUseIsr) { rig_->setDio1Action(onRigIsrRxPacket);
radio_->setDio1Action(onLoraDataAvailableIsr); #else
} else {
radio_->setDio1Action(onLoraDataAvailableIsrNoRead);
}
#else
#pragma message("Using SX127X") #pragma message("Using SX127X")
LOG_INFO("Using SX127X module"); LOG_INFO("Using SX127X module");
radio_->clearDio0Action(); radio_->clearDio0Action();
if (config_.LoraUseIsr) { radio_->setDio0Action(onRigIsrRxPacket);
radio_->setDio0Action(onLoraDataAvailableIsr); #endif
} else {
radio_->setDio0Action(onLoraDataAvailableIsrNoRead);
}
#endif
if (isImplicitHeaderMode_) { if (rigIsImplicitMode_) {
radio_->implicitHeader(0xff); rig_->implicitHeader(0xff);
} else { } else {
radio_->explicitHeader(); rig_->explicitHeader();
} }
state = radio_->startReceive(); state = rig_->startReceive();
if (state != RADIOLIB_ERR_NONE) { if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("Receive start error:", state); LOG_ERROR("Receive start error:", state);
} }
#else // USE_RADIOLIB
LoRa.setPins(config_.LoraPinSs, config_.LoraPinRst, config_.LoraPinA);
int retryCnt = 0;
while (!LoRa.begin(loraFreq)) {
LOG_WARN("LoRa init retry", retryCnt);
delay(CfgConnRetryMs);
if (retryCnt++ >= CfgConnRetryMaxTimes) {
LOG_ERROR("LoRa init failed");
return;
}
}
LoRa.setSyncWord(sync);
LoRa.setSpreadingFactor(sf);
LoRa.setSignalBandwidth(bw);
LoRa.setCodingRate4(cr);
LoRa.setTxPower(pwr);
if (config_.crcBytes > 0) {
LoRa.enableCrc();
}
if (config_.LoraUseIsr) {
LoRa.onReceive(onLoraDataAvailableIsr);
LoRa.receive();
}
#endif // USE_RADIOLIB
LOG_INFO("LoRa initialized"); LOG_INFO("LoRa initialized");
} }
@ -328,7 +271,7 @@ void Service::loop()
if (needsWifi() && WiFi.status() != WL_CONNECTED) { if (needsWifi() && WiFi.status() != WL_CONNECTED) {
reconnectWifi(); reconnectWifi();
} }
if (needsAprsis() && !aprsisConn_.connected() && config_.EnablePersistentAprsConnection) { if (needsAprsis() && !aprsisConnection_.connected() && config_.EnablePersistentAprsConnection) {
reconnectAprsis(); reconnectAprsis();
} }
if (config_.KissEnableTcpIp) { if (config_.KissEnableTcpIp) {
@ -337,147 +280,110 @@ void Service::loop()
// RX path, Rig -> Serial // RX path, Rig -> Serial
bool isRigToSerialProcessed = false; bool isRigToSerialProcessed = false;
#ifdef USE_RADIOLIB
isRigToSerialProcessed = processRigToSerial(); isRigToSerialProcessed = processRigToSerial();
#else
if (config_.LoraUseIsr) {
isRigToSerialProcessed = processRigToSerial();
} else {
if (int packetSize = LoRa.parsePacket()) {
loraReceive(packetSize);
isRigToSerialProcessed = true;
}
}
#endif
// TX path, Serial -> Rig // TX path, Serial -> Rig
if (!isRigToSerialProcessed) { if (!isRigToSerialProcessed) {
long currentTime = millis(); long currentTime = millis();
if (!isLoraRxBusy() && currentTime > csmaSlotTimePrev_ + csmaSlotTime_ && random(0, 255) < csmaP_) { if (!isRigRxBusy() && currentTime > csmaSlotTimePrev_ + csmaSlotTime_ && random(0, 255) < csmaP_) {
if (aprsisConn_.available() > 0) { if (aprsisConnection_.available() > 0) {
onAprsisDataAvailable(); onAprsisDataAvailable();
} }
if (needsBeacon()) { if (needsBeacon()) {
sendPeriodicBeacon(); sendPeriodicBeacon();
} }
bool allTxProcessed = processSerialToRig(); processSerialToRig();
if (allTxProcessed) {
#ifdef USE_RADIOLIB
int state = radio_->startReceive();
if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("Start receive error: ", state);
}
#else
if (config_.LoraUseIsr) {
LoRa.receive();
}
#endif
}
csmaSlotTimePrev_ = currentTime; csmaSlotTimePrev_ = currentTime;
} }
} }
delay(CfgPollDelayMs); delay(CfgPollDelayMs);
} }
bool Service::isLoraRxBusy() { bool Service::isRigRxBusy() {
#ifdef USE_RADIOLIB return config_.LoraUseCad && rigIsRxActive_;
#ifdef USE_SX126X
return config_.LoraUseCad && loraDataAvailable_;
#else
return config_.LoraUseCad && (radio_->getModemStatus() & 0x01); // SX1278_STATUS_SIG_DETECT
#endif
#else
return false;
#endif
} }
#ifdef USE_RADIOLIB ICACHE_RAM_ATTR void Service::onRigIsrRxPacket() {
ICACHE_RAM_ATTR void Service::onLoraDataAvailableIsrNoRead() {
BaseType_t xHigherPriorityTaskWoken; BaseType_t xHigherPriorityTaskWoken;
uint32_t interruptStatusBits = 0; if (rigIsRxIsrEnabled_) {
rigIsRxActive_ = true;
if (interruptEnabled_) { uint32_t radioReceiveBit = RadioTaskBits::Receive;
loraDataAvailable_ = true; xTaskNotifyFromISR(rigTaskHandle_, radioReceiveBit, eSetBits, &xHigherPriorityTaskWoken);
interruptStatusBits |= 1;
xTaskNotifyFromISR(rxTaskHandle_, interruptStatusBits, eSetBits, &xHigherPriorityTaskWoken);
} }
} }
ICACHE_RAM_ATTR void Service::onLoraDataAvailableIsr() { void Service::rigTask(void *self) {
if (interruptEnabled_) { LOG_INFO("Radio task started");
int packetSize = radio_->getPacketLength();
if (packetSize > 0) {
int state = radio_->readData(rxBuf_, packetSize);
if (state == RADIOLIB_ERR_NONE) {
queueRigToSerialIsr(Cmd::Data, rxBuf_, packetSize);
} else {
LOG_ERROR("Read data error: ", state);
}
state = radio_->startReceive();
if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("Start receive error: ", state);
}
}
}
}
void Service::processIncomingDataTask(void *param) {
LOG_INFO("Incoming data process task started");
uint32_t interruptStatusBits;
while (true) { while (true) {
xTaskNotifyWait(0, 0x00, &interruptStatusBits, portMAX_DELAY); uint32_t commandBits = 0;
xTaskNotifyWaitIndexed(0, 0x00, ULONG_MAX, &commandBits, portMAX_DELAY);
if (interruptStatusBits & 0x01) { if (commandBits & RadioTaskBits::Receive) {
int packetSize = radio_->getPacketLength(); ((Service*)self)->onRigTaskRxPacket();
}
if (packetSize > 0) { else if (commandBits & RadioTaskBits::Transmit) {
((Service*)self)->onRigTaskTxPacket();
int state = radio_->readData(rxBuf_, packetSize);
if (state == RADIOLIB_ERR_NONE) {
queueRigToSerialIsr(Cmd::Data, rxBuf_, packetSize);
} else {
LOG_ERROR("Read data error: ", state);
}
state = radio_->startReceive();
if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("Start receive error: ", state);
}
}
loraDataAvailable_ = false;
} }
} }
} }
#else // USE_RADIOLIB void Service::onRigTaskRxPacket() {
int packetSize = rig_->getPacketLength();
if (packetSize > 0) {
byte rxBuf[packetSize];
int state = rig_->readData(rxBuf, packetSize);
if (state == RADIOLIB_ERR_NONE) {
queueRigToSerial(Cmd::Data, rxBuf, packetSize);
} else {
LOG_ERROR("Read data error: ", state);
}
ICACHE_RAM_ATTR void Service::onLoraDataAvailableIsr(int packetSize) state = rig_->startReceive();
{ if (state != RADIOLIB_ERR_NONE) {
int rxBufIndex = 0; LOG_ERROR("Start receive error: ", state);
}
for (int i = 0; i < packetSize; i++) {
rxBuf_[rxBufIndex++] = LoRa.read();
} }
queueRigToSerialIsr(Cmd::Data, rxBuf_, rxBufIndex); rigIsRxActive_ = false;
} }
#endif // USE_RADIOLIB void Service::onRigTaskTxPacket() {
while (rigTxQueueIndex_.size() > 0) {
int txPacketSize = rigTxQueueIndex_.shift();
byte txBuf[txPacketSize];
for (int i = 0; i < txPacketSize; i++) {
txBuf[i] = rigTxQueue_.shift();
}
rigIsRxIsrEnabled_ = false;
int state = rig_->transmit(txBuf, txPacketSize);
if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("TX error: ", state);
}
vTaskDelay(1);
}
int state = rig_->startReceive();
if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("Start receive error: ", state);
}
rigIsRxIsrEnabled_ = true;
if (config_.PttEnable) {
delay(config_.PttTxTailMs);
digitalWrite(config_.PttPin, LOW);
}
if (splitEnabled()) {
setFreq(config_.LoraFreqRx);
}
}
void Service::sendPeriodicBeacon() void Service::sendPeriodicBeacon()
{ {
long currentMs = millis(); long currentMs = millis();
if (previousBeaconMs_ == 0 || currentMs - previousBeaconMs_ >= config_.AprsRawBeaconPeriodMinutes * 60 * 1000) { if (beaconLastTimestampMs_ == 0 || currentMs - beaconLastTimestampMs_ >= config_.AprsRawBeaconPeriodMinutes * 60 * 1000) {
AX25::Payload payload(config_.AprsRawBeacon); AX25::Payload payload(config_.AprsRawBeacon);
if (payload.IsValid()) { if (payload.IsValid()) {
sendAX25ToLora(payload); sendAx25PayloadToRig(payload);
if (config_.EnableRfToIs) { if (config_.EnableRfToIs) {
sendToAprsis(payload.ToString()); sendToAprsis(payload.ToString());
} }
@ -486,7 +392,7 @@ void Service::sendPeriodicBeacon()
else { else {
LOG_ERROR("Beacon payload is invalid"); LOG_ERROR("Beacon payload is invalid");
} }
previousBeaconMs_ = currentMs; beaconLastTimestampMs_ = currentMs;
} }
} }
@ -495,13 +401,13 @@ void Service::sendToAprsis(const String &aprsMessage)
if (needsWifi() && WiFi.status() != WL_CONNECTED) { if (needsWifi() && WiFi.status() != WL_CONNECTED) {
reconnectWifi(); reconnectWifi();
} }
if (needsAprsis() && !aprsisConn_.connected()) { if (needsAprsis() && !aprsisConnection_.connected()) {
reconnectAprsis(); reconnectAprsis();
} }
aprsisConn_.println(aprsMessage); aprsisConnection_.println(aprsMessage);
if (!config_.EnablePersistentAprsConnection) { if (!config_.EnablePersistentAprsConnection) {
aprsisConn_.stop(); aprsisConnection_.stop();
} }
} }
@ -509,8 +415,8 @@ void Service::onAprsisDataAvailable()
{ {
String aprsisData; String aprsisData;
while (aprsisConn_.available() > 0) { while (aprsisConnection_.available() > 0) {
char c = aprsisConn_.read(); char c = aprsisConnection_.read();
if (c == '\r') continue; if (c == '\r') continue;
if (c == '\n') break; if (c == '\n') break;
aprsisData += c; aprsisData += c;
@ -525,7 +431,7 @@ void Service::onAprsisDataAvailable()
if (config_.EnableIsToRf && aprsisData.length() > 0) { if (config_.EnableIsToRf && aprsisData.length() > 0) {
AX25::Payload payload(aprsisData); AX25::Payload payload(aprsisData);
if (payload.IsValid()) { if (payload.IsValid()) {
sendAX25ToLora(payload); sendAx25PayloadToRig(payload);
} }
else { else {
LOG_WARN("Unknown payload from APRSIS, ignoring"); LOG_WARN("Unknown payload from APRSIS, ignoring");
@ -543,7 +449,7 @@ void Service::sendSignalReportEvent(int rssi, float snr)
sendRigToSerial(Cmd::SignalReport, (const byte *)&signalReport, sizeof(SignalReport)); sendRigToSerial(Cmd::SignalReport, (const byte *)&signalReport, sizeof(SignalReport));
} }
bool Service::sendAX25ToLora(const AX25::Payload &payload) bool Service::sendAx25PayloadToRig(const AX25::Payload &payload)
{ {
int bytesWritten; int bytesWritten;
byte buf[CfgMaxPacketSize]; byte buf[CfgMaxPacketSize];
@ -573,11 +479,7 @@ void Service::onRigPacket(void *packet, int packetLength)
performFrequencyCorrection(); performFrequencyCorrection();
} }
if (config_.KissEnableExtensions) { if (config_.KissEnableExtensions) {
#ifdef USE_RADIOLIB sendSignalReportEvent(rig_->getRSSI(), rig_->getSNR());
sendSignalReportEvent(radio_->getRSSI(), radio_->getSNR());
#else
sendSignalReportEvent(LoRa.packetRssi(), LoRa.packetSnr());
#endif
} }
if (!config_.IsClientMode) { if (!config_.IsClientMode) {
processIncomingRawPacketAsServer((const byte*)packet, packetLength); processIncomingRawPacketAsServer((const byte*)packet, packetLength);
@ -585,51 +487,25 @@ void Service::onRigPacket(void *packet, int packetLength)
} }
void Service::performFrequencyCorrection() { void Service::performFrequencyCorrection() {
#ifdef USE_RADIOLIB #ifdef USE_SX126X
#ifdef USE_SX126X
long frequencyErrorHz = 0; long frequencyErrorHz = 0;
#else
long frequencyErrorHz = radio_->getFrequencyError();
#endif
#else #else
long frequencyErrorHz = LoRa.packetFrequencyError(); long frequencyErrorHz = radio_->getFrequencyError();
#endif #endif
if (abs(frequencyErrorHz) > config_.AutoFreqCorrectionDeltaHz) { if (abs(frequencyErrorHz) > config_.AutoFreqCorrectionDeltaHz) {
config_.LoraFreqRx -= frequencyErrorHz; config_.LoraFreqRx -= frequencyErrorHz;
LOG_INFO("Correcting frequency:", frequencyErrorHz); LOG_INFO("Correcting frequency:", frequencyErrorHz);
setupFreq(config_.LoraFreqRx); setFreq(config_.LoraFreqRx);
} }
} }
void Service::setupFreq(long loraFreq) const { void Service::setFreq(long loraFreq) const {
#ifdef USE_RADIOLIB rig_->setFrequency((float)config_.LoraFreqRx / 1e6);
radio_->setFrequency((float)config_.LoraFreqRx / 1e6); int state = rig_->startReceive();
int state = radio_->startReceive(); if (state != RADIOLIB_ERR_NONE) {
if (state != RADIOLIB_ERR_NONE) { LOG_ERROR("Start receive error:", state);
LOG_ERROR("Start receive error:", state);
}
#else
LoRa.setFrequency(config_.LoraFreqRx);
if (config_.LoraUseIsr) {
LoRa.idle();
LoRa.receive();
}
#endif
}
#ifndef USE_RADIOLIB
void Service::loraReceive(int packetSize)
{
int rxBufIndex = 0;
byte rxBuf[packetSize];
while (LoRa.available()) {
rxBuf[rxBufIndex++] = LoRa.read();
} }
sendRigToSerial(Cmd::Data, rxBuf, rxBufIndex);
onRigPacket(rxBuf, rxBufIndex);
} }
#endif
void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLength) { void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLength) {
@ -646,19 +522,12 @@ void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLen
} }
if (payload.IsValid()) { if (payload.IsValid()) {
float snr = rig_->getSNR();
#ifdef USE_RADIOLIB int rssi = rig_->getRSSI();
float snr = radio_->getSNR(); #ifdef USE_SX126X
int rssi = radio_->getRSSI();
#ifdef USE_SX126X
long frequencyError = 0; long frequencyError = 0;
#else
long frequencyError = radio_->getFrequencyError();
#endif
#else #else
float snr = LoRa.packetSnr(); long frequencyError = radio_->getFrequencyError();
int rssi = LoRa.packetRssi();
long frequencyError = LoRa.packetFrequencyError();
#endif #endif
String signalReport = String("rssi: ") + String signalReport = String("rssi: ") +
String(snr < 0 ? rssi + snr : rssi) + String(snr < 0 ? rssi + snr : rssi) +
@ -679,8 +548,8 @@ void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLen
sendToAprsis(textPayload); sendToAprsis(textPayload);
LOG_INFO("Packet sent to APRS-IS"); LOG_INFO("Packet sent to APRS-IS");
} }
if (config_.EnableRepeater && payload.Digirepeat(ownCallsign_)) { if (config_.EnableRepeater && payload.Digirepeat(aprsMyCallsign_)) {
sendAX25ToLora(payload); sendAx25PayloadToRig(payload);
LOG_INFO("Packet digirepeated"); LOG_INFO("Packet digirepeated");
} }
} else { } else {
@ -690,86 +559,51 @@ void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLen
bool Service::onRigTxBegin() bool Service::onRigTxBegin()
{ {
rigCurrentTxPacketSize_ = 0;
if (splitEnabled()) { if (splitEnabled()) {
setupFreq(config_.LoraFreqTx); setFreq(config_.LoraFreqTx);
} }
if (config_.PttEnable) { if (config_.PttEnable) {
digitalWrite(config_.PttPin, HIGH); digitalWrite(config_.PttPin, HIGH);
delay(config_.PttTxDelayMs); delay(config_.PttTxDelayMs);
} else {
delay(CfgPollDelayMs);
} }
#ifdef USE_RADIOLIB
return true; return true;
#else
return (LoRa.beginPacket(isImplicitHeaderMode_) == 1);
#endif
} }
void Service::onRigTx(byte b) void Service::onRigTx(byte b)
{ {
LOG_TRACE((char)b, String(b, HEX)); LOG_TRACE((char)b, String(b, HEX));
#ifdef USE_RADIOLIB rigTxQueue_.push(b);
txQueue_.push(b); rigCurrentTxPacketSize_++;
#else
LoRa.write(b);
#endif
} }
void Service::onRigTxEnd() void Service::onRigTxEnd()
{ {
#ifdef USE_RADIOLIB rigTxQueueIndex_.push(rigCurrentTxPacketSize_);
int txPacketSize = txQueue_.size(); uint32_t radioTransmitBit = RadioTaskBits::Transmit;
byte txBuf[txPacketSize]; xTaskNotify(rigTaskHandle_, radioTransmitBit, eSetBits);
for (int i = 0; i < txPacketSize; i++) {
txBuf[i] = txQueue_.shift();
}
interruptEnabled_ = false;
int state = radio_->transmit(txBuf, txPacketSize);
if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("TX error: ", state);
}
interruptEnabled_ = true;
#endif
if (config_.PttEnable) {
#ifndef USE_RADIOLIB
LoRa.endPacket(false);
#endif
delay(config_.PttTxTailMs);
digitalWrite(config_.PttPin, LOW);
} else {
#ifndef USE_RADIOLIB
LoRa.endPacket(true);
#endif
}
if (splitEnabled()) {
setupFreq(config_.LoraFreqRx);
}
} }
void Service::attachKissNetworkClient() void Service::attachKissNetworkClient()
{ {
// connected, client dropped off // connected, client dropped off
if (isKissConn_) { if (isKissClientConnected) {
if (!kissConn_.connected()) { if (!kissConnnection_.connected()) {
LOG_INFO("KISS TCP/IP client disconnected"); LOG_INFO("KISS TCP/IP client disconnected");
isKissConn_ = false; isKissClientConnected = false;
kissConn_.stop(); kissConnnection_.stop();
} }
} }
WiFiClient wifiClient = kissServer_->available(); WiFiClient wifiClient = kissServer_->available();
// new client connected // new client connected
if (wifiClient && wifiClient.connected()) { if (wifiClient && wifiClient.connected()) {
// drop off current one // drop off current one
if (isKissConn_) { if (isKissClientConnected) {
kissConn_.stop(); kissConnnection_.stop();
} }
LOG_INFO("New KISS TCP/IP client connected"); LOG_INFO("New KISS TCP/IP client connected");
kissConn_ = wifiClient; kissConnnection_ = wifiClient;
isKissConn_ = true; isKissClientConnected = true;
} }
} }
@ -779,8 +613,8 @@ void Service::onSerialTx(byte b)
if (config_.UsbSerialEnable) { if (config_.UsbSerialEnable) {
Serial.write(b); Serial.write(b);
} }
else if (isKissConn_) { else if (isKissClientConnected) {
kissConn_.write(b); kissConnnection_.write(b);
} }
else if (config_.BtEnableBle) { else if (config_.BtEnableBle) {
serialBLE_.write(b); serialBLE_.write(b);
@ -795,8 +629,8 @@ bool Service::onSerialRxHasData()
if (config_.UsbSerialEnable) { if (config_.UsbSerialEnable) {
return Serial.available(); return Serial.available();
} }
else if (isKissConn_) { else if (isKissClientConnected) {
return kissConn_.available(); return kissConnnection_.available();
} }
else if (config_.BtEnableBle) { else if (config_.BtEnableBle) {
return serialBLE_.available(); return serialBLE_.available();
@ -813,12 +647,12 @@ bool Service::onSerialRx(byte *b)
if (config_.UsbSerialEnable) { if (config_.UsbSerialEnable) {
rxResult = Serial.read(); rxResult = Serial.read();
} }
else if (isKissConn_) { else if (isKissClientConnected) {
rxResult = kissConn_.read(); rxResult = kissConnnection_.read();
// client dropped off // client dropped off
if (rxResult == -1) { if (rxResult == -1) {
kissConn_.stop(); kissConnnection_.stop();
isKissConn_ = false; isKissClientConnected = false;
} }
} }
else { else {
@ -872,7 +706,7 @@ void Service::onRadioControlCommand(const std::vector<byte> &rawCommand) {
config_.LoraSync = be16toh(setHardware->sync); config_.LoraSync = be16toh(setHardware->sync);
int crcType = setHardware->crc ? config_.LoraCrc : 0; int crcType = setHardware->crc ? config_.LoraCrc : 0;
setupLora(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf, setupRig(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf,
config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, crcType, config_.LoraExplicit); config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, crcType, config_.LoraExplicit);
} else { } else {
LOG_ERROR("Radio control command of wrong size"); LOG_ERROR("Radio control command of wrong size");

Wyświetl plik

@ -12,11 +12,7 @@
#include "config.h" #include "config.h"
#endif #endif
#ifdef USE_RADIOLIB
#include <RadioLib.h> #include <RadioLib.h>
#else
#include <LoRa.h>
#endif
#include <WiFi.h> #include <WiFi.h>
#include <endian.h> #include <endian.h>
@ -41,30 +37,26 @@ private:
void printConfig(); void printConfig();
void setupWifi(const String &wifiName, const String &wifiKey); void setupWifi(const String &wifiName, const String &wifiKey);
void setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit); void setupRig(long freq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit);
void setupFreq(long loraFreq) const; void setFreq(long freq) const;
void setupBt(const String &btName); void setupBt(const String &btName);
void reconnectWifi() const; void reconnectWifi() const;
bool reconnectAprsis(); bool reconnectAprsis();
void attachKissNetworkClient(); void attachKissNetworkClient();
bool isLoraRxBusy(); bool isRigRxBusy();
#ifdef USE_RADIOLIB void onRigTaskRxPacket();
void onLoraDataAvailable(); void onRigTaskTxPacket();
static void processIncomingDataTask(void *param); static void rigTask(void *self);
static ICACHE_RAM_ATTR void onLoraDataAvailableIsr(); static ICACHE_RAM_ATTR void onRigIsrRxPacket();
static ICACHE_RAM_ATTR void onLoraDataAvailableIsrNoRead();
#else
static ICACHE_RAM_ATTR void onLoraDataAvailableIsr(int packetSize);
void loraReceive(int packetSize);
#endif
void onAprsisDataAvailable(); void onAprsisDataAvailable();
void sendSignalReportEvent(int rssi, float snr); void sendSignalReportEvent(int rssi, float snr);
void sendPeriodicBeacon(); void sendPeriodicBeacon();
void sendToAprsis(const String &aprsMessage); void sendToAprsis(const String &aprsMessage);
bool sendAX25ToLora(const AX25::Payload &payload); bool sendAx25PayloadToRig(const AX25::Payload &payload);
void processIncomingRawPacketAsServer(const byte *packet, int packetLength); void processIncomingRawPacketAsServer(const byte *packet, int packetLength);
void performFrequencyCorrection(); void performFrequencyCorrection();
@ -120,13 +112,14 @@ private:
} __attribute__((packed)); } __attribute__((packed));
private: private:
const String CfgLoraprsVersion = "LoRAPRS 0.1"; const String CfgLoraprsVersion = "LoRAPRS 1.0.2";
// processor config // processor config
const int CfgConnRetryMs = 500; // connection retry delay, e.g. wifi const int CfgConnRetryMs = 500; // connection retry delay, e.g. wifi
static const int CfgPollDelayMs = 20; // main loop delay const int CfgPollDelayMs = 20; // main loop delay
const int CfgConnRetryMaxTimes = 10; // number of connection retries const int CfgConnRetryMaxTimes = 10; // number of connection retries
static const int CfgMaxPacketSize = 256; // maximum packet size static const int CfgMaxPacketSize = 256; // maximum packet size
static const int CfgRadioQueueSize = 1024; // radio queue size
// csma parameters, overriden with KISS commands // csma parameters, overriden with KISS commands
const long CfgCsmaPersistence = 100; // 255 for real time, lower for higher traffic const long CfgCsmaPersistence = 100; // 255 for real time, lower for higher traffic
@ -134,37 +127,46 @@ private:
// kiss static parameters // kiss static parameters
const int CfgKissPort = 8001; // kiss tcp/ip server port const int CfgKissPort = 8001; // kiss tcp/ip server port
// radio task commands
enum RadioTaskBits {
Receive = 0x01,
Transmit = 0x02
};
private: private:
// config // config
Config config_; Config config_;
String aprsLoginCommand_; String aprsLoginCommand_;
AX25::Callsign ownCallsign_; AX25::Callsign aprsMyCallsign_;
bool isImplicitHeaderMode_;
// csma // csma
byte csmaP_; byte csmaP_;
long csmaSlotTime_; long csmaSlotTime_;
long csmaSlotTimePrev_; long csmaSlotTimePrev_;
// state // beacon state
long previousBeaconMs_; long beaconLastTimestampMs_;
// peripherals // peripherals, radio
static byte rxBuf_[CfgMaxPacketSize]; static TaskHandle_t rigTaskHandle_;
#ifdef USE_RADIOLIB static volatile bool rigIsRxActive_;
static TaskHandle_t rxTaskHandle_; static bool rigIsRxIsrEnabled_;
static volatile bool loraDataAvailable_; bool rigIsImplicitMode_;
static bool interruptEnabled_; int rigCurrentTxPacketSize_;
CircularBuffer<uint8_t, CfgMaxPacketSize> txQueue_; CircularBuffer<uint8_t, CfgRadioQueueSize> rigTxQueue_;
static std::shared_ptr<MODULE_NAME> radio_; CircularBuffer<uint8_t, CfgRadioQueueSize> rigTxQueueIndex_;
#endif std::shared_ptr<MODULE_NAME> rig_;
// bluetooth, wifi
BluetoothSerial serialBt_; BluetoothSerial serialBt_;
BLESerial serialBLE_; BLESerial serialBLE_;
WiFiClient aprsisConn_; WiFiClient aprsisConnection_;
// kiss server
std::shared_ptr<WiFiServer> kissServer_; std::shared_ptr<WiFiServer> kissServer_;
WiFiClient kissConn_; WiFiClient kissConnnection_;
bool isKissConn_; bool isKissClientConnected_;
}; };
} // LoraPrs } // LoraPrs