Add kiss tx queue and move config defiens to config

pull/15/head
sh123 2021-01-29 13:47:04 +02:00
rodzic baa3c06df9
commit 30601b75e8
5 zmienionych plików z 183 dodań i 123 usunięć

Wyświetl plik

@ -43,6 +43,7 @@ Install via libraries:
- Arduino ESP32 library: https://github.com/espressif/arduino-esp32 - Arduino ESP32 library: https://github.com/espressif/arduino-esp32
- LoRa arduino library: https://github.com/sandeepmistry/arduino-LoRa - LoRa arduino library: https://github.com/sandeepmistry/arduino-LoRa
- Arduino Timer library: https://github.com/contrem/arduino-timer - Arduino Timer library: https://github.com/contrem/arduino-timer
- cppQueue library: https://github.com/SMFSW/Queue
# Software Setup # Software Setup
- when setting up APRSDroid, use **"TNC (KISS)"** connection protocol in Connection Preferences -> Connection Protocol - when setting up APRSDroid, use **"TNC (KISS)"** connection protocol in Connection Preferences -> Connection Protocol

25
config.h 100644
Wyświetl plik

@ -0,0 +1,25 @@
#define CFG_IS_CLIENT_MODE true
#define CFG_LORA_FREQ 433.775E6
#define CFG_LORA_BW 125e3
#define CFG_LORA_SF 12
#define CFG_LORA_CR 7
#define CFG_LORA_PWR 20
#define CFG_LORA_ENABLE_CRC true
#define CFG_BT_NAME "loraprs"
#define CFG_APRS_LOGIN "NOCALL-10"
#define CFG_APRS_PASS "12345"
#define CFG_APRS_FILTER "r/35.60/139.80/25"
#define CFG_APRS_RAW_BKN "NOCALL-10>APZMDM,WIDE1-1:!0000.00N/00000.00E#LoRA 433.775MHz/BW125/SF12/CR7/0x34"
#define CFG_WIFI_SSID "<ssid>"
#define CFG_WIFI_KEY "<key>"
#define CFG_FREQ_CORR false
#define CFG_PERSISTENT_APRS true
#define CFG_DIGIREPEAT false
#define CFG_RF_TO_IS true
#define CFG_IS_TO_RF false
#define CFG_BEACON false

Wyświetl plik

