Config and AX25 message reafactoring (#5)

* Refactoring

* Add support for aprsis filter and print out incoming packets into the serial
pull/6/head
sh123 2020-06-12 17:34:23 +03:00 zatwierdzone przez GitHub
rodzic 80963bdf11
commit 1ae790c5d5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 292 dodań i 196 usunięć

88
aprsmsg.cpp 100644
Wyświetl plik

@ -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

38
aprsmsg.h 100644
Wyświetl plik

@ -0,0 +1,38 @@
#ifndef APRSMSG_H
#define APRSMSG_H
#include <Arduino.h>
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

Wyświetl plik

@ -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 "<your access point name>"
#define LORAPRS_WIFI_KEY "<your access point 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 = "<wifi ssid>";
cfg.WifiKey = "<wifi key>";
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);
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -7,32 +7,61 @@
#include <WiFi.h>
#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