diff --git a/data_embed/index.html b/data_embed/index.html
index eefaee3..b7fd316 100644
--- a/data_embed/index.html
+++ b/data_embed/index.html
@@ -1520,7 +1520,7 @@ body{background:var(--msh-bg);color:var(--msh-text);font-family:"Inter",system-u
Enable Serial
@@ -1552,7 +1552,7 @@ body{background:var(--msh-bg);color:var(--msh-text);font-family:"Inter",system-u
Accept own frames
@@ -1572,6 +1572,23 @@ body{background:var(--msh-bg);color:var(--msh-text);font-family:"Inter",system-u
+
+
+
+
+
+
diff --git a/data_embed/script.js b/data_embed/script.js
index 1f935cd..848c9c6 100644
--- a/data_embed/script.js
+++ b/data_embed/script.js
@@ -226,6 +226,7 @@ function loadSettings(settings) {
document.getElementById("tnc.enableSerial").checked = settings.tnc.enableSerial;
document.getElementById("tnc.acceptOwn").checked = settings.tnc.acceptOwn;
document.getElementById("tnc.aprsBridgeActive").checked = settings.tnc.aprsBridgeActive;
+ document.getElementById("tnc.kissProtocol").value = settings.tnc.kissProtocol ? "KISS" : "TNC2";
}
// MQTT
diff --git a/include/configuration.h b/include/configuration.h
index 3927e12..69e9667 100644
--- a/include/configuration.h
+++ b/include/configuration.h
@@ -132,6 +132,7 @@ public:
bool enableSerial;
bool acceptOwn;
bool aprsBridgeActive;
+ bool kissProtocol; // true = KISS, false = TNC2 -- applies to both Serial and TCP server
};
class OTA {
diff --git a/src/LoRa_APRS_iGate.cpp b/src/LoRa_APRS_iGate.cpp
index 99f5a6f..fab911f 100644
--- a/src/LoRa_APRS_iGate.cpp
+++ b/src/LoRa_APRS_iGate.cpp
@@ -68,7 +68,7 @@ ___________________________________________________________________*/
#endif
-String versionDate = "2026-09-07";
+String versionDate = "2026-09-15";
String versionNumber = "4.0.1";
Configuration Config;
WiFiClient aprsIsClient;
diff --git a/src/configuration.cpp b/src/configuration.cpp
index a6ed87f..306045a 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -157,6 +157,7 @@ bool Configuration::writeFile() {
data["tnc"]["enableSerial"] = tnc.enableSerial;
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
data["tnc"]["aprsBridgeActive"] = tnc.aprsBridgeActive;
+ data["tnc"]["kissProtocol"] = tnc.kissProtocol;
data["mqtt"]["active"] = mqtt.active;
data["mqtt"]["server"] = mqtt.server;
@@ -369,11 +370,13 @@ bool Configuration::readFile() {
if (data["tnc"]["enableServer"].isNull() ||
data["tnc"]["enableSerial"].isNull() ||
data["tnc"]["acceptOwn"].isNull() ||
- data["tnc"]["aprsBridgeActive"].isNull()) needsRewrite = true;
+ data["tnc"]["aprsBridgeActive"].isNull() ||
+ data["tnc"]["kissProtocol"].isNull()) needsRewrite = true;
tnc.enableServer = data["tnc"]["enableServer"] | false;
tnc.enableSerial = data["tnc"]["enableSerial"] | false;
tnc.acceptOwn = data["tnc"]["acceptOwn"] | false;
tnc.aprsBridgeActive = data["tnc"]["aprsBridgeActive"] | false;
+ tnc.kissProtocol = data["tnc"]["kissProtocol"] | true;
if (data["mqtt"]["active"].isNull() ||
data["mqtt"]["server"].isNull() ||
@@ -538,6 +541,7 @@ void Configuration::setDefaultValues() {
tnc.enableSerial = false;
tnc.acceptOwn = false;
tnc.aprsBridgeActive = false;
+ tnc.kissProtocol = true;
mqtt.active = false;
mqtt.server = "";
diff --git a/src/tnc_utils.cpp b/src/tnc_utils.cpp
index f53cd14..99f6328 100644
--- a/src/tnc_utils.cpp
+++ b/src/tnc_utils.cpp
@@ -78,7 +78,23 @@ namespace TNC_Utils {
}
}
- void handleInputData(char character, int bufferIndex) {
+ void processInputFrame(const String& frame, int bufferIndex, const String& protocolLabel) {
+ if (bufferIndex != -1) {
+ Utils::print("<--- Got from TNC (" + protocolLabel + ") : ");
+ Utils::println(frame);
+ }
+
+ String sender = frame.substring(0,frame.indexOf(">"));
+
+ if (Config.tnc.acceptOwn || sender != Config.callsign) {
+ if (Config.loramodule.txActive) STATION_Utils::addToOutputPacketBuffer(frame);
+ if (Config.tnc.aprsBridgeActive && Config.aprs_is.active && passcodeValid && aprsIsClient.connected()) APRS_IS_Utils::upload(frame);
+ } else {
+ Utils::println("Ignored own frame from " + protocolLabel);
+ }
+ }
+
+ void handleInputDataKISS(char character, int bufferIndex) {
String* data = (bufferIndex == -1) ? &inputSerialBuffer : &inputServerBuffer[bufferIndex];
if (data->length() == 0 && character != (char)FEND) return;
@@ -89,19 +105,7 @@ namespace TNC_Utils {
const String& frame = decodeKISS(*data, isDataFrame);
if (isDataFrame) {
- if (bufferIndex != -1) {
- Utils::print("<--- Got from TNC : ");
- Utils::println(frame);
- }
-
- String sender = frame.substring(0,frame.indexOf(">"));
-
- if (Config.tnc.acceptOwn || sender != Config.callsign) {
- if (Config.loramodule.txActive) STATION_Utils::addToOutputPacketBuffer(frame);
- if (Config.tnc.aprsBridgeActive && Config.aprs_is.active && passcodeValid && aprsIsClient.connected()) APRS_IS_Utils::upload(frame);
- } else {
- Utils::println("Ignored own frame from KISS");
- }
+ processInputFrame(frame, bufferIndex, "KISS");
}
data->clear();
}
@@ -111,6 +115,36 @@ namespace TNC_Utils {
}
}
+ void handleInputDataTNC2(char character, int bufferIndex) {
+ String* data = (bufferIndex == -1) ? &inputSerialBuffer : &inputServerBuffer[bufferIndex];
+
+ if (character == '\r') return;
+
+ if (character == '\n') {
+ String frame = *data;
+ frame.trim();
+ data->clear();
+
+ if (frame.length() > 0 && frame.indexOf(':') != -1 && frame.indexOf('>') != -1) {
+ processInputFrame(frame, bufferIndex, "TNC2");
+ }
+ return;
+ }
+
+ data->concat(character);
+ if (data->length() > 255) {
+ data->clear();
+ }
+ }
+
+ void handleInputData(char character, int bufferIndex) {
+ if (Config.tnc.kissProtocol) {
+ handleInputDataKISS(character, bufferIndex);
+ } else {
+ handleInputDataTNC2(character, bufferIndex);
+ }
+ }
+
void readFromClients() {
for (int i = 0; i < MAX_CLIENTS; i++) {
auto client = clients[i];
@@ -138,13 +172,13 @@ namespace TNC_Utils {
void sendToClients(const String& packet, bool stripBytes) {
String cleanPacket = stripBytes ? packet.substring(3): packet;
- const String kissEncoded = encodeKISS(cleanPacket);
+ const String encoded = Config.tnc.kissProtocol ? encodeKISS(cleanPacket) : (cleanPacket + "\r\n");
for (int i = 0; i < MAX_CLIENTS; i++) {
auto client = clients[i];
if (client != nullptr) {
if (client->connected()) {
- client->print(kissEncoded);
+ client->print(encoded);
client->flush();
} else {
delete client;
@@ -158,7 +192,7 @@ namespace TNC_Utils {
void sendToSerial(const String& packet, bool stripBytes) {
String cleanPacket = stripBytes ? packet.substring(3): packet;
- Serial.print(encodeKISS(cleanPacket));
+ Serial.print(Config.tnc.kissProtocol ? encodeKISS(cleanPacket) : (cleanPacket + "\r\n"));
Serial.flush();
}
diff --git a/src/web_utils.cpp b/src/web_utils.cpp
index 5ed6b36..87b4a55 100644
--- a/src/web_utils.cpp
+++ b/src/web_utils.cpp
@@ -322,6 +322,7 @@ namespace WEB_Utils {
Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true);
Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true);
Config.tnc.aprsBridgeActive = request->hasParam("tnc.aprsBridgeActive", true);
+ Config.tnc.kissProtocol = getParamStringSafe("tnc.kissProtocol", Config.tnc.kissProtocol ? "KISS" : "TNC2") != "TNC2";
Config.mqtt.active = request->hasParam("mqtt.active", true);
if (Config.mqtt.active) {