From 1ae790c5d5646ac0ec6d205fb627d7af6205e759 Mon Sep 17 00:00:00 2001 From: sh123 Date: Fri, 12 Jun 2020 17:34:23 +0300 Subject: [PATCH] Config and AX25 message reafactoring (#5) * Refactoring * Add support for aprsis filter and print out incoming packets into the serial --- aprsmsg.cpp | 88 ++++++++++++++++++++ aprsmsg.h | 38 +++++++++ esp32_loraprs.ino | 70 ++++++++-------- loraprs.cpp | 203 ++++++++++++++++++---------------------------- loraprs.h | 89 ++++++++++++-------- 5 files changed, 292 insertions(+), 196 deletions(-) create mode 100644 aprsmsg.cpp create mode 100644 aprsmsg.h diff --git a/aprsmsg.cpp b/aprsmsg.cpp new file mode 100644 index 0000000..3b8d5fb --- /dev/null +++ b/aprsmsg.cpp @@ -0,0 +1,88 @@ +#include "aprsmsg.h" + +namespace AX25 { + +Payload::Payload(byte *rxPayload, int payloadLength) +{ + parsePayload(rxPayload, payloadLength); +} + +String Payload::ToText(const String &customComment) +{ + String txt = srcCall_ + String(">") + dstCall_; + + if (rptFirst_.length() > 0) { + txt += String(",") + rptFirst_; + } + if (rptSecond_.length() > 0) { + txt += String(",") + rptSecond_; + } + + txt += String(":") + body_; + + if (body_.startsWith("=")) { + txt += customComment; + } + + return txt + String("\n"); +} + +bool Payload::parsePayload(byte *rxPayload, int payloadLength) +{ + byte *rxPtr = rxPayload; + + dstCall_ = decodeCall(rxPtr); + rxPtr += CallsignSize; + if (rxPtr >= rxPayload + payloadLength) return false; + + srcCall_ = decodeCall(rxPtr); + rxPtr += CallsignSize; + if (rxPtr >= rxPayload + payloadLength) return false; + + if ((rxPayload[2 * CallsignSize - 1] & 1) == 0) { + rptFirst_ = decodeCall(rxPtr); + rxPtr += CallsignSize; + if (rxPtr >= rxPayload + payloadLength) return false; + + if ((rxPayload[3 * CallsignSize - 1] & 1) == 0) { + rptSecond_ = decodeCall(rxPtr); + rxPtr += CallsignSize; + } + } + if ((rxPtr + 1) >= rxPayload + payloadLength) return false; + + if (*(rxPtr++) != AX25Ctrl::UI) return false; + if (*(rxPtr++) != AX25Pid::NoLayer3) return false; + + while (rxPtr < rxPayload + payloadLength) { + body_ += String((char)*(rxPtr++)); + } + + return true; +} + +String Payload::decodeCall(byte *rxPtr) +{ + byte callsign[CallsignSize]; + char ssid; + + byte *ptr = rxPtr; + + memset(callsign, 0, sizeof(callsign)); + + for (int i = 0; i < 6; i++) { + char c = *(ptr++) >> 1; + callsign[i] = (c == ' ') ? '\0' : c; + } + callsign[CallsignSize-1] = '\0'; + ssid = (*ptr >> 1); + + String result = String((char*)callsign); + + if (result.length() > 0 && ssid >= '0' && ssid <= '9') { + result += String("-") + String(ssid); + } + return result; +} + +} // AX25 diff --git a/aprsmsg.h b/aprsmsg.h new file mode 100644 index 0000000..58047ee --- /dev/null +++ b/aprsmsg.h @@ -0,0 +1,38 @@ +#ifndef APRSMSG_H +#define APRSMSG_H + +#include + +namespace AX25 { + +class Payload +{ +public: + Payload(byte *rxPayload, int payloadLength); + String ToText(const String &customComment); + +private: + String decodeCall(byte *rxPtr); + bool parsePayload(byte *rxPayload, int payloadLength); + +private: + enum AX25Ctrl { + UI = 0x03 + }; + + enum AX25Pid { + NoLayer3 = 0xf0 + }; + + const int MaxPayloadSize = 16; + const int CallsignSize = 7; + +private: + String srcCall_, dstCall_; + String rptFirst_, rptSecond_; + String body_; +}; + +} // AX25 + +#endif // APRSMSG_H diff --git a/esp32_loraprs.ino b/esp32_loraprs.ino index 831cd8b..b935ba3 100644 --- a/esp32_loraprs.ino +++ b/esp32_loraprs.ino @@ -5,46 +5,39 @@ #define LED_BUILTIN 2 #define LED_TOGGLE_PERIOD 1000 -#define LORAPRS_CLIENT - -// https://vienna.iaru-r1.org/wp-content/uploads/2019/01/VIE19-C5-015-OEVSV-LORA-APRS-433-MHz.pdf -#ifdef LORAPRS_CLIENT -// calibrate client based on server frequency drift report -#define LORAPRS_FREQ 433.775E6 -//#define LORAPRS_FREQ 433.7688E6 -#else -#define LORAPRS_FREQ 433.775E6 -//#define LORAPRS_FREQ 433.770E6 -#endif - -#ifdef LORAPRS_CLIENT -#define LORAPRS_BT_NAME "loraprs_client" -#define LORAPRS_WIFI_SSID "" -#define LORAPRS_WIFI_KEY "" -#define LORAPRS_LOGIN "NOCALL-0" -#define LORAPRS_PASS "00000" -#define LORAPRS_FREQ_CORR false -#else -#define LORAPRS_BT_NAME "" -#define LORAPRS_WIFI_SSID "" -#define LORAPRS_WIFI_KEY "" -#define LORAPRS_LOGIN "NOCALL-0" -#define LORAPRS_PASS "12345" -#define LORAPRS_FREQ_CORR false -//#define LORAPRS_FREQ_CORR true -#endif - -LoraPrs loraPrs( - LORAPRS_FREQ, - LORAPRS_BT_NAME, - LORAPRS_WIFI_SSID, - LORAPRS_WIFI_KEY, - LORAPRS_LOGIN, - LORAPRS_PASS, - LORAPRS_FREQ_CORR); +LoraPrsConfig cfg; +LoraPrs loraPrs; auto watchdogLedTimer = timer_create_default(); +void initializeConfig() { + cfg.IsClientMode = true; + + cfg.LoraFreq = 433.775E6; // 433.7688E6; + cfg.LoraBw = 125e3; + cfg.LoraSf = 12; + cfg.LoraCodingRate = 7; + cfg.LoraSync = 0xf3; + cfg.LoraPower = 20; + + cfg.AprsHost = "rotate.aprs2.net"; + cfg.AprsPort = 14580; + cfg.AprsLogin = "NOCALL-1"; + cfg.AprsPass = "00000"; + cfg.AprsFilter = "r/35.60/139.80/25"; + + cfg.BtName = "loraprs"; + + cfg.WifiSsid = ""; + cfg.WifiKey = ""; + + cfg.EnableSignalReport = true; + cfg.EnableAutoFreqCorrection = true; + cfg.EnablePersistentAprsConnection = true; + cfg.EnableIsToRf = false; + cfg.EnableRepeater = false; +} + void setup() { pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, 1); @@ -52,7 +45,8 @@ void setup() { Serial.begin(115200); while (!Serial); - loraPrs.setup(); + initializeConfig(); + loraPrs.setup(cfg); watchdogLedTimer.every(LED_TOGGLE_PERIOD, toggleWatchdogLed); } diff --git a/loraprs.cpp b/loraprs.cpp index 0ed75f4..b7a0a0a 100644 --- a/loraprs.cpp +++ b/loraprs.cpp @@ -1,36 +1,46 @@ #include "loraprs.h" -LoraPrs::LoraPrs(long loraFreq, const String &btName, const String &wifiName, const String &wifiKey, - const String &aprsLoginCallsign, const String &aprsPass, bool autoCorrectFreq) +LoraPrs::LoraPrs() : serialBt_() - , loraFreq_(loraFreq) - , autoCorrectFreq_(autoCorrectFreq) - , btName_(btName) - , wifiName_(wifiName) - , wifiKey_(wifiKey) , kissState_(KissState::Void) , kissCmd_(KissCmd::NoCmd) { - aprsLogin_ = ""; - aprsLogin_ += "user "; - aprsLogin_ += aprsLoginCallsign; - aprsLogin_ += " pass "; - aprsLogin_ += aprsPass; - aprsLogin_ += " vers "; - aprsLogin_ += CfgLoraprsVersion; - aprsLogin_ += "\n"; } -void LoraPrs::setup() +void LoraPrs::setup(const LoraPrsConfig &conf) { - setupWifi(wifiName_, wifiKey_); - setupLora(loraFreq_); - setupBt(btName_); + isClient_ = conf.IsClientMode; + loraFreq_ = conf.LoraFreq; + + aprsLogin_ = String("user ") + conf.AprsLogin + String(" pass ") + + conf.AprsPass + String(" vers ") + CfgLoraprsVersion; + if (conf.AprsFilter.length() > 0) { + aprsLogin_ += String(" filter ") + conf.AprsFilter; + } + aprsLogin_ += String("\n"); + + aprsHost_ = conf.AprsHost; + aprsPort_ = conf.AprsPort; + + autoCorrectFreq_ = conf.EnableAutoFreqCorrection; + addSignalReport_ = conf.EnableSignalReport; + persistentConn_ = conf.EnablePersistentAprsConnection; + enableIsToRf_ = conf.EnableIsToRf; + enableRepeater_ = conf.EnableRepeater; + + setupWifi(conf.WifiSsid, conf.WifiKey); + + setupLora(conf.LoraFreq, conf.LoraBw, conf.LoraSf, conf.LoraCodingRate, conf.LoraPower, conf.LoraSync); + setupBt(conf.BtName); + + if (!isClient_ && persistentConn_) { + reconnectAprsis(); + } } void LoraPrs::setupWifi(const String &wifiName, const String &wifiKey) { - if (wifiName.length() != 0) { + if (!isClient_) { Serial.print("WIFI connecting to " + wifiName); WiFi.setHostname("loraprs"); @@ -59,7 +69,21 @@ void LoraPrs::reconnectWifi() { Serial.println("ok"); } -void LoraPrs::setupLora(int loraFreq) +bool LoraPrs::reconnectAprsis() { + + Serial.print("APRSIS connecting..."); + + if (!aprsisConn_.connect(aprsHost_.c_str(), aprsPort_)) { + Serial.println("Failed to connect to " + aprsHost_ + ":" + aprsPort_); + return false; + } + Serial.println("ok"); + + aprsisConn_.print(aprsLogin_); + return true; +} + +void LoraPrs::setupLora(int loraFreq, int bw, byte sf, byte cr, byte pwr, byte sync) { Serial.print("LoRa init..."); @@ -69,11 +93,11 @@ void LoraPrs::setupLora(int loraFreq) Serial.print("."); delay(500); } - LoRa.setSyncWord(CfgSync); - LoRa.setSpreadingFactor(CfgSpread); - LoRa.setSignalBandwidth(CfgBw); - LoRa.setCodingRate4(CfgCodingRate); - LoRa.setTxPower(CfgPower); + LoRa.setSyncWord(sync); + LoRa.setSpreadingFactor(sf); + LoRa.setSignalBandwidth(bw); + LoRa.setCodingRate4(cr); + LoRa.setTxPower(pwr); LoRa.enableCrc(); Serial.println("ok"); @@ -81,7 +105,7 @@ void LoraPrs::setupLora(int loraFreq) void LoraPrs::setupBt(const String &btName) { - if (btName.length() != 0) { + if (isClient_) { Serial.print("BT init " + btName + "..."); if (serialBt_.begin(btName)) { @@ -96,111 +120,46 @@ void LoraPrs::setupBt(const String &btName) void LoraPrs::loop() { - if (WiFi.status() != WL_CONNECTED && wifiName_.length() != 0) { - reconnectWifi(); + if (!isClient_) { + if (WiFi.status() != WL_CONNECTED) { + reconnectWifi(); + } + if (!aprsisConn_.connected()) { + reconnectAprsis(); + } + while (aprsisConn_.available() > 0) { + char c = aprsisConn_.read(); + Serial.print(c); + } } if (serialBt_.available()) { - onBtReceived(); + onBtDataAvailable(); } if (int packetSize = LoRa.parsePacket()) { - onLoraReceived(packetSize); + onLoraDataAvailable(packetSize); } delay(10); } -void LoraPrs::onAprsReceived(const String &aprsMessage) +void LoraPrs::onRfAprsReceived(const String &aprsMessage) { - if (WiFi.status() == WL_CONNECTED) { - WiFiClient wifiClient; + if (isClient_) return; + + if (WiFi.status() != WL_CONNECTED) { + reconnectWifi(); + } + if (!aprsisConn_.connected()) { + reconnectAprsis(); + } + Serial.print(aprsMessage); + aprsisConn_.print(aprsMessage); - if (!wifiClient.connect(CfgAprsHost.c_str(), CfgAprsPort)) { - Serial.println("Failed to connect to " + CfgAprsHost + ":" + CfgAprsPort); - return; - } - wifiClient.print(aprsLogin_); - Serial.print(aprsMessage); - wifiClient.print(aprsMessage); - wifiClient.stop(); - } - else { - Serial.println("Wifi not connected, not sent"); + if (!persistentConn_) { + aprsisConn_.stop(); } } -String LoraPrs::decodeCall(byte *rxPtr) -{ - byte callsign[7]; - char ssid; - - byte *ptr = rxPtr; - - memset(callsign, 0, sizeof(callsign)); - - for (int i = 0; i < 6; i++) { - char c = *(ptr++) >> 1; - callsign[i] = (c == ' ') ? '\0' : c; - } - callsign[6] = '\0'; - ssid = (*ptr >> 1); - - String result = String((char*)callsign); - if (result.length() > 0 && ssid >= '0' && ssid <= '9') { - result += String("-") + String(ssid); - } - return result; -} - -String LoraPrs::convertAX25ToAprs(byte *rxPayload, int payloadLength, const String &signalReport) -{ - byte *rxPtr = rxPayload; - String srcCall, dstCall, rptFirst, rptSecond, result; - - dstCall = decodeCall(rxPtr); - rxPtr += 7; - - srcCall = decodeCall(rxPtr); - rxPtr += 7; - - if ((rxPayload[13] & 1) == 0) { - rptFirst = decodeCall(rxPtr); - rxPtr += 7; - - if ((rxPayload[20] & 1) == 0) { - rptSecond = decodeCall(rxPtr); - rxPtr += 7; - } - } - - if (*(rxPtr++) != AX25Ctrl::UI) return result; - if (*(rxPtr++) != AX25Pid::NoLayer3) return result; - - result += srcCall + String(">") + dstCall; - - if (rptFirst.length() > 0) { - result += String(",") + rptFirst; - } - if (rptSecond.length() > 0) { - result += String(",") + rptSecond; - } - - result += ":"; - - bool appendReport = ((char)*rxPtr == '='); - - while (rxPtr < rxPayload + payloadLength) { - result += String((char)*(rxPtr++)); - } - - if (appendReport) { - result += signalReport; - } - - result += "\n"; - - return result; -} - -void LoraPrs::onLoraReceived(int packetSize) +void LoraPrs::onLoraDataAvailable(int packetSize) { int rxBufIndex = 0; byte rxBuf[packetSize]; @@ -247,10 +206,10 @@ void LoraPrs::onLoraReceived(int packetSize) LoRa.setFrequency(loraFreq_); } - String aprsMsg = convertAX25ToAprs(rxBuf, rxBufIndex, signalReport); + String aprsMsg = AX25::Payload(rxBuf, rxBufIndex).ToText(addSignalReport_ ? signalReport : String()); if (aprsMsg.length() != 0) { - onAprsReceived(aprsMsg); + onRfAprsReceived(aprsMsg); } delay(50); @@ -262,7 +221,7 @@ void LoraPrs::kissResetState() kissState_ = KissState::Void; } -void LoraPrs::onBtReceived() +void LoraPrs::onBtDataAvailable() { while (serialBt_.available()) { byte txByte = serialBt_.read(); diff --git a/loraprs.h b/loraprs.h index 701ef6a..12bb21d 100644 --- a/loraprs.h +++ b/loraprs.h @@ -7,32 +7,61 @@ #include #include "BluetoothSerial.h" +#include "aprsmsg.h" + +class LoraPrsConfig +{ +public: + bool IsClientMode; + + long LoraFreq; + int LoraBw; + byte LoraSf; + byte LoraCodingRate; + byte LoraSync; + byte LoraPower; + + int AprsPort; + String AprsHost; + String AprsLogin; + String AprsPass; + String AprsFilter; + + String BtName; + + String WifiSsid; + String WifiKey; + + bool EnableSignalReport; + bool EnableAutoFreqCorrection; + bool EnablePersistentAprsConnection; + bool EnableIsToRf; + bool EnableRepeater; +}; class LoraPrs { public: - LoraPrs(long loraFreq, const String &btName, const String &wifiName, const String &wifiKey, - const String &aprsLoginCallsign, const String& aprsPass, bool autoCorrectFreq); + LoraPrs(); - void setup(); + void setup(const LoraPrsConfig &conf); void loop(); private: void setupWifi(const String &wifiName, const String &wifiKey); - void setupLora(int loraFreq); + void setupLora(int loraFreq, int bw, byte sf, byte cr, byte pwr, byte sync); void setupBt(const String &btName); void reconnectWifi(); - - void onLoraReceived(int packetSize); - void onBtReceived(); - void onAprsReceived(const String & aprsMessage); - + bool reconnectAprsis(); + + void onLoraDataAvailable(int packetSize); + void onBtDataAvailable(); + + void onRfAprsReceived(const String &aprsMessage); + void kissResetState(); - String convertAX25ToAprs(byte *rxPayload, int payloadLength, const String &signalReport); - String decodeCall(byte *rxPtr); - private: enum KissMarker { Fend = 0xc0, @@ -53,43 +82,31 @@ private: NoCmd = 0x80 }; - enum AX25Ctrl { - UI = 0x03 - }; - - enum AX25Pid { - NoLayer3 = 0xf0 - }; - const String CfgLoraprsVersion = "LoRAPRS 0.1"; - + const byte CfgPinSs = 5; const byte CfgPinRst = 26; const byte CfgPinDio0 = 14; - const int CfgBw = 125e3; - const byte CfgSpread = 12; - //const int CfgBw = 20e3; - //const byte CfgSpread = 9; - const byte CfgCodingRate = 7; - const byte CfgSync = 0xf3; - const byte CfgPower = 20; - - const int CfgAprsPort = 14580; - const String CfgAprsHost = "rotate.aprs2.net"; - private: + bool isClient_; long loraFreq_; - bool autoCorrectFreq_; - String btName_; - String wifiName_; - String wifiKey_; + + String aprsHost_; + int aprsPort_; String aprsLogin_; + bool autoCorrectFreq_; + bool addSignalReport_; + bool persistentConn_; + bool enableIsToRf_; + bool enableRepeater_; + KissCmd kissCmd_; KissState kissState_; BluetoothSerial serialBt_; + WiFiClient aprsisConn_; }; #endif // LORAPRS_H