2025-07-15 22:28:23 +02:00
|
|
|
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
|
|
|
|
*
|
|
|
|
|
* This file is part of LoRa APRS iGate.
|
|
|
|
|
*
|
|
|
|
|
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-03-09 16:25:23 +01:00
|
|
|
#include <WiFi.h>
|
2025-09-09 00:00:23 +02:00
|
|
|
#include "ESPmDNS.h"
|
2024-03-17 12:21:11 +01:00
|
|
|
#include "configuration.h"
|
2024-04-20 15:27:20 +02:00
|
|
|
#include "station_utils.h"
|
2025-07-26 15:17:32 +02:00
|
|
|
#include "kiss_protocol.h"
|
2025-10-15 20:11:07 +02:00
|
|
|
#include "aprs_is_utils.h"
|
2025-07-26 15:17:32 +02:00
|
|
|
#include "kiss_utils.h"
|
2025-09-09 00:00:23 +02:00
|
|
|
#include "tnc_utils.h"
|
2024-03-17 12:21:11 +01:00
|
|
|
#include "utils.h"
|
|
|
|
|
|
2025-03-10 07:02:48 +01:00
|
|
|
|
2025-10-15 20:11:07 +02:00
|
|
|
extern Configuration Config;
|
|
|
|
|
extern WiFiClient aprsIsClient;
|
|
|
|
|
extern bool passcodeValid;
|
2024-03-09 16:25:23 +01:00
|
|
|
|
|
|
|
|
#define MAX_CLIENTS 4
|
2024-03-17 12:21:11 +01:00
|
|
|
#define INPUT_BUFFER_SIZE (2 + MAX_CLIENTS)
|
2024-03-09 16:25:23 +01:00
|
|
|
|
|
|
|
|
#define TNC_PORT 8001
|
|
|
|
|
|
|
|
|
|
WiFiClient* clients[MAX_CLIENTS];
|
|
|
|
|
|
|
|
|
|
WiFiServer tncServer(TNC_PORT);
|
|
|
|
|
|
2024-03-17 12:21:11 +01:00
|
|
|
String inputServerBuffer[INPUT_BUFFER_SIZE];
|
|
|
|
|
String inputSerialBuffer = "";
|
2024-03-09 16:25:23 +01:00
|
|
|
|
2024-11-06 16:47:42 +01:00
|
|
|
|
2024-03-09 16:25:23 +01:00
|
|
|
namespace TNC_Utils {
|
2024-04-04 20:54:49 +02:00
|
|
|
|
2024-03-09 16:25:23 +01:00
|
|
|
void setup() {
|
2025-04-24 14:07:29 +02:00
|
|
|
if (Config.tnc.enableServer && Config.digi.ecoMode == 0) {
|
2024-03-17 12:21:11 +01:00
|
|
|
tncServer.stop();
|
|
|
|
|
tncServer.begin();
|
2025-09-09 00:00:23 +02:00
|
|
|
String host = "igate-" + Config.callsign;
|
|
|
|
|
if (!MDNS.begin(host.c_str())) {
|
|
|
|
|
Serial.println("Error Starting mDNS");
|
|
|
|
|
tncServer.stop();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!MDNS.addService("tnc", "tcp", TNC_PORT)) {
|
|
|
|
|
Serial.println("Error: Could not add mDNS service");
|
|
|
|
|
}
|
|
|
|
|
Serial.println("TNC server started successfully");
|
|
|
|
|
Serial.println("mDNS Host: " + host + ".local");
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
2024-03-09 16:25:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkNewClients() {
|
2024-09-23 19:31:45 +02:00
|
|
|
WiFiClient new_client = tncServer.accept();
|
2024-03-09 16:25:23 +01:00
|
|
|
if (new_client.connected()) {
|
|
|
|
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
|
|
|
|
WiFiClient* client = clients[i];
|
|
|
|
|
if (client == nullptr) {
|
|
|
|
|
clients[i] = new WiFiClient(new_client);
|
2024-03-17 12:21:11 +01:00
|
|
|
Utils::println("New TNC client connected");
|
2024-03-09 16:25:23 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 19:28:08 +01:00
|
|
|
void handleInputData(char character, int bufferIndex) {
|
2025-02-12 19:28:48 +01:00
|
|
|
String* data = (bufferIndex == -1) ? &inputSerialBuffer : &inputServerBuffer[bufferIndex];
|
|
|
|
|
if (data->length() == 0 && character != (char)FEND) return;
|
2025-05-18 14:44:46 +02:00
|
|
|
|
2024-03-17 19:28:08 +01:00
|
|
|
data->concat(character);
|
2024-03-09 16:25:23 +01:00
|
|
|
|
2024-03-17 19:28:08 +01:00
|
|
|
if (character == (char)FEND && data->length() > 3) {
|
2024-03-09 16:25:23 +01:00
|
|
|
bool isDataFrame = false;
|
2024-03-17 19:28:08 +01:00
|
|
|
const String& frame = decodeKISS(*data, isDataFrame);
|
2024-03-09 16:25:23 +01:00
|
|
|
|
|
|
|
|
if (isDataFrame) {
|
2024-03-17 19:28:08 +01:00
|
|
|
if (bufferIndex != -1) {
|
|
|
|
|
Utils::print("<--- Got from TNC : ");
|
|
|
|
|
Utils::println(frame);
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
2024-03-09 16:25:23 +01:00
|
|
|
|
2024-03-17 12:21:11 +01:00
|
|
|
String sender = frame.substring(0,frame.indexOf(">"));
|
|
|
|
|
|
|
|
|
|
if (Config.tnc.acceptOwn || sender != Config.callsign) {
|
2025-10-15 20:11:07 +02:00
|
|
|
if (Config.loramodule.txActive) STATION_Utils::addToOutputPacketBuffer(frame);
|
|
|
|
|
if (Config.tnc.aprsBridgeActive && Config.aprs_is.active && passcodeValid && aprsIsClient.connected()) APRS_IS_Utils::upload(frame);
|
2024-03-17 19:28:08 +01:00
|
|
|
} else {
|
|
|
|
|
Utils::println("Ignored own frame from KISS");
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-17 19:28:08 +01:00
|
|
|
data->clear();
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-17 19:28:08 +01:00
|
|
|
if (data->length() > 255) {
|
|
|
|
|
data->clear();
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-09 16:25:23 +01:00
|
|
|
void readFromClients() {
|
|
|
|
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
|
|
|
|
auto client = clients[i];
|
|
|
|
|
if (client != nullptr) {
|
|
|
|
|
if (client->connected()) {
|
|
|
|
|
while (client->available() > 0) {
|
|
|
|
|
char character = client->read();
|
2024-03-17 19:28:08 +01:00
|
|
|
handleInputData(character, 2 + i);
|
2024-03-09 16:25:23 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
delete client;
|
|
|
|
|
clients[i] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 12:21:11 +01:00
|
|
|
void readFromSerial() {
|
|
|
|
|
while (Serial.available() > 0) {
|
|
|
|
|
char character = Serial.read();
|
2024-03-17 19:28:08 +01:00
|
|
|
handleInputData(character, -1);
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 22:12:38 +02:00
|
|
|
void sendToClients(const String& packet, bool stripBytes) {
|
|
|
|
|
String cleanPacket = stripBytes ? packet.substring(3): packet;
|
2024-03-09 16:25:23 +01:00
|
|
|
|
2024-05-15 22:41:07 +02:00
|
|
|
const String kissEncoded = encodeKISS(cleanPacket);
|
2024-03-09 16:25:23 +01:00
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
|
|
|
|
auto client = clients[i];
|
|
|
|
|
if (client != nullptr) {
|
|
|
|
|
if (client->connected()) {
|
|
|
|
|
client->print(kissEncoded);
|
|
|
|
|
client->flush();
|
|
|
|
|
} else {
|
|
|
|
|
delete client;
|
|
|
|
|
clients[i] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-17 12:21:11 +01:00
|
|
|
Utils::print("---> Sent to TNC : ");
|
2024-05-15 22:41:07 +02:00
|
|
|
Utils::println(cleanPacket);
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-15 22:12:38 +02:00
|
|
|
void sendToSerial(const String& packet, bool stripBytes) {
|
|
|
|
|
String cleanPacket = stripBytes ? packet.substring(3): packet;
|
2024-05-15 22:41:07 +02:00
|
|
|
Serial.print(encodeKISS(cleanPacket));
|
2024-03-17 12:21:11 +01:00
|
|
|
Serial.flush();
|
2024-03-09 16:25:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void loop() {
|
2025-04-24 14:07:29 +02:00
|
|
|
if (Config.digi.ecoMode == 0) {
|
2024-10-07 04:23:22 +02:00
|
|
|
if (Config.tnc.enableServer) {
|
|
|
|
|
checkNewClients();
|
|
|
|
|
readFromClients();
|
|
|
|
|
}
|
|
|
|
|
if (Config.tnc.enableSerial) {
|
|
|
|
|
readFromSerial();
|
|
|
|
|
}
|
2024-03-17 12:21:11 +01:00
|
|
|
}
|
2024-03-09 16:25:23 +01:00
|
|
|
}
|
|
|
|
|
}
|