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
- LoRa arduino library: https://github.com/sandeepmistry/arduino-LoRa
- Arduino Timer library: https://github.com/contrem/arduino-timer
- cppQueue library: https://github.com/SMFSW/Queue
# Software Setup
- 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

@ -3,62 +3,77 @@
#include "loraprs_service.h"
#define LED_BUILTIN 2
#define LED_TOGGLE_PERIOD 1000
#define LED_TOGGLE_PERIOD 1000
LoraPrs::Config cfg;
LoraPrs::Service loraPrsService;
#if __has_include("/tmp/esp32_loraprs_config.h")
#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
cfg.IsClientMode = true;
cfg.IsClientMode = CFG_IS_CLIENT_MODE;
// lora parameters
cfg.LoraFreq = 433.775E6;
cfg.LoraBw = 125e3;
cfg.LoraSf = 12;
cfg.LoraCodingRate = 7;
cfg.LoraFreq = CFG_LORA_FREQ;
cfg.LoraBw = CFG_LORA_BW;
cfg.LoraSf = CFG_LORA_SF;
cfg.LoraCodingRate = CFG_LORA_CR;
cfg.LoraSync = 0x34;
cfg.LoraPower = 20;
cfg.LoraEnableCrc = true; // set to false for speech data
cfg.LoraPower = CFG_LORA_PWR;
cfg.LoraEnableCrc = CFG_LORA_ENABLE_CRC; // set to false for speech data
// aprs configuration
cfg.AprsHost = "rotate.aprs2.net";
cfg.AprsPort = 14580;
cfg.AprsLogin = "NOCALL-10";
cfg.AprsPass = "12345";
cfg.AprsFilter = "r/35.60/139.80/25"; // multiple filters are space separated
cfg.AprsRawBeacon = "NOCALL-10>APZMDM,WIDE1-1:!0000.00N/00000.00E#LoRA 433.775MHz/BW125/SF12/CR7/0x34";
cfg.AprsLogin = CFG_APRS_LOGIN;
cfg.AprsPass = CFG_APRS_PASS;
cfg.AprsFilter = CFG_APRS_FILTER; // multiple filters are space separated
cfg.AprsRawBeacon = CFG_APRS_RAW_BKN;
cfg.AprsRawBeaconPeriodMinutes = 20;
// bluetooth device name
cfg.BtName = "loraprs";
cfg.BtName = CFG_BT_NAME;
// server mode wifi paramaters
cfg.WifiSsid = "<wifi ssid>";
cfg.WifiKey = "<wifi key>";
cfg.WifiSsid = CFG_WIFI_SSID;
cfg.WifiKey = CFG_WIFI_KEY;
// 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.EnablePersistentAprsConnection = true; // keep aprsis connection open, otherwise connect on new data only
cfg.EnableRfToIs = true; // send data from rf to aprsis
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
cfg.EnablePersistentAprsConnection = CFG_PERSISTENT_APRS; // keep aprsis connection open, otherwise connect on new data only
cfg.EnableRfToIs = CFG_RF_TO_IS; // send data from rf to aprsis
cfg.EnableIsToRf = CFG_IS_TO_RF; // send data from aprsis to rf
cfg.EnableRepeater = CFG_DIGIREPEAT; // digirepeat incoming packets
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() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, 1);
Serial.begin(115200);
while (!Serial);
LoraPrs::Config config;
initializeConfig();
loraPrsService.setup(cfg);
initializeConfig(config);
loraPrsService.setup(config);
watchdogLedTimer.every(LED_TOGGLE_PERIOD, toggleWatchdogLed);
}

Wyświetl plik

