diff --git a/common_settings.ini b/common_settings.ini
index e195b5c..6e4b012 100644
--- a/common_settings.ini
+++ b/common_settings.ini
@@ -35,7 +35,8 @@ lib_deps =
jgromes/RadioLib @ 7.1.0
mathieucarbou/AsyncTCP @ 3.2.5
mathieucarbou/ESPAsyncWebServer @ 3.2.3
- mikalhart/TinyGPSPlus @ 1.0.3
+ mikalhart/TinyGPSPlus @ 1.0.3
+ richonguzman/APRSPacketLib @1.0.0
display_libs =
adafruit/Adafruit GFX Library @ 1.11.9
adafruit/Adafruit SSD1306 @ 2.5.10
diff --git a/include/battery_utils.h b/include/battery_utils.h
index e766906..dfdee5b 100644
--- a/include/battery_utils.h
+++ b/include/battery_utils.h
@@ -32,9 +32,6 @@ namespace BATTERY_Utils {
float checkExternalVoltage();
void startupBatteryHealth();
- String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType);
- String generateEncodedTelemetry();
-
}
#endif
\ No newline at end of file
diff --git a/include/telemetry_utils.h b/include/telemetry_utils.h
new file mode 100644
index 0000000..77df52e
--- /dev/null
+++ b/include/telemetry_utils.h
@@ -0,0 +1,33 @@
+/* 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 .
+ */
+
+#ifndef TELEMETRY_UTILS_H_
+#define TELEMETRY_UTILS_H_
+
+#include
+
+
+namespace TELEMETRY_Utils {
+
+ void sendEquationsUnitsParameters();
+ String generateEncodedTelemetryBytes(float value, bool counterBytes, byte telemetryType);
+ String generateEncodedTelemetry();
+
+}
+
+#endif
\ No newline at end of file
diff --git a/src/battery_utils.cpp b/src/battery_utils.cpp
index 6b0ace7..478380a 100644
--- a/src/battery_utils.cpp
+++ b/src/battery_utils.cpp
@@ -37,8 +37,6 @@ float multiplyCorrection = 0.035;
float voltageDividerTransformation = 0.0;
-int telemetryCounter = random(1,999);
-
#ifdef HAS_ADC_CALIBRATION
@@ -228,43 +226,4 @@ 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;
- }
-
}
\ No newline at end of file
diff --git a/src/telemetry_utils.cpp b/src/telemetry_utils.cpp
new file mode 100644
index 0000000..2b86d0b
--- /dev/null
+++ b/src/telemetry_utils.cpp
@@ -0,0 +1,130 @@
+/* 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 .
+ */
+
+#include
+#include
+#include
+#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& 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 getEquationCoefficients() {
+ std::vector 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 getUnitLabels() {
+ std::vector labels;
+ if (Config.battery.sendInternalVoltage) labels.push_back("VDC");
+ if (Config.battery.sendExternalVoltage) labels.push_back("VDC");
+ return labels;
+ }
+
+ std::vector getParameterNames() {
+ std::vector 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& 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;
+ }
+
+}
\ No newline at end of file
diff --git a/src/utils.cpp b/src/utils.cpp
index 4d9cbe2..e5efa40 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -18,6 +18,7 @@
#include
#include
+#include "telemetry_utils.h"
#include "configuration.h"
#include "station_utils.h"
#include "battery_utils.h"
@@ -133,77 +134,6 @@ namespace Utils {
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() {
uint32_t lastTx = millis() - lastBeaconTx;
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
@@ -225,7 +155,7 @@ namespace Utils {
!Config.wxsensor.active &&
(Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage) &&
(lastBeaconTx > 0)) {
- sendInitialTelemetryPackets();
+ TELEMETRY_Utils::sendEquationsUnitsParameters();
}
STATION_Utils::deleteNotHeard();
@@ -309,7 +239,7 @@ namespace Utils {
#endif
if (Config.battery.sendVoltageAsTelemetry && !Config.wxsensor.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)){
- String encodedTelemetry = BATTERY_Utils::generateEncodedTelemetry();
+ String encodedTelemetry = TELEMETRY_Utils::generateEncodedTelemetry();
beaconPacket += encodedTelemetry;
secondaryBeaconPacket += encodedTelemetry;
}