kopia lustrzana https://github.com/sh123/esp32_loraprs
Implement IS to RF gating (#6)
* Add aprs string parsing * Added method for conversion to binary ax25 packet * Check if message is valid before forwarding to APRS-IS * Added APRIS data sending into RF * Added Lora sending for IS packets * Fixed bugs in packet encoding * Updated readmepull/15/head
rodzic
c88ed243bc
commit
b945b46fbb
|
@ -43,7 +43,7 @@ All work was done on ESP32-WROOM with custom made LoRa shield, if your ESP32 boa
|
|||
- other useful options are
|
||||
- `cfg.EnableSignalReport` set to `true` to enable signal report, it will be added as a comment to APRS-IS submitted location
|
||||
- `cfg.EnablePersistentAprsConnection` set to `false` to avoid keeping connection open to APRS-IS
|
||||
- **[INACTIVE YET]** `cfg.EnableIsToRf` set to `true` to forward APRS-IS traffic to RF
|
||||
- `cfg.EnableIsToRf` set to `true` to forward APRS-IS traffic to RF, see also `cfg.AprsFilter` for traffic filtering
|
||||
- **[INACTIVE YET]** `cfg.EnableRepeater` set to `true` to enable packet repeater
|
||||
|
||||
# Test Results
|
||||
|
|
147
ax25_payload.cpp
147
ax25_payload.cpp
|
@ -3,11 +3,71 @@
|
|||
namespace AX25 {
|
||||
|
||||
Payload::Payload(byte *rxPayload, int payloadLength)
|
||||
: rptCallsCount_(0)
|
||||
{
|
||||
parsePayload(rxPayload, payloadLength);
|
||||
isValid_ = parsePayload(rxPayload, payloadLength);
|
||||
}
|
||||
|
||||
String Payload::ToText(const String &customComment)
|
||||
Payload::Payload(String inputText)
|
||||
: rptCallsCount_(0)
|
||||
{
|
||||
isValid_ = parseString(inputText);
|
||||
}
|
||||
|
||||
void Payload::Dump()
|
||||
{
|
||||
Serial.println();
|
||||
Serial.print("valid: "); Serial.println(isValid_);
|
||||
Serial.println("src: " + srcCall_);
|
||||
Serial.println("dst: " + dstCall_);
|
||||
Serial.print("rpt: ");
|
||||
for (int i = 0; i < rptCallsCount_; i++) {
|
||||
Serial.print(rptCalls_[i] + " ");
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println("info: " + info_);
|
||||
}
|
||||
|
||||
int Payload::ToBinary(byte *txPayload, int bufferLength)
|
||||
{
|
||||
byte *txPtr = txPayload;
|
||||
byte *txEnd = txPayload + bufferLength;
|
||||
|
||||
// destination address
|
||||
if (!encodeCall(dstCall_, txPtr, CallsignSize)) return 0;
|
||||
txPtr += CallsignSize;
|
||||
if (txPtr >= txEnd) return 0;
|
||||
|
||||
// source address
|
||||
if (!encodeCall(srcCall_, txPtr, CallsignSize)) return 0;
|
||||
txPtr += CallsignSize;
|
||||
if (txPtr >= txEnd) return 0;
|
||||
|
||||
// digipeater addresses
|
||||
for (int i = 0; i < rptCallsCount_; i++) {
|
||||
if (!encodeCall(rptCalls_[i], txPtr, CallsignSize)) return 0;
|
||||
txPtr += CallsignSize;
|
||||
if (txPtr >= txEnd) return 0;
|
||||
}
|
||||
*(txPtr - 1) |= 1;
|
||||
|
||||
// control + protocol id
|
||||
if ((txPtr + 2) >= txEnd) return 0;
|
||||
*(txPtr++) = AX25Ctrl::UI;
|
||||
*(txPtr++) = AX25Pid::NoLayer3;
|
||||
|
||||
// information field
|
||||
for (int i = 0; i < info_.length(); i++) {
|
||||
char c = info_.charAt(i);
|
||||
if (c == '\0') break;
|
||||
*(txPtr++) = c;
|
||||
if (txPtr >= txEnd) break;
|
||||
}
|
||||
|
||||
return (int)(txPtr-txPayload);
|
||||
}
|
||||
|
||||
String Payload::ToText(String customComment)
|
||||
{
|
||||
String txt = srcCall_ + String(">") + dstCall_;
|
||||
|
||||
|
@ -26,15 +86,15 @@ String Payload::ToText(const String &customComment)
|
|||
return txt + String("\n");
|
||||
}
|
||||
|
||||
bool Payload::parsePayload(byte *rxPayload, int payloadLength)
|
||||
bool Payload::parsePayload(const byte *rxPayload, int payloadLength)
|
||||
{
|
||||
byte *rxPtr = rxPayload;
|
||||
byte *rxEnd = rxPayload + payloadLength;
|
||||
const byte *rxPtr = rxPayload;
|
||||
const byte *rxEnd = rxPayload + payloadLength;
|
||||
|
||||
// destination address
|
||||
dstCall_ = decodeCall(rxPtr);
|
||||
rxPtr += CallsignSize;
|
||||
if (rxPtr >= rxPayload + payloadLength) return false;
|
||||
if (rxPtr >= rxEnd) return false;
|
||||
|
||||
// source address
|
||||
srcCall_ = decodeCall(rxPtr);
|
||||
|
@ -69,25 +129,88 @@ bool Payload::parsePayload(byte *rxPayload, int payloadLength)
|
|||
return true;
|
||||
}
|
||||
|
||||
String Payload::decodeCall(byte *rxPtr)
|
||||
bool Payload::parseString(String inputText)
|
||||
{
|
||||
int rptIndex = inputText.indexOf('>');
|
||||
int infoIndex = inputText.indexOf(':');
|
||||
|
||||
// bad format
|
||||
if (rptIndex == -1 || infoIndex == -1) return false;
|
||||
|
||||
info_ = inputText.substring(infoIndex + 1);
|
||||
srcCall_ = inputText.substring(0, rptIndex);
|
||||
String paths = inputText.substring(rptIndex + 1, infoIndex);
|
||||
|
||||
rptCallsCount_ = 0;
|
||||
int index = 0;
|
||||
while (rptCallsCount_ <= RptMaxCount) {
|
||||
int nextIndex = paths.indexOf(',', index);
|
||||
String pathItem = paths.substring(index, nextIndex == -1 ? paths.length() : nextIndex);
|
||||
if (index == 0) {
|
||||
dstCall_ = pathItem;
|
||||
}
|
||||
else {
|
||||
rptCallsCount_++;
|
||||
rptCalls_[rptCallsCount_ - 1] = pathItem;
|
||||
}
|
||||
if (nextIndex == -1) break;
|
||||
index = nextIndex + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Payload::encodeCall(String inputText, byte *txPtr, int bufferLength)
|
||||
{
|
||||
if (bufferLength < CallsignSize) return false;
|
||||
|
||||
String callsign = inputText;
|
||||
byte ssid = 0;
|
||||
|
||||
int delimIndex = inputText.indexOf('-');
|
||||
if (delimIndex + 1 >= inputText.length()) return false;
|
||||
|
||||
if (delimIndex != -1) {
|
||||
callsign = inputText.substring(0, delimIndex);
|
||||
ssid = inputText.substring(delimIndex + 1).toInt();
|
||||
}
|
||||
|
||||
byte *ptr = txPtr;
|
||||
|
||||
memset(ptr, 0, bufferLength);
|
||||
|
||||
for (int i = 0; i < CallsignSize - 1; i++) {
|
||||
if (i < callsign.length()) {
|
||||
char c = callsign.charAt(i);
|
||||
*(ptr++) = c << 1;
|
||||
}
|
||||
else {
|
||||
*(ptr++) = char(' ') << 1;
|
||||
}
|
||||
}
|
||||
*(txPtr + CallsignSize - 1) = ssid << 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String Payload::decodeCall(const byte *rxPtr)
|
||||
{
|
||||
byte callsign[CallsignSize];
|
||||
char ssid;
|
||||
|
||||
byte *ptr = rxPtr;
|
||||
const byte *ptr = rxPtr;
|
||||
|
||||
memset(callsign, 0, sizeof(callsign));
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int i = 0; i < CallsignSize - 1; i++) {
|
||||
char c = *(ptr++) >> 1;
|
||||
callsign[i] = (c == ' ') ? '\0' : c;
|
||||
}
|
||||
callsign[CallsignSize-1] = '\0';
|
||||
ssid = (*ptr >> 1);
|
||||
byte ssid = (*ptr >> 1) & 0x0f;
|
||||
|
||||
String result = String((char*)callsign);
|
||||
|
||||
if (result.length() > 0 && ssid >= '0' && ssid <= '9') {
|
||||
if (result.length() > 0 && ssid != 0) {
|
||||
result += String("-") + String(ssid);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -9,11 +9,21 @@ class Payload
|
|||
{
|
||||
public:
|
||||
Payload(byte *rxPayload, int payloadLength);
|
||||
String ToText(const String &customComment);
|
||||
Payload(String inputText);
|
||||
|
||||
inline bool IsValid() const { return isValid_; }
|
||||
|
||||
String ToText(String customComment);
|
||||
int ToBinary(byte *txPayload, int bufferLength);
|
||||
|
||||
void Dump();
|
||||
|
||||
private:
|
||||
String decodeCall(byte *rxPtr);
|
||||
bool parsePayload(byte *rxPayload, int payloadLength);
|
||||
String decodeCall(const byte *rxPtr);
|
||||
bool encodeCall(String callsign, byte *txPtr, int bufferLength);
|
||||
|
||||
bool parseString(String inputText);
|
||||
bool parsePayload(const byte *rxPayload, int payloadLength);
|
||||
|
||||
private:
|
||||
enum AX25Ctrl {
|
||||
|
@ -28,6 +38,7 @@ private:
|
|||
const int RptMaxCount = 7;
|
||||
|
||||
private:
|
||||
bool isValid_;
|
||||
String srcCall_, dstCall_;
|
||||
String rptCalls_[7];
|
||||
int rptCallsCount_;
|
||||
|
|
61
loraprs.cpp
61
loraprs.cpp
|
@ -56,8 +56,8 @@ void LoraPrs::setupWifi(const String &wifiName, const String &wifiKey)
|
|||
}
|
||||
}
|
||||
|
||||
void LoraPrs::reconnectWifi() {
|
||||
|
||||
void LoraPrs::reconnectWifi()
|
||||
{
|
||||
Serial.print("WIFI re-connecting...");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0,0,0,0)) {
|
||||
|
@ -69,8 +69,8 @@ void LoraPrs::reconnectWifi() {
|
|||
Serial.println("ok");
|
||||
}
|
||||
|
||||
bool LoraPrs::reconnectAprsis() {
|
||||
|
||||
bool LoraPrs::reconnectAprsis()
|
||||
{
|
||||
Serial.print("APRSIS connecting...");
|
||||
|
||||
if (!aprsisConn_.connect(aprsHost_.c_str(), aprsPort_)) {
|
||||
|
@ -127,9 +127,8 @@ void LoraPrs::loop()
|
|||
if (!aprsisConn_.connected()) {
|
||||
reconnectAprsis();
|
||||
}
|
||||
while (aprsisConn_.available() > 0) {
|
||||
char c = aprsisConn_.read();
|
||||
Serial.print(c);
|
||||
if (aprsisConn_.available() > 0) {
|
||||
onAprsisDataAvailable();
|
||||
}
|
||||
}
|
||||
if (serialBt_.available()) {
|
||||
|
@ -141,8 +140,10 @@ void LoraPrs::loop()
|
|||
delay(10);
|
||||
}
|
||||
|
||||
void LoraPrs::onRfAprsReceived(const String &aprsMessage)
|
||||
void LoraPrs::onRfAprsReceived(String aprsMessage)
|
||||
{
|
||||
Serial.print(aprsMessage);
|
||||
|
||||
if (isClient_) return;
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
|
@ -151,7 +152,6 @@ void LoraPrs::onRfAprsReceived(const String &aprsMessage)
|
|||
if (!aprsisConn_.connected()) {
|
||||
reconnectAprsis();
|
||||
}
|
||||
Serial.print(aprsMessage);
|
||||
aprsisConn_.print(aprsMessage);
|
||||
|
||||
if (!persistentConn_) {
|
||||
|
@ -159,6 +159,40 @@ void LoraPrs::onRfAprsReceived(const String &aprsMessage)
|
|||
}
|
||||
}
|
||||
|
||||
void LoraPrs::onAprsisDataAvailable()
|
||||
{
|
||||
String aprsisData;
|
||||
|
||||
while (aprsisConn_.available() > 0) {
|
||||
char c = aprsisConn_.read();
|
||||
if (c == '\r') continue;
|
||||
Serial.print(c);
|
||||
if (c == '\n') break;
|
||||
aprsisData += c;
|
||||
}
|
||||
|
||||
if (enableIsToRf_ && aprsisData.length() > 0) {
|
||||
AX25::Payload payload(aprsisData);
|
||||
|
||||
if (payload.IsValid()) {
|
||||
|
||||
byte buf[512];
|
||||
int bytesWritten = payload.ToBinary(buf, sizeof(buf));
|
||||
if (bytesWritten > 0) {
|
||||
LoRa.beginPacket();
|
||||
LoRa.write(buf, bytesWritten);
|
||||
LoRa.endPacket(true);
|
||||
}
|
||||
else {
|
||||
Serial.println("Failed to serialize payload");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Serial.println("Invalid payload from APRSIS");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoraPrs::onLoraDataAvailable(int packetSize)
|
||||
{
|
||||
int rxBufIndex = 0;
|
||||
|
@ -206,10 +240,13 @@ void LoraPrs::onLoraDataAvailable(int packetSize)
|
|||
LoRa.setFrequency(loraFreq_);
|
||||
}
|
||||
|
||||
String aprsMsg = AX25::Payload(rxBuf, rxBufIndex).ToText(addSignalReport_ ? signalReport : String());
|
||||
AX25::Payload payload(rxBuf, rxBufIndex);
|
||||
|
||||
if (aprsMsg.length() != 0) {
|
||||
onRfAprsReceived(aprsMsg);
|
||||
if (payload.IsValid()) {
|
||||
onRfAprsReceived(payload.ToText(addSignalReport_ ? signalReport : String()));
|
||||
}
|
||||
else {
|
||||
Serial.println("Invalid payload from LoRA");
|
||||
}
|
||||
|
||||
delay(50);
|
||||
|
|
|
@ -56,8 +56,9 @@ private:
|
|||
|
||||
void onLoraDataAvailable(int packetSize);
|
||||
void onBtDataAvailable();
|
||||
void onAprsisDataAvailable();
|
||||
|
||||
void onRfAprsReceived(const String &aprsMessage);
|
||||
void onRfAprsReceived(String aprsMessage);
|
||||
|
||||
void kissResetState();
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue