kopia lustrzana https://github.com/sh123/esp32_loraprs
Implement digirepeating and own beacon announcement
rodzic
9c06c93ec0
commit
752128197e
|
@ -96,17 +96,18 @@ bool Callsign::fromString(String callsign)
|
||||||
{
|
{
|
||||||
// "ABCDEF-XX"
|
// "ABCDEF-XX"
|
||||||
if (callsign.length() > CallsignSize + 2 || callsign.length() == 0) return false;
|
if (callsign.length() > CallsignSize + 2 || callsign.length() == 0) return false;
|
||||||
|
|
||||||
int delimIndex = callsign.indexOf('-');
|
int delimIndex = callsign.indexOf('-');
|
||||||
|
|
||||||
// "ABCDEF-"
|
// "ABCDEF-"
|
||||||
if (delimIndex = callsign.length() - 1) return false;
|
if (delimIndex != -1 && delimIndex == callsign.length() - 1) return false;
|
||||||
|
|
||||||
call_ = callsign;
|
call_ = callsign;
|
||||||
ssid_ = 0;
|
ssid_ = 0;
|
||||||
|
|
||||||
if (delimIndex == -1) {
|
if (delimIndex == -1) {
|
||||||
// "ABCDEFG"
|
// "ABCDEF"
|
||||||
if (callsign.length() >= CallsignSize) return false;
|
if (call_.length() >= CallsignSize) return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
call_ = callsign.substring(0, delimIndex);
|
call_ = callsign.substring(0, delimIndex);
|
||||||
|
@ -134,6 +135,8 @@ bool Callsign::fromBinary(const byte *rxPtr, int length)
|
||||||
|
|
||||||
ssid_ = (*ptr >> 1) & 0x0f;
|
ssid_ = (*ptr >> 1) & 0x0f;
|
||||||
call_ = String((char*)callsign);
|
call_ = String((char*)callsign);
|
||||||
|
|
||||||
|
if (call_.length() == 0) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,21 +105,26 @@ bool Payload::fromBinary(const byte *rxPayload, int payloadLength)
|
||||||
|
|
||||||
// destination address
|
// destination address
|
||||||
dstCall_ = AX25::Callsign(rxPtr, CallsignSize);
|
dstCall_ = AX25::Callsign(rxPtr, CallsignSize);
|
||||||
|
if (!dstCall_.IsValid()) return false;
|
||||||
rxPtr += CallsignSize;
|
rxPtr += CallsignSize;
|
||||||
if (rxPtr >= rxEnd) return false;
|
if (rxPtr >= rxEnd) return false;
|
||||||
|
|
||||||
// source address
|
// source address
|
||||||
srcCall_ = AX25::Callsign(rxPtr, CallsignSize);
|
srcCall_ = AX25::Callsign(rxPtr, CallsignSize);
|
||||||
|
if (!srcCall_.IsValid()) return false;
|
||||||
rxPtr += CallsignSize;
|
rxPtr += CallsignSize;
|
||||||
if (rxPtr >= rxEnd) return false;
|
if (rxPtr >= rxEnd) return false;
|
||||||
|
|
||||||
rptCallsCount_ = 0;
|
rptCallsCount_ = 0;
|
||||||
|
|
||||||
// digipeater addresses
|
// digipeater addresses
|
||||||
for (int i = 0; i < RptMaxCount; i++) {
|
for (int i = 0, j = 0; i < RptMaxCount; i++) {
|
||||||
if ((rxPayload[(i + 2) * CallsignSize - 1] & 1) == 0) {
|
if ((rxPayload[(i + 2) * CallsignSize - 1] & 1) == 0) {
|
||||||
rptCalls_[i] = AX25::Callsign(rxPtr, CallsignSize);
|
rptCalls_[j] = AX25::Callsign(rxPtr, CallsignSize);
|
||||||
rptCallsCount_++;
|
if (rptCalls_[j].IsValid()) {
|
||||||
|
rptCallsCount_++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
rxPtr += CallsignSize;
|
rxPtr += CallsignSize;
|
||||||
if (rxPtr >= rxEnd) return false;
|
if (rxPtr >= rxEnd) return false;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +155,8 @@ bool Payload::fromString(String inputText)
|
||||||
if (rptIndex == -1 || infoIndex == -1) return false;
|
if (rptIndex == -1 || infoIndex == -1) return false;
|
||||||
|
|
||||||
info_ = inputText.substring(infoIndex + 1);
|
info_ = inputText.substring(infoIndex + 1);
|
||||||
srcCall_ = inputText.substring(0, rptIndex);
|
srcCall_ = AX25::Callsign(inputText.substring(0, rptIndex));
|
||||||
|
if (!srcCall_.IsValid()) return false;
|
||||||
String paths = inputText.substring(rptIndex + 1, infoIndex);
|
String paths = inputText.substring(rptIndex + 1, infoIndex);
|
||||||
|
|
||||||
rptCallsCount_ = 0;
|
rptCallsCount_ = 0;
|
||||||
|
@ -159,11 +165,14 @@ bool Payload::fromString(String inputText)
|
||||||
int nextIndex = paths.indexOf(',', index);
|
int nextIndex = paths.indexOf(',', index);
|
||||||
String pathItem = paths.substring(index, nextIndex == -1 ? paths.length() : nextIndex);
|
String pathItem = paths.substring(index, nextIndex == -1 ? paths.length() : nextIndex);
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
dstCall_ = pathItem;
|
dstCall_ = AX25::Callsign(pathItem);
|
||||||
|
if (!dstCall_.IsValid()) return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rptCallsCount_++;
|
rptCalls_[rptCallsCount_] = AX25::Callsign(pathItem);
|
||||||
rptCalls_[rptCallsCount_ - 1] = pathItem;
|
if (rptCalls_[rptCallsCount_].IsValid()) {
|
||||||
|
rptCallsCount_++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (nextIndex == -1) break;
|
if (nextIndex == -1) break;
|
||||||
index = nextIndex + 1;
|
index = nextIndex + 1;
|
||||||
|
|
|
@ -11,32 +11,42 @@ LoraPrs loraPrs;
|
||||||
auto watchdogLedTimer = timer_create_default();
|
auto watchdogLedTimer = timer_create_default();
|
||||||
|
|
||||||
void initializeConfig() {
|
void initializeConfig() {
|
||||||
cfg.IsClientMode = true;
|
|
||||||
|
|
||||||
cfg.LoraFreq = 433.775E6; // 433.7688E6;
|
// client/server mode switch
|
||||||
|
cfg.IsClientMode = false;
|
||||||
|
|
||||||
|
// lora parameters
|
||||||
|
cfg.LoraFreq = 433.775E6;
|
||||||
cfg.LoraBw = 125e3;
|
cfg.LoraBw = 125e3;
|
||||||
cfg.LoraSf = 12;
|
cfg.LoraSf = 12;
|
||||||
cfg.LoraCodingRate = 7;
|
cfg.LoraCodingRate = 7;
|
||||||
cfg.LoraSync = 0xf3;
|
cfg.LoraSync = 0xf3;
|
||||||
cfg.LoraPower = 20;
|
cfg.LoraPower = 20;
|
||||||
|
|
||||||
|
// aprs configuration
|
||||||
cfg.AprsHost = "rotate.aprs2.net";
|
cfg.AprsHost = "rotate.aprs2.net";
|
||||||
cfg.AprsPort = 14580;
|
cfg.AprsPort = 14580;
|
||||||
cfg.AprsLogin = "NOCALL-1";
|
cfg.AprsLogin = "NOCALL-1";
|
||||||
cfg.AprsPass = "00000";
|
cfg.AprsPass = "12345";
|
||||||
cfg.AprsFilter = "r/35.60/139.80/25";
|
cfg.AprsFilter = "r/35.60/139.80/25";
|
||||||
|
cfg.AprsRawBeacon = "NOCALL-1>APZMDM,WIDE1-1:!0000.00N/00000.00E#LoRA 433.775MHz/BW125/SF12/CR7/0xf3";
|
||||||
|
cfg.AprsRawBeaconPeriodMinutes = 20;
|
||||||
|
|
||||||
|
// bluetooth device name
|
||||||
cfg.BtName = "loraprs";
|
cfg.BtName = "loraprs";
|
||||||
|
|
||||||
|
// server mode wifi paramaters
|
||||||
cfg.WifiSsid = "<wifi ssid>";
|
cfg.WifiSsid = "<wifi ssid>";
|
||||||
cfg.WifiKey = "<wifi key>";
|
cfg.WifiKey = "<wifi key>";
|
||||||
|
|
||||||
cfg.EnableSignalReport = true;
|
// configuration flags and features
|
||||||
cfg.EnableAutoFreqCorrection = true;
|
cfg.EnableAutoFreqCorrection = true; // automatic tune to any incoming packet frequency
|
||||||
cfg.EnablePersistentAprsConnection = true;
|
cfg.EnableSignalReport = true; // signal report will be added to the comment sent to aprsis
|
||||||
cfg.EnableRfToIs = true;
|
cfg.EnablePersistentAprsConnection = true; // keep aprsis connection open, otherwise connect on new data only
|
||||||
cfg.EnableIsToRf = false;
|
cfg.EnableRfToIs = true; // send data from rf to aprsis
|
||||||
cfg.EnableRepeater = false;
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
98
loraprs.cpp
98
loraprs.cpp
|
@ -9,6 +9,9 @@ LoraPrs::LoraPrs()
|
||||||
|
|
||||||
void LoraPrs::setup(const LoraPrsConfig &conf)
|
void LoraPrs::setup(const LoraPrsConfig &conf)
|
||||||
{
|
{
|
||||||
|
previousBeaconMs_ = 0;
|
||||||
|
|
||||||
|
// config
|
||||||
isClient_ = conf.IsClientMode;
|
isClient_ = conf.IsClientMode;
|
||||||
loraFreq_ = conf.LoraFreq;
|
loraFreq_ = conf.LoraFreq;
|
||||||
ownCallsign_ = conf.AprsLogin;
|
ownCallsign_ = conf.AprsLogin;
|
||||||
|
@ -22,6 +25,8 @@ void LoraPrs::setup(const LoraPrsConfig &conf)
|
||||||
|
|
||||||
aprsHost_ = conf.AprsHost;
|
aprsHost_ = conf.AprsHost;
|
||||||
aprsPort_ = conf.AprsPort;
|
aprsPort_ = conf.AprsPort;
|
||||||
|
aprsBeacon_ = conf.AprsRawBeacon;
|
||||||
|
aprsBeaconPeriodMinutes_ = conf.AprsRawBeaconPeriodMinutes;
|
||||||
|
|
||||||
autoCorrectFreq_ = conf.EnableAutoFreqCorrection;
|
autoCorrectFreq_ = conf.EnableAutoFreqCorrection;
|
||||||
addSignalReport_ = conf.EnableSignalReport;
|
addSignalReport_ = conf.EnableSignalReport;
|
||||||
|
@ -29,13 +34,20 @@ void LoraPrs::setup(const LoraPrsConfig &conf)
|
||||||
enableRfToIs_ = conf.EnableRfToIs;
|
enableRfToIs_ = conf.EnableRfToIs;
|
||||||
enableIsToRf_ = conf.EnableIsToRf;
|
enableIsToRf_ = conf.EnableIsToRf;
|
||||||
enableRepeater_ = conf.EnableRepeater;
|
enableRepeater_ = conf.EnableRepeater;
|
||||||
|
enableBeacon_ = conf.EnableBeacon;
|
||||||
setupWifi(conf.WifiSsid, conf.WifiKey);
|
|
||||||
|
// peripherals
|
||||||
setupLora(conf.LoraFreq, conf.LoraBw, conf.LoraSf, conf.LoraCodingRate, conf.LoraPower, conf.LoraSync);
|
setupLora(conf.LoraFreq, conf.LoraBw, conf.LoraSf, conf.LoraCodingRate, conf.LoraPower, conf.LoraSync);
|
||||||
setupBt(conf.BtName);
|
|
||||||
|
if (needsWifi()) {
|
||||||
|
setupWifi(conf.WifiSsid, conf.WifiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsBt()) {
|
||||||
|
setupBt(conf.BtName);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isClient_ && persistentConn_) {
|
if (needsAprsis() && persistentConn_) {
|
||||||
reconnectAprsis();
|
reconnectAprsis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,31 +119,27 @@ void LoraPrs::setupLora(int loraFreq, int bw, byte sf, byte cr, byte pwr, byte s
|
||||||
|
|
||||||
void LoraPrs::setupBt(const String &btName)
|
void LoraPrs::setupBt(const String &btName)
|
||||||
{
|
{
|
||||||
if (isClient_) {
|
Serial.print("BT init " + btName + "...");
|
||||||
Serial.print("BT init " + btName + "...");
|
|
||||||
|
|
||||||
if (serialBt_.begin(btName)) {
|
if (serialBt_.begin(btName)) {
|
||||||
Serial.println("ok");
|
Serial.println("ok");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("failed");
|
Serial.println("failed");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoraPrs::loop()
|
void LoraPrs::loop()
|
||||||
{
|
{
|
||||||
if (!isClient_) {
|
if (needsWifi() && WiFi.status() != WL_CONNECTED) {
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
reconnectWifi();
|
||||||
reconnectWifi();
|
}
|
||||||
}
|
if (needsAprsis() && !aprsisConn_.connected() && persistentConn_) {
|
||||||
if (!aprsisConn_.connected()) {
|
reconnectAprsis();
|
||||||
reconnectAprsis();
|
}
|
||||||
}
|
if (aprsisConn_.available() > 0) {
|
||||||
if (aprsisConn_.available() > 0) {
|
onAprsisDataAvailable();
|
||||||
onAprsisDataAvailable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (serialBt_.available()) {
|
if (serialBt_.available()) {
|
||||||
onBtDataAvailable();
|
onBtDataAvailable();
|
||||||
|
@ -139,15 +147,37 @@ void LoraPrs::loop()
|
||||||
if (int packetSize = LoRa.parsePacket()) {
|
if (int packetSize = LoRa.parsePacket()) {
|
||||||
onLoraDataAvailable(packetSize);
|
onLoraDataAvailable(packetSize);
|
||||||
}
|
}
|
||||||
|
if (needsBeacon()) {
|
||||||
|
sendBeacon();
|
||||||
|
}
|
||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoraPrs::sendBeacon()
|
||||||
|
{
|
||||||
|
long currentMs = millis();
|
||||||
|
|
||||||
|
if (previousBeaconMs_ == 0 || currentMs - previousBeaconMs_ >= aprsBeaconPeriodMinutes_ * 60 * 1000) {
|
||||||
|
AX25::Payload payload(aprsBeacon_);
|
||||||
|
if (payload.IsValid()) {
|
||||||
|
sendToLora(payload);
|
||||||
|
if (enableRfToIs_) {
|
||||||
|
sendToAprsis(payload.ToString(String()));
|
||||||
|
}
|
||||||
|
Serial.println("Sent beacon");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.println("Beacon payload is invalid");
|
||||||
|
}
|
||||||
|
previousBeaconMs_ = currentMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
void LoraPrs::sendToAprsis(String aprsMessage)
|
void LoraPrs::sendToAprsis(String aprsMessage)
|
||||||
{
|
{
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
if (needsWifi() && WiFi.status() != WL_CONNECTED) {
|
||||||
reconnectWifi();
|
reconnectWifi();
|
||||||
}
|
}
|
||||||
if (!aprsisConn_.connected()) {
|
if (needsAprsis() && !aprsisConn_.connected()) {
|
||||||
reconnectAprsis();
|
reconnectAprsis();
|
||||||
}
|
}
|
||||||
aprsisConn_.print(aprsMessage);
|
aprsisConn_.print(aprsMessage);
|
||||||
|
@ -171,17 +201,17 @@ void LoraPrs::onAprsisDataAvailable()
|
||||||
|
|
||||||
if (enableIsToRf_ && aprsisData.length() > 0) {
|
if (enableIsToRf_ && aprsisData.length() > 0) {
|
||||||
AX25::Payload payload(aprsisData);
|
AX25::Payload payload(aprsisData);
|
||||||
sendToLora(payload);
|
if (payload.IsValid()) {
|
||||||
|
sendToLora(payload);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.println("Invalid payload from APRSIS");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoraPrs::sendToLora(const AX25::Payload &payload)
|
bool LoraPrs::sendToLora(const AX25::Payload &payload)
|
||||||
{
|
{
|
||||||
if (!payload.IsValid()) {
|
|
||||||
Serial.println("Invalid payload from APRSIS");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte buf[512];
|
byte buf[512];
|
||||||
int bytesWritten = payload.ToBinary(buf, sizeof(buf));
|
int bytesWritten = payload.ToBinary(buf, sizeof(buf));
|
||||||
if (bytesWritten <= 0) {
|
if (bytesWritten <= 0) {
|
||||||
|
@ -191,7 +221,7 @@ bool LoraPrs::sendToLora(const AX25::Payload &payload)
|
||||||
}
|
}
|
||||||
LoRa.beginPacket();
|
LoRa.beginPacket();
|
||||||
LoRa.write(buf, bytesWritten);
|
LoRa.write(buf, bytesWritten);
|
||||||
LoRa.endPacket(true);
|
LoRa.endPacket();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +277,7 @@ void LoraPrs::onLoraDataAvailable(int packetSize)
|
||||||
if (payload.IsValid()) {
|
if (payload.IsValid()) {
|
||||||
String textPayload = payload.ToString(addSignalReport_ ? signalReport : String());
|
String textPayload = payload.ToString(addSignalReport_ ? signalReport : String());
|
||||||
Serial.print(textPayload);
|
Serial.print(textPayload);
|
||||||
|
|
||||||
if (enableRfToIs_ && !isClient_) {
|
if (enableRfToIs_ && !isClient_) {
|
||||||
sendToAprsis(textPayload);
|
sendToAprsis(textPayload);
|
||||||
}
|
}
|
||||||
|
|
21
loraprs.h
21
loraprs.h
|
@ -25,6 +25,8 @@ struct LoraPrsConfig
|
||||||
String AprsLogin;
|
String AprsLogin;
|
||||||
String AprsPass;
|
String AprsPass;
|
||||||
String AprsFilter;
|
String AprsFilter;
|
||||||
|
String AprsRawBeacon;
|
||||||
|
int AprsRawBeaconPeriodMinutes;
|
||||||
|
|
||||||
String BtName;
|
String BtName;
|
||||||
|
|
||||||
|
@ -37,6 +39,7 @@ struct LoraPrsConfig
|
||||||
bool EnableRfToIs;
|
bool EnableRfToIs;
|
||||||
bool EnableIsToRf;
|
bool EnableIsToRf;
|
||||||
bool EnableRepeater;
|
bool EnableRepeater;
|
||||||
|
bool EnableBeacon;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LoraPrs
|
class LoraPrs
|
||||||
|
@ -59,11 +62,18 @@ private:
|
||||||
void onBtDataAvailable();
|
void onBtDataAvailable();
|
||||||
void onAprsisDataAvailable();
|
void onAprsisDataAvailable();
|
||||||
|
|
||||||
|
void sendBeacon();
|
||||||
|
|
||||||
void sendToAprsis(String aprsMessage);
|
void sendToAprsis(String aprsMessage);
|
||||||
bool sendToLora(const AX25::Payload &payload);
|
bool sendToLora(const AX25::Payload &payload);
|
||||||
|
|
||||||
void kissResetState();
|
void kissResetState();
|
||||||
|
|
||||||
|
inline bool needsAprsis() const { return !isClient_ && (enableRfToIs_ || enableIsToRf_); }
|
||||||
|
inline bool needsWifi() const { return needsAprsis(); }
|
||||||
|
inline bool needsBt() const { return isClient_; }
|
||||||
|
inline bool needsBeacon() const { return !isClient_ && enableBeacon_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum KissMarker {
|
enum KissMarker {
|
||||||
Fend = 0xc0,
|
Fend = 0xc0,
|
||||||
|
@ -85,21 +95,23 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
||||||
const String CfgAprsSoftware = "APZLRA";
|
|
||||||
|
|
||||||
const byte CfgPinSs = 5;
|
const byte CfgPinSs = 5;
|
||||||
const byte CfgPinRst = 26;
|
const byte CfgPinRst = 26;
|
||||||
const byte CfgPinDio0 = 14;
|
const byte CfgPinDio0 = 14;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// config
|
||||||
bool isClient_;
|
bool isClient_;
|
||||||
long loraFreq_;
|
long loraFreq_;
|
||||||
|
|
||||||
String ownCallsign_;
|
String ownCallsign_;
|
||||||
|
|
||||||
String aprsHost_;
|
String aprsHost_;
|
||||||
int aprsPort_;
|
int aprsPort_;
|
||||||
String aprsLogin_;
|
String aprsLogin_;
|
||||||
|
String aprsBeacon_;
|
||||||
|
int aprsBeaconPeriodMinutes_;
|
||||||
|
|
||||||
bool autoCorrectFreq_;
|
bool autoCorrectFreq_;
|
||||||
bool addSignalReport_;
|
bool addSignalReport_;
|
||||||
|
@ -107,10 +119,15 @@ private:
|
||||||
bool enableRfToIs_;
|
bool enableRfToIs_;
|
||||||
bool enableIsToRf_;
|
bool enableIsToRf_;
|
||||||
bool enableRepeater_;
|
bool enableRepeater_;
|
||||||
|
bool enableBeacon_;
|
||||||
|
|
||||||
|
// state
|
||||||
KissCmd kissCmd_;
|
KissCmd kissCmd_;
|
||||||
KissState kissState_;
|
KissState kissState_;
|
||||||
|
|
||||||
|
long previousBeaconMs_;
|
||||||
|
|
||||||
|
// peripherals
|
||||||
BluetoothSerial serialBt_;
|
BluetoothSerial serialBt_;
|
||||||
WiFiClient aprsisConn_;
|
WiFiClient aprsisConn_;
|
||||||
};
|
};
|
||||||
|
|
Ładowanie…
Reference in New Issue