kopia lustrzana https://github.com/sh123/esp32_loraprs
Add support for KISS extension for radio control and signal reports
rodzic
1f1b3efb7e
commit
4c178d8c65
1
config.h
1
config.h
|
@ -33,3 +33,4 @@
|
||||||
#define CFG_RF_TO_IS true
|
#define CFG_RF_TO_IS true
|
||||||
#define CFG_IS_TO_RF false
|
#define CFG_IS_TO_RF false
|
||||||
#define CFG_BEACON false
|
#define CFG_BEACON false
|
||||||
|
#define CFG_KISS_EXTENSIONS false
|
||||||
|
|
|
@ -25,7 +25,7 @@ void initializeConfig(LoraPrs::Config &cfg) {
|
||||||
|
|
||||||
// client/server mode switch
|
// client/server mode switch
|
||||||
cfg.IsClientMode = CFG_IS_CLIENT_MODE;
|
cfg.IsClientMode = CFG_IS_CLIENT_MODE;
|
||||||
|
|
||||||
// lora parameters
|
// lora parameters
|
||||||
cfg.LoraFreq = CFG_LORA_FREQ;
|
cfg.LoraFreq = CFG_LORA_FREQ;
|
||||||
cfg.LoraBw = CFG_LORA_BW;
|
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.EnableIsToRf = CFG_IS_TO_RF; // send data from aprsis to rf
|
||||||
cfg.EnableRepeater = CFG_DIGIREPEAT; // digirepeat incoming packets
|
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.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;
|
LoraPrs::Service loraPrsService;
|
||||||
|
|
|
@ -61,12 +61,13 @@ void Processor::resetState()
|
||||||
state_ = State::Void;
|
state_ = State::Void;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Processor::processCommand(unsigned char rxByte) {
|
bool Processor::processCommand(byte rxByte) {
|
||||||
|
|
||||||
switch (rxByte) {
|
switch (rxByte) {
|
||||||
case Cmd::Data:
|
case Cmd::Data:
|
||||||
if (!onRigTxBegin()) return false;
|
if (!onRigTxBegin()) return false;
|
||||||
state_ = State::GetData;
|
state_ = State::GetData;
|
||||||
|
dataType_ = DataType::Raw;
|
||||||
break;
|
break;
|
||||||
case Cmd::P:
|
case Cmd::P:
|
||||||
state_ = State::GetP;
|
state_ = State::GetP;
|
||||||
|
@ -74,6 +75,11 @@ bool Processor::processCommand(unsigned char rxByte) {
|
||||||
case Cmd::SlotTime:
|
case Cmd::SlotTime:
|
||||||
state_ = State::GetSlotTime;
|
state_ = State::GetSlotTime;
|
||||||
break;
|
break;
|
||||||
|
case Cmd::RadioControl:
|
||||||
|
state_ = State::GetData;
|
||||||
|
dataType_ = DataType::Control;
|
||||||
|
cmdBuffer_.clear();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// unknown command
|
// unknown command
|
||||||
resetState();
|
resetState();
|
||||||
|
@ -83,7 +89,34 @@ bool Processor::processCommand(unsigned char rxByte) {
|
||||||
return true;
|
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_) {
|
switch (state_) {
|
||||||
case State::Void:
|
case State::Void:
|
||||||
|
@ -106,18 +139,7 @@ bool Processor::receiveByte(unsigned char rxByte) {
|
||||||
state_ = State::GetData;
|
state_ = State::GetData;
|
||||||
break;
|
break;
|
||||||
case State::GetData:
|
case State::GetData:
|
||||||
if (rxByte == Marker::Fesc) {
|
processData(rxByte);
|
||||||
state_ = State::Escape;
|
|
||||||
}
|
|
||||||
else if (rxByte == Marker::Fend) {
|
|
||||||
if (cmd_ == Cmd::Data) {
|
|
||||||
onRigTxEnd();
|
|
||||||
}
|
|
||||||
resetState();
|
|
||||||
}
|
|
||||||
else if (cmd_ == Cmd::Data) {
|
|
||||||
onRigTx(rxByte);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case State::Escape:
|
case State::Escape:
|
||||||
if (rxByte == Marker::Tfend) {
|
if (rxByte == Marker::Tfend) {
|
||||||
|
|
|
@ -40,22 +40,20 @@ protected:
|
||||||
SlotTime = 0x03,
|
SlotTime = 0x03,
|
||||||
|
|
||||||
// extended to modem
|
// extended to modem
|
||||||
Frequency = 0x10,
|
RadioControl = 0x10,
|
||||||
Bandwidth = 0x11,
|
|
||||||
Power = 0x12,
|
|
||||||
SyncWord = 0x13,
|
|
||||||
SpreadingFactor = 0x14,
|
|
||||||
CodingRate = 0x15,
|
|
||||||
EnableCrc = 0x16,
|
|
||||||
|
|
||||||
// extended events from modem
|
// extended from modem
|
||||||
SignalLevelRssi = 0x30,
|
RadioSignalLevel = 0x30,
|
||||||
SignalLevelSnr = 0x31,
|
|
||||||
|
|
||||||
// end of cmds
|
// end of cmds
|
||||||
NoCmd = 0x80
|
NoCmd = 0x80
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum DataType {
|
||||||
|
Raw = 0,
|
||||||
|
Control
|
||||||
|
};
|
||||||
|
|
||||||
const int CfgTxQueueSize = 4096;
|
const int CfgTxQueueSize = 4096;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -68,20 +66,20 @@ protected:
|
||||||
virtual bool onSerialRx(byte *b) = 0;
|
virtual bool onSerialRx(byte *b) = 0;
|
||||||
|
|
||||||
virtual void onControlCommand(Cmd cmd, byte value) = 0;
|
virtual void onControlCommand(Cmd cmd, byte value) = 0;
|
||||||
/*
|
virtual void onRadioControlCommand(const std::vector<byte> &command) = 0;
|
||||||
virtual void onControlCommand(Cmd cmd, int value) = 0;
|
|
||||||
virtual void onControlCommand(Cmd cmd, long value) = 0;
|
|
||||||
*/
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool receiveByte(unsigned char rxByte);
|
bool receiveByte(byte rxByte);
|
||||||
bool processCommand(unsigned char rxByte);
|
void processData(byte rxByte);
|
||||||
|
bool processCommand(byte rxByte);
|
||||||
void resetState();
|
void resetState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Cmd cmd_;
|
Cmd cmd_;
|
||||||
|
DataType dataType_;
|
||||||
State state_;
|
State state_;
|
||||||
std::shared_ptr<cppQueue> txQueue_;
|
std::shared_ptr<cppQueue> txQueue_;
|
||||||
|
std::vector<byte> cmdBuffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Kiss
|
} // Kiss
|
||||||
|
|
|
@ -13,8 +13,8 @@ struct Config
|
||||||
long LoraBw; // lora bandwidth, e.g. 125e3
|
long LoraBw; // lora bandwidth, e.g. 125e3
|
||||||
int LoraSf; // lora spreading factor, e.g. 12
|
int LoraSf; // lora spreading factor, e.g. 12
|
||||||
int LoraCodingRate; // lora coding rate, e.g. 7
|
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 LoraPower; // lora power level in dbm, 20
|
||||||
|
int LoraSync; // lora sync word/packet id, 0x3f
|
||||||
bool LoraEnableCrc; // lora crc check enabled
|
bool LoraEnableCrc; // lora crc check enabled
|
||||||
|
|
||||||
byte LoraPinSs; // lora ss pin
|
byte LoraPinSs; // lora ss pin
|
||||||
|
@ -41,6 +41,7 @@ struct Config
|
||||||
bool EnableIsToRf; // true - enable APRS-IS to RF submission
|
bool EnableIsToRf; // true - enable APRS-IS to RF submission
|
||||||
bool EnableRepeater; // true - digirepeat incoming packets based on WIDEn-n paths
|
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 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
|
} // LoraPrs
|
||||||
|
|
|
@ -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)
|
bool Service::sendAX25ToLora(const AX25::Payload &payload)
|
||||||
{
|
{
|
||||||
byte buf[CfgMaxAX25PayloadSize];
|
byte buf[CfgMaxAX25PayloadSize];
|
||||||
|
@ -250,12 +260,16 @@ void Service::onLoraDataAvailable(int packetSize)
|
||||||
}
|
}
|
||||||
serialSend(rxBuf, rxBufIndex);
|
serialSend(rxBuf, rxBufIndex);
|
||||||
long frequencyError = LoRa.packetFrequencyError();
|
long frequencyError = LoRa.packetFrequencyError();
|
||||||
|
|
||||||
if (config_.EnableAutoFreqCorrection) {
|
if (config_.EnableAutoFreqCorrection) {
|
||||||
config_.LoraFreq -= frequencyError;
|
config_.LoraFreq -= frequencyError;
|
||||||
LoRa.setFrequency(config_.LoraFreq);
|
LoRa.setFrequency(config_.LoraFreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_.EnableKissExtensions) {
|
||||||
|
sendSignalReportEvent(LoRa.packetRssi(), LoRa.packetSnr());
|
||||||
|
}
|
||||||
|
|
||||||
if (!config_.IsClientMode) {
|
if (!config_.IsClientMode) {
|
||||||
processIncomingRawPacketAsServer(rxBuf, rxBufIndex);
|
processIncomingRawPacketAsServer(rxBuf, rxBufIndex);
|
||||||
}
|
}
|
||||||
|
@ -347,5 +361,23 @@ void Service::onControlCommand(Cmd cmd, byte value)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Service::onRadioControlCommand(const std::vector<byte> &rawCommand) {
|
||||||
|
|
||||||
|
if (config_.EnableKissExtensions && rawCommand.size() == sizeof(LoraControlCommand)) {
|
||||||
|
const struct LoraControlCommand * controlCommand = reinterpret_cast<const struct LoraControlCommand*>(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
|
} // LoraPrs
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <LoRa.h>
|
#include <LoRa.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <cppQueue.h>
|
#include <cppQueue.h>
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
#include "BluetoothSerial.h"
|
#include "BluetoothSerial.h"
|
||||||
#include "ax25_payload.h"
|
#include "ax25_payload.h"
|
||||||
|
@ -33,6 +34,7 @@ private:
|
||||||
void onLoraDataAvailable(int packetSize);
|
void onLoraDataAvailable(int packetSize);
|
||||||
void onAprsisDataAvailable();
|
void onAprsisDataAvailable();
|
||||||
|
|
||||||
|
void sendSignalReportEvent(int rssi, float snr);
|
||||||
void sendPeriodicBeacon();
|
void sendPeriodicBeacon();
|
||||||
void sendToAprsis(const String &aprsMessage);
|
void sendToAprsis(const String &aprsMessage);
|
||||||
bool sendAX25ToLora(const AX25::Payload &payload);
|
bool sendAX25ToLora(const AX25::Payload &payload);
|
||||||
|
@ -55,7 +57,24 @@ protected:
|
||||||
virtual bool onSerialRx(byte *b);
|
virtual bool onSerialRx(byte *b);
|
||||||
|
|
||||||
virtual void onControlCommand(Cmd cmd, byte value);
|
virtual void onControlCommand(Cmd cmd, byte value);
|
||||||
|
virtual void onRadioControlCommand(const std::vector<byte> &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:
|
private:
|
||||||
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
||||||
|
|
||||||
|
@ -65,7 +84,7 @@ private:
|
||||||
const int CfgWiFiConnRetryMaxTimes = 10;
|
const int CfgWiFiConnRetryMaxTimes = 10;
|
||||||
const int CfgMaxAX25PayloadSize = 512;
|
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 CfgCsmaPersistence = 100; // 255 for real time, lower for higher traffic
|
||||||
const long CfgCsmaSlotTimeMs = 500; // 0 for real time, otherwise set to average tx duration
|
const long CfgCsmaSlotTimeMs = 500; // 0 for real time, otherwise set to average tx duration
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue