From 30601b75e8ac1339f9acc51299adff2404b5caaf Mon Sep 17 00:00:00 2001 From: sh123 Date: Fri, 29 Jan 2021 13:47:04 +0200 Subject: [PATCH] Add kiss tx queue and move config defiens to config --- README.md | 1 + config.h | 25 ++++++ esp32_loraprs.ino | 69 ++++++++++------ loraprs_service.cpp | 195 ++++++++++++++++++++++++-------------------- loraprs_service.h | 16 ++-- 5 files changed, 183 insertions(+), 123 deletions(-) create mode 100644 config.h diff --git a/README.md b/README.md index 2f86d41..3e0c574 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Install via libraries: - Arduino ESP32 library: https://github.com/espressif/arduino-esp32 - LoRa arduino library: https://github.com/sandeepmistry/arduino-LoRa - Arduino Timer library: https://github.com/contrem/arduino-timer +- cppQueue library: https://github.com/SMFSW/Queue # Software Setup - when setting up APRSDroid, use **"TNC (KISS)"** connection protocol in Connection Preferences -> Connection Protocol diff --git a/config.h b/config.h new file mode 100644 index 0000000..ed4c107 --- /dev/null +++ b/config.h @@ -0,0 +1,25 @@ +#define CFG_IS_CLIENT_MODE true + +#define CFG_LORA_FREQ 433.775E6 +#define CFG_LORA_BW 125e3 +#define CFG_LORA_SF 12 +#define CFG_LORA_CR 7 +#define CFG_LORA_PWR 20 +#define CFG_LORA_ENABLE_CRC true + +#define CFG_BT_NAME "loraprs" + +#define CFG_APRS_LOGIN "NOCALL-10" +#define CFG_APRS_PASS "12345" +#define CFG_APRS_FILTER "r/35.60/139.80/25" +#define CFG_APRS_RAW_BKN "NOCALL-10>APZMDM,WIDE1-1:!0000.00N/00000.00E#LoRA 433.775MHz/BW125/SF12/CR7/0x34" + +#define CFG_WIFI_SSID "" +#define CFG_WIFI_KEY "" + +#define CFG_FREQ_CORR false +#define CFG_PERSISTENT_APRS true +#define CFG_DIGIREPEAT false +#define CFG_RF_TO_IS true +#define CFG_IS_TO_RF false +#define CFG_BEACON false diff --git a/esp32_loraprs.ino b/esp32_loraprs.ino index e450d2d..31accd5 100644 --- a/esp32_loraprs.ino +++ b/esp32_loraprs.ino @@ -3,62 +3,77 @@ #include "loraprs_service.h" #define LED_BUILTIN 2 -#define LED_TOGGLE_PERIOD 1000 +#define LED_TOGGLE_PERIOD 1000 -LoraPrs::Config cfg; -LoraPrs::Service loraPrsService; +#if __has_include("/tmp/esp32_loraprs_config.h") +#pragma message("Using external config") +#include "/tmp/esp32_loraprs_config.h" +#else +#pragma message("Using default config") +#include "config.h" +#endif -auto watchdogLedTimer = timer_create_default(); +#if CFG_IS_CLIENT_MODE == true +#pragma message("Configured for client mode") +#else +#pragma message("Configured for server mode") +#endif -void initializeConfig() { +void initializeConfig(LoraPrs::Config &cfg) { // client/server mode switch - cfg.IsClientMode = true; + cfg.IsClientMode = CFG_IS_CLIENT_MODE; // lora parameters - cfg.LoraFreq = 433.775E6; - cfg.LoraBw = 125e3; - cfg.LoraSf = 12; - cfg.LoraCodingRate = 7; + cfg.LoraFreq = CFG_LORA_FREQ; + cfg.LoraBw = CFG_LORA_BW; + cfg.LoraSf = CFG_LORA_SF; + cfg.LoraCodingRate = CFG_LORA_CR; cfg.LoraSync = 0x34; - cfg.LoraPower = 20; - cfg.LoraEnableCrc = true; // set to false for speech data + cfg.LoraPower = CFG_LORA_PWR; + cfg.LoraEnableCrc = CFG_LORA_ENABLE_CRC; // set to false for speech data // aprs configuration cfg.AprsHost = "rotate.aprs2.net"; cfg.AprsPort = 14580; - cfg.AprsLogin = "NOCALL-10"; - cfg.AprsPass = "12345"; - cfg.AprsFilter = "r/35.60/139.80/25"; // multiple filters are space separated - cfg.AprsRawBeacon = "NOCALL-10>APZMDM,WIDE1-1:!0000.00N/00000.00E#LoRA 433.775MHz/BW125/SF12/CR7/0x34"; + cfg.AprsLogin = CFG_APRS_LOGIN; + cfg.AprsPass = CFG_APRS_PASS; + cfg.AprsFilter = CFG_APRS_FILTER; // multiple filters are space separated + cfg.AprsRawBeacon = CFG_APRS_RAW_BKN; cfg.AprsRawBeaconPeriodMinutes = 20; // bluetooth device name - cfg.BtName = "loraprs"; + cfg.BtName = CFG_BT_NAME; // server mode wifi paramaters - cfg.WifiSsid = ""; - cfg.WifiKey = ""; + cfg.WifiSsid = CFG_WIFI_SSID; + cfg.WifiKey = CFG_WIFI_KEY; // configuration flags and features - cfg.EnableAutoFreqCorrection = false; // automatic tune to any incoming packet frequency + cfg.EnableAutoFreqCorrection = CFG_FREQ_CORR; // automatic tune to any incoming packet frequency cfg.EnableSignalReport = true; // signal report will be added to the comment sent to aprsis - cfg.EnablePersistentAprsConnection = true; // keep aprsis connection open, otherwise connect on new data only - cfg.EnableRfToIs = true; // send data from rf to aprsis - cfg.EnableIsToRf = false; // send data from aprsis to rf - cfg.EnableRepeater = false; // digirepeat incoming packets - cfg.EnableBeacon = false; // enable periodic AprsRawBeacon beacon to rf and aprsis if rf to aprsis is enabled + cfg.EnablePersistentAprsConnection = CFG_PERSISTENT_APRS; // keep aprsis connection open, otherwise connect on new data only + cfg.EnableRfToIs = CFG_RF_TO_IS; // send data from rf to aprsis + cfg.EnableIsToRf = CFG_IS_TO_RF; // send data from aprsis to rf + cfg.EnableRepeater = CFG_DIGIREPEAT; // digirepeat incoming packets + cfg.EnableBeacon = CFG_BEACON; // enable periodic AprsRawBeacon beacon to rf and aprsis if rf to aprsis is enabled } +LoraPrs::Service loraPrsService; + +auto watchdogLedTimer = timer_create_default(); + void setup() { pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, 1); Serial.begin(115200); while (!Serial); + + LoraPrs::Config config; - initializeConfig(); - loraPrsService.setup(cfg); + initializeConfig(config); + loraPrsService.setup(config); watchdogLedTimer.every(LED_TOGGLE_PERIOD, toggleWatchdogLed); } diff --git a/loraprs_service.cpp b/loraprs_service.cpp index 1fbd849..372bab9 100644 --- a/loraprs_service.cpp +++ b/loraprs_service.cpp @@ -7,10 +7,15 @@ Service::Service() , kissCmd_(KissCmd::NoCmd) , csmaP_(CfgCsmaPersistence) , csmaSlotTime_(CfgCsmaSlotTimeMs) + , txQueue_(new cppQueue(sizeof(unsigned char), CfgLoraTxQueueSize)) , serialBt_() { } +Service::~Service() { + delete txQueue_; +} + void Service::setup(const Config &conf) { previousBeaconMs_ = 0; @@ -141,13 +146,14 @@ void Service::setupBt(const String &btName) } void Service::loop() -{ +{ if (needsWifi() && WiFi.status() != WL_CONNECTED) { reconnectWifi(); } if (needsAprsis() && !aprsisConn_.connected() && persistentConn_) { reconnectAprsis(); } + // RX path if (int packetSize = LoRa.parsePacket()) { onLoraDataAvailable(packetSize); @@ -155,14 +161,14 @@ void Service::loop() // TX path else { if (random(0, 255) < csmaP_) { - if (serialBt_.available()) { - onBtDataAvailable(); - } - else if (aprsisConn_.available() > 0) { + if (aprsisConn_.available() > 0) { onAprsisDataAvailable(); } else if (needsBeacon()) { sendPeriodicBeacon(); + } + else { + processTx(); } } else { @@ -179,7 +185,7 @@ void Service::sendPeriodicBeacon() if (previousBeaconMs_ == 0 || currentMs - previousBeaconMs_ >= aprsBeaconPeriodMinutes_ * 60 * 1000) { AX25::Payload payload(aprsBeacon_); if (payload.IsValid()) { - sendToLora(payload); + sendAX25ToLora(payload); if (enableRfToIs_) { sendToAprsis(payload.ToString()); } @@ -222,7 +228,7 @@ void Service::onAprsisDataAvailable() if (enableIsToRf_ && aprsisData.length() > 0) { AX25::Payload payload(aprsisData); if (payload.IsValid()) { - sendToLora(payload); + sendAX25ToLora(payload); } else { Serial.println("Invalid payload from APRSIS"); @@ -230,7 +236,7 @@ void Service::onAprsisDataAvailable() } } -bool Service::sendToLora(const AX25::Payload &payload) +bool Service::sendAX25ToLora(const AX25::Payload &payload) { byte buf[512]; int bytesWritten = payload.ToBinary(buf, sizeof(buf)); @@ -305,7 +311,7 @@ void Service::onLoraDataAvailable(int packetSize) Serial.println("Packet sent to APRS-IS"); } if (enableRepeater_ && payload.Digirepeat(ownCallsign_)) { - sendToLora(payload); + sendAX25ToLora(payload); Serial.println("Packet digirepeated"); } } @@ -315,102 +321,111 @@ void Service::onLoraDataAvailable(int packetSize) } } +void Service::processTx() +{ + while (serialBt_.available() || !txQueue_->isEmpty()) { + + if (serialBt_.available()) { + int rxResult = serialBt_.read(); + if (rxResult != -1) { + byte rxByte = (byte)rxResult; + if (!txQueue_->push((void *)&rxByte)) { + Serial.println("TX queue is full"); + } + } + } + if (!txQueue_->isEmpty()) { + byte qRxByte; + if (txQueue_->peek((void *)&qRxByte)) { + if (kissReceiveByte(qRxByte)) { + txQueue_->drop(); + } + } + } + } +} + void Service::kissResetState() { kissCmd_ = KissCmd::NoCmd; kissState_ = KissState::Void; } -bool Service::loraBeginPacketAndWait() -{ - for (int i = 0; i < CfgLoraTxWaitMs; i++) { - if (LoRa.beginPacket() == 1) { +bool Service::kissProcessCommand(unsigned char rxByte) { + + switch (rxByte) { + case KissCmd::Data: + if (LoRa.beginPacket() == 0) return false; + kissState_ = KissState::GetData; + break; + case KissCmd::P: + kissState_ = KissState::GetP; + break; + case KissCmd::SlotTime: + kissState_ = KissState::GetSlotTime; + break; + default: + // unknown command + kissResetState(); return true; - } - delay(1); } - return false; + kissCmd_ = (KissCmd)rxByte; + return true; } -void Service::onBtDataAvailable() -{ - while (serialBt_.available()) { - - int rxResult = serialBt_.read(); - if (rxResult == -1) break; - - byte rxByte = (byte)rxResult; +bool Service::kissReceiveByte(unsigned char rxByte) { - switch (kissState_) { - case KissState::Void: - if (rxByte == KissMarker::Fend) { - kissCmd_ = KissCmd::NoCmd; - kissState_ = KissState::GetCmd; + switch (kissState_) { + case KissState::Void: + if (rxByte == KissMarker::Fend) { + kissCmd_ = KissCmd::NoCmd; + kissState_ = KissState::GetCmd; + } + break; + case KissState::GetCmd: + if (rxByte != KissMarker::Fend) { + if (!kissProcessCommand(rxByte)) return false; + } + break; + case KissState::GetP: + csmaP_ = rxByte; + kissState_ = KissState::GetData; + break; + case KissState::GetSlotTime: + csmaSlotTime_ = (long)rxByte * 10; + kissState_ = KissState::GetData; + break; + case KissState::GetData: + if (rxByte == KissMarker::Fesc) { + kissState_ = KissState::Escape; + } + else if (rxByte == KissMarker::Fend) { + if (kissCmd_ == KissCmd::Data) { + LoRa.endPacket(true); } - break; - case KissState::GetCmd: - if (rxByte != KissMarker::Fend) { - if (rxByte == KissCmd::Data) { - if (!loraBeginPacketAndWait()) { - Serial.println("LoRa buffer overflow, dropping packet"); - kissResetState(); - return; - } - kissCmd_ = (KissCmd)rxByte; - kissState_ = KissState::GetData; - } - else if (rxByte == KissCmd::P) { - kissCmd_ = (KissCmd)rxByte; - kissState_ = KissState::GetP; - } - else if (rxByte == KissCmd::SlotTime) { - kissCmd_ = (KissCmd)rxByte; - kissState_ = KissState::GetSlotTime; - } - else { - kissResetState(); - } - } - break; - case KissState::GetP: - csmaP_ = rxByte; + kissResetState(); + } + else if (kissCmd_ == KissCmd::Data) { + LoRa.write(rxByte); + } + break; + case KissState::Escape: + if (rxByte == KissMarker::Tfend) { + LoRa.write(KissMarker::Fend); kissState_ = KissState::GetData; - break; - case KissState::GetSlotTime: - csmaSlotTime_ = (long)rxByte * 10; + } + else if (rxByte == KissMarker::Tfesc) { + LoRa.write(KissMarker::Fesc); kissState_ = KissState::GetData; - break; - case KissState::GetData: - if (rxByte == KissMarker::Fesc) { - kissState_ = KissState::Escape; - } - else if (rxByte == KissMarker::Fend) { - if (kissCmd_ == KissCmd::Data) { - LoRa.endPacket(true); - } - kissResetState(); - } - else if (kissCmd_ == KissCmd::Data) { - LoRa.write(rxByte); - } - break; - case KissState::Escape: - if (rxByte == KissMarker::Tfend) { - LoRa.write(KissMarker::Fend); - kissState_ = KissState::GetData; - } - else if (rxByte == KissMarker::Tfesc) { - LoRa.write(KissMarker::Fesc); - kissState_ = KissState::GetData; - } - else { - kissResetState(); - } - break; - default: - break; - } + } + else { + kissResetState(); + } + break; + default: + break; } + return true; } } // LoraPrs diff --git a/loraprs_service.h b/loraprs_service.h index fff6b96..16d1c1a 100644 --- a/loraprs_service.h +++ b/loraprs_service.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "BluetoothSerial.h" #include "ax25_payload.h" @@ -16,6 +17,7 @@ class Service { public: Service(); + ~Service(); void setup(const Config &conf); void loop(); @@ -28,16 +30,17 @@ private: void reconnectWifi(); bool reconnectAprsis(); + void processTx(); + void onLoraDataAvailable(int packetSize); - void onBtDataAvailable(); void onAprsisDataAvailable(); void sendPeriodicBeacon(); - void sendToAprsis(String aprsMessage); - bool sendToLora(const AX25::Payload &payload); - - bool loraBeginPacketAndWait(); + bool sendAX25ToLora(const AX25::Payload &payload); + + bool kissReceiveByte(unsigned char rxByte); + bool kissProcessCommand(unsigned char rxByte); void kissResetState(); inline bool needsAprsis() const { return !isClient_ && (enableRfToIs_ || enableIsToRf_); } @@ -72,7 +75,7 @@ private: const String CfgLoraprsVersion = "LoRAPRS 0.1"; const int CfgPollDelayMs = 5; - const int CfgLoraTxWaitMs = 2000; + const int CfgLoraTxQueueSize = 1024; // tx when lower than this value from random 0..255 // use lower value for high traffic, use 255 for real time @@ -110,6 +113,7 @@ private: long previousBeaconMs_; byte csmaP_; long csmaSlotTime_; + cppQueue *txQueue_; // peripherals BluetoothSerial serialBt_;