kopia lustrzana https://github.com/sh123/esp32_loraprs
Switched over to KISS usage
rodzic
0d19b3cb44
commit
66a0c65c18
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2019 sh123
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -13,7 +13,7 @@ Can be used in two modes:
|
||||||
- Arduino Timer library: https://github.com/contrem/arduino-timer
|
- Arduino Timer library: https://github.com/contrem/arduino-timer
|
||||||
|
|
||||||
# Software Setup
|
# Software Setup
|
||||||
- when setting up APRSDroid, use **"TNC (plaintext TNC2)"** connection protocol in Connection Preferences -> Connection Protocol
|
- when setting up APRSDroid, use **"TNC (KISS)"** connection protocol in Connection Preferences -> Connection Protocol
|
||||||
- go to esp32_loraprs.ino and make next changes based on your requirements
|
- go to esp32_loraprs.ino and make next changes based on your requirements
|
||||||
- comment out / remove **LORAPRS_CLIENT** define if you are planning to run server mode for APRS-IS iGate
|
- comment out / remove **LORAPRS_CLIENT** define if you are planning to run server mode for APRS-IS iGate
|
||||||
- for server mode fill **LORAPRS_WIFI_SSID** and **LORAPRS_WIFI_KEY** with your WiFI AP data
|
- for server mode fill **LORAPRS_WIFI_SSID** and **LORAPRS_WIFI_KEY** with your WiFI AP data
|
||||||
|
|
|
@ -7,7 +7,11 @@
|
||||||
|
|
||||||
#define LORAPRS_CLIENT
|
#define LORAPRS_CLIENT
|
||||||
|
|
||||||
|
#ifdef LORAPRS_CLIENT
|
||||||
|
#define LORAPRS_FREQ 432.499E6
|
||||||
|
#else
|
||||||
#define LORAPRS_FREQ 432.5E6
|
#define LORAPRS_FREQ 432.5E6
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef LORAPRS_CLIENT
|
#ifdef LORAPRS_CLIENT
|
||||||
#define LORAPRS_BT_NAME "loraprs_client"
|
#define LORAPRS_BT_NAME "loraprs_client"
|
||||||
|
|
208
loraprs.cpp
208
loraprs.cpp
|
@ -1,12 +1,14 @@
|
||||||
#include "loraprs.h"
|
#include "loraprs.h"
|
||||||
|
|
||||||
LoraPrs::LoraPrs(int loraFreq, String btName, String wifiName,
|
LoraPrs::LoraPrs(int loraFreq, const String & btName, const String & wifiName,
|
||||||
String wifiKey, String aprsLoginCallsign, String aprsPass)
|
const String & wifiKey, const String & aprsLoginCallsign, const String & aprsPass)
|
||||||
: serialBt_()
|
: serialBt_()
|
||||||
, loraFreq_(loraFreq)
|
, loraFreq_(loraFreq)
|
||||||
, btName_(btName)
|
, btName_(btName)
|
||||||
, wifiName_(wifiName)
|
, wifiName_(wifiName)
|
||||||
, wifiKey_(wifiKey)
|
, wifiKey_(wifiKey)
|
||||||
|
, kissState_(KissState::Void)
|
||||||
|
, kissCmd_(KissCmd::NoCmd)
|
||||||
{
|
{
|
||||||
aprsLogin_ = "";
|
aprsLogin_ = "";
|
||||||
aprsLogin_ += "user ";
|
aprsLogin_ += "user ";
|
||||||
|
@ -25,7 +27,7 @@ void LoraPrs::setup()
|
||||||
setupBt(btName_);
|
setupBt(btName_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoraPrs::setupWifi(String wifiName, String wifiKey)
|
void LoraPrs::setupWifi(const String & wifiName, const String & wifiKey)
|
||||||
{
|
{
|
||||||
if (wifiName.length() != 0) {
|
if (wifiName.length() != 0) {
|
||||||
Serial.print("WIFI connecting to " + wifiName);
|
Serial.print("WIFI connecting to " + wifiName);
|
||||||
|
@ -71,11 +73,12 @@ void LoraPrs::setupLora(int loraFreq)
|
||||||
LoRa.setSignalBandwidth(CfgBw);
|
LoRa.setSignalBandwidth(CfgBw);
|
||||||
LoRa.setCodingRate4(CfgCodingRate);
|
LoRa.setCodingRate4(CfgCodingRate);
|
||||||
LoRa.setTxPower(CfgPower);
|
LoRa.setTxPower(CfgPower);
|
||||||
|
LoRa.enableCrc();
|
||||||
|
|
||||||
Serial.println("ok");
|
Serial.println("ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoraPrs::setupBt(String btName)
|
void LoraPrs::setupBt(const String & btName)
|
||||||
{
|
{
|
||||||
if (btName.length() != 0) {
|
if (btName.length() != 0) {
|
||||||
Serial.print("BT init " + btName + "...");
|
Serial.print("BT init " + btName + "...");
|
||||||
|
@ -98,16 +101,14 @@ void LoraPrs::loop()
|
||||||
if (serialBt_.available()) {
|
if (serialBt_.available()) {
|
||||||
onBtReceived();
|
onBtReceived();
|
||||||
}
|
}
|
||||||
if (LoRa.parsePacket()) {
|
if (int packetSize = LoRa.parsePacket()) {
|
||||||
onLoraReceived();
|
onLoraReceived(packetSize);
|
||||||
}
|
}
|
||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoraPrs::onAprsReceived(String aprsMessage)
|
void LoraPrs::onAprsReceived(const String & aprsMessage)
|
||||||
{
|
{
|
||||||
Serial.print(aprsMessage);
|
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
WiFiClient wifiClient;
|
WiFiClient wifiClient;
|
||||||
|
|
||||||
|
@ -116,35 +117,196 @@ void LoraPrs::onAprsReceived(String aprsMessage)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wifiClient.print(aprsLogin_);
|
wifiClient.print(aprsLogin_);
|
||||||
|
Serial.print(aprsMessage);
|
||||||
wifiClient.print(aprsMessage);
|
wifiClient.print(aprsMessage);
|
||||||
wifiClient.stop();
|
wifiClient.stop();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Serial.println("Wifi not connected, not sent");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoraPrs::onLoraReceived()
|
String LoraPrs::decodeCall(byte *rxPtr)
|
||||||
{
|
{
|
||||||
String buf;
|
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 (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;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoraPrs::onLoraReceived(int packetSize)
|
||||||
|
{
|
||||||
|
int rxBufIndex = 0;
|
||||||
|
byte rxBuf[packetSize];
|
||||||
|
|
||||||
|
serialBt_.write(KissMarker::Fend);
|
||||||
|
serialBt_.write(KissCmd::Data);
|
||||||
|
|
||||||
while (LoRa.available()) {
|
while (LoRa.available()) {
|
||||||
char c = (char)LoRa.read();
|
byte rxByte = LoRa.read();
|
||||||
if (c != '\n')
|
|
||||||
buf += c;
|
if (rxByte == KissMarker::Fend) {
|
||||||
|
serialBt_.write(KissMarker::Fesc);
|
||||||
|
serialBt_.write(KissMarker::Tfend);
|
||||||
}
|
}
|
||||||
for (int i; i < buf.length(); i++) {
|
else if (rxByte == KissMarker::Fesc) {
|
||||||
serialBt_.write((uint8_t)buf[i]);
|
serialBt_.write(KissMarker::Fesc);
|
||||||
|
serialBt_.write(KissMarker::Tfesc);
|
||||||
}
|
}
|
||||||
onAprsReceived(buf + " " +
|
else {
|
||||||
"RSSI: " + String(LoRa.packetRssi()) + ", " +
|
rxBuf[rxBufIndex++] = rxByte;
|
||||||
"SNR: " + String(LoRa.packetSnr()) + "dB, " +
|
serialBt_.write(rxByte);
|
||||||
"ERR: " + String(LoRa.packetFrequencyError()) + "Hz\n");
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serialBt_.write(KissMarker::Fend);
|
||||||
|
|
||||||
|
String signalReport = String(" ") +
|
||||||
|
String("RSSI: ") +
|
||||||
|
String(LoRa.packetRssi()) +
|
||||||
|
String(", ") +
|
||||||
|
String("SNR: ") +
|
||||||
|
String(LoRa.packetSnr()) +
|
||||||
|
String("dB, ") +
|
||||||
|
String("ERR: ") +
|
||||||
|
String(LoRa.packetFrequencyError()) +
|
||||||
|
String("Hz\n");
|
||||||
|
|
||||||
|
String aprsMsg = convertAX25ToAprs(rxBuf, rxBufIndex, signalReport);
|
||||||
|
|
||||||
|
if (aprsMsg.length() != 0) {
|
||||||
|
onAprsReceived(aprsMsg);
|
||||||
|
}
|
||||||
|
|
||||||
delay(50);
|
delay(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoraPrs::kissResetState()
|
||||||
|
{
|
||||||
|
kissCmd_ = KissCmd::NoCmd;
|
||||||
|
kissState_ = KissState::Void;
|
||||||
|
}
|
||||||
|
|
||||||
void LoraPrs::onBtReceived()
|
void LoraPrs::onBtReceived()
|
||||||
{
|
{
|
||||||
LoRa.beginPacket();
|
|
||||||
while (serialBt_.available()) {
|
while (serialBt_.available()) {
|
||||||
char c = (char)serialBt_.read();
|
byte txByte = serialBt_.read();
|
||||||
LoRa.print(c);
|
|
||||||
|
switch (kissState_) {
|
||||||
|
case KissState::Void:
|
||||||
|
if (txByte == KissMarker::Fend) {
|
||||||
|
kissCmd_ = KissCmd::NoCmd;
|
||||||
|
kissState_ = KissState::GetCmd;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case KissState::GetCmd:
|
||||||
|
if (txByte != KissMarker::Fend) {
|
||||||
|
if (txByte == KissCmd::Data) {
|
||||||
|
LoRa.beginPacket();
|
||||||
|
kissCmd_ = (KissCmd)txByte;
|
||||||
|
kissState_ = KissState::GetData;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kissResetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KissState::GetData:
|
||||||
|
if (txByte == KissMarker::Fesc) {
|
||||||
|
kissState_ = KissState::Escape;
|
||||||
|
}
|
||||||
|
else if (txByte == KissMarker::Fend) {
|
||||||
|
if (kissCmd_ == KissCmd::Data) {
|
||||||
LoRa.endPacket();
|
LoRa.endPacket();
|
||||||
|
}
|
||||||
|
kissResetState();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LoRa.write(txByte);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KissState::Escape:
|
||||||
|
if (txByte == KissMarker::Tfend) {
|
||||||
|
LoRa.write(KissMarker::Fend);
|
||||||
|
kissState_ = KissState::GetData;
|
||||||
|
}
|
||||||
|
else if (txByte == KissMarker::Tfesc) {
|
||||||
|
LoRa.write(KissMarker::Fesc);
|
||||||
|
kissState_ = KissState::GetData;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kissResetState();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(20);
|
||||||
}
|
}
|
||||||
|
|
79
loraprs.h
79
loraprs.h
|
@ -11,6 +11,60 @@
|
||||||
class LoraPrs
|
class LoraPrs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
LoraPrs(int loraFreq, const String & btName, const String & wifiName,
|
||||||
|
const String & wifiKey, const String & aprsLoginCallsign, const String & aprsPass);
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupWifi(const String & wifiName, const String & wifiKey);
|
||||||
|
void setupLora(int loraFreq);
|
||||||
|
void setupBt(const String & btName);
|
||||||
|
|
||||||
|
void reconnectWifi();
|
||||||
|
|
||||||
|
void onLoraReceived(int packetSize);
|
||||||
|
void onBtReceived();
|
||||||
|
|
||||||
|
void kissResetState();
|
||||||
|
|
||||||
|
void onAprsReceived(const String & aprsMessage);
|
||||||
|
|
||||||
|
bool isAX25CrcValid(byte *rxPayload, int payloadLength);
|
||||||
|
String convertAX25ToAprs(byte *rxPayload, int payloadLength, const String & signalReport);
|
||||||
|
String decodeCall(byte *rxPtr);
|
||||||
|
|
||||||
|
uint16_t updateCrcCcit(uint8_t newByte, uint16_t prevCrc);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum KissMarker {
|
||||||
|
Fend = 0xc0,
|
||||||
|
Fesc = 0xdb,
|
||||||
|
Tfend = 0xdc,
|
||||||
|
Tfesc = 0xdd
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KissState {
|
||||||
|
Void = 0,
|
||||||
|
GetCmd,
|
||||||
|
GetData,
|
||||||
|
Escape
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KissCmd {
|
||||||
|
Data = 0x00,
|
||||||
|
NoCmd = 0x80
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AX25Ctrl {
|
||||||
|
UI = 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AX25Pid {
|
||||||
|
NoLayer3 = 0xf0
|
||||||
|
};
|
||||||
|
|
||||||
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
||||||
|
|
||||||
const byte CfgPinSs = 5;
|
const byte CfgPinSs = 5;
|
||||||
|
@ -26,32 +80,17 @@ public:
|
||||||
const int CfgAprsPort = 14580;
|
const int CfgAprsPort = 14580;
|
||||||
const String CfgAprsHost = "rotate.aprs2.net";
|
const String CfgAprsHost = "rotate.aprs2.net";
|
||||||
|
|
||||||
public:
|
|
||||||
LoraPrs(int loraFreq, String btName, String wifiName,
|
|
||||||
String wifiKey, String aprsLoginCallsign, String aprsPass);
|
|
||||||
|
|
||||||
void setup();
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupWifi(String wifiName, String wifiKey);
|
|
||||||
void setupLora(int loraFreq);
|
|
||||||
void setupBt(String btName);
|
|
||||||
|
|
||||||
void reconnectWifi();
|
|
||||||
|
|
||||||
void onLoraReceived();
|
|
||||||
void onBtReceived();
|
|
||||||
void onAprsReceived(String aprsMessage);
|
|
||||||
|
|
||||||
private:
|
|
||||||
BluetoothSerial serialBt_;
|
|
||||||
|
|
||||||
int loraFreq_;
|
int loraFreq_;
|
||||||
String btName_;
|
String btName_;
|
||||||
String wifiName_;
|
String wifiName_;
|
||||||
String wifiKey_;
|
String wifiKey_;
|
||||||
String aprsLogin_;
|
String aprsLogin_;
|
||||||
|
|
||||||
|
KissCmd kissCmd_;
|
||||||
|
KissState kissState_;
|
||||||
|
|
||||||
|
BluetoothSerial serialBt_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LORAPRS_H
|
#endif // LORAPRS_H
|
||||||
|
|
Ładowanie…
Reference in New Issue