Extract KISS logic into base class and improve CSMA logic

pull/15/head
sh123 2021-02-02 17:53:28 +02:00
rodzic f0f202b799
commit b222573f1f
4 zmienionych plików z 287 dodań i 169 usunięć

141
kiss_processor.cpp 100644
Wyświetl plik

@ -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

88
kiss_processor.h 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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_;