forked from iarv/LoRa_APRS_iGate
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d65b2c93f |
+1
-2
@@ -35,8 +35,7 @@ lib_deps =
|
|||||||
jgromes/RadioLib @ 7.1.0
|
jgromes/RadioLib @ 7.1.0
|
||||||
mathieucarbou/AsyncTCP @ 3.2.5
|
mathieucarbou/AsyncTCP @ 3.2.5
|
||||||
mathieucarbou/ESPAsyncWebServer @ 3.2.3
|
mathieucarbou/ESPAsyncWebServer @ 3.2.3
|
||||||
mikalhart/TinyGPSPlus @ 1.0.3
|
mikalhart/TinyGPSPlus @ 1.0.3
|
||||||
richonguzman/APRSPacketLib @1.0.0
|
|
||||||
display_libs =
|
display_libs =
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
adafruit/Adafruit GFX Library @ 1.11.9
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
adafruit/Adafruit SSD1306 @ 2.5.10
|
||||||
|
|||||||
@@ -35,11 +35,16 @@ namespace APRS_IS_Utils {
|
|||||||
void processLoRaPacket(const String& packet);
|
void processLoRaPacket(const String& packet);
|
||||||
|
|
||||||
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType);
|
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType);
|
||||||
void processAPRSISPacket(const String& packet);
|
void processAPRSISPacket();//const String& packet);
|
||||||
void listenAPRSIS();
|
void listenAPRSIS();
|
||||||
|
|
||||||
void firstConnection();
|
void firstConnection();
|
||||||
|
|
||||||
|
bool startListenerAPRSISTask(uint32_t stackSize = 8192, UBaseType_t priority = 1);
|
||||||
|
void stopListenerAPRSISTask();
|
||||||
|
void suspendListenerAPRSISTask();
|
||||||
|
void resumeListenerAPRSISTask();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -32,6 +32,9 @@ namespace BATTERY_Utils {
|
|||||||
float checkExternalVoltage();
|
float checkExternalVoltage();
|
||||||
void startupBatteryHealth();
|
void startupBatteryHealth();
|
||||||
|
|
||||||
|
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType);
|
||||||
|
String generateEncodedTelemetry();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -41,6 +41,7 @@ namespace POWER_Utils {
|
|||||||
|
|
||||||
double getBatteryVoltage();
|
double getBatteryVoltage();
|
||||||
bool isBatteryConnected();
|
bool isBatteryConnected();
|
||||||
|
void activateMeasurement();
|
||||||
void activateGPS();
|
void activateGPS();
|
||||||
void deactivateGPS();
|
void deactivateGPS();
|
||||||
void activateLoRa();
|
void activateLoRa();
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
/* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TELEMETRY_UTILS_H_
|
|
||||||
#define TELEMETRY_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace TELEMETRY_Utils {
|
|
||||||
|
|
||||||
void sendEquationsUnitsParameters();
|
|
||||||
String generateEncodedTelemetryBytes(float value, bool counterBytes, byte telemetryType);
|
|
||||||
String generateEncodedTelemetry();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+36
-2
@@ -66,7 +66,7 @@ ___________________________________________________________________*/
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
String versionDate = "2025-08-26";
|
String versionDate = "2025-08-20";
|
||||||
Configuration Config;
|
Configuration Config;
|
||||||
WiFiClient espClient;
|
WiFiClient espClient;
|
||||||
#ifdef HAS_GPS
|
#ifdef HAS_GPS
|
||||||
@@ -96,6 +96,11 @@ std::vector<ReceivedPacket> receivedPackets;
|
|||||||
String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine;
|
String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine;
|
||||||
//#define STARTUP_DELAY 5 //min
|
//#define STARTUP_DELAY 5 //min
|
||||||
|
|
||||||
|
#ifdef HAS_TWO_CORES
|
||||||
|
QueueHandle_t aprsIsTxQueue = NULL;
|
||||||
|
QueueHandle_t aprsIsRxQueue = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
@@ -122,11 +127,39 @@ void setup() {
|
|||||||
A7670_Utils::setup();
|
A7670_Utils::setup();
|
||||||
#endif
|
#endif
|
||||||
Utils::checkRebootMode();
|
Utils::checkRebootMode();
|
||||||
|
|
||||||
APRS_IS_Utils::firstConnection();
|
APRS_IS_Utils::firstConnection();
|
||||||
SLEEP_Utils::checkSerial();
|
SLEEP_Utils::checkSerial();
|
||||||
|
|
||||||
|
// Crear queues con verificación detallada
|
||||||
|
//Serial.println("Creando aprsIsTxQueue...");
|
||||||
|
aprsIsTxQueue = xQueueCreate(50, sizeof(String));
|
||||||
|
//Serial.printf("aprsIsTxQueue = %p\n", aprsIsTxQueue);
|
||||||
|
|
||||||
|
//Serial.println("Creando aprsIsRxQueue...");
|
||||||
|
aprsIsRxQueue = xQueueCreate(50, sizeof(String));
|
||||||
|
//Serial.printf("aprsIsRxQueue = %p\n", aprsIsRxQueue);
|
||||||
|
|
||||||
|
// Verificación crítica
|
||||||
|
if (aprsIsRxQueue == NULL || aprsIsTxQueue == NULL) {
|
||||||
|
Serial.println("FATAL: Error creando queues!");
|
||||||
|
while(1) {
|
||||||
|
Serial.println("STUCK - Queues failed");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("Queues creadas OK");
|
||||||
|
|
||||||
|
// Iniciar el task de APRSIS
|
||||||
|
if (!APRS_IS_Utils::startListenerAPRSISTask()) {
|
||||||
|
Serial.println("Error: No se pudo crear el task de APRSIS");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
//Serial.println("Loop tick: " + String(millis()));
|
||||||
|
//delay(1000);
|
||||||
|
|
||||||
if (Config.digi.ecoMode == 1) {
|
if (Config.digi.ecoMode == 1) {
|
||||||
SLEEP_Utils::checkWakeUpFlag();
|
SLEEP_Utils::checkWakeUpFlag();
|
||||||
Utils::checkBeaconInterval();
|
Utils::checkBeaconInterval();
|
||||||
@@ -201,7 +234,8 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Config.aprs_is.active) {
|
if (Config.aprs_is.active) {
|
||||||
APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
|
APRS_IS_Utils::processAPRSISPacket();
|
||||||
|
//APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
|
||||||
}
|
}
|
||||||
|
|
||||||
STATION_Utils::processOutputPacketBuffer();
|
STATION_Utils::processOutputPacketBuffer();
|
||||||
|
|||||||
+151
-82
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
extern WiFiClient espClient;
|
extern WiFiClient espClient;
|
||||||
|
extern QueueHandle_t aprsIsRxQueue;
|
||||||
extern uint32_t lastScreenOn;
|
extern uint32_t lastScreenOn;
|
||||||
extern String firstLine;
|
extern String firstLine;
|
||||||
extern String secondLine;
|
extern String secondLine;
|
||||||
@@ -52,6 +53,9 @@ bool passcodeValid = false;
|
|||||||
|
|
||||||
namespace APRS_IS_Utils {
|
namespace APRS_IS_Utils {
|
||||||
|
|
||||||
|
// Handle del task (opcional, para poder controlarlo después)
|
||||||
|
TaskHandle_t aprsisTaskHandle = NULL;
|
||||||
|
|
||||||
void upload(const String& line) {
|
void upload(const String& line) {
|
||||||
espClient.print(line + "\r\n");
|
espClient.print(line + "\r\n");
|
||||||
}
|
}
|
||||||
@@ -274,95 +278,103 @@ namespace APRS_IS_Utils {
|
|||||||
return outputPacket;
|
return outputPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processAPRSISPacket(const String& packet) {
|
//uint32_t lastLog = 0;
|
||||||
if (!passcodeValid && packet.indexOf(Config.callsign) != -1) {
|
void processAPRSISPacket() {
|
||||||
if (packet.indexOf("unverified") != -1 ) {
|
/*Serial.println("processAPRSISPacket");
|
||||||
Serial.println("\n****APRS PASSCODE NOT VALID****\n");
|
|
||||||
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 0);
|
|
||||||
while (1) {};
|
|
||||||
} else if (packet.indexOf("verified") != -1 ) {
|
|
||||||
passcodeValid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (passcodeValid && !packet.startsWith("#")) {
|
|
||||||
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();
|
|
||||||
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 += ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
String ackPacket = Config.callsign;
|
if (millis() - lastLog > 5000) { // Cada 5 segundos
|
||||||
ackPacket += ">APLRG1,TCPIP,qAC::";
|
UBaseType_t packets = uxQueueMessagesWaiting(aprsIsRxQueue);
|
||||||
ackPacket += Sender;
|
UBaseType_t spaces = uxQueueSpacesAvailable(aprsIsRxQueue);
|
||||||
ackPacket += ":";
|
Serial.printf("[STATS] APRSIS Queue: %d/%d (%.1f%% full)\n",
|
||||||
ackPacket += ackMessage;
|
packets, packets + spaces,
|
||||||
#ifdef HAS_A7670
|
(packets * 100.0) / (packets + spaces));
|
||||||
A7670_Utils::uploadToAPRSIS(ackPacket);
|
lastLog = millis();
|
||||||
#else
|
}*/
|
||||||
upload(ackPacket);
|
|
||||||
#endif
|
|
||||||
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
|
String packet;
|
||||||
|
if (xQueueReceive(aprsIsRxQueue, &packet, 0) == pdTRUE) {
|
||||||
|
|
||||||
|
if (passcodeValid && !packet.startsWith("#")) {
|
||||||
|
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();
|
||||||
|
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 += ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
String ackPacket = Config.callsign;
|
||||||
|
ackPacket += ">APLRG1,TCPIP,qAC::";
|
||||||
|
ackPacket += Sender;
|
||||||
|
ackPacket += ":";
|
||||||
|
ackPacket += ackMessage;
|
||||||
|
#ifdef HAS_A7670
|
||||||
|
A7670_Utils::uploadToAPRSIS(ackPacket);
|
||||||
|
#else
|
||||||
|
upload(ackPacket);
|
||||||
|
#endif
|
||||||
|
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
|
||||||
|
} else {
|
||||||
|
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
|
||||||
|
}
|
||||||
|
if (receivedMessage.indexOf("?") == 0) {
|
||||||
|
Utils::println("Rx Query (APRS-IS) : " + packet);
|
||||||
|
Sender.trim();
|
||||||
|
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) {
|
||||||
|
displayToggle(true);
|
||||||
|
}
|
||||||
|
lastScreenOn = millis();
|
||||||
|
delay(500);
|
||||||
|
#ifdef HAS_A7670
|
||||||
|
A7670_Utils::uploadToAPRSIS(queryAnswer);
|
||||||
|
#else
|
||||||
|
upload(queryAnswer);
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
} else {
|
} else {
|
||||||
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
|
Utils::print("Rx Message (APRS-IS): " + packet);
|
||||||
}
|
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
|
||||||
if (receivedMessage.indexOf("?") == 0) {
|
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
|
||||||
Utils::println("Rx Query (APRS-IS) : " + packet);
|
|
||||||
Sender.trim();
|
|
||||||
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) {
|
|
||||||
displayToggle(true);
|
displayToggle(true);
|
||||||
|
lastScreenOn = millis();
|
||||||
|
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
}
|
}
|
||||||
lastScreenOn = millis();
|
|
||||||
delay(500);
|
|
||||||
#ifdef HAS_A7670
|
|
||||||
A7670_Utils::uploadToAPRSIS(queryAnswer);
|
|
||||||
#else
|
|
||||||
upload(queryAnswer);
|
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
|
||||||
} else {
|
Utils::print("Rx Object (APRS-IS) : " + packet);
|
||||||
Utils::print("Rx Message (APRS-IS): " + packet);
|
if (STATION_Utils::checkObjectTime(packet)) {
|
||||||
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
|
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
|
||||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
|
|
||||||
displayToggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
Serial.println();
|
||||||
|
} else {
|
||||||
|
Serial.println(" ---> Rejected (Time): No Tx");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
|
|
||||||
Utils::print("Rx Object (APRS-IS) : " + packet);
|
|
||||||
if (STATION_Utils::checkObjectTime(packet)) {
|
|
||||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
|
|
||||||
displayToggle(true);
|
|
||||||
lastScreenOn = millis();
|
|
||||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
|
||||||
Serial.println();
|
|
||||||
} else {
|
|
||||||
Serial.println(" ---> Rejected (Time): No Tx");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,8 +386,9 @@ namespace APRS_IS_Utils {
|
|||||||
if (espClient.connected()) {
|
if (espClient.connected()) {
|
||||||
if (espClient.available()) {
|
if (espClient.available()) {
|
||||||
String aprsisPacket = espClient.readStringUntil('\r');
|
String aprsisPacket = espClient.readStringUntil('\r');
|
||||||
aprsisPacket.trim(); // Serial.println(aprsisPacket);
|
aprsisPacket.trim(); //Serial.println(aprsisPacket);
|
||||||
processAPRSISPacket(aprsisPacket);
|
xQueueSend(aprsIsRxQueue, &aprsisPacket, 0);
|
||||||
|
//processAPRSISPacket(aprsisPacket);
|
||||||
lastRxTime = millis();
|
lastRxTime = millis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,9 +399,65 @@ namespace APRS_IS_Utils {
|
|||||||
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) {
|
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) {
|
||||||
connect();
|
connect();
|
||||||
while (!passcodeValid) {
|
while (!passcodeValid) {
|
||||||
listenAPRSIS();
|
if (espClient.connected() && espClient.available()) {
|
||||||
|
String aprsisPacket = espClient.readStringUntil('\r');
|
||||||
|
aprsisPacket.trim();
|
||||||
|
if (!passcodeValid && aprsisPacket.indexOf(Config.callsign) != -1) {
|
||||||
|
if (aprsisPacket.indexOf("unverified") != -1 ) {
|
||||||
|
Serial.println("\n****APRS PASSCODE NOT VALID****\n");
|
||||||
|
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 0);
|
||||||
|
while (1) {};
|
||||||
|
} else if (aprsisPacket.indexOf("verified") != -1 ) {
|
||||||
|
Serial.println("(APRS PASSCODE VALIDATED)");
|
||||||
|
passcodeValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void aprsisListenerTask(void *parameter) {
|
||||||
|
while (true) {
|
||||||
|
listenAPRSIS();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10)); // 10ms delay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Función para iniciar el task
|
||||||
|
bool startListenerAPRSISTask(uint32_t stackSize, UBaseType_t priority) {
|
||||||
|
BaseType_t result = xTaskCreatePinnedToCore(
|
||||||
|
aprsisListenerTask, // Función del task
|
||||||
|
"APRSIS_Listener", // Nombre del task
|
||||||
|
stackSize, // Stack size en words (no bytes)
|
||||||
|
NULL, // Parámetro pasado al task
|
||||||
|
priority, // Prioridad
|
||||||
|
&aprsisTaskHandle, // Handle del task
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return (result == pdPASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Función opcional para detener el task
|
||||||
|
void stopListenerAPRSISTask() {
|
||||||
|
if (aprsisTaskHandle != NULL) {
|
||||||
|
vTaskDelete(aprsisTaskHandle);
|
||||||
|
aprsisTaskHandle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Función opcional para suspender/reanudar el task
|
||||||
|
void suspendListenerAPRSISTask() {
|
||||||
|
if (aprsisTaskHandle != NULL) {
|
||||||
|
vTaskSuspend(aprsisTaskHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resumeListenerAPRSISTask() {
|
||||||
|
if (aprsisTaskHandle != NULL) {
|
||||||
|
vTaskResume(aprsisTaskHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -37,6 +37,8 @@ float multiplyCorrection = 0.035;
|
|||||||
|
|
||||||
float voltageDividerTransformation = 0.0;
|
float voltageDividerTransformation = 0.0;
|
||||||
|
|
||||||
|
int telemetryCounter = random(1,999);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAS_ADC_CALIBRATION
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
@@ -226,4 +228,43 @@ namespace BATTERY_Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType) { // 0 = internal battery(0-4,2V) , 1 = external battery(0-15V)
|
||||||
|
String encodedBytes;
|
||||||
|
int tempValue;
|
||||||
|
|
||||||
|
if (firstBytes) {
|
||||||
|
tempValue = value;
|
||||||
|
} else {
|
||||||
|
switch (voltageType) {
|
||||||
|
case 0:
|
||||||
|
tempValue = value * 100; // Internal voltage calculation
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
tempValue = (value * 100) / 2; // External voltage calculation
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tempValue = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int firstByte = tempValue / 91;
|
||||||
|
tempValue -= firstByte * 91;
|
||||||
|
|
||||||
|
encodedBytes = char(firstByte + 33);
|
||||||
|
encodedBytes += char(tempValue + 33);
|
||||||
|
return encodedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
String generateEncodedTelemetry() {
|
||||||
|
String telemetry = "|";
|
||||||
|
telemetry += generateEncodedTelemetryBytes(telemetryCounter, true, 0);
|
||||||
|
telemetryCounter++;
|
||||||
|
if (telemetryCounter == 1000) telemetryCounter = 0;
|
||||||
|
if (Config.battery.sendInternalVoltage) telemetry += generateEncodedTelemetryBytes(checkInternalVoltage(), false, 0);
|
||||||
|
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(checkExternalVoltage(), false, 1);
|
||||||
|
telemetry += "|";
|
||||||
|
return telemetry;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+2
-1
@@ -101,7 +101,8 @@ void displaySetup() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
if (!display.begin(0x3c, false)) {
|
if (!display.begin(0x3c)) {
|
||||||
|
//if (!display.begin(0x3c, false)) {
|
||||||
displayFound = true;
|
displayFound = true;
|
||||||
if (Config.display.turn180) display.setRotation(2);
|
if (Config.display.turn180) display.setRotation(2);
|
||||||
display.clearDisplay();
|
display.clearDisplay();
|
||||||
|
|||||||
+11
-11
@@ -88,16 +88,6 @@ namespace POWER_Utils {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
|
||||||
void activateMeasurement() {
|
|
||||||
PMU.disableTSPinMeasure();
|
|
||||||
PMU.enableBattDetection();
|
|
||||||
PMU.enableVbusVoltageMeasure();
|
|
||||||
PMU.enableBattVoltageMeasure();
|
|
||||||
PMU.enableSystemVoltageMeasure();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
double getBatteryVoltage() {
|
double getBatteryVoltage() {
|
||||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
||||||
return (PMU.getBattVoltage() / 1000.0);
|
return (PMU.getBattVoltage() / 1000.0);
|
||||||
@@ -112,7 +102,17 @@ namespace POWER_Utils {
|
|||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void activateMeasurement() {
|
||||||
|
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
||||||
|
PMU.disableTSPinMeasure();
|
||||||
|
PMU.enableBattDetection();
|
||||||
|
PMU.enableVbusVoltageMeasure();
|
||||||
|
PMU.enableBattVoltageMeasure();
|
||||||
|
PMU.enableSystemVoltageMeasure();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void activateGPS() {
|
void activateGPS() {
|
||||||
#ifdef HAS_AXP192
|
#ifdef HAS_AXP192
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
/* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <APRSPacketLib.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <vector>
|
|
||||||
#include "telemetry_utils.h"
|
|
||||||
#include "aprs_is_utils.h"
|
|
||||||
#include "configuration.h"
|
|
||||||
#include "station_utils.h"
|
|
||||||
#include "battery_utils.h"
|
|
||||||
#include "lora_utils.h"
|
|
||||||
#include "wx_utils.h"
|
|
||||||
#include "display.h"
|
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
|
||||||
extern bool sendStartTelemetry;
|
|
||||||
|
|
||||||
int telemetryCounter = random(1,999);
|
|
||||||
|
|
||||||
|
|
||||||
namespace TELEMETRY_Utils {
|
|
||||||
|
|
||||||
String joinWithCommas(const std::vector<String>& items) {
|
|
||||||
String result;
|
|
||||||
for (size_t i = 0; i < items.size(); ++i) {
|
|
||||||
result += items[i];
|
|
||||||
if (i < items.size() - 1) result += ",";
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<String> getEquationCoefficients() {
|
|
||||||
std::vector<String> coefficients;
|
|
||||||
if (Config.battery.sendInternalVoltage) coefficients.push_back("0,0.01,0");
|
|
||||||
if (Config.battery.sendExternalVoltage) coefficients.push_back("0,0.02,0");
|
|
||||||
return coefficients;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<String> getUnitLabels() {
|
|
||||||
std::vector<String> labels;
|
|
||||||
if (Config.battery.sendInternalVoltage) labels.push_back("VDC");
|
|
||||||
if (Config.battery.sendExternalVoltage) labels.push_back("VDC");
|
|
||||||
return labels;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<String> getParameterNames() {
|
|
||||||
std::vector<String> names;
|
|
||||||
if (Config.battery.sendInternalVoltage) names.push_back("V_Batt");
|
|
||||||
if (Config.battery.sendExternalVoltage) names.push_back("V_Ext");
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendBaseTelemetryPacket(const String& prefix, const std::vector<String>& values) {
|
|
||||||
String packet = prefix + joinWithCommas(values);
|
|
||||||
|
|
||||||
if (Config.beacon.sendViaAPRSIS) {
|
|
||||||
String baseAPRSISTelemetryPacket = APRSPacketLib::generateMessagePacket(Config.callsign, "APLRG1", "TCPIP,qAC", Config.callsign, packet);
|
|
||||||
#ifdef HAS_A7670
|
|
||||||
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket);
|
|
||||||
#else
|
|
||||||
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket);
|
|
||||||
#endif
|
|
||||||
delay(300);
|
|
||||||
} else if (Config.beacon.sendViaRF) {
|
|
||||||
String baseRFTelemetryPacket = APRSPacketLib::generateMessagePacket(Config.callsign, "APLRG1", Config.beacon.path, Config.callsign, packet);
|
|
||||||
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket);
|
|
||||||
delay(3000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendEquationsUnitsParameters() {
|
|
||||||
sendBaseTelemetryPacket("EQNS.", getEquationCoefficients());
|
|
||||||
sendBaseTelemetryPacket("UNIT.", getUnitLabels());
|
|
||||||
sendBaseTelemetryPacket("PARM.", getParameterNames());
|
|
||||||
sendStartTelemetry = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateEncodedTelemetryBytes(float value, bool counterBytes, byte telemetryType) {
|
|
||||||
String encodedBytes;
|
|
||||||
int tempValue;
|
|
||||||
|
|
||||||
if (counterBytes) {
|
|
||||||
tempValue = value;
|
|
||||||
} else {
|
|
||||||
switch (telemetryType) {
|
|
||||||
case 0: tempValue = value * 100; break; // Internal voltage (0-4,2V), Humidity, Gas calculation
|
|
||||||
case 1: tempValue = (value * 100) / 2; break; // External voltage calculation (0-15V)
|
|
||||||
case 2: tempValue = (value * 10) + 500; break; // Temperature
|
|
||||||
case 3: tempValue = (value * 8); break; // Pressure
|
|
||||||
default: tempValue = value; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int firstByte = tempValue / 91;
|
|
||||||
tempValue -= firstByte * 91;
|
|
||||||
|
|
||||||
encodedBytes = char(firstByte + 33);
|
|
||||||
encodedBytes += char(tempValue + 33);
|
|
||||||
return encodedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateEncodedTelemetry() {
|
|
||||||
String telemetry = "|";
|
|
||||||
telemetry += generateEncodedTelemetryBytes(telemetryCounter, true, 0);
|
|
||||||
telemetryCounter++;
|
|
||||||
if (telemetryCounter == 1000) telemetryCounter = 0;
|
|
||||||
if (Config.battery.sendInternalVoltage) telemetry += generateEncodedTelemetryBytes(BATTERY_Utils::checkInternalVoltage(), false, 0);
|
|
||||||
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(BATTERY_Utils::checkExternalVoltage(), false, 1);
|
|
||||||
telemetry += "|";
|
|
||||||
return telemetry;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+73
-3
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
#include <TinyGPS++.h>
|
#include <TinyGPS++.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "telemetry_utils.h"
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "station_utils.h"
|
#include "station_utils.h"
|
||||||
#include "battery_utils.h"
|
#include "battery_utils.h"
|
||||||
@@ -134,6 +133,77 @@ namespace Utils {
|
|||||||
fourthLine = buffer;
|
fourthLine = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendInitialTelemetryPackets() {
|
||||||
|
char sender[10]; // 9 characters + null terminator
|
||||||
|
snprintf(sender, sizeof(sender), "%-9s", Config.callsign.c_str()); // Left-align with spaces
|
||||||
|
|
||||||
|
String baseAPRSISTelemetryPacket = Config.callsign;
|
||||||
|
baseAPRSISTelemetryPacket += ">APLRG1,TCPIP,qAC::";
|
||||||
|
baseAPRSISTelemetryPacket += sender;
|
||||||
|
baseAPRSISTelemetryPacket += ":";
|
||||||
|
|
||||||
|
String baseRFTelemetryPacket = Config.callsign;
|
||||||
|
baseRFTelemetryPacket += ">APLRG1";
|
||||||
|
if (Config.beacon.path.indexOf("WIDE") != -1) {
|
||||||
|
baseRFTelemetryPacket += ",";
|
||||||
|
baseRFTelemetryPacket += Config.beacon.path;
|
||||||
|
}
|
||||||
|
baseRFTelemetryPacket += "::";
|
||||||
|
baseRFTelemetryPacket += sender;
|
||||||
|
baseRFTelemetryPacket += ":";
|
||||||
|
|
||||||
|
String telemetryPacket1 = "EQNS.";
|
||||||
|
if (Config.battery.sendInternalVoltage) {
|
||||||
|
telemetryPacket1 += "0,0.01,0";
|
||||||
|
}
|
||||||
|
if (Config.battery.sendExternalVoltage) {
|
||||||
|
telemetryPacket1 += String(Config.battery.sendInternalVoltage ? ",0,0.02,0" : "0,0.02,0");
|
||||||
|
}
|
||||||
|
|
||||||
|
String telemetryPacket2 = "UNIT.";
|
||||||
|
if (Config.battery.sendInternalVoltage) {
|
||||||
|
telemetryPacket2 += "VDC";
|
||||||
|
}
|
||||||
|
if (Config.battery.sendExternalVoltage) {
|
||||||
|
telemetryPacket2 += String(Config.battery.sendInternalVoltage ? ",VDC" : "VDC");
|
||||||
|
}
|
||||||
|
|
||||||
|
String telemetryPacket3 = "PARM.";
|
||||||
|
if (Config.battery.sendInternalVoltage) {
|
||||||
|
telemetryPacket3 += "V_Batt";
|
||||||
|
}
|
||||||
|
if (Config.battery.sendExternalVoltage) {
|
||||||
|
telemetryPacket3 += String(Config.battery.sendInternalVoltage ? ",V_Ext" : "V_Ext");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.beacon.sendViaAPRSIS) {
|
||||||
|
#ifdef HAS_A7670
|
||||||
|
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket + telemetryPacket1);
|
||||||
|
delay(300);
|
||||||
|
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket + telemetryPacket2);
|
||||||
|
delay(300);
|
||||||
|
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket + telemetryPacket3);
|
||||||
|
delay(300);
|
||||||
|
#else
|
||||||
|
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket + telemetryPacket1);
|
||||||
|
delay(300);
|
||||||
|
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket + telemetryPacket2);
|
||||||
|
delay(300);
|
||||||
|
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket + telemetryPacket3);
|
||||||
|
delay(300);
|
||||||
|
#endif
|
||||||
|
delay(300);
|
||||||
|
} else if (Config.beacon.sendViaRF) {
|
||||||
|
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket + telemetryPacket1);
|
||||||
|
delay(3000);
|
||||||
|
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket + telemetryPacket2);
|
||||||
|
delay(3000);
|
||||||
|
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket + telemetryPacket3);
|
||||||
|
delay(3000);
|
||||||
|
}
|
||||||
|
sendStartTelemetry = false;
|
||||||
|
}
|
||||||
|
|
||||||
void checkBeaconInterval() {
|
void checkBeaconInterval() {
|
||||||
uint32_t lastTx = millis() - lastBeaconTx;
|
uint32_t lastTx = millis() - lastBeaconTx;
|
||||||
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
|
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
|
||||||
@@ -155,7 +225,7 @@ namespace Utils {
|
|||||||
!Config.wxsensor.active &&
|
!Config.wxsensor.active &&
|
||||||
(Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage) &&
|
(Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage) &&
|
||||||
(lastBeaconTx > 0)) {
|
(lastBeaconTx > 0)) {
|
||||||
TELEMETRY_Utils::sendEquationsUnitsParameters();
|
sendInitialTelemetryPackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
STATION_Utils::deleteNotHeard();
|
STATION_Utils::deleteNotHeard();
|
||||||
@@ -239,7 +309,7 @@ namespace Utils {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Config.battery.sendVoltageAsTelemetry && !Config.wxsensor.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)){
|
if (Config.battery.sendVoltageAsTelemetry && !Config.wxsensor.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)){
|
||||||
String encodedTelemetry = TELEMETRY_Utils::generateEncodedTelemetry();
|
String encodedTelemetry = BATTERY_Utils::generateEncodedTelemetry();
|
||||||
beaconPacket += encodedTelemetry;
|
beaconPacket += encodedTelemetry;
|
||||||
secondaryBeaconPacket += encodedTelemetry;
|
secondaryBeaconPacket += encodedTelemetry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
|
||||||
// Aditional Config
|
// Aditional Config
|
||||||
|
#define HAS_TWO_CORES
|
||||||
#define INTERNAL_LED_PIN 25 // Green Led
|
#define INTERNAL_LED_PIN 25 // Green Led
|
||||||
#define BATTERY_PIN 35
|
#define BATTERY_PIN 35
|
||||||
#define HAS_ADC_CALIBRATION
|
#define HAS_ADC_CALIBRATION
|
||||||
|
|||||||
Reference in New Issue
Block a user