kopia lustrzana https://github.com/sh123/esp32_loraprs
Remove dependency on arduino-LoRa library, use RadioLib only (#42)
- Remove dependency on arduino-LoRa library, use RadioLib only - Run radio TX on the same task where RX is running - Refactor for better readabilityplatformio 1.0.2
rodzic
aba3fd1645
commit
8a25bf13cb
|
@ -24,9 +24,7 @@ Modules, which are used by users and known to work
|
||||||
# Dependencies
|
# Dependencies
|
||||||
Install via libraries:
|
Install via libraries:
|
||||||
- Arduino ESP32 library: https://github.com/espressif/arduino-esp32
|
- Arduino ESP32 library: https://github.com/espressif/arduino-esp32
|
||||||
- LoRa library
|
- RadioLib library: https://github.com/jgromes/RadioLib
|
||||||
- RadioLib library (⚠use github master version): https://github.com/jgromes/RadioLib
|
|
||||||
- or 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
|
||||||
- CircularBuffer library: https://github.com/rlogiacco/CircularBuffer
|
- CircularBuffer library: https://github.com/rlogiacco/CircularBuffer
|
||||||
- DebugLog library: https://github.com/hideakitai/DebugLog
|
- DebugLog library: https://github.com/hideakitai/DebugLog
|
||||||
|
|
5
config.h
5
config.h
|
@ -1,6 +1,3 @@
|
||||||
// comment out for arduino-Lora usage
|
|
||||||
#define USE_RADIOLIB
|
|
||||||
|
|
||||||
// Uncomment for SX126X module usage
|
// Uncomment for SX126X module usage
|
||||||
#define USE_SX126X
|
#define USE_SX126X
|
||||||
|
|
||||||
|
@ -50,10 +47,8 @@
|
||||||
|
|
||||||
// CAD and ISR usage selection
|
// CAD and ISR usage selection
|
||||||
#ifdef USE_SX126X
|
#ifdef USE_SX126X
|
||||||
#define CFG_LORA_USE_ISR false // reading in ISR does not work on sx126x
|
|
||||||
#define CFG_LORA_USE_CAD true // do not transmit if channel is busy
|
#define CFG_LORA_USE_CAD true // do not transmit if channel is busy
|
||||||
#else
|
#else
|
||||||
#define CFG_LORA_USE_ISR true // true - read incoming data in ISR, false - do not read in ISR
|
|
||||||
#define CFG_LORA_USE_CAD true // set to true to utilize carrier detection
|
#define CFG_LORA_USE_CAD true // set to true to utilize carrier detection
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ void initializeConfig(LoraPrs::Config &cfg) {
|
||||||
cfg.LoraPinB = CFG_LORA_PIN_B; // (sx127x - dio1, sx126x/sx128x - busy)
|
cfg.LoraPinB = CFG_LORA_PIN_B; // (sx127x - dio1, sx126x/sx128x - busy)
|
||||||
cfg.LoraPinSwitchRx = CFG_LORA_PIN_RXEN; // (sx127x - unused, sx126x - RXEN pin number)
|
cfg.LoraPinSwitchRx = CFG_LORA_PIN_RXEN; // (sx127x - unused, sx126x - RXEN pin number)
|
||||||
cfg.LoraPinSwitchTx = CFG_LORA_PIN_TXEN; // (sx127x - unused, sx126x - TXEN pin number)
|
cfg.LoraPinSwitchTx = CFG_LORA_PIN_TXEN; // (sx127x - unused, sx126x - TXEN pin number)
|
||||||
cfg.LoraUseIsr = CFG_LORA_USE_ISR; // set to true for incoming packet ISR usage (stream mode, e.g. speech)
|
|
||||||
cfg.LoraUseCad = CFG_LORA_USE_CAD; // carrier detect
|
cfg.LoraUseCad = CFG_LORA_USE_CAD; // carrier detect
|
||||||
|
|
||||||
// aprs configuration
|
// aprs configuration
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
namespace Kiss {
|
namespace Kiss {
|
||||||
|
|
||||||
CircularBuffer<uint8_t, Processor::CfgSerialToRigQueueSize> Processor::serialToRigQueue_;
|
|
||||||
CircularBuffer<uint8_t, Processor::CfgRigToSerialQueueSize> Processor::rigToSerialQueue_;
|
|
||||||
CircularBuffer<uint8_t, Processor::CfgRigToSerialQueueSize> Processor::rigToSerialQueueIndex_;
|
|
||||||
|
|
||||||
Processor::Processor()
|
Processor::Processor()
|
||||||
: disableKiss_(false)
|
: disableKiss_(false)
|
||||||
, isRawIdle_(true)
|
, isRawIdle_(true)
|
||||||
|
@ -52,7 +48,7 @@ void Processor::sendRigToSerial(Cmd cmd, const byte *packet, int packetLength) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_RAM_ATTR Processor::queueRigToSerialIsr(Cmd cmd, const byte *packet, int packetLength) {
|
void Processor::queueRigToSerial(Cmd cmd, const byte *packet, int packetLength) {
|
||||||
if (!rigToSerialQueueIndex_.unshift(packetLength)) {
|
if (!rigToSerialQueueIndex_.unshift(packetLength)) {
|
||||||
LOG_WARN("Rig to serial queue is full!");
|
LOG_WARN("Rig to serial queue is full!");
|
||||||
return;
|
return;
|
||||||
|
@ -299,7 +295,6 @@ bool Processor::receiveByteKiss(byte rxByte)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Processor::receiveByte(byte rxByte) {
|
bool Processor::receiveByte(byte rxByte) {
|
||||||
|
|
||||||
if (disableKiss_) {
|
if (disableKiss_) {
|
||||||
return receiveByteRaw(rxByte);
|
return receiveByteRaw(rxByte);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,7 @@ public:
|
||||||
Processor();
|
Processor();
|
||||||
|
|
||||||
void sendRigToSerial(Cmd cmd, const byte *packet, int packetLength);
|
void sendRigToSerial(Cmd cmd, const byte *packet, int packetLength);
|
||||||
static void ICACHE_RAM_ATTR queueRigToSerialIsr(Cmd cmd, const byte *packet, int packetLength);
|
void queueRigToSerial(Cmd cmd, const byte *packet, int packetLength);
|
||||||
|
|
||||||
void queueSerialToRig(Cmd cmd, const byte *packet, int packetLength);
|
void queueSerialToRig(Cmd cmd, const byte *packet, int packetLength);
|
||||||
|
|
||||||
bool processRigToSerial();
|
bool processRigToSerial();
|
||||||
|
@ -93,9 +92,9 @@ private:
|
||||||
DataType dataType_;
|
DataType dataType_;
|
||||||
std::vector<byte> cmdBuffer_;
|
std::vector<byte> cmdBuffer_;
|
||||||
|
|
||||||
static CircularBuffer<uint8_t, CfgSerialToRigQueueSize> serialToRigQueue_;
|
CircularBuffer<uint8_t, CfgSerialToRigQueueSize> serialToRigQueue_;
|
||||||
static CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueue_;
|
CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueue_;
|
||||||
static CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueueIndex_;
|
CircularBuffer<uint8_t, CfgRigToSerialQueueSize> rigToSerialQueueIndex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Kiss
|
} // Kiss
|
||||||
|
|
|
@ -29,7 +29,6 @@ struct Config
|
||||||
byte LoraPinB; // (sx127x - dio1, sx126x/sx128x - busy)
|
byte LoraPinB; // (sx127x - dio1, sx126x/sx128x - busy)
|
||||||
byte LoraPinSwitchRx; // (sx127x - unused, sx126x - RXEN pin number)
|
byte LoraPinSwitchRx; // (sx127x - unused, sx126x - RXEN pin number)
|
||||||
byte LoraPinSwitchTx; // (sx127x - unused, sx126x - TXEN pin number)
|
byte LoraPinSwitchTx; // (sx127x - unused, sx126x - TXEN pin number)
|
||||||
bool LoraUseIsr; // true to use interrupts, false for fallback polling, e.g. if Dio0 is not connected
|
|
||||||
bool LoraUseCad; // use carrier detect before transmitting
|
bool LoraUseCad; // use carrier detect before transmitting
|
||||||
|
|
||||||
// usb
|
// usb
|
||||||
|
|
|
@ -2,38 +2,29 @@
|
||||||
|
|
||||||
namespace LoraPrs {
|
namespace LoraPrs {
|
||||||
|
|
||||||
byte Service::rxBuf_[CfgMaxPacketSize];
|
TaskHandle_t Service::rigTaskHandle_;
|
||||||
|
volatile bool Service::rigIsRxActive_ = false;
|
||||||
#ifdef USE_RADIOLIB
|
bool Service::rigIsRxIsrEnabled_ = true;
|
||||||
#pragma message("Using RadioLib")
|
|
||||||
TaskHandle_t Service::rxTaskHandle_;
|
|
||||||
volatile bool Service::loraDataAvailable_ = false;
|
|
||||||
bool Service::interruptEnabled_ = true;
|
|
||||||
std::shared_ptr<MODULE_NAME> Service::radio_;
|
|
||||||
#else
|
|
||||||
#pragma message("Using arduino-LoRa")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Service::Service()
|
Service::Service()
|
||||||
: Kiss::Processor()
|
: Kiss::Processor()
|
||||||
, csmaP_(CfgCsmaPersistence)
|
, csmaP_(CfgCsmaPersistence)
|
||||||
, csmaSlotTime_(CfgCsmaSlotTimeMs)
|
, csmaSlotTime_(CfgCsmaSlotTimeMs)
|
||||||
, csmaSlotTimePrev_(0)
|
, csmaSlotTimePrev_(0)
|
||||||
|
, rigCurrentTxPacketSize_(0)
|
||||||
, serialBt_()
|
, serialBt_()
|
||||||
, serialBLE_()
|
, serialBLE_()
|
||||||
, kissServer_(new WiFiServer(CfgKissPort))
|
, kissServer_(new WiFiServer(CfgKissPort))
|
||||||
, isKissConn_(false)
|
, isKissClientConnected(false)
|
||||||
{
|
{
|
||||||
#ifdef USE_RADIOLIB
|
rigIsRxIsrEnabled_ = true;
|
||||||
interruptEnabled_ = true;
|
rigIsRxActive_ = false;
|
||||||
loraDataAvailable_ = false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::setup(const Config &conf)
|
void Service::setup(const Config &conf)
|
||||||
{
|
{
|
||||||
config_ = conf;
|
config_ = conf;
|
||||||
previousBeaconMs_ = 0;
|
beaconLastTimestampMs_ = 0;
|
||||||
disableKiss_ = conf.EnableTextPackets;
|
disableKiss_ = conf.EnableTextPackets;
|
||||||
|
|
||||||
LOG_SET_OPTION(false, false, true); // disable file, line, enable func
|
LOG_SET_OPTION(false, false, true); // disable file, line, enable func
|
||||||
|
@ -54,8 +45,8 @@ void Service::setup(const Config &conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// APRS-IS loging callsign validity
|
// APRS-IS loging callsign validity
|
||||||
ownCallsign_ = AX25::Callsign(config_.AprsLogin);
|
aprsMyCallsign_ = AX25::Callsign(config_.AprsLogin);
|
||||||
if (!ownCallsign_.IsValid()) {
|
if (!aprsMyCallsign_.IsValid()) {
|
||||||
LOG_ERROR("Own callsign", config_.AprsLogin, "is not valid");
|
LOG_ERROR("Own callsign", config_.AprsLogin, "is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,15 +59,11 @@ void Service::setup(const Config &conf)
|
||||||
aprsLoginCommand_ += String("\n");
|
aprsLoginCommand_ += String("\n");
|
||||||
|
|
||||||
// peripherals, LoRa
|
// peripherals, LoRa
|
||||||
setupLora(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf,
|
setupRig(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf,
|
||||||
config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, config_.LoraCrc, config_.LoraExplicit);
|
config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, config_.LoraCrc, config_.LoraExplicit);
|
||||||
|
|
||||||
#ifdef USE_RADIOLIB
|
// start radio task
|
||||||
if (!config_.LoraUseIsr) {
|
xTaskCreate(rigTask, "rigTask", 4096, this, 5, &rigTaskHandle_);
|
||||||
LOG_INFO("Reading data on separate task");
|
|
||||||
xTaskCreate(processIncomingDataTask, "processIncomingDataTask", 10000, NULL, 1, &rxTaskHandle_);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// peripherls, WiFi
|
// peripherls, WiFi
|
||||||
if (needsWifi()) {
|
if (needsWifi()) {
|
||||||
|
@ -102,11 +89,6 @@ void Service::setup(const Config &conf)
|
||||||
|
|
||||||
void Service::printConfig() {
|
void Service::printConfig() {
|
||||||
LOG_INFO("Current mode:", config_.IsClientMode ? "NORMAL" : "APRS-IS iGate");
|
LOG_INFO("Current mode:", config_.IsClientMode ? "NORMAL" : "APRS-IS iGate");
|
||||||
#ifdef USE_RADIOLIB
|
|
||||||
LOG_INFO("Built with RadioLib library");
|
|
||||||
#else
|
|
||||||
LOG_INFO("Built with arduino-LoRa library");
|
|
||||||
#endif
|
|
||||||
LOG_INFO(disableKiss_ ? "Using TNC2 text mode" : "Using TNC KISS and AX.25 mode");
|
LOG_INFO(disableKiss_ ? "Using TNC2 text mode" : "Using TNC KISS and AX.25 mode");
|
||||||
LOG_INFO("UsbSerialEnable:", config_.UsbSerialEnable ? "yes" : "no");
|
LOG_INFO("UsbSerialEnable:", config_.UsbSerialEnable ? "yes" : "no");
|
||||||
if (!config_.IsClientMode) {
|
if (!config_.IsClientMode) {
|
||||||
|
@ -184,20 +166,20 @@ bool Service::reconnectAprsis()
|
||||||
{
|
{
|
||||||
LOG_INFO("APRSIS connecting to", config_.AprsHost);
|
LOG_INFO("APRSIS connecting to", config_.AprsHost);
|
||||||
|
|
||||||
if (!aprsisConn_.connect(config_.AprsHost.c_str(), config_.AprsPort)) {
|
if (!aprsisConnection_.connect(config_.AprsHost.c_str(), config_.AprsPort)) {
|
||||||
LOG_ERROR("Failed to connect to", config_.AprsHost, ":", config_.AprsPort);
|
LOG_ERROR("Failed to connect to", config_.AprsHost, ":", config_.AprsPort);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOG_INFO("APRSIS connected");
|
LOG_INFO("APRSIS connected");
|
||||||
aprsisConn_.print(aprsLoginCommand_);
|
aprsisConnection_.print(aprsLoginCommand_);
|
||||||
LOG_INFO("APRSIS logged in");
|
LOG_INFO("APRSIS logged in");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit)
|
void Service::setupRig(long loraFreq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit)
|
||||||
{
|
{
|
||||||
isImplicitHeaderMode_ = !isExplicit;
|
rigIsImplicitMode_ = !isExplicit;
|
||||||
isImplicitHeaderMode_ = sf == 6; // must be implicit for SF6
|
rigIsImplicitMode_ = sf == 6; // must be implicit for SF6
|
||||||
int loraSpeed = (int)(sf * (4.0 / cr) / (pow(2.0, sf) / bw));
|
int loraSpeed = (int)(sf * (4.0 / cr) / (pow(2.0, sf) / bw));
|
||||||
|
|
||||||
LOG_INFO("Initializing LoRa");
|
LOG_INFO("Initializing LoRa");
|
||||||
|
@ -208,7 +190,7 @@ void Service::setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int syn
|
||||||
LOG_INFO("Power:", pwr, "dBm");
|
LOG_INFO("Power:", pwr, "dBm");
|
||||||
LOG_INFO("Sync:", "0x" + String(sync, HEX));
|
LOG_INFO("Sync:", "0x" + String(sync, HEX));
|
||||||
LOG_INFO("CRC:", crcBytes);
|
LOG_INFO("CRC:", crcBytes);
|
||||||
LOG_INFO("Header:", isImplicitHeaderMode_ ? "implicit" : "explicit");
|
LOG_INFO("Header:", rigIsImplicitMode_ ? "implicit" : "explicit");
|
||||||
LOG_INFO("Speed:", loraSpeed, "bps");
|
LOG_INFO("Speed:", loraSpeed, "bps");
|
||||||
LOG_INFO("TOA (compressed):", 37.0 / ((double)loraSpeed / 8.0), "sec");
|
LOG_INFO("TOA (compressed):", 37.0 / ((double)loraSpeed / 8.0), "sec");
|
||||||
LOG_INFO("TOA (uncompressed):", 64.0 / ((double)loraSpeed / 8.0), "sec");
|
LOG_INFO("TOA (uncompressed):", 64.0 / ((double)loraSpeed / 8.0), "sec");
|
||||||
|
@ -234,74 +216,35 @@ void Service::setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int syn
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
LOG_INFO("Min level:", -174 + 10 * log10(bw) + 6 + snrLimit, "dBm");
|
LOG_INFO("Min level:", -174 + 10 * log10(bw) + 6 + snrLimit, "dBm");
|
||||||
|
rig_ = std::make_shared<MODULE_NAME>(new Module(config_.LoraPinSs, config_.LoraPinA, config_.LoraPinRst, config_.LoraPinB));
|
||||||
#ifdef USE_RADIOLIB
|
int state = rig_->begin((float)loraFreq / 1e6, (float)bw / 1e3, sf, cr, sync, pwr);
|
||||||
radio_ = std::make_shared<MODULE_NAME>(new Module(config_.LoraPinSs, config_.LoraPinA, config_.LoraPinRst, config_.LoraPinB));
|
|
||||||
int state = radio_->begin((float)loraFreq / 1e6, (float)bw / 1e3, sf, cr, sync, pwr);
|
|
||||||
if (state != RADIOLIB_ERR_NONE) {
|
if (state != RADIOLIB_ERR_NONE) {
|
||||||
LOG_ERROR("Radio start error:", state);
|
LOG_ERROR("Radio start error:", state);
|
||||||
}
|
}
|
||||||
radio_->setCRC(crcBytes);
|
rig_->setCRC(crcBytes);
|
||||||
#ifdef USE_SX126X
|
#ifdef USE_SX126X
|
||||||
#pragma message("Using SX126X")
|
#pragma message("Using SX126X")
|
||||||
LOG_INFO("Using SX126X module");
|
LOG_INFO("Using SX126X module");
|
||||||
radio_->setRfSwitchPins(config_.LoraPinSwitchRx, config_.LoraPinSwitchTx);
|
rig_->setRfSwitchPins(config_.LoraPinSwitchRx, config_.LoraPinSwitchTx);
|
||||||
radio_->clearDio1Action();
|
rig_->clearDio1Action();
|
||||||
if (config_.LoraUseIsr) {
|
rig_->setDio1Action(onRigIsrRxPacket);
|
||||||
radio_->setDio1Action(onLoraDataAvailableIsr);
|
#else
|
||||||
} else {
|
|
||||||
radio_->setDio1Action(onLoraDataAvailableIsrNoRead);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#pragma message("Using SX127X")
|
#pragma message("Using SX127X")
|
||||||
LOG_INFO("Using SX127X module");
|
LOG_INFO("Using SX127X module");
|
||||||
radio_->clearDio0Action();
|
radio_->clearDio0Action();
|
||||||
if (config_.LoraUseIsr) {
|
radio_->setDio0Action(onRigIsrRxPacket);
|
||||||
radio_->setDio0Action(onLoraDataAvailableIsr);
|
#endif
|
||||||
} else {
|
|
||||||
radio_->setDio0Action(onLoraDataAvailableIsrNoRead);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (isImplicitHeaderMode_) {
|
if (rigIsImplicitMode_) {
|
||||||
radio_->implicitHeader(0xff);
|
rig_->implicitHeader(0xff);
|
||||||
} else {
|
} else {
|
||||||
radio_->explicitHeader();
|
rig_->explicitHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
state = radio_->startReceive();
|
state = rig_->startReceive();
|
||||||
if (state != RADIOLIB_ERR_NONE) {
|
if (state != RADIOLIB_ERR_NONE) {
|
||||||
LOG_ERROR("Receive start error:", state);
|
LOG_ERROR("Receive start error:", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // USE_RADIOLIB
|
|
||||||
|
|
||||||
LoRa.setPins(config_.LoraPinSs, config_.LoraPinRst, config_.LoraPinA);
|
|
||||||
|
|
||||||
int retryCnt = 0;
|
|
||||||
while (!LoRa.begin(loraFreq)) {
|
|
||||||
LOG_WARN("LoRa init retry", retryCnt);
|
|
||||||
delay(CfgConnRetryMs);
|
|
||||||
if (retryCnt++ >= CfgConnRetryMaxTimes) {
|
|
||||||
LOG_ERROR("LoRa init failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoRa.setSyncWord(sync);
|
|
||||||
LoRa.setSpreadingFactor(sf);
|
|
||||||
LoRa.setSignalBandwidth(bw);
|
|
||||||
LoRa.setCodingRate4(cr);
|
|
||||||
LoRa.setTxPower(pwr);
|
|
||||||
|
|
||||||
if (config_.crcBytes > 0) {
|
|
||||||
LoRa.enableCrc();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config_.LoraUseIsr) {
|
|
||||||
LoRa.onReceive(onLoraDataAvailableIsr);
|
|
||||||
LoRa.receive();
|
|
||||||
}
|
|
||||||
#endif // USE_RADIOLIB
|
|
||||||
|
|
||||||
LOG_INFO("LoRa initialized");
|
LOG_INFO("LoRa initialized");
|
||||||
}
|
}
|
||||||
|
@ -328,7 +271,7 @@ void Service::loop()
|
||||||
if (needsWifi() && WiFi.status() != WL_CONNECTED) {
|
if (needsWifi() && WiFi.status() != WL_CONNECTED) {
|
||||||
reconnectWifi();
|
reconnectWifi();
|
||||||
}
|
}
|
||||||
if (needsAprsis() && !aprsisConn_.connected() && config_.EnablePersistentAprsConnection) {
|
if (needsAprsis() && !aprsisConnection_.connected() && config_.EnablePersistentAprsConnection) {
|
||||||
reconnectAprsis();
|
reconnectAprsis();
|
||||||
}
|
}
|
||||||
if (config_.KissEnableTcpIp) {
|
if (config_.KissEnableTcpIp) {
|
||||||
|
@ -337,147 +280,110 @@ void Service::loop()
|
||||||
|
|
||||||
// RX path, Rig -> Serial
|
// RX path, Rig -> Serial
|
||||||
bool isRigToSerialProcessed = false;
|
bool isRigToSerialProcessed = false;
|
||||||
|
|
||||||
#ifdef USE_RADIOLIB
|
|
||||||
isRigToSerialProcessed = processRigToSerial();
|
isRigToSerialProcessed = processRigToSerial();
|
||||||
#else
|
|
||||||
if (config_.LoraUseIsr) {
|
|
||||||
isRigToSerialProcessed = processRigToSerial();
|
|
||||||
} else {
|
|
||||||
if (int packetSize = LoRa.parsePacket()) {
|
|
||||||
loraReceive(packetSize);
|
|
||||||
isRigToSerialProcessed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TX path, Serial -> Rig
|
// TX path, Serial -> Rig
|
||||||
if (!isRigToSerialProcessed) {
|
if (!isRigToSerialProcessed) {
|
||||||
|
|
||||||
long currentTime = millis();
|
long currentTime = millis();
|
||||||
if (!isLoraRxBusy() && currentTime > csmaSlotTimePrev_ + csmaSlotTime_ && random(0, 255) < csmaP_) {
|
if (!isRigRxBusy() && currentTime > csmaSlotTimePrev_ + csmaSlotTime_ && random(0, 255) < csmaP_) {
|
||||||
if (aprsisConn_.available() > 0) {
|
if (aprsisConnection_.available() > 0) {
|
||||||
onAprsisDataAvailable();
|
onAprsisDataAvailable();
|
||||||
}
|
}
|
||||||
if (needsBeacon()) {
|
if (needsBeacon()) {
|
||||||
sendPeriodicBeacon();
|
sendPeriodicBeacon();
|
||||||
}
|
}
|
||||||
bool allTxProcessed = processSerialToRig();
|
processSerialToRig();
|
||||||
if (allTxProcessed) {
|
|
||||||
#ifdef USE_RADIOLIB
|
|
||||||
int state = radio_->startReceive();
|
|
||||||
if (state != RADIOLIB_ERR_NONE) {
|
|
||||||
LOG_ERROR("Start receive error: ", state);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (config_.LoraUseIsr) {
|
|
||||||
LoRa.receive();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
csmaSlotTimePrev_ = currentTime;
|
csmaSlotTimePrev_ = currentTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delay(CfgPollDelayMs);
|
delay(CfgPollDelayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Service::isLoraRxBusy() {
|
bool Service::isRigRxBusy() {
|
||||||
#ifdef USE_RADIOLIB
|
return config_.LoraUseCad && rigIsRxActive_;
|
||||||
#ifdef USE_SX126X
|
|
||||||
return config_.LoraUseCad && loraDataAvailable_;
|
|
||||||
#else
|
|
||||||
return config_.LoraUseCad && (radio_->getModemStatus() & 0x01); // SX1278_STATUS_SIG_DETECT
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_RADIOLIB
|
ICACHE_RAM_ATTR void Service::onRigIsrRxPacket() {
|
||||||
|
|
||||||
ICACHE_RAM_ATTR void Service::onLoraDataAvailableIsrNoRead() {
|
|
||||||
BaseType_t xHigherPriorityTaskWoken;
|
BaseType_t xHigherPriorityTaskWoken;
|
||||||
uint32_t interruptStatusBits = 0;
|
if (rigIsRxIsrEnabled_) {
|
||||||
|
rigIsRxActive_ = true;
|
||||||
if (interruptEnabled_) {
|
uint32_t radioReceiveBit = RadioTaskBits::Receive;
|
||||||
loraDataAvailable_ = true;
|
xTaskNotifyFromISR(rigTaskHandle_, radioReceiveBit, eSetBits, &xHigherPriorityTaskWoken);
|
||||||
interruptStatusBits |= 1;
|
|
||||||
xTaskNotifyFromISR(rxTaskHandle_, interruptStatusBits, eSetBits, &xHigherPriorityTaskWoken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ICACHE_RAM_ATTR void Service::onLoraDataAvailableIsr() {
|
void Service::rigTask(void *self) {
|
||||||
if (interruptEnabled_) {
|
LOG_INFO("Radio task started");
|
||||||
int packetSize = radio_->getPacketLength();
|
|
||||||
|
|
||||||
if (packetSize > 0) {
|
|
||||||
|
|
||||||
int state = radio_->readData(rxBuf_, packetSize);
|
|
||||||
if (state == RADIOLIB_ERR_NONE) {
|
|
||||||
queueRigToSerialIsr(Cmd::Data, rxBuf_, packetSize);
|
|
||||||
} else {
|
|
||||||
LOG_ERROR("Read data error: ", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio_->startReceive();
|
|
||||||
if (state != RADIOLIB_ERR_NONE) {
|
|
||||||
LOG_ERROR("Start receive error: ", state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Service::processIncomingDataTask(void *param) {
|
|
||||||
LOG_INFO("Incoming data process task started");
|
|
||||||
uint32_t interruptStatusBits;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
xTaskNotifyWait(0, 0x00, &interruptStatusBits, portMAX_DELAY);
|
uint32_t commandBits = 0;
|
||||||
|
xTaskNotifyWaitIndexed(0, 0x00, ULONG_MAX, &commandBits, portMAX_DELAY);
|
||||||
if (interruptStatusBits & 0x01) {
|
if (commandBits & RadioTaskBits::Receive) {
|
||||||
int packetSize = radio_->getPacketLength();
|
((Service*)self)->onRigTaskRxPacket();
|
||||||
|
}
|
||||||
if (packetSize > 0) {
|
else if (commandBits & RadioTaskBits::Transmit) {
|
||||||
|
((Service*)self)->onRigTaskTxPacket();
|
||||||
int state = radio_->readData(rxBuf_, packetSize);
|
|
||||||
if (state == RADIOLIB_ERR_NONE) {
|
|
||||||
queueRigToSerialIsr(Cmd::Data, rxBuf_, packetSize);
|
|
||||||
} else {
|
|
||||||
LOG_ERROR("Read data error: ", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio_->startReceive();
|
|
||||||
if (state != RADIOLIB_ERR_NONE) {
|
|
||||||
LOG_ERROR("Start receive error: ", state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loraDataAvailable_ = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // USE_RADIOLIB
|
void Service::onRigTaskRxPacket() {
|
||||||
|
int packetSize = rig_->getPacketLength();
|
||||||
|
if (packetSize > 0) {
|
||||||
|
byte rxBuf[packetSize];
|
||||||
|
int state = rig_->readData(rxBuf, packetSize);
|
||||||
|
if (state == RADIOLIB_ERR_NONE) {
|
||||||
|
queueRigToSerial(Cmd::Data, rxBuf, packetSize);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Read data error: ", state);
|
||||||
|
}
|
||||||
|
|
||||||
ICACHE_RAM_ATTR void Service::onLoraDataAvailableIsr(int packetSize)
|
state = rig_->startReceive();
|
||||||
{
|
if (state != RADIOLIB_ERR_NONE) {
|
||||||
int rxBufIndex = 0;
|
LOG_ERROR("Start receive error: ", state);
|
||||||
|
}
|
||||||
for (int i = 0; i < packetSize; i++) {
|
|
||||||
rxBuf_[rxBufIndex++] = LoRa.read();
|
|
||||||
}
|
}
|
||||||
queueRigToSerialIsr(Cmd::Data, rxBuf_, rxBufIndex);
|
rigIsRxActive_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_RADIOLIB
|
void Service::onRigTaskTxPacket() {
|
||||||
|
while (rigTxQueueIndex_.size() > 0) {
|
||||||
|
int txPacketSize = rigTxQueueIndex_.shift();
|
||||||
|
byte txBuf[txPacketSize];
|
||||||
|
|
||||||
|
for (int i = 0; i < txPacketSize; i++) {
|
||||||
|
txBuf[i] = rigTxQueue_.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
rigIsRxIsrEnabled_ = false;
|
||||||
|
int state = rig_->transmit(txBuf, txPacketSize);
|
||||||
|
if (state != RADIOLIB_ERR_NONE) {
|
||||||
|
LOG_ERROR("TX error: ", state);
|
||||||
|
}
|
||||||
|
vTaskDelay(1);
|
||||||
|
}
|
||||||
|
int state = rig_->startReceive();
|
||||||
|
if (state != RADIOLIB_ERR_NONE) {
|
||||||
|
LOG_ERROR("Start receive error: ", state);
|
||||||
|
}
|
||||||
|
rigIsRxIsrEnabled_ = true;
|
||||||
|
if (config_.PttEnable) {
|
||||||
|
delay(config_.PttTxTailMs);
|
||||||
|
digitalWrite(config_.PttPin, LOW);
|
||||||
|
}
|
||||||
|
if (splitEnabled()) {
|
||||||
|
setFreq(config_.LoraFreqRx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Service::sendPeriodicBeacon()
|
void Service::sendPeriodicBeacon()
|
||||||
{
|
{
|
||||||
long currentMs = millis();
|
long currentMs = millis();
|
||||||
|
|
||||||
if (previousBeaconMs_ == 0 || currentMs - previousBeaconMs_ >= config_.AprsRawBeaconPeriodMinutes * 60 * 1000) {
|
if (beaconLastTimestampMs_ == 0 || currentMs - beaconLastTimestampMs_ >= config_.AprsRawBeaconPeriodMinutes * 60 * 1000) {
|
||||||
AX25::Payload payload(config_.AprsRawBeacon);
|
AX25::Payload payload(config_.AprsRawBeacon);
|
||||||
if (payload.IsValid()) {
|
if (payload.IsValid()) {
|
||||||
sendAX25ToLora(payload);
|
sendAx25PayloadToRig(payload);
|
||||||
if (config_.EnableRfToIs) {
|
if (config_.EnableRfToIs) {
|
||||||
sendToAprsis(payload.ToString());
|
sendToAprsis(payload.ToString());
|
||||||
}
|
}
|
||||||
|
@ -486,7 +392,7 @@ void Service::sendPeriodicBeacon()
|
||||||
else {
|
else {
|
||||||
LOG_ERROR("Beacon payload is invalid");
|
LOG_ERROR("Beacon payload is invalid");
|
||||||
}
|
}
|
||||||
previousBeaconMs_ = currentMs;
|
beaconLastTimestampMs_ = currentMs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,13 +401,13 @@ void Service::sendToAprsis(const String &aprsMessage)
|
||||||
if (needsWifi() && WiFi.status() != WL_CONNECTED) {
|
if (needsWifi() && WiFi.status() != WL_CONNECTED) {
|
||||||
reconnectWifi();
|
reconnectWifi();
|
||||||
}
|
}
|
||||||
if (needsAprsis() && !aprsisConn_.connected()) {
|
if (needsAprsis() && !aprsisConnection_.connected()) {
|
||||||
reconnectAprsis();
|
reconnectAprsis();
|
||||||
}
|
}
|
||||||
aprsisConn_.println(aprsMessage);
|
aprsisConnection_.println(aprsMessage);
|
||||||
|
|
||||||
if (!config_.EnablePersistentAprsConnection) {
|
if (!config_.EnablePersistentAprsConnection) {
|
||||||
aprsisConn_.stop();
|
aprsisConnection_.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,8 +415,8 @@ void Service::onAprsisDataAvailable()
|
||||||
{
|
{
|
||||||
String aprsisData;
|
String aprsisData;
|
||||||
|
|
||||||
while (aprsisConn_.available() > 0) {
|
while (aprsisConnection_.available() > 0) {
|
||||||
char c = aprsisConn_.read();
|
char c = aprsisConnection_.read();
|
||||||
if (c == '\r') continue;
|
if (c == '\r') continue;
|
||||||
if (c == '\n') break;
|
if (c == '\n') break;
|
||||||
aprsisData += c;
|
aprsisData += c;
|
||||||
|
@ -525,7 +431,7 @@ void Service::onAprsisDataAvailable()
|
||||||
if (config_.EnableIsToRf && aprsisData.length() > 0) {
|
if (config_.EnableIsToRf && aprsisData.length() > 0) {
|
||||||
AX25::Payload payload(aprsisData);
|
AX25::Payload payload(aprsisData);
|
||||||
if (payload.IsValid()) {
|
if (payload.IsValid()) {
|
||||||
sendAX25ToLora(payload);
|
sendAx25PayloadToRig(payload);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_WARN("Unknown payload from APRSIS, ignoring");
|
LOG_WARN("Unknown payload from APRSIS, ignoring");
|
||||||
|
@ -543,7 +449,7 @@ void Service::sendSignalReportEvent(int rssi, float snr)
|
||||||
sendRigToSerial(Cmd::SignalReport, (const byte *)&signalReport, sizeof(SignalReport));
|
sendRigToSerial(Cmd::SignalReport, (const byte *)&signalReport, sizeof(SignalReport));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Service::sendAX25ToLora(const AX25::Payload &payload)
|
bool Service::sendAx25PayloadToRig(const AX25::Payload &payload)
|
||||||
{
|
{
|
||||||
int bytesWritten;
|
int bytesWritten;
|
||||||
byte buf[CfgMaxPacketSize];
|
byte buf[CfgMaxPacketSize];
|
||||||
|
@ -573,11 +479,7 @@ void Service::onRigPacket(void *packet, int packetLength)
|
||||||
performFrequencyCorrection();
|
performFrequencyCorrection();
|
||||||
}
|
}
|
||||||
if (config_.KissEnableExtensions) {
|
if (config_.KissEnableExtensions) {
|
||||||
#ifdef USE_RADIOLIB
|
sendSignalReportEvent(rig_->getRSSI(), rig_->getSNR());
|
||||||
sendSignalReportEvent(radio_->getRSSI(), radio_->getSNR());
|
|
||||||
#else
|
|
||||||
sendSignalReportEvent(LoRa.packetRssi(), LoRa.packetSnr());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if (!config_.IsClientMode) {
|
if (!config_.IsClientMode) {
|
||||||
processIncomingRawPacketAsServer((const byte*)packet, packetLength);
|
processIncomingRawPacketAsServer((const byte*)packet, packetLength);
|
||||||
|
@ -585,51 +487,25 @@ void Service::onRigPacket(void *packet, int packetLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::performFrequencyCorrection() {
|
void Service::performFrequencyCorrection() {
|
||||||
#ifdef USE_RADIOLIB
|
#ifdef USE_SX126X
|
||||||
#ifdef USE_SX126X
|
|
||||||
long frequencyErrorHz = 0;
|
long frequencyErrorHz = 0;
|
||||||
#else
|
|
||||||
long frequencyErrorHz = radio_->getFrequencyError();
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
long frequencyErrorHz = LoRa.packetFrequencyError();
|
long frequencyErrorHz = radio_->getFrequencyError();
|
||||||
#endif
|
#endif
|
||||||
if (abs(frequencyErrorHz) > config_.AutoFreqCorrectionDeltaHz) {
|
if (abs(frequencyErrorHz) > config_.AutoFreqCorrectionDeltaHz) {
|
||||||
config_.LoraFreqRx -= frequencyErrorHz;
|
config_.LoraFreqRx -= frequencyErrorHz;
|
||||||
LOG_INFO("Correcting frequency:", frequencyErrorHz);
|
LOG_INFO("Correcting frequency:", frequencyErrorHz);
|
||||||
setupFreq(config_.LoraFreqRx);
|
setFreq(config_.LoraFreqRx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::setupFreq(long loraFreq) const {
|
void Service::setFreq(long loraFreq) const {
|
||||||
#ifdef USE_RADIOLIB
|
rig_->setFrequency((float)config_.LoraFreqRx / 1e6);
|
||||||
radio_->setFrequency((float)config_.LoraFreqRx / 1e6);
|
int state = rig_->startReceive();
|
||||||
int state = radio_->startReceive();
|
if (state != RADIOLIB_ERR_NONE) {
|
||||||
if (state != RADIOLIB_ERR_NONE) {
|
LOG_ERROR("Start receive error:", state);
|
||||||
LOG_ERROR("Start receive error:", state);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
LoRa.setFrequency(config_.LoraFreqRx);
|
|
||||||
if (config_.LoraUseIsr) {
|
|
||||||
LoRa.idle();
|
|
||||||
LoRa.receive();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef USE_RADIOLIB
|
|
||||||
void Service::loraReceive(int packetSize)
|
|
||||||
{
|
|
||||||
int rxBufIndex = 0;
|
|
||||||
byte rxBuf[packetSize];
|
|
||||||
|
|
||||||
while (LoRa.available()) {
|
|
||||||
rxBuf[rxBufIndex++] = LoRa.read();
|
|
||||||
}
|
}
|
||||||
sendRigToSerial(Cmd::Data, rxBuf, rxBufIndex);
|
|
||||||
onRigPacket(rxBuf, rxBufIndex);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLength) {
|
void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLength) {
|
||||||
|
|
||||||
|
@ -646,19 +522,12 @@ void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLen
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.IsValid()) {
|
if (payload.IsValid()) {
|
||||||
|
float snr = rig_->getSNR();
|
||||||
#ifdef USE_RADIOLIB
|
int rssi = rig_->getRSSI();
|
||||||
float snr = radio_->getSNR();
|
#ifdef USE_SX126X
|
||||||
int rssi = radio_->getRSSI();
|
|
||||||
#ifdef USE_SX126X
|
|
||||||
long frequencyError = 0;
|
long frequencyError = 0;
|
||||||
#else
|
|
||||||
long frequencyError = radio_->getFrequencyError();
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
float snr = LoRa.packetSnr();
|
long frequencyError = radio_->getFrequencyError();
|
||||||
int rssi = LoRa.packetRssi();
|
|
||||||
long frequencyError = LoRa.packetFrequencyError();
|
|
||||||
#endif
|
#endif
|
||||||
String signalReport = String("rssi: ") +
|
String signalReport = String("rssi: ") +
|
||||||
String(snr < 0 ? rssi + snr : rssi) +
|
String(snr < 0 ? rssi + snr : rssi) +
|
||||||
|
@ -679,8 +548,8 @@ void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLen
|
||||||
sendToAprsis(textPayload);
|
sendToAprsis(textPayload);
|
||||||
LOG_INFO("Packet sent to APRS-IS");
|
LOG_INFO("Packet sent to APRS-IS");
|
||||||
}
|
}
|
||||||
if (config_.EnableRepeater && payload.Digirepeat(ownCallsign_)) {
|
if (config_.EnableRepeater && payload.Digirepeat(aprsMyCallsign_)) {
|
||||||
sendAX25ToLora(payload);
|
sendAx25PayloadToRig(payload);
|
||||||
LOG_INFO("Packet digirepeated");
|
LOG_INFO("Packet digirepeated");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -690,86 +559,51 @@ void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLen
|
||||||
|
|
||||||
bool Service::onRigTxBegin()
|
bool Service::onRigTxBegin()
|
||||||
{
|
{
|
||||||
|
rigCurrentTxPacketSize_ = 0;
|
||||||
if (splitEnabled()) {
|
if (splitEnabled()) {
|
||||||
setupFreq(config_.LoraFreqTx);
|
setFreq(config_.LoraFreqTx);
|
||||||
}
|
}
|
||||||
if (config_.PttEnable) {
|
if (config_.PttEnable) {
|
||||||
digitalWrite(config_.PttPin, HIGH);
|
digitalWrite(config_.PttPin, HIGH);
|
||||||
delay(config_.PttTxDelayMs);
|
delay(config_.PttTxDelayMs);
|
||||||
} else {
|
|
||||||
delay(CfgPollDelayMs);
|
|
||||||
}
|
}
|
||||||
#ifdef USE_RADIOLIB
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
|
||||||
return (LoRa.beginPacket(isImplicitHeaderMode_) == 1);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::onRigTx(byte b)
|
void Service::onRigTx(byte b)
|
||||||
{
|
{
|
||||||
LOG_TRACE((char)b, String(b, HEX));
|
LOG_TRACE((char)b, String(b, HEX));
|
||||||
#ifdef USE_RADIOLIB
|
rigTxQueue_.push(b);
|
||||||
txQueue_.push(b);
|
rigCurrentTxPacketSize_++;
|
||||||
#else
|
|
||||||
LoRa.write(b);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::onRigTxEnd()
|
void Service::onRigTxEnd()
|
||||||
{
|
{
|
||||||
#ifdef USE_RADIOLIB
|
rigTxQueueIndex_.push(rigCurrentTxPacketSize_);
|
||||||
int txPacketSize = txQueue_.size();
|
uint32_t radioTransmitBit = RadioTaskBits::Transmit;
|
||||||
byte txBuf[txPacketSize];
|
xTaskNotify(rigTaskHandle_, radioTransmitBit, eSetBits);
|
||||||
|
|
||||||
for (int i = 0; i < txPacketSize; i++) {
|
|
||||||
txBuf[i] = txQueue_.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
interruptEnabled_ = false;
|
|
||||||
int state = radio_->transmit(txBuf, txPacketSize);
|
|
||||||
if (state != RADIOLIB_ERR_NONE) {
|
|
||||||
LOG_ERROR("TX error: ", state);
|
|
||||||
}
|
|
||||||
interruptEnabled_ = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (config_.PttEnable) {
|
|
||||||
#ifndef USE_RADIOLIB
|
|
||||||
LoRa.endPacket(false);
|
|
||||||
#endif
|
|
||||||
delay(config_.PttTxTailMs);
|
|
||||||
digitalWrite(config_.PttPin, LOW);
|
|
||||||
} else {
|
|
||||||
#ifndef USE_RADIOLIB
|
|
||||||
LoRa.endPacket(true);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (splitEnabled()) {
|
|
||||||
setupFreq(config_.LoraFreqRx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::attachKissNetworkClient()
|
void Service::attachKissNetworkClient()
|
||||||
{
|
{
|
||||||
// connected, client dropped off
|
// connected, client dropped off
|
||||||
if (isKissConn_) {
|
if (isKissClientConnected) {
|
||||||
if (!kissConn_.connected()) {
|
if (!kissConnnection_.connected()) {
|
||||||
LOG_INFO("KISS TCP/IP client disconnected");
|
LOG_INFO("KISS TCP/IP client disconnected");
|
||||||
isKissConn_ = false;
|
isKissClientConnected = false;
|
||||||
kissConn_.stop();
|
kissConnnection_.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WiFiClient wifiClient = kissServer_->available();
|
WiFiClient wifiClient = kissServer_->available();
|
||||||
// new client connected
|
// new client connected
|
||||||
if (wifiClient && wifiClient.connected()) {
|
if (wifiClient && wifiClient.connected()) {
|
||||||
// drop off current one
|
// drop off current one
|
||||||
if (isKissConn_) {
|
if (isKissClientConnected) {
|
||||||
kissConn_.stop();
|
kissConnnection_.stop();
|
||||||
}
|
}
|
||||||
LOG_INFO("New KISS TCP/IP client connected");
|
LOG_INFO("New KISS TCP/IP client connected");
|
||||||
kissConn_ = wifiClient;
|
kissConnnection_ = wifiClient;
|
||||||
isKissConn_ = true;
|
isKissClientConnected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -779,8 +613,8 @@ void Service::onSerialTx(byte b)
|
||||||
if (config_.UsbSerialEnable) {
|
if (config_.UsbSerialEnable) {
|
||||||
Serial.write(b);
|
Serial.write(b);
|
||||||
}
|
}
|
||||||
else if (isKissConn_) {
|
else if (isKissClientConnected) {
|
||||||
kissConn_.write(b);
|
kissConnnection_.write(b);
|
||||||
}
|
}
|
||||||
else if (config_.BtEnableBle) {
|
else if (config_.BtEnableBle) {
|
||||||
serialBLE_.write(b);
|
serialBLE_.write(b);
|
||||||
|
@ -795,8 +629,8 @@ bool Service::onSerialRxHasData()
|
||||||
if (config_.UsbSerialEnable) {
|
if (config_.UsbSerialEnable) {
|
||||||
return Serial.available();
|
return Serial.available();
|
||||||
}
|
}
|
||||||
else if (isKissConn_) {
|
else if (isKissClientConnected) {
|
||||||
return kissConn_.available();
|
return kissConnnection_.available();
|
||||||
}
|
}
|
||||||
else if (config_.BtEnableBle) {
|
else if (config_.BtEnableBle) {
|
||||||
return serialBLE_.available();
|
return serialBLE_.available();
|
||||||
|
@ -813,12 +647,12 @@ bool Service::onSerialRx(byte *b)
|
||||||
if (config_.UsbSerialEnable) {
|
if (config_.UsbSerialEnable) {
|
||||||
rxResult = Serial.read();
|
rxResult = Serial.read();
|
||||||
}
|
}
|
||||||
else if (isKissConn_) {
|
else if (isKissClientConnected) {
|
||||||
rxResult = kissConn_.read();
|
rxResult = kissConnnection_.read();
|
||||||
// client dropped off
|
// client dropped off
|
||||||
if (rxResult == -1) {
|
if (rxResult == -1) {
|
||||||
kissConn_.stop();
|
kissConnnection_.stop();
|
||||||
isKissConn_ = false;
|
isKissClientConnected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -872,7 +706,7 @@ void Service::onRadioControlCommand(const std::vector<byte> &rawCommand) {
|
||||||
config_.LoraSync = be16toh(setHardware->sync);
|
config_.LoraSync = be16toh(setHardware->sync);
|
||||||
int crcType = setHardware->crc ? config_.LoraCrc : 0;
|
int crcType = setHardware->crc ? config_.LoraCrc : 0;
|
||||||
|
|
||||||
setupLora(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf,
|
setupRig(config_.LoraFreqRx, config_.LoraBw, config_.LoraSf,
|
||||||
config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, crcType, config_.LoraExplicit);
|
config_.LoraCodingRate, config_.LoraPower, config_.LoraSync, crcType, config_.LoraExplicit);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Radio control command of wrong size");
|
LOG_ERROR("Radio control command of wrong size");
|
||||||
|
|
|
@ -12,11 +12,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_RADIOLIB
|
|
||||||
#include <RadioLib.h>
|
#include <RadioLib.h>
|
||||||
#else
|
|
||||||
#include <LoRa.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
|
@ -41,30 +37,26 @@ private:
|
||||||
void printConfig();
|
void printConfig();
|
||||||
|
|
||||||
void setupWifi(const String &wifiName, const String &wifiKey);
|
void setupWifi(const String &wifiName, const String &wifiKey);
|
||||||
void setupLora(long loraFreq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit);
|
void setupRig(long freq, long bw, int sf, int cr, int pwr, int sync, int crcBytes, bool isExplicit);
|
||||||
void setupFreq(long loraFreq) const;
|
void setFreq(long freq) const;
|
||||||
void setupBt(const String &btName);
|
void setupBt(const String &btName);
|
||||||
|
|
||||||
void reconnectWifi() const;
|
void reconnectWifi() const;
|
||||||
bool reconnectAprsis();
|
bool reconnectAprsis();
|
||||||
void attachKissNetworkClient();
|
void attachKissNetworkClient();
|
||||||
|
|
||||||
bool isLoraRxBusy();
|
bool isRigRxBusy();
|
||||||
#ifdef USE_RADIOLIB
|
void onRigTaskRxPacket();
|
||||||
void onLoraDataAvailable();
|
void onRigTaskTxPacket();
|
||||||
static void processIncomingDataTask(void *param);
|
static void rigTask(void *self);
|
||||||
static ICACHE_RAM_ATTR void onLoraDataAvailableIsr();
|
static ICACHE_RAM_ATTR void onRigIsrRxPacket();
|
||||||
static ICACHE_RAM_ATTR void onLoraDataAvailableIsrNoRead();
|
|
||||||
#else
|
|
||||||
static ICACHE_RAM_ATTR void onLoraDataAvailableIsr(int packetSize);
|
|
||||||
void loraReceive(int packetSize);
|
|
||||||
#endif
|
|
||||||
void onAprsisDataAvailable();
|
void onAprsisDataAvailable();
|
||||||
|
|
||||||
void sendSignalReportEvent(int rssi, float snr);
|
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 sendAx25PayloadToRig(const AX25::Payload &payload);
|
||||||
void processIncomingRawPacketAsServer(const byte *packet, int packetLength);
|
void processIncomingRawPacketAsServer(const byte *packet, int packetLength);
|
||||||
void performFrequencyCorrection();
|
void performFrequencyCorrection();
|
||||||
|
|
||||||
|
@ -120,13 +112,14 @@ private:
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
const String CfgLoraprsVersion = "LoRAPRS 1.0.2";
|
||||||
|
|
||||||
// processor config
|
// processor config
|
||||||
const int CfgConnRetryMs = 500; // connection retry delay, e.g. wifi
|
const int CfgConnRetryMs = 500; // connection retry delay, e.g. wifi
|
||||||
static const int CfgPollDelayMs = 20; // main loop delay
|
const int CfgPollDelayMs = 20; // main loop delay
|
||||||
const int CfgConnRetryMaxTimes = 10; // number of connection retries
|
const int CfgConnRetryMaxTimes = 10; // number of connection retries
|
||||||
static const int CfgMaxPacketSize = 256; // maximum packet size
|
static const int CfgMaxPacketSize = 256; // maximum packet size
|
||||||
|
static const int CfgRadioQueueSize = 1024; // radio queue size
|
||||||
|
|
||||||
// csma parameters, 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
|
||||||
|
@ -134,37 +127,46 @@ private:
|
||||||
|
|
||||||
// kiss static parameters
|
// kiss static parameters
|
||||||
const int CfgKissPort = 8001; // kiss tcp/ip server port
|
const int CfgKissPort = 8001; // kiss tcp/ip server port
|
||||||
|
|
||||||
|
// radio task commands
|
||||||
|
enum RadioTaskBits {
|
||||||
|
Receive = 0x01,
|
||||||
|
Transmit = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// config
|
// config
|
||||||
Config config_;
|
Config config_;
|
||||||
String aprsLoginCommand_;
|
String aprsLoginCommand_;
|
||||||
AX25::Callsign ownCallsign_;
|
AX25::Callsign aprsMyCallsign_;
|
||||||
bool isImplicitHeaderMode_;
|
|
||||||
|
|
||||||
// csma
|
// csma
|
||||||
byte csmaP_;
|
byte csmaP_;
|
||||||
long csmaSlotTime_;
|
long csmaSlotTime_;
|
||||||
long csmaSlotTimePrev_;
|
long csmaSlotTimePrev_;
|
||||||
|
|
||||||
// state
|
// beacon state
|
||||||
long previousBeaconMs_;
|
long beaconLastTimestampMs_;
|
||||||
|
|
||||||
// peripherals
|
// peripherals, radio
|
||||||
static byte rxBuf_[CfgMaxPacketSize];
|
static TaskHandle_t rigTaskHandle_;
|
||||||
#ifdef USE_RADIOLIB
|
static volatile bool rigIsRxActive_;
|
||||||
static TaskHandle_t rxTaskHandle_;
|
static bool rigIsRxIsrEnabled_;
|
||||||
static volatile bool loraDataAvailable_;
|
bool rigIsImplicitMode_;
|
||||||
static bool interruptEnabled_;
|
int rigCurrentTxPacketSize_;
|
||||||
CircularBuffer<uint8_t, CfgMaxPacketSize> txQueue_;
|
CircularBuffer<uint8_t, CfgRadioQueueSize> rigTxQueue_;
|
||||||
static std::shared_ptr<MODULE_NAME> radio_;
|
CircularBuffer<uint8_t, CfgRadioQueueSize> rigTxQueueIndex_;
|
||||||
#endif
|
std::shared_ptr<MODULE_NAME> rig_;
|
||||||
|
|
||||||
|
// bluetooth, wifi
|
||||||
BluetoothSerial serialBt_;
|
BluetoothSerial serialBt_;
|
||||||
BLESerial serialBLE_;
|
BLESerial serialBLE_;
|
||||||
WiFiClient aprsisConn_;
|
WiFiClient aprsisConnection_;
|
||||||
|
|
||||||
|
// kiss server
|
||||||
std::shared_ptr<WiFiServer> kissServer_;
|
std::shared_ptr<WiFiServer> kissServer_;
|
||||||
WiFiClient kissConn_;
|
WiFiClient kissConnnection_;
|
||||||
bool isKissConn_;
|
bool isKissClientConnected_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // LoraPrs
|
} // LoraPrs
|
||||||
|
|
Ładowanie…
Reference in New Issue