From 4c178d8c65f101f2159e337886dff9743f1c8ebc Mon Sep 17 00:00:00 2001 From: sh123 Date: Fri, 5 Feb 2021 18:57:06 +0200 Subject: [PATCH] Add support for KISS extension for radio control and signal reports --- config.h | 1 + esp32_loraprs.ino | 3 ++- kiss_processor.cpp | 50 ++++++++++++++++++++++++++++++++------------- kiss_processor.h | 32 ++++++++++++++--------------- loraprs_config.h | 3 ++- loraprs_service.cpp | 36 ++++++++++++++++++++++++++++++-- loraprs_service.h | 21 ++++++++++++++++++- 7 files changed, 110 insertions(+), 36 deletions(-) diff --git a/config.h b/config.h index 1dcb874..6ffd12e 100644 --- a/config.h +++ b/config.h @@ -33,3 +33,4 @@ #define CFG_RF_TO_IS true #define CFG_IS_TO_RF false #define CFG_BEACON false +#define CFG_KISS_EXTENSIONS false diff --git a/esp32_loraprs.ino b/esp32_loraprs.ino index 8c174ec..729075d 100644 --- a/esp32_loraprs.ino +++ b/esp32_loraprs.ino @@ -25,7 +25,7 @@ void initializeConfig(LoraPrs::Config &cfg) { // client/server mode switch cfg.IsClientMode = CFG_IS_CLIENT_MODE; - + // lora parameters cfg.LoraFreq = CFG_LORA_FREQ; cfg.LoraBw = CFG_LORA_BW; @@ -64,6 +64,7 @@ void initializeConfig(LoraPrs::Config &cfg) { 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 + cfg.EnableKissExtensions = CFG_KISS_EXTENSIONS; // radio control and signal reports } LoraPrs::Service loraPrsService; diff --git a/kiss_processor.cpp b/kiss_processor.cpp index a82f70d..10fc066 100644 --- a/kiss_processor.cpp +++ b/kiss_processor.cpp @@ -61,12 +61,13 @@ void Processor::resetState() state_ = State::Void; } -bool Processor::processCommand(unsigned char rxByte) { +bool Processor::processCommand(byte rxByte) { switch (rxByte) { case Cmd::Data: if (!onRigTxBegin()) return false; state_ = State::GetData; + dataType_ = DataType::Raw; break; case Cmd::P: state_ = State::GetP; @@ -74,6 +75,11 @@ bool Processor::processCommand(unsigned char rxByte) { case Cmd::SlotTime: state_ = State::GetSlotTime; break; + case Cmd::RadioControl: + state_ = State::GetData; + dataType_ = DataType::Control; + cmdBuffer_.clear(); + break; default: // unknown command resetState(); @@ -83,7 +89,34 @@ bool Processor::processCommand(unsigned char rxByte) { return true; } -bool Processor::receiveByte(unsigned char rxByte) { +void Processor::processData(byte rxByte) { + switch (rxByte) { + case Marker::Fesc: + state_ = State::Escape; + break; + case Marker::Fend: + if (cmd_ == Cmd::Data) { + if (dataType_ == DataType::Raw) { + onRigTxEnd(); + } else if (dataType_ == DataType::Control) { + onRadioControlCommand(cmdBuffer_); + } + } + resetState(); + break; + default: + if (cmd_ == Cmd::Data) { + if (dataType_ == DataType::Raw) { + onRigTx(rxByte); + } else if (dataType_ == DataType::Control) { + cmdBuffer_.push_back(rxByte); + } + } + break; + } +} + +bool Processor::receiveByte(byte rxByte) { switch (state_) { case State::Void: @@ -106,18 +139,7 @@ bool Processor::receiveByte(unsigned char rxByte) { state_ = State::GetData; break; case State::GetData: - if (rxByte == Marker::Fesc) { - state_ = State::Escape; - } - else if (rxByte == Marker::Fend) { - if (cmd_ == Cmd::Data) { - onRigTxEnd(); - } - resetState(); - } - else if (cmd_ == Cmd::Data) { - onRigTx(rxByte); - } + processData(rxByte); break; case State::Escape: if (rxByte == Marker::Tfend) { diff --git a/kiss_processor.h b/kiss_processor.h index dbc9cd8..a370d40 100644 --- a/kiss_processor.h +++ b/kiss_processor.h @@ -40,22 +40,20 @@ protected: SlotTime = 0x03, // extended to modem - Frequency = 0x10, - Bandwidth = 0x11, - Power = 0x12, - SyncWord = 0x13, - SpreadingFactor = 0x14, - CodingRate = 0x15, - EnableCrc = 0x16, + RadioControl = 0x10, - // extended events from modem - SignalLevelRssi = 0x30, - SignalLevelSnr = 0x31, + // extended from modem + RadioSignalLevel = 0x30, // end of cmds NoCmd = 0x80 }; + enum DataType { + Raw = 0, + Control + }; + const int CfgTxQueueSize = 4096; protected: @@ -68,20 +66,20 @@ protected: virtual bool onSerialRx(byte *b) = 0; virtual void onControlCommand(Cmd cmd, byte value) = 0; - /* - virtual void onControlCommand(Cmd cmd, int value) = 0; - virtual void onControlCommand(Cmd cmd, long value) = 0; - */ + virtual void onRadioControlCommand(const std::vector &command) = 0; private: - bool receiveByte(unsigned char rxByte); - bool processCommand(unsigned char rxByte); + bool receiveByte(byte rxByte); + void processData(byte rxByte); + bool processCommand(byte rxByte); void resetState(); - + private: Cmd cmd_; + DataType dataType_; State state_; std::shared_ptr txQueue_; + std::vector cmdBuffer_; }; } // Kiss diff --git a/loraprs_config.h b/loraprs_config.h index f7e7aad..ef4121a 100644 --- a/loraprs_config.h +++ b/loraprs_config.h @@ -13,8 +13,8 @@ struct Config long LoraBw; // lora bandwidth, e.g. 125e3 int LoraSf; // lora spreading factor, e.g. 12 int LoraCodingRate; // lora coding rate, e.g. 7 - int LoraSync; // lora sync word/packet id, 0x3f int LoraPower; // lora power level in dbm, 20 + int LoraSync; // lora sync word/packet id, 0x3f bool LoraEnableCrc; // lora crc check enabled byte LoraPinSs; // lora ss pin @@ -41,6 +41,7 @@ struct Config bool EnableIsToRf; // true - enable APRS-IS to RF submission bool EnableRepeater; // true - digirepeat incoming packets based on WIDEn-n paths bool EnableBeacon; // true - send AprsRawBeacon to RF and APRS-IS if EnableRfToIs is true + bool EnableKissExtensions; // true - enable kiss extensions for radio control and signal reports }; } // LoraPrs diff --git a/loraprs_service.cpp b/loraprs_service.cpp index 1032aaf..6e9a9d1 100644 --- a/loraprs_service.cpp +++ b/loraprs_service.cpp @@ -225,6 +225,16 @@ void Service::onAprsisDataAvailable() } } +void Service::sendSignalReportEvent(int rssi, float snr) +{ + struct LoraSignalLevelEvent event; + + event.rssi = htobe16(rssi); + event.snr = htobe16(snr * 100); + + serialSend((const byte *)&event, sizeof(LoraSignalLevelEvent)); +} + bool Service::sendAX25ToLora(const AX25::Payload &payload) { byte buf[CfgMaxAX25PayloadSize]; @@ -250,12 +260,16 @@ void Service::onLoraDataAvailable(int packetSize) } serialSend(rxBuf, rxBufIndex); long frequencyError = LoRa.packetFrequencyError(); - + if (config_.EnableAutoFreqCorrection) { config_.LoraFreq -= frequencyError; LoRa.setFrequency(config_.LoraFreq); } + if (config_.EnableKissExtensions) { + sendSignalReportEvent(LoRa.packetRssi(), LoRa.packetSnr()); + } + if (!config_.IsClientMode) { processIncomingRawPacketAsServer(rxBuf, rxBufIndex); } @@ -347,5 +361,23 @@ void Service::onControlCommand(Cmd cmd, byte value) break; } } - + +void Service::onRadioControlCommand(const std::vector &rawCommand) { + + if (config_.EnableKissExtensions && rawCommand.size() == sizeof(LoraControlCommand)) { + const struct LoraControlCommand * controlCommand = reinterpret_cast(rawCommand.data()); + + config_.LoraFreq = be32toh(controlCommand->freq); + config_.LoraBw = be32toh(controlCommand->bw); + config_.LoraSf = be16toh(controlCommand->sf); + config_.LoraCodingRate = be16toh(controlCommand->cr); + config_.LoraPower = be16toh(controlCommand->pwr); + config_.LoraSync = be16toh(controlCommand->sync); + config_.LoraEnableCrc = controlCommand->crc; + + setupLora(config_.LoraFreq, config_.LoraBw, config_.LoraSf, + config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, config_.LoraEnableCrc); + } +} + } // LoraPrs diff --git a/loraprs_service.h b/loraprs_service.h index 9023756..13012f7 100644 --- a/loraprs_service.h +++ b/loraprs_service.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "BluetoothSerial.h" #include "ax25_payload.h" @@ -33,6 +34,7 @@ private: void onLoraDataAvailable(int packetSize); void onAprsisDataAvailable(); + void sendSignalReportEvent(int rssi, float snr); void sendPeriodicBeacon(); void sendToAprsis(const String &aprsMessage); bool sendAX25ToLora(const AX25::Payload &payload); @@ -55,7 +57,24 @@ protected: virtual bool onSerialRx(byte *b); virtual void onControlCommand(Cmd cmd, byte value); + virtual void onRadioControlCommand(const std::vector &command); + +private: + struct LoraSignalLevelEvent { + int rssi; + int snr; + } __attribute__((packed)); + struct LoraControlCommand { + long freq; + long bw; + int sf; + int cr; + int pwr; + int sync; + bool crc; + } __attribute__((packed)); + private: const String CfgLoraprsVersion = "LoRAPRS 0.1"; @@ -65,7 +84,7 @@ private: const int CfgWiFiConnRetryMaxTimes = 10; const int CfgMaxAX25PayloadSize = 512; - // csma paramters, overriden with KISS commands + // csma parameters, overriden with KISS commands const long CfgCsmaPersistence = 100; // 255 for real time, lower for higher traffic const long CfgCsmaSlotTimeMs = 500; // 0 for real time, otherwise set to average tx duration