@ -7,10 +7,15 @@ Service::Service()
, kissCmd_(KissCmd::NoCmd)
, csmaP_(CfgCsmaPersistence)
, csmaSlotTime_(CfgCsmaSlotTimeMs)
, txQueue_(new cppQueue(sizeof(unsigned char), CfgLoraTxQueueSize))
, serialBt_()
{
}
Service::~Service() {
delete txQueue_;
}
void Service::setup(const Config &conf)
{
previousBeaconMs_ = 0;
@ -141,13 +146,14 @@ void Service::setupBt(const String &btName)
}
void Service::loop()
{
{
if (needsWifi() && WiFi.status() != WL_CONNECTED) {
reconnectWifi();
}
if (needsAprsis() && !aprsisConn_.connected() && persistentConn_) {
reconnectAprsis();
}
// RX path
if (int packetSize = LoRa.parsePacket()) {
onLoraDataAvailable(packetSize);
@ -155,14 +161,14 @@ void Service::loop()
// TX path
else {
if (random(0, 255) < csmaP_) {
if (serialBt_.available()) {
onBtDataAvailable();
}
else if (aprsisConn_.available() > 0) {
if (aprsisConn_.available() > 0) {
onAprsisDataAvailable();
}
else if (needsBeacon()) {
sendPeriodicBeacon();
}
else {
processTx();
}
}
else {
@ -179,7 +185,7 @@ void Service::sendPeriodicBeacon()
if (previousBeaconMs_ == 0 || currentMs - previousBeaconMs_ >= aprsBeaconPeriodMinutes_ * 60 * 1000) {
AX25::Payload payload(aprsBeacon_);
if (payload.IsValid()) {
sendToLora(payload);
sendAX25ToLora(payload);
if (enableRfToIs_) {
sendToAprsis(payload.ToString());
}
@ -222,7 +228,7 @@ void Service::onAprsisDataAvailable()
if (enableIsToRf_ && aprsisData.length() > 0) {
AX25::Payload payload(aprsisData);
if (payload.IsValid()) {
sendToLora(payload);
sendAX25ToLora(payload);
}
else {
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];
int bytesWritten = payload.ToBinary(buf, sizeof(buf));
@ -305,7 +311,7 @@ void Service::onLoraDataAvailable(int packetSize)
Serial.println("Packet sent to APRS-IS");
}
if (enableRepeater_ && payload.Digirepeat(ownCallsign_)) {
sendToLora(payload);
sendAX25ToLora(payload);
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()
{
kissCmd_ = KissCmd::NoCmd;
kissState_ = KissState::Void;
}
bool Service::loraBeginPacketAndWait()
{
for (int i = 0; i < CfgLoraTxWaitMs; i++) {
if (LoRa.beginPacket() == 1) {
bool Service::kissProcessCommand(unsigned char rxByte) {
switch (rxByte) {
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;
}
delay(1);
}
return false;
kissCmd_ = (KissCmd)rxByte;
return true;
}
void Service::onBtDataAvailable()
{
while (serialBt_.available()) {
int rxResult = serialBt_.read();
if (rxResult == -1) break;
byte rxByte = (byte)rxResult;
bool Service::kissReceiveByte(unsigned char rxByte) {
switch (kissState_) {
case KissState::Void:
if (rxByte == KissMarker::Fend) {
kissCmd_ = KissCmd::NoCmd;
kissState_ = KissState::GetCmd;
switch (kissState_) {
case KissState::Void:
if (rxByte == KissMarker::Fend) {
kissCmd_ = KissCmd::NoCmd;
kissState_ = KissState::GetCmd;
}
break;
case KissState::GetCmd:
if (rxByte != KissMarker::Fend) {
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;
case KissState::GetCmd:
if (rxByte != KissMarker::Fend) {
if (rxByte == KissCmd::Data) {
if (!loraBeginPacketAndWait()) {
Serial.println("LoRa buffer overflow, dropping packet");
kissResetState();
return;
}
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;
kissResetState();
}
else if (kissCmd_ == KissCmd::Data) {
LoRa.write(rxByte);
}
break;
case KissState::Escape:
if (rxByte == KissMarker::Tfend) {
LoRa.write(KissMarker::Fend);
kissState_ = KissState::GetData;
break;
case KissState::GetSlotTime:
csmaSlotTime_ = (long)rxByte * 10;
}
else if (rxByte == KissMarker::Tfesc) {
LoRa.write(KissMarker::Fesc);
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);
}
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;
}
}
else {
kissResetState();
}
break;
default:
break;
}
return true;
}
} // LoraPrs

Wyświetl plik

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