@ -5,51 +5,64 @@
#define LED_BUILTIN 2 #define LED_BUILTIN 2
#define LED_TOGGLE_PERIOD 1000 #define LED_TOGGLE_PERIOD 1000
LoraPrs::Config cfg; #if __has_include("/tmp/esp32_loraprs_config.h")
LoraPrs::Service loraPrsService; #pragma message("Using external config")
#include "/tmp/esp32_loraprs_config.h"
#else
#pragma message("Using default config")
#include "config.h"
#endif
auto watchdogLedTimer = timer_create_default(); #if CFG_IS_CLIENT_MODE == true
#pragma message("Configured for client mode")
#else
#pragma message("Configured for server mode")
#endif
void initializeConfig() { void initializeConfig(LoraPrs::Config &cfg) {
// client/server mode switch // client/server mode switch
cfg.IsClientMode = true; cfg.IsClientMode = CFG_IS_CLIENT_MODE;
// lora parameters // lora parameters
cfg.LoraFreq = 433.775E6; cfg.LoraFreq = CFG_LORA_FREQ;
cfg.LoraBw = 125e3; cfg.LoraBw = CFG_LORA_BW;
cfg.LoraSf = 12; cfg.LoraSf = CFG_LORA_SF;
cfg.LoraCodingRate = 7; cfg.LoraCodingRate = CFG_LORA_CR;
cfg.LoraSync = 0x34; cfg.LoraSync = 0x34;
cfg.LoraPower = 20; cfg.LoraPower = CFG_LORA_PWR;
cfg.LoraEnableCrc = true; // set to false for speech data cfg.LoraEnableCrc = CFG_LORA_ENABLE_CRC; // set to false for speech data
// aprs configuration // aprs configuration
cfg.AprsHost = "rotate.aprs2.net"; cfg.AprsHost = "rotate.aprs2.net";
cfg.AprsPort = 14580; cfg.AprsPort = 14580;
cfg.AprsLogin = "NOCALL-10"; cfg.AprsLogin = CFG_APRS_LOGIN;
cfg.AprsPass = "12345"; cfg.AprsPass = CFG_APRS_PASS;
cfg.AprsFilter = "r/35.60/139.80/25"; // multiple filters are space separated cfg.AprsFilter = CFG_APRS_FILTER; // multiple filters are space separated
cfg.AprsRawBeacon = "NOCALL-10>APZMDM,WIDE1-1:!0000.00N/00000.00E#LoRA 433.775MHz/BW125/SF12/CR7/0x34"; cfg.AprsRawBeacon = CFG_APRS_RAW_BKN;
cfg.AprsRawBeaconPeriodMinutes = 20; cfg.AprsRawBeaconPeriodMinutes = 20;
// bluetooth device name // bluetooth device name
cfg.BtName = "loraprs"; cfg.BtName = CFG_BT_NAME;
// server mode wifi paramaters // server mode wifi paramaters
cfg.WifiSsid = "<wifi ssid>"; cfg.WifiSsid = CFG_WIFI_SSID;
cfg.WifiKey = "<wifi key>"; cfg.WifiKey = CFG_WIFI_KEY;
// configuration flags and features // configuration flags and features
cfg.EnableAutoFreqCorrection = false; // automatic tune to any incoming packet frequency cfg.EnableAutoFreqCorrection = CFG_FREQ_CORR; // automatic tune to any incoming packet frequency
cfg.EnableSignalReport = true; // signal report will be added to the comment sent to aprsis cfg.EnableSignalReport = true; // signal report will be added to the comment sent to aprsis
cfg.EnablePersistentAprsConnection = true; // keep aprsis connection open, otherwise connect on new data only cfg.EnablePersistentAprsConnection = CFG_PERSISTENT_APRS; // keep aprsis connection open, otherwise connect on new data only
cfg.EnableRfToIs = true; // send data from rf to aprsis cfg.EnableRfToIs = CFG_RF_TO_IS; // send data from rf to aprsis
cfg.EnableIsToRf = false; // send data from aprsis to rf cfg.EnableIsToRf = CFG_IS_TO_RF; // send data from aprsis to rf
cfg.EnableRepeater = false; // digirepeat incoming packets cfg.EnableRepeater = CFG_DIGIREPEAT; // digirepeat incoming packets
cfg.EnableBeacon = false; // enable periodic AprsRawBeacon beacon to rf and aprsis if rf to aprsis is enabled cfg.EnableBeacon = CFG_BEACON; // enable periodic AprsRawBeacon beacon to rf and aprsis if rf to aprsis is enabled
} }
LoraPrs::Service loraPrsService;
auto watchdogLedTimer = timer_create_default();
void setup() { void setup() {
pinMode(LED_BUILTIN, OUTPUT); pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, 1); digitalWrite(LED_BUILTIN, 1);
@ -57,8 +70,10 @@ void setup() {
Serial.begin(115200); Serial.begin(115200);
while (!Serial); while (!Serial);
initializeConfig(); LoraPrs::Config config;
loraPrsService.setup(cfg);
initializeConfig(config);
loraPrsService.setup(config);
watchdogLedTimer.every(LED_TOGGLE_PERIOD, toggleWatchdogLed); watchdogLedTimer.every(LED_TOGGLE_PERIOD, toggleWatchdogLed);
} }

Wyświetl plik

