kopia lustrzana https://github.com/sh123/esp32_loraprs
Extract KISS logic into base class and improve CSMA logic
rodzic
f0f202b799
commit
b222573f1f
|
@ -0,0 +1,141 @@
|
|||
#include "kiss_processor.h"
|
||||
|
||||
namespace Kiss {
|
||||
|
||||
Processor::Processor()
|
||||
: cmd_(Cmd::NoCmd)
|
||||
, state_(State::Void)
|
||||
, txQueue_(new cppQueue(sizeof(unsigned char), CfgTxQueueSize))
|
||||
{
|
||||
}
|
||||
|
||||
void Processor::serialSend(const byte *data, int dataLength) {
|
||||
onSerialTx((byte)Marker::Fend);
|
||||
onSerialTx((byte)Cmd::Data);
|
||||
|
||||
for (int i = 0; i < dataLength; i++) {
|
||||
byte rxByte = data[i];
|
||||
|
||||
if (rxByte == Marker::Fend) {
|
||||
onSerialTx((byte)Marker::Fesc);
|
||||
onSerialTx((byte)Marker::Tfend);
|
||||
}
|
||||
else if (rxByte == Marker::Fesc) {
|
||||
onSerialTx((byte)Marker::Fesc);
|
||||
onSerialTx((byte)Marker::Tfesc);
|
||||
}
|
||||
else {
|
||||
onSerialTx(rxByte);
|
||||
}
|
||||
}
|
||||
|
||||
onSerialTx((byte)Marker::Fend);
|
||||
}
|
||||
|
||||
void Processor::serialProcessRx()
|
||||
{
|
||||
while (onSerialRxHasData() || !txQueue_->isEmpty()) {
|
||||
byte rxByte;
|
||||
if (onSerialRxHasData()) {
|
||||
if (onSerialRx(&rxByte)) {
|
||||
if (!txQueue_->push((void *)&rxByte)) {
|
||||
Serial.println("TX queue is full");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!txQueue_->isEmpty()) {
|
||||
if (txQueue_->peek((void *)&rxByte)) {
|
||||
if (receiveByte(rxByte)) {
|
||||
txQueue_->drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::resetState()
|
||||
{
|
||||
cmd_ = Cmd::NoCmd;
|
||||
state_ = State::Void;
|
||||
}
|
||||
|
||||
bool Processor::processCommand(unsigned char rxByte) {
|
||||
|
||||
switch (rxByte) {
|
||||
case Cmd::Data:
|
||||
if (!onRigTxBegin()) return false;
|
||||
state_ = State::GetData;
|
||||
break;
|
||||
case Cmd::P:
|
||||
state_ = State::GetP;
|
||||
break;
|
||||
case Cmd::SlotTime:
|
||||
state_ = State::GetSlotTime;
|
||||
break;
|
||||
default:
|
||||
// unknown command
|
||||
resetState();
|
||||
return true;
|
||||
}
|
||||
cmd_ = (Cmd)rxByte;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Processor::receiveByte(unsigned char rxByte) {
|
||||
|
||||
switch (state_) {
|
||||
case State::Void:
|
||||
if (rxByte == Marker::Fend) {
|
||||
cmd_ = Cmd::NoCmd;
|
||||
state_ = State::GetCmd;
|
||||
}
|
||||
break;
|
||||
case State::GetCmd:
|
||||
if (rxByte != Marker::Fend) {
|
||||
if (!processCommand(rxByte)) return false;
|
||||
}
|
||||
break;
|
||||
case State::GetP:
|
||||
onControlCommand(Cmd::P, rxByte);
|
||||
state_ = State::GetData;
|
||||
break;
|
||||
case State::GetSlotTime:
|
||||
onControlCommand(Cmd::SlotTime, 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);
|
||||
}
|
||||
break;
|
||||
case State::Escape:
|
||||
if (rxByte == Marker::Tfend) {
|
||||
onRigTx((byte)Marker::Fend);
|
||||
state_ = State::GetData;
|
||||
}
|
||||
else if (rxByte == Marker::Tfesc) {
|
||||
onRigTx((byte)Marker::Fesc);
|
||||
state_ = State::GetData;
|
||||
}
|
||||
else {
|
||||
resetState();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // kiss
|
|
@ -0,0 +1,88 @@
|
|||
#ifndef KISS_PROCESSOR_H
|
||||
#define KISS_PROCESSOR_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <cppQueue.h>
|
||||
#include <memory>
|
||||
|
||||
namespace Kiss {
|
||||
|
||||
class Processor {
|
||||
|
||||
public:
|
||||
Processor();
|
||||
|
||||
void serialSend(const byte *data, int dataLength);
|
||||
void serialProcessRx();
|
||||
|
||||
protected:
|
||||
enum Marker {
|
||||
Fend = 0xc0,
|
||||
Fesc = 0xdb,
|
||||
Tfend = 0xdc,
|
||||
Tfesc = 0xdd
|
||||
};
|
||||
|
||||
enum State {
|
||||
Void = 0,
|
||||
GetCmd,
|
||||
GetData,
|
||||
GetP,
|
||||
GetSlotTime,
|
||||
Escape
|
||||
};
|
||||
|
||||
enum Cmd {
|
||||
|
||||
// generic
|
||||
Data = 0x00,
|
||||
P = 0x02,
|
||||
SlotTime = 0x03,
|
||||
|
||||
// extended to modem
|
||||
Frequency = 0x10,
|
||||
Bandwidth = 0x11,
|
||||
Power = 0x12,
|
||||
SyncWord = 0x13,
|
||||
SpreadingFactor = 0x14,
|
||||
CodingRate = 0x15,
|
||||
EnableCrc = 0x16,
|
||||
|
||||
// extended events from modem
|
||||
SignalLevel = 0x30,
|
||||
|
||||
// end of cmds
|
||||
NoCmd = 0x80
|
||||
};
|
||||
|
||||
const int CfgTxQueueSize = 4096;
|
||||
|
||||
protected:
|
||||
virtual bool onRigTxBegin() = 0;
|
||||
virtual void onRigTx(byte data) = 0;
|
||||
virtual void onRigTxEnd() = 0;
|
||||
|
||||
virtual void onSerialTx(byte data) = 0;
|
||||
virtual bool onSerialRxHasData() = 0;
|
||||
virtual bool onSerialRx(byte *data) = 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;
|
||||
*/
|
||||
|
||||
private:
|
||||
bool receiveByte(unsigned char rxByte);
|
||||
bool processCommand(unsigned char rxByte);
|
||||
void resetState();
|
||||
|
||||
private:
|
||||
Cmd cmd_;
|
||||
State state_;
|
||||
std::shared_ptr<cppQueue> txQueue_;
|
||||
};
|
||||
|
||||
} // Kiss
|
||||
|
||||
#endif // KISS_PROCESSOR_H
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
namespace LoraPrs {
|
||||
|
||||
Service::Service()
|
||||
: csmaP_(CfgCsmaPersistence)
|
||||
Service::Service()
|
||||
: Kiss::Processor()
|
||||
, csmaP_(CfgCsmaPersistence)
|
||||
, csmaSlotTime_(CfgCsmaSlotTimeMs)
|
||||
, kissState_(KissState::Void)
|
||||
, kissCmd_(KissCmd::NoCmd)
|
||||
, kissTxQueue_(new cppQueue(sizeof(unsigned char), CfgLoraTxQueueSize))
|
||||
, csmaSlotTimePrev_(0)
|
||||
, serialBt_()
|
||||
{
|
||||
}
|
||||
|
@ -145,13 +144,13 @@ void Service::loop()
|
|||
reconnectAprsis();
|
||||
}
|
||||
|
||||
// RX path
|
||||
// RX path, Rig -> Serial
|
||||
if (int packetSize = LoRa.parsePacket()) {
|
||||
onLoraDataAvailable(packetSize);
|
||||
}
|
||||
// TX path
|
||||
// TX path, Serial -> Rig
|
||||
else {
|
||||
if (random(0, 255) < csmaP_) {
|
||||
if (millis() > csmaSlotTimePrev_ + csmaSlotTime_ && random(0, 255) < csmaP_) {
|
||||
if (aprsisConn_.available() > 0) {
|
||||
onAprsisDataAvailable();
|
||||
}
|
||||
|
@ -159,11 +158,9 @@ void Service::loop()
|
|||
sendPeriodicBeacon();
|
||||
}
|
||||
else {
|
||||
processTx();
|
||||
serialProcessRx();
|
||||
}
|
||||
}
|
||||
else {
|
||||
delay(csmaSlotTime_);
|
||||
csmaSlotTimePrev_ = millis();
|
||||
}
|
||||
}
|
||||
delay(CfgPollDelayMs);
|
||||
|
@ -234,7 +231,6 @@ bool Service::sendAX25ToLora(const AX25::Payload &payload)
|
|||
if (bytesWritten <= 0) {
|
||||
Serial.println("Failed to serialize payload");
|
||||
return false;
|
||||
|
||||
}
|
||||
LoRa.beginPacket();
|
||||
LoRa.write(buf, bytesWritten);
|
||||
|
@ -247,27 +243,11 @@ void Service::onLoraDataAvailable(int packetSize)
|
|||
int rxBufIndex = 0;
|
||||
byte rxBuf[packetSize];
|
||||
|
||||
serialBt_.write(KissMarker::Fend);
|
||||
serialBt_.write(KissCmd::Data);
|
||||
|
||||
while (LoRa.available()) {
|
||||
byte rxByte = LoRa.read();
|
||||
|
||||
if (rxByte == KissMarker::Fend) {
|
||||
serialBt_.write(KissMarker::Fesc);
|
||||
serialBt_.write(KissMarker::Tfend);
|
||||
}
|
||||
else if (rxByte == KissMarker::Fesc) {
|
||||
serialBt_.write(KissMarker::Fesc);
|
||||
serialBt_.write(KissMarker::Tfesc);
|
||||
}
|
||||
else {
|
||||
rxBuf[rxBufIndex++] = rxByte;
|
||||
serialBt_.write(rxByte);
|
||||
}
|
||||
rxBuf[rxBufIndex++] = rxByte;
|
||||
}
|
||||
|
||||
serialBt_.write(KissMarker::Fend);
|
||||
serialSend(rxBuf, rxBufIndex);
|
||||
long frequencyError = LoRa.packetFrequencyError();
|
||||
|
||||
if (config_.EnableAutoFreqCorrection) {
|
||||
|
@ -317,113 +297,54 @@ void Service::processIncomingRawPacketAsServer(const byte *packet, int packetLen
|
|||
}
|
||||
}
|
||||
|
||||
void Service::processTx()
|
||||
bool Service::onRigTxBegin()
|
||||
{
|
||||
while (serialBt_.available() || !kissTxQueue_->isEmpty()) {
|
||||
|
||||
if (serialBt_.available()) {
|
||||
int rxResult = serialBt_.read();
|
||||
if (rxResult != -1) {
|
||||
byte rxByte = (byte)rxResult;
|
||||
if (!kissTxQueue_->push((void *)&rxByte)) {
|
||||
Serial.println("TX queue is full");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!kissTxQueue_->isEmpty()) {
|
||||
byte qRxByte;
|
||||
if (kissTxQueue_->peek((void *)&qRxByte)) {
|
||||
if (kissReceiveByte(qRxByte)) {
|
||||
kissTxQueue_->drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
yield();
|
||||
}
|
||||
delay(CfgPollDelayMs); // LoRa may drop packet if removed
|
||||
return (LoRa.beginPacket() == 1);
|
||||
}
|
||||
|
||||
void Service::kissResetState()
|
||||
void Service::onRigTx(byte data)
|
||||
{
|
||||
kissCmd_ = KissCmd::NoCmd;
|
||||
kissState_ = KissState::Void;
|
||||
LoRa.write(data);
|
||||
}
|
||||
|
||||
bool Service::kissProcessCommand(unsigned char rxByte) {
|
||||
void Service::onRigTxEnd()
|
||||
{
|
||||
LoRa.endPacket(true);
|
||||
}
|
||||
|
||||
switch (rxByte) {
|
||||
case KissCmd::Data:
|
||||
delay(CfgPollDelayMs); // LoRa may drop packet if removed
|
||||
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;
|
||||
void Service::onSerialTx(byte data)
|
||||
{
|
||||
serialBt_.write(data);
|
||||
}
|
||||
|
||||
bool Service::onSerialRxHasData()
|
||||
{
|
||||
return serialBt_.available();
|
||||
}
|
||||
|
||||
bool Service::onSerialRx(byte *data)
|
||||
{
|
||||
int rxResult = serialBt_.read();
|
||||
if (rxResult == -1) {
|
||||
return false;
|
||||
}
|
||||
kissCmd_ = (KissCmd)rxByte;
|
||||
*data = (byte)rxResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::kissReceiveByte(unsigned char rxByte) {
|
||||
|
||||
switch (kissState_) {
|
||||
case KissState::Void:
|
||||
if (rxByte == KissMarker::Fend) {
|
||||
kissCmd_ = KissCmd::NoCmd;
|
||||
kissState_ = KissState::GetCmd;
|
||||
}
|
||||
void Service::onControlCommand(Cmd cmd, byte value)
|
||||
{
|
||||
switch (cmd) {
|
||||
case Cmd::P:
|
||||
csmaP_ = value;
|
||||
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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
case Cmd::SlotTime:
|
||||
csmaSlotTime_ = (long)value * 10;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // LoraPrs
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
|
||||
#include "BluetoothSerial.h"
|
||||
#include "ax25_payload.h"
|
||||
#include "kiss_processor.h"
|
||||
#include "loraprs_config.h"
|
||||
|
||||
namespace LoraPrs {
|
||||
|
||||
class Service
|
||||
class Service : public Kiss::Processor
|
||||
{
|
||||
public:
|
||||
Service();
|
||||
|
@ -50,46 +51,18 @@ private:
|
|||
inline bool needsBt() const { return config_.IsClientMode; }
|
||||
inline bool needsBeacon() const { return !config_.IsClientMode && config_.EnableBeacon; }
|
||||
|
||||
protected:
|
||||
virtual bool onRigTxBegin();
|
||||
virtual void onRigTx(byte data);
|
||||
virtual void onRigTxEnd();
|
||||
|
||||
virtual void onSerialTx(byte data);
|
||||
virtual bool onSerialRxHasData();
|
||||
virtual bool onSerialRx(byte *data);
|
||||
|
||||
virtual void onControlCommand(Cmd cmd, byte value);
|
||||
|
||||
private:
|
||||
enum KissMarker {
|
||||
Fend = 0xc0,
|
||||
Fesc = 0xdb,
|
||||
Tfend = 0xdc,
|
||||
Tfesc = 0xdd
|
||||
};
|
||||
|
||||
enum KissState {
|
||||
Void = 0,
|
||||
GetCmd,
|
||||
GetData,
|
||||
GetP,
|
||||
GetSlotTime,
|
||||
Escape
|
||||
};
|
||||
|
||||
enum KissCmd {
|
||||
|
||||
// generic
|
||||
Data = 0x00,
|
||||
P = 0x02,
|
||||
SlotTime = 0x03,
|
||||
|
||||
// extended to modem
|
||||
Frequency = 0x10,
|
||||
Bandwidth = 0x11,
|
||||
Power = 0x12,
|
||||
SyncWord = 0x13,
|
||||
SpreadingFactor = 0x14,
|
||||
CodingRate = 0x15,
|
||||
EnableCrc = 0x16,
|
||||
|
||||
// extended events from modem
|
||||
SignalLevel = 0x30,
|
||||
|
||||
// end of cmds
|
||||
NoCmd = 0x80
|
||||
};
|
||||
|
||||
const String CfgLoraprsVersion = "LoRAPRS 0.1";
|
||||
|
||||
// module pinouts
|
||||
|
@ -100,7 +73,6 @@ private:
|
|||
// processor config
|
||||
const int CfgConnRetryMs = 500;
|
||||
const int CfgPollDelayMs = 5;
|
||||
const int CfgLoraTxQueueSize = 4096;
|
||||
const int CfgWiFiConnRetryMaxTimes = 10;
|
||||
const int CfgMaxAX25PayloadSize = 512;
|
||||
|
||||
|
@ -113,14 +85,10 @@ private:
|
|||
Config config_;
|
||||
byte csmaP_;
|
||||
long csmaSlotTime_;
|
||||
long csmaSlotTimePrev_;
|
||||
String aprsLoginCommand_;
|
||||
AX25::Callsign ownCallsign_;
|
||||
|
||||
// kiss
|
||||
KissState kissState_;
|
||||
KissCmd kissCmd_;
|
||||
std::shared_ptr<cppQueue>kissTxQueue_;
|
||||
|
||||
// state
|
||||
long previousBeaconMs_;
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue