LoRa_APRS_iGate/src/aprs_is_utils.cpp

399 lines
17 KiB
C++
Raw Normal View History

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/>.
*/
2025-11-30 14:15:01 +01:00
#include <APRSPacketLib.h>
2023-06-06 17:30:32 +02:00
#include <WiFi.h>
#include "configuration.h"
2023-06-08 06:58:10 +02:00
#include "aprs_is_utils.h"
2023-06-08 05:55:31 +02:00
#include "station_utils.h"
2025-04-25 00:01:00 +02:00
#include "board_pinout.h"
2024-01-22 13:11:01 +01:00
#include "syslog_utils.h"
2023-06-08 06:58:10 +02:00
#include "query_utils.h"
2024-04-23 18:30:57 +02:00
#include "A7670_utils.h"
2024-01-12 05:36:04 +01:00
#include "digi_utils.h"
2025-10-15 20:11:07 +02:00
#include "tnc_utils.h"
2023-06-08 06:58:10 +02:00
#include "display.h"
2023-06-09 07:12:13 +02:00
#include "utils.h"
2023-06-06 17:30:32 +02:00
2025-03-10 07:02:48 +01:00
extern Configuration Config;
2025-08-27 19:25:11 +02:00
extern WiFiClient aprsIsClient;
extern uint32_t lastScreenOn;
extern String firstLine;
extern String secondLine;
extern String thirdLine;
extern String fourthLine;
extern String fifthLine;
extern String sixthLine;
extern String seventhLine;
2024-04-23 18:30:57 +02:00
extern bool modemLoggedToAPRSIS;
2024-05-22 21:29:00 +02:00
extern bool backUpDigiMode;
2025-08-29 20:51:07 +02:00
extern String versionNumber;
2024-04-23 20:10:04 +02:00
2024-12-06 16:03:37 +01:00
uint32_t lastRxTime = millis();
bool passcodeValid = false;
2024-05-14 05:30:15 +02:00
2024-06-19 01:55:33 +02:00
#ifdef HAS_A7670
2025-05-18 14:44:46 +02:00
extern bool stationBeacon;
2024-04-23 18:30:57 +02:00
#endif
2023-06-06 17:30:32 +02:00
namespace APRS_IS_Utils {
2024-05-15 23:47:29 +02:00
void upload(const String& line) {
2025-08-27 19:25:11 +02:00
aprsIsClient.print(line + "\r\n");
2023-10-08 14:39:44 +02:00
}
2023-06-06 17:30:32 +02:00
2024-03-17 12:21:11 +01:00
void connect() {
2024-02-24 14:09:05 +01:00
Serial.print("Connecting to APRS-IS ... ");
uint8_t count = 0;
2025-08-27 19:25:11 +02:00
while (!aprsIsClient.connect(Config.aprs_is.server.c_str(), Config.aprs_is.port) && count < 20) {
2024-02-24 14:09:05 +01:00
Serial.println("Didn't connect with server...");
delay(1000);
2025-08-27 19:25:11 +02:00
aprsIsClient.stop();
aprsIsClient.flush();
2024-02-24 14:09:05 +01:00
Serial.println("Run client.stop");
Serial.println("Trying to connect with Server: " + String(Config.aprs_is.server) + " AprsServerPort: " + String(Config.aprs_is.port));
count++;
Serial.println("Try: " + String(count));
}
if (count == 20) {
Serial.println("Tried: " + String(count) + " FAILED!");
2024-12-06 16:03:37 +01:00
} else {
2024-03-17 12:21:11 +01:00
Serial.println("Connected!\n(Server: " + String(Config.aprs_is.server) + " / Port: " + String(Config.aprs_is.port) + ")");
2024-03-07 17:46:38 +01:00
// String filter = "t/m/" + Config.callsign + "/" + (String)Config.aprs_is.reportingDistance;
2024-07-05 19:12:21 +02:00
String aprsAuth = "user ";
aprsAuth += Config.callsign;
aprsAuth += " pass ";
aprsAuth += Config.aprs_is.passcode;
2025-08-29 20:51:07 +02:00
aprsAuth += " vers CA2RXUiGate ";
aprsAuth += versionNumber;
aprsAuth += " filter ";
2024-07-05 19:12:21 +02:00
aprsAuth += Config.aprs_is.filter;
upload(aprsAuth);
2024-02-24 14:09:05 +01:00
}
2023-06-06 18:37:22 +02:00
}
2024-02-24 14:09:05 +01:00
void checkStatus() {
String wifiState, aprsisState;
if (WiFi.status() == WL_CONNECTED) {
2024-03-17 12:21:11 +01:00
wifiState = "OK";
2024-04-23 18:30:57 +02:00
} else {
2025-04-24 14:07:29 +02:00
if (backUpDigiMode || Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) {
2024-05-22 21:41:25 +02:00
wifiState = "--";
2024-05-22 21:29:00 +02:00
} else {
wifiState = "AP";
2025-05-18 14:44:46 +02:00
}
2024-04-29 15:19:25 +02:00
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
2024-08-14 18:32:34 +02:00
displayToggle(true);
2024-02-24 14:09:05 +01:00
}
lastScreenOn = millis();
}
2024-03-07 17:46:38 +01:00
if (!Config.aprs_is.active) {
2024-03-17 12:21:11 +01:00
aprsisState = "OFF";
2024-04-23 18:30:57 +02:00
} else {
2024-06-19 01:55:33 +02:00
#ifdef HAS_A7670
2024-05-11 18:59:07 +02:00
if (modemLoggedToAPRSIS) {
aprsisState = "OK";
} else {
aprsisState = "--";
}
2024-04-23 18:30:57 +02:00
#else
2025-08-27 19:25:11 +02:00
if (aprsIsClient.connected()) {
2024-05-11 18:59:07 +02:00
aprsisState = "OK";
} else {
aprsisState = "--";
}
2024-04-23 18:30:57 +02:00
#endif
2024-04-29 15:19:25 +02:00
if(aprsisState == "--" && !Config.display.alwaysOn && Config.display.timeout != 0) {
2024-08-14 18:32:34 +02:00
displayToggle(true);
2024-04-23 18:30:57 +02:00
lastScreenOn = millis();
2024-11-06 16:47:42 +01:00
}
2024-02-24 14:09:05 +01:00
}
2024-05-30 22:27:07 +02:00
secondLine = "WiFi: ";
secondLine += wifiState;
secondLine += " APRS-IS: ";
secondLine += aprsisState;
2023-06-06 18:37:22 +02:00
}
2024-06-20 20:06:14 +02:00
String checkForStartingBytes(const String& packet) {
if (packet.indexOf("\x3c\xff\x01") != -1) {
return packet.substring(0, packet.indexOf("\x3c\xff\x01"));
} else {
return packet;
2024-06-08 21:50:55 +02:00
}
2024-06-20 20:06:14 +02:00
}
String buildPacketToUpload(const String& packet) {
2025-07-26 15:10:36 +02:00
String packetToUpload = packet.substring(3, packet.indexOf(":"));
if (Config.aprs_is.active && passcodeValid && Config.aprs_is.messagesToRF) {
packetToUpload += ",qAR,";
2024-06-07 06:10:04 +02:00
} else {
2025-07-26 15:10:36 +02:00
packetToUpload += ",qAO,";
2024-02-24 14:09:05 +01:00
}
2025-07-26 15:10:36 +02:00
packetToUpload += Config.callsign;
packetToUpload += checkForStartingBytes(packet.substring(packet.indexOf(":")));
return packetToUpload;
2023-10-08 14:39:44 +02:00
}
2023-06-08 03:25:31 +02:00
2024-06-28 22:05:04 +02:00
bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty) {
2024-05-15 23:47:29 +02:00
String receivedMessage;
2024-03-17 12:21:11 +01:00
if (packet.indexOf("{") > 0) { // ack?
2024-05-30 22:27:07 +02:00
String ackMessage = "ack";
ackMessage.concat(packet.substring(packet.indexOf("{") + 1));
2024-03-18 18:48:56 +01:00
ackMessage.trim();
//Serial.println(ackMessage);
2025-05-18 14:44:46 +02:00
String addToBuffer = Config.callsign;
2024-10-08 17:19:22 +02:00
addToBuffer += ">APLRG1";
if (!thirdParty) addToBuffer += ",RFONLY";
if (Config.beacon.path != "") {
addToBuffer += ",";
addToBuffer += Config.beacon.path;
}
addToBuffer += "::";
2024-05-15 23:47:29 +02:00
String processedSender = sender;
2024-03-17 12:21:11 +01:00
for (int i = sender.length(); i < 9; i++) {
2024-05-15 23:47:29 +02:00
processedSender += ' ';
2024-03-18 18:48:56 +01:00
}
addToBuffer += processedSender;
addToBuffer += ":";
addToBuffer += ackMessage;
STATION_Utils::addToOutputPacketBuffer(addToBuffer);
2024-03-17 12:21:11 +01:00
receivedMessage = packet.substring(packet.indexOf(":") + 1, packet.indexOf("{"));
} else {
2024-03-17 12:21:11 +01:00
receivedMessage = packet.substring(packet.indexOf(":") + 1);
2024-03-18 18:48:56 +01:00
}
if (receivedMessage.indexOf("?") == 0) {
2024-04-29 15:19:25 +02:00
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
2024-08-14 18:32:34 +02:00
displayToggle(true);
2024-03-18 18:48:56 +01:00
}
2024-06-28 22:05:04 +02:00
STATION_Utils::addToOutputPacketBuffer(QUERY_Utils::process(receivedMessage, sender, false, thirdParty));
2024-03-18 18:48:56 +01:00
lastScreenOn = millis();
2024-08-14 18:32:34 +02:00
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, "Callsign = " + sender, "TYPE --> QUERY", 0);
2024-03-18 18:48:56 +01:00
return true;
2024-03-17 12:21:11 +01:00
}
else {
2024-03-18 18:48:56 +01:00
return false;
}
}
2024-05-15 23:47:29 +02:00
void processLoRaPacket(const String& packet) {
2025-08-27 19:25:11 +02:00
if (passcodeValid && (aprsIsClient.connected() || modemLoggedToAPRSIS)) {
2025-03-03 15:19:25 +01:00
if (packet.indexOf("NOGATE") == -1 && packet.indexOf("RFONLY") == -1) {
int firstColonIndex = packet.indexOf(":");
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) {
const String& Sender = packet.substring(3, packet.indexOf(">"));
if (Sender != Config.callsign && Utils::checkValidCallsign(Sender)) {
STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(packet.substring(3), 0); // LoRa-APRS
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
bool queryMessage = false;
if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
queryMessage = processReceivedLoRaMessage(Sender, checkForStartingBytes(AddresseeAndMessage), false);
}
if (!queryMessage) {
const String& aprsPacket = buildPacketToUpload(packet);
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
displayToggle(true);
2024-03-27 15:26:02 +01:00
}
2025-03-03 15:19:25 +01:00
lastScreenOn = millis();
#ifdef HAS_A7670
stationBeacon = true;
A7670_Utils::uploadToAPRSIS(aprsPacket);
stationBeacon = false;
#else
upload(aprsPacket);
#endif
Utils::println("---> Uploaded to APRS-IS");
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
2024-02-24 14:09:05 +01:00
}
2024-11-06 16:47:42 +01:00
}
2024-03-17 12:21:11 +01:00
}
2023-10-08 14:39:44 +02:00
}
2024-02-24 14:09:05 +01:00
}
2023-06-08 05:55:31 +02:00
}
2023-06-06 21:53:06 +02:00
2024-06-24 16:32:58 +02:00
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType) {
String packet = aprsisPacket;
packet.trim();
2025-11-30 14:15:01 +01:00
String outputPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
2024-06-24 16:32:58 +02:00
outputPacket += ":}";
outputPacket += packet.substring(0, packet.indexOf(",")); // Callsign>Tocall
outputPacket.concat(",TCPIP,");
outputPacket.concat(Config.callsign);
outputPacket.concat("*");
switch (packetType) {
case 0: // gps
if (packet.indexOf(":=") > 0) {
outputPacket += packet.substring(packet.indexOf(":="));
} else {
outputPacket += packet.substring(packet.indexOf(":!"));
}
break;
case 1: // messages
outputPacket += packet.substring(packet.indexOf("::"));
break;
case 2: // status
outputPacket += packet.substring(packet.indexOf(":>"));
break;
case 3: // telemetry
outputPacket += packet.substring(packet.indexOf("::"));
break;
case 4: // mic-e
if (packet.indexOf(":`") > 0) {
outputPacket += packet.substring(packet.indexOf(":`"));
} else {
outputPacket += packet.substring(packet.indexOf(":'"));
}
break;
case 5: // object
outputPacket += packet.substring(packet.indexOf(":;"));
break;
}
return outputPacket;
}
2024-05-15 23:47:29 +02:00
void processAPRSISPacket(const String& packet) {
2024-12-06 16:03:37 +01:00
if (!passcodeValid && packet.indexOf(Config.callsign) != -1) {
if (packet.indexOf("unverified") != -1 ) {
Serial.println("\n****APRS PASSCODE NOT VALID****\n");
2025-12-22 23:56:15 +01:00
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 3000);
aprsIsClient.stop();
Config.aprs_is.active = false;
2024-12-06 16:03:37 +01:00
} else if (packet.indexOf("verified") != -1 ) {
passcodeValid = true;
}
}
if (passcodeValid && !packet.startsWith("#")) {
2024-06-08 18:52:47 +02:00
if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
String Sender = packet.substring(0, packet.indexOf(">"));
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
2024-12-06 16:03:37 +01:00
if (Addressee == Config.callsign) { // its for me!
String receivedMessage;
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
String ackMessage = "ack";
ackMessage += AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{") + 1);
ackMessage.trim();
delay(4000);
for (int i = Sender.length(); i < 9; i++) {
Sender += ' ';
2024-02-24 14:09:05 +01:00
}
String ackPacket = Config.callsign;
ackPacket += ">APLRG1,TCPIP,qAC::";
ackPacket += Sender;
ackPacket += ":";
ackPacket += ackMessage;
2024-06-19 01:55:33 +02:00
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(ackPacket);
#else
upload(ackPacket);
2024-11-06 16:47:42 +01:00
#endif
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
2024-03-27 15:26:02 +01:00
} else {
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
}
if (receivedMessage.indexOf("?") == 0) {
2025-02-25 17:54:36 +01:00
Utils::println("Rx Query (APRS-IS) : " + packet);
2025-03-18 18:02:09 +01:00
Sender.trim();
2024-06-28 22:05:04 +02:00
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false);
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
2024-08-14 18:32:34 +02:00
displayToggle(true);
2024-02-24 14:09:05 +01:00
}
lastScreenOn = millis();
delay(500);
2024-06-19 01:55:33 +02:00
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(queryAnswer);
#else
upload(queryAnswer);
2024-09-22 18:34:13 +02:00
#endif
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
fifthLine = "APRS-IS ----> APRS-IS";
sixthLine = Config.callsign;
for (int j = sixthLine.length();j < 9;j++) {
sixthLine += " ";
}
sixthLine += "> ";
sixthLine += Sender;
seventhLine = "QUERY = ";
seventhLine += receivedMessage;
}
2025-02-28 20:48:26 +01:00
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
} else {
2025-02-25 17:54:36 +01:00
Utils::print("Rx Message (APRS-IS): " + packet);
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
2024-08-14 18:32:34 +02:00
displayToggle(true);
lastScreenOn = millis();
Utils::typeOfPacket(packet, 1); // APRS-LoRa
2025-02-28 20:48:26 +01:00
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
2024-02-24 14:09:05 +01:00
}
}
2024-06-08 18:52:47 +02:00
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
2025-02-25 17:54:36 +01:00
Utils::print("Rx Object (APRS-IS) : " + packet);
if (STATION_Utils::checkObjectTime(packet)) {
2025-02-24 21:57:24 +01:00
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
displayToggle(true);
lastScreenOn = millis();
Utils::typeOfPacket(packet, 1); // APRS-LoRa
2025-02-24 22:49:17 +01:00
Serial.println();
} else {
2025-02-25 17:54:36 +01:00
Serial.println(" ---> Rejected (Time): No Tx");
2025-02-24 21:57:24 +01:00
}
2024-06-07 23:29:55 +02:00
}
2025-10-15 20:11:07 +02:00
if (Config.tnc.aprsBridgeActive) {
if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS
if (Config.tnc.enableSerial) TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
}
2023-06-09 07:12:13 +02:00
}
2023-10-08 14:39:44 +02:00
}
2023-06-09 07:12:13 +02:00
2024-03-27 15:26:02 +01:00
void listenAPRSIS() {
2024-06-19 01:55:33 +02:00
#ifdef HAS_A7670
2024-05-11 18:59:07 +02:00
A7670_Utils::listenAPRSIS();
2024-04-23 18:30:57 +02:00
#else
2025-08-27 19:25:11 +02:00
if (aprsIsClient.connected()) {
if (aprsIsClient.available()) {
String aprsisPacket = aprsIsClient.readStringUntil('\r');
2025-02-24 21:08:53 +01:00
aprsisPacket.trim(); // Serial.println(aprsisPacket);
2024-05-11 18:59:07 +02:00
processAPRSISPacket(aprsisPacket);
lastRxTime = millis();
}
2024-02-24 14:09:05 +01:00
}
2024-04-23 18:30:57 +02:00
#endif
2023-07-31 05:53:59 +02:00
}
2024-02-24 14:09:05 +01:00
2024-12-06 16:03:37 +01:00
void firstConnection() {
2025-08-27 19:25:11 +02:00
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !aprsIsClient.connected()) {
2024-12-06 16:03:37 +01:00
connect();
while (!passcodeValid) {
listenAPRSIS();
}
}
}
2023-06-06 17:30:32 +02:00
}