@ -7,10 +7,15 @@ Service::Service()
, kissCmd_(KissCmd::NoCmd) , kissCmd_(KissCmd::NoCmd)
, csmaP_(CfgCsmaPersistence) , csmaP_(CfgCsmaPersistence)
, csmaSlotTime_(CfgCsmaSlotTimeMs) , csmaSlotTime_(CfgCsmaSlotTimeMs)
, txQueue_(new cppQueue(sizeof(unsigned char), CfgLoraTxQueueSize))
, serialBt_() , serialBt_()
{ {
} }
Service::~Service() {
delete txQueue_;
}
void Service::setup(const Config &conf) void Service::setup(const Config &conf)
{ {
previousBeaconMs_ = 0; previousBeaconMs_ = 0;
@ -148,6 +153,7 @@ void Service::loop()
if (needsAprsis() && !aprsisConn_.connected() && persistentConn_) { if (needsAprsis() && !aprsisConn_.connected() && persistentConn_) {
reconnectAprsis(); reconnectAprsis();
} }
// RX path // RX path
if (int packetSize = LoRa.parsePacket()) { if (int packetSize = LoRa.parsePacket()) {
onLoraDataAvailable(packetSize); onLoraDataAvailable(packetSize);
@ -155,15 +161,15 @@ void Service::loop()
// TX path // TX path
else { else {
if (random(0, 255) < csmaP_) { if (random(0, 255) < csmaP_) {
if (serialBt_.available()) { if (aprsisConn_.available() > 0) {
onBtDataAvailable();
}
else if (aprsisConn_.available() > 0) {
onAprsisDataAvailable(); onAprsisDataAvailable();
} }
else if (needsBeacon()) { else if (needsBeacon()) {
sendPeriodicBeacon(); sendPeriodicBeacon();
} }
else {
processTx();
}
} }
else { else {
delay(csmaSlotTime_); delay(csmaSlotTime_);
@ -179,7 +185,7 @@ void Service::sendPeriodicBeacon()
if (previousBeaconMs_ == 0 || currentMs - previousBeaconMs_ >= aprsBeaconPeriodMinutes_ * 60 * 1000) { if (previousBeaconMs_ == 0 || currentMs - previousBeaconMs_ >= aprsBeaconPeriodMinutes_ * 60 * 1000) {
AX25::Payload payload(aprsBeacon_); AX25::Payload payload(aprsBeacon_);
if (payload.IsValid()) { if (payload.IsValid()) {
sendToLora(payload); sendAX25ToLora(payload);
if (enableRfToIs_) { if (enableRfToIs_) {
sendToAprsis(payload.ToString()); sendToAprsis(payload.ToString());
} }
@ -222,7 +228,7 @@ void Service::onAprsisDataAvailable()
if (enableIsToRf_ && aprsisData.length() > 0) { if (enableIsToRf_ && aprsisData.length() > 0) {
AX25::Payload payload(aprsisData); AX25::Payload payload(aprsisData);
if (payload.IsValid()) { if (payload.IsValid()) {
sendToLora(payload); sendAX25ToLora(payload);
} }
else { else {
Serial.println("Invalid payload from APRSIS"); Serial.println("Invalid payload from APRSIS");
@ -230,7 +236,7 @@ void Service::onAprsisDataAvailable()
} }
} }
bool Service::sendToLora(const AX25::Payload &payload) bool Service::sendAX25ToLora(const AX25::Payload &payload)
{ {
byte buf[512]; byte buf[512];
int bytesWritten = payload.ToBinary(buf, sizeof(buf)); int bytesWritten = payload.ToBinary(buf, sizeof(buf));
@ -305,7 +311,7 @@ void Service::onLoraDataAvailable(int packetSize)
Serial.println("Packet sent to APRS-IS"); Serial.println("Packet sent to APRS-IS");
} }
if (enableRepeater_ && payload.Digirepeat(ownCallsign_)) { if (enableRepeater_ && payload.Digirepeat(ownCallsign_)) {
sendToLora(payload); sendAX25ToLora(payload);
Serial.println("Packet digirepeated"); Serial.println("Packet digirepeated");
} }
} }
@ -315,102 +321,111 @@ void Service::onLoraDataAvailable(int packetSize)
} }
} }
void Service::processTx()
{
while (serialBt_.available() || !txQueue_->isEmpty()) {
if (serialBt_.available()) {
int rxResult = serialBt_.read();
if (rxResult != -1) {
byte rxByte = (byte)rxResult;
if (!txQueue_->push((void *)&rxByte)) {
Serial.println("TX queue is full");
}
}
}
if (!txQueue_->isEmpty()) {
byte qRxByte;
if (txQueue_->peek((void *)&qRxByte)) {
if (kissReceiveByte(qRxByte)) {
txQueue_->drop();
}
}
}
}
}
void Service::kissResetState() void Service::kissResetState()
{ {
kissCmd_ = KissCmd::NoCmd; kissCmd_ = KissCmd::NoCmd;
kissState_ = KissState::Void; kissState_ = KissState::Void;
} }
bool Service::loraBeginPacketAndWait() bool Service::kissProcessCommand(unsigned char rxByte) {
{
for (int i = 0; i < CfgLoraTxWaitMs; i++) { switch (rxByte) {
if (LoRa.beginPacket() == 1) { case KissCmd::Data:
if (LoRa.beginPacket() == 0) return false;
kissState_ = KissState::GetData;
break;
case KissCmd::P:
kissState_ = KissState::GetP;
break;
case KissCmd::SlotTime:
kissState_ = KissState::GetSlotTime;
break;
default:
// unknown command
kissResetState();
return true; return true;
}
delay(1);
} }
return false; kissCmd_ = (KissCmd)rxByte;
return true;
} }
void Service::onBtDataAvailable() bool Service::kissReceiveByte(unsigned char rxByte) {
{
while (serialBt_.available()) {
int rxResult = serialBt_.read(); switch (kissState_) {
if (rxResult == -1) break; case KissState::Void:
if (rxByte == KissMarker::Fend) {
byte rxByte = (byte)rxResult; kissCmd_ = KissCmd::NoCmd;
kissState_ = KissState::GetCmd;
switch (kissState_) { }
case KissState::Void: break;
if (rxByte == KissMarker::Fend) { case KissState::GetCmd:
kissCmd_ = KissCmd::NoCmd; if (rxByte != KissMarker::Fend) {
kissState_ = KissState::GetCmd; if (!kissProcessCommand(rxByte)) return false;
}
break;
case KissState::GetP:
csmaP_ = rxByte;
kissState_ = KissState::GetData;
break;
case KissState::GetSlotTime:
csmaSlotTime_ = (long)rxByte * 10;
kissState_ = KissState::GetData;
break;
case KissState::GetData:
if (rxByte == KissMarker::Fesc) {
kissState_ = KissState::Escape;
}
else if (rxByte == KissMarker::Fend) {
if (kissCmd_ == KissCmd::Data) {
LoRa.endPacket(true);
} }
break; kissResetState();
case KissState::GetCmd: }
if (rxByte != KissMarker::Fend) { else if (kissCmd_ == KissCmd::Data) {
if (rxByte == KissCmd::Data) { LoRa.write(rxByte);
if (!loraBeginPacketAndWait()) { }
Serial.println("LoRa buffer overflow, dropping packet"); break;
kissResetState(); case KissState::Escape:
return; if (rxByte == KissMarker::Tfend) {
} LoRa.write(KissMarker::Fend);
kissCmd_ = (KissCmd)rxByte;
kissState_ = KissState::GetData;
}
else if (rxByte == KissCmd::P) {
kissCmd_ = (KissCmd)rxByte;
kissState_ = KissState::GetP;
}
else if (rxByte == KissCmd::SlotTime) {
kissCmd_ = (KissCmd)rxByte;
kissState_ = KissState::GetSlotTime;
}
else {
kissResetState();
}
}
break;
case KissState::GetP:
csmaP_ = rxByte;
kissState_ = KissState::GetData; kissState_ = KissState::GetData;
break; }
case KissState::GetSlotTime: else if (rxByte == KissMarker::Tfesc) {
csmaSlotTime_ = (long)rxByte * 10; LoRa.write(KissMarker::Fesc);
kissState_ = KissState::GetData; kissState_ = KissState::GetData;
break; }
case KissState::GetData: else {
if (rxByte == KissMarker::Fesc) { kissResetState();
kissState_ = KissState::Escape; }
} break;
else if (rxByte == KissMarker::Fend) { default:
if (kissCmd_ == KissCmd::Data) { break;
LoRa.endPacket(true);
}
kissResetState();
}
else if (kissCmd_ == KissCmd::Data) {
LoRa.write(rxByte);
}
break;
case KissState::Escape:
if (rxByte == KissMarker::Tfend) {
LoRa.write(KissMarker::Fend);
kissState_ = KissState::GetData;
}
else if (rxByte == KissMarker::Tfesc) {
LoRa.write(KissMarker::Fesc);
kissState_ = KissState::GetData;
}
else {
kissResetState();
}
break;
default:
break;
}
} }
return true;
} }
} // LoraPrs } // LoraPrs

Wyświetl plik

@ -5,6 +5,7 @@
#include <SPI.h> #include <SPI.h>
#include <LoRa.h> #include <LoRa.h>
#include <WiFi.h> #include <WiFi.h>
#include <cppQueue.h>
#include "BluetoothSerial.h" #include "BluetoothSerial.h"
#include "ax25_payload.h" #include "ax25_payload.h"
@ -16,6 +17,7 @@ class Service
{ {
public: public:
Service(); Service();
~Service();
void setup(const Config &conf); void setup(const Config &conf);
void loop(); void loop();
@ -28,16 +30,17 @@ private:
void reconnectWifi(); void reconnectWifi();
bool reconnectAprsis(); bool reconnectAprsis();
void processTx();
void onLoraDataAvailable(int packetSize); void onLoraDataAvailable(int packetSize);
void onBtDataAvailable();
void onAprsisDataAvailable(); void onAprsisDataAvailable();
void sendPeriodicBeacon(); void sendPeriodicBeacon();
void sendToAprsis(String aprsMessage); void sendToAprsis(String aprsMessage);
bool sendToLora(const AX25::Payload &payload); bool sendAX25ToLora(const AX25::Payload &payload);
bool loraBeginPacketAndWait(); bool kissReceiveByte(unsigned char rxByte);
bool kissProcessCommand(unsigned char rxByte);
void kissResetState(); void kissResetState();
inline bool needsAprsis() const { return !isClient_ && (enableRfToIs_ || enableIsToRf_); } inline bool needsAprsis() const { return !isClient_ && (enableRfToIs_ || enableIsToRf_); }
@ -72,7 +75,7 @@ private:
const String CfgLoraprsVersion = "LoRAPRS 0.1"; const String CfgLoraprsVersion = "LoRAPRS 0.1";
const int CfgPollDelayMs = 5; const int CfgPollDelayMs = 5;
const int CfgLoraTxWaitMs = 2000; const int CfgLoraTxQueueSize = 1024;
// tx when lower than this value from random 0..255 // tx when lower than this value from random 0..255
// use lower value for high traffic, use 255 for real time // use lower value for high traffic, use 255 for real time
@ -110,6 +113,7 @@ private:
long previousBeaconMs_; long previousBeaconMs_;
byte csmaP_; byte csmaP_;
long csmaSlotTime_; long csmaSlotTime_;
cppQueue *txQueue_;
// peripherals // peripherals
BluetoothSerial serialBt_; BluetoothSerial serialBt_;