Compare commits

..

1 Commits

Author SHA1 Message Date
richonguzman 2d65b2c93f no test yet 2025-08-26 17:34:51 -04:00
183 changed files with 2446 additions and 3989 deletions
+2 -12
View File
@@ -19,8 +19,6 @@ jobs:
chip: esp32s3 chip: esp32s3
- name: heltec-lora32-v2 - name: heltec-lora32-v2
chip: esp32 chip: esp32
- name: heltec-lora32-v2_915
chip: esp32
- name: heltec_wifi_lora_32_V3 - name: heltec_wifi_lora_32_V3
chip: esp32s3 chip: esp32s3
- name: heltec_wifi_lora_32_V3_2 - name: heltec_wifi_lora_32_V3_2
@@ -31,8 +29,6 @@ jobs:
chip: esp32s3 chip: esp32s3
- name: heltec_wireless_stick_lite_v3_display - name: heltec_wireless_stick_lite_v3_display
chip: esp32s3 chip: esp32s3
- name: heltec_wireless_bridge
chip: esp32
- name: ESP32_DIY_LoRa - name: ESP32_DIY_LoRa
chip: esp32 chip: esp32
- name: ESP32_DIY_LoRa_915 - name: ESP32_DIY_LoRa_915
@@ -71,11 +67,7 @@ jobs:
chip: esp32s3 chip: esp32s3
- name: heltec_ht-ct62 - name: heltec_ht-ct62
chip: esp32c3 chip: esp32c3
- name: heltec_wireless_paper_v1 - name: heltec_wireless_paper
chip: esp32s3
- name: heltec_wireless_paper_v1_2
chip: esp32s3
- name: heltec_vision_master_e290
chip: esp32s3 chip: esp32s3
- name: OE5HWN_MeshCom - name: OE5HWN_MeshCom
chip: esp32 chip: esp32
@@ -98,9 +90,7 @@ jobs:
- name: XIAO_ESP32S3_WIO_SX1262 - name: XIAO_ESP32S3_WIO_SX1262
chip: esp32s3 chip: esp32s3
- name: TROY_LoRa_APRS - name: TROY_LoRa_APRS
chip: esp32 chip: esp32
- name: ESP32_9M2IBR_1W_LoRa_GPS
chip: esp32
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
+26 -35
View File
@@ -2,45 +2,53 @@
This firmware is for using ESP32 based boards with LoRa Modules and GPS to live in the APRS world. This firmware is for using ESP32 based boards with LoRa Modules and GPS to live in the APRS world.
![Screenshot](https://github.com/richonguzman/LoRa_APRS_iGate/raw/main/images/iGateOledScreen.jpeg) ![Screenshot](https://github.com/richonguzman/LoRa_APRS_iGate/blob/main/images/iGateOledScreen.jpeg)
__(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__
<br /> <br />
____________________________________________________ ____________________________________________________
# <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">WEB FLASHER/INSTALLER</a>
# <a href="https://drive.google.com/file/d/1Hff_Szd7ks8RC7_RiV6POxPJlclbO05M/view?usp=sharing" target="_blank">LoRa APRS iGate CA2RXU Firmware Manual</a>
____________________________________________________
## You can support this project to continue to grow: ## You can support this project to continue to grow:
[<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/raw/main/images/github-sponsors.png">](https://github.com/sponsors/richonguzman) [<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/raw/main/images/paypalme.png">](http://paypal.me/richonguzman) [<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/blob/main/images/github-sponsors.png">](https://github.com/sponsors/richonguzman) [<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/blob/main/images/paypalme.png">](http://paypal.me/richonguzman)
<br />
# WEB FLASHER/INSTALLER is <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">here</a>
____________________________________________________ ____________________________________________________
## SUPPORTED BOARDS (<a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/Supported-Boards-and-Buying-Links" target="_blank">Buying links</a>). # WIKI
### FAQ, BME280, TNC and more --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/00.-FAQ-(frequently-asked-questions)" target="_blank">here</a>.
### Installation Guide --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/01.-Installation-Guide" target="_blank">here</a>.
<br />
# SUPPORTED BOARDS
### Buying links --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/108.-Supported-Boards-and-Buying-Links" target="_blank">here</a>.
(NOTE: all boards with 433-868-915 MHz versions) (NOTE: all boards with 433-868-915 MHz versions)
- TTGO Lilygo LoRa32 T3S3 V1.2 and LoRa32 V2.1 (V1.6 is the same). - TTGO Lilygo LoRa32 T3S3 V1.2 and LoRa32 V2.1 (V1.6 is the same).
- TTGO T-Beam V1.0 , V1.1, V1.2 (also variations with SX1262 and SX1268 LoRa Modules) and Supreme V3. - TTGO T-Beam V1.0 , V1.1, V1.2 (also variations with SX1262 and SX1268 LoRa Modules).
- T-Deck Plus (and also regular T-Deck with/without GPS). - T-Deck Plus (and also regular T-Deck with/without GPS).
- HELTEC V2, V3, V3.2, T114, Wireless Stick, Wireless Stick Lite V3/V3.2, HT-CT62, Wireless Tracker, Wireless Paper. - HELTEC V2, V3, V3.2, T114, Wireless Stick, Wireless Stick Lite, HT-CT62, Wireless Tracker, Wireless Paper.
- RAK Wireless 4631 + 19007(or 19003) - RAK Wireless 4631 + 19007(19003)
- Faketec V3 (NRF52840 + Heltec HTRA62(SX1262)) - Faketec (NRF52840 + Heltec HTRA62(SX1262))
- QRP Labs LightGateway 1.0 and Plus 1.0. - QRP Labs LightGateway 1.0 and Plus 1.0.
- ESP32 + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions. - Faketec V3 (NRF52840 + Heltec HTRA62 SX1262)
- ESP32 Wroom + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
- ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version. - ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version.
@@ -50,25 +58,8 @@ ____________________________________________________
<br /> <br />
# Timeline (Versions): ## Timeline (Versions):
- 2026-02-26 9M2IBR ESP32 1W (400M30S) + GPS board added.
- 2026-02-25 Code Improvements: reduced String comparisons and improved logic for faster code execution.
- 2026-02-15 Digipeater code/logic improved.
- 2026-02-08 Heltec V2 915MHz added.
- 2026-02-05 Improved "digiBackupMode" to check "APRS-IS" server connection.
- 2026-01-07 Tactical Callsign added.
- 2026-01-05 Heltec V4 support added.
- 2025-12-22 Heltec Wireless Paper V1.2 and VisionMaster E290 Added. Thanks HA5SZI.
- 2025-12-18 TCXO and packet decoding updates.
- 2025-12-01 APRSPacketLib updates, AHT20 sensor added, INA219 support added.
- 2025-10-15 APRS Bridge for TNC added.
- 2025-10-13 Rx and Tx Frequencies are now with fully configurable.
- 2025-10-13 Startup Delay to allow the Router/Modem to start WiFiAP before connecting.
- 2025-10-12 Choose to send Beacon on Rx or Tx frequency.
- 2025-10-11 User defined NTP server and send beacon over MQTT added.
- 2025-10-10 Converted the Wiki into a PDF manual.
- 2025-09-26 Heltec Wireless Bridge support added.
- 2025-09-09 MQTT added (pub+sub), Status defined by Op now and many fixes more.
- 2025-06-20 Digipeaters now with updated EcoMode (Board Sleeps until packet Rx reducing current consumption to almost 10% at idle). - 2025-06-20 Digipeaters now with updated EcoMode (Board Sleeps until packet Rx reducing current consumption to almost 10% at idle).
- 2025-06-20 New Boards Added: Heltec T114 MeshNode, Faketec V3 as Digipeaters and QRP Labs LightGateway Plus 1.0. - 2025-06-20 New Boards Added: Heltec T114 MeshNode, Faketec V3 as Digipeaters and QRP Labs LightGateway Plus 1.0.
- 2025-06-19 DateVersion format Change. Licence changed into GNU GPLv3. - 2025-06-19 DateVersion format Change. Licence changed into GNU GPLv3.
@@ -90,7 +81,7 @@ ____________________________________________________
- 2024.10.08 New EcoMode for Remote Digipeaters without WiFi/WiFiAP, Screen, Leds (Example: LILYGO LoRa32 uses only 24mA, with WifiAP 150mA). APRS Message/Queries can start/stop this mode too. - 2024.10.08 New EcoMode for Remote Digipeaters without WiFi/WiFiAP, Screen, Leds (Example: LILYGO LoRa32 uses only 24mA, with WifiAP 150mA). APRS Message/Queries can start/stop this mode too.
- 2024.10.06 Cross Frequency Digipeater Rules added. - 2024.10.06 Cross Frequency Digipeater Rules added.
- 2024.09.23 Libraries Update for SDK3 - 2024.09.23 Libraries Update for SDK3
- 2024.09.23 Added Enconded Telemetry for Battery (+ External Voltage) in Station GPS Beacon Packet. - 2024.09.23 Added Enconded Telemetry for Battery (+ External Voltage) in Station GPS Beacon Packet.
- 2024.08.23 Wemos S2 Mini DIY LoRa added. - 2024.08.23 Wemos S2 Mini DIY LoRa added.
- 2024.08.19 HELTEC Wireless Paper working (still missing Epaper code). - 2024.08.19 HELTEC Wireless Paper working (still missing Epaper code).
- 2024.08.13 Web Authentication for WebUI. Thanks Mitja S57PNX. - 2024.08.13 Web Authentication for WebUI. Thanks Mitja S57PNX.
+24 -27
View File
@@ -2,43 +2,40 @@
build_flags = build_flags =
-Werror -Wall -Werror -Wall
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
-D RADIOLIB_EXCLUDE_CC1101=1 -DRADIOLIB_EXCLUDE_CC1101=1
-D RADIOLIB_EXCLUDE_RF69=1 -DRADIOLIB_EXCLUDE_NRF24=1
-D RADIOLIB_EXCLUDE_RFM2X=1 -DRADIOLIB_EXCLUDE_RF69=1
-D RADIOLIB_EXCLUDE_SX1231=1 -DRADIOLIB_EXCLUDE_SX1231=1
-D RADIOLIB_EXCLUDE_SX1233=1 -DRADIOLIB_EXCLUDE_SX1233=1
-D RADIOLIB_EXCLUDE_SI443X=1 -DRADIOLIB_EXCLUDE_SI443X=1
-D RADIOLIB_EXCLUDE_NRF24=1 -DRADIOLIB_EXCLUDE_RFM2X=1
-D RADIOLIB_EXCLUDE_AFSK=1 -DRADIOLIB_EXCLUDE_AFSK=1
-D RADIOLIB_EXCLUDE_APRS=1 -DRADIOLIB_EXCLUDE_BELL=1
-D RADIOLIB_EXCLUDE_AX25=1 -DRADIOLIB_EXCLUDE_HELLSCHREIBER=1
-D RADIOLIB_EXCLUDE_BELL=1 -DRADIOLIB_EXCLUDE_MORSE=1
-D RADIOLIB_EXCLUDE_FSK4=1 -DRADIOLIB_EXCLUDE_RTTY=1
-D RADIOLIB_EXCLUDE_HELLSCHREIBER=1 -DRADIOLIB_EXCLUDE_SSTV=1
-D RADIOLIB_EXCLUDE_LORAWAN=1 -DRADIOLIB_EXCLUDE_AX25=1
-D RADIOLIB_EXCLUDE_MORSE=1 -DRADIOLIB_EXCLUDE_DIRECT_RECEIVE=1
-D RADIOLIB_EXCLUDE_PAGER=1 -DRADIOLIB_EXCLUDE_BELL=1
-D RADIOLIB_EXCLUDE_DIRECT_RECEIVE=1 -DRADIOLIB_EXCLUDE_PAGER=1
-D RADIOLIB_EXCLUDE_RTTY=1 -DRADIOLIB_EXCLUDE_FSK4=1
-D RADIOLIB_EXCLUDE_SSTV=1 -DRADIOLIB_EXCLUDE_APRS=1
-DRADIOLIB_EXCLUDE_LORAWAN=1
-I variants/${PIOENV} -I variants/${PIOENV}
lib_deps = lib_deps =
adafruit/Adafruit Unified Sensor @ 1.1.14 adafruit/Adafruit Unified Sensor @ 1.1.14
adafruit/Adafruit AHTX0 @ 2.0.5
adafruit/Adafruit BME280 Library @ 2.2.4 adafruit/Adafruit BME280 Library @ 2.2.4
adafruit/Adafruit BMP280 Library @ 2.6.8 adafruit/Adafruit BMP280 Library @ 2.6.8
adafruit/Adafruit BME680 Library @ 2.0.4 adafruit/Adafruit BME680 Library @ 2.0.4
adafruit/Adafruit INA219 @ 1.2.3
adafruit/Adafruit Si7021 Library @ 1.5.3 adafruit/Adafruit Si7021 Library @ 1.5.3
arduino-libraries/NTPClient @ 3.2.1 arduino-libraries/NTPClient @ 3.2.1
ayushsharma82/ElegantOTA @ 3.1.7 ayushsharma82/ElegantOTA @ 3.1.5
bblanchon/ArduinoJson @ 6.21.3 bblanchon/ArduinoJson @ 6.21.3
jgromes/RadioLib @ 7.1.0 jgromes/RadioLib @ 7.1.0
knolleary/PubSubClient @ 2.8 mathieucarbou/AsyncTCP @ 3.2.5
ESP32Async/AsyncTCP @ 3.4.9 mathieucarbou/ESPAsyncWebServer @ 3.2.3
ESP32Async/ESPAsyncWebServer @ 3.9.3 mikalhart/TinyGPSPlus @ 1.0.3
mikalhart/TinyGPSPlus @ 1.0.3
richonguzman/APRSPacketLib @ 1.0.4
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
+24 -43
View File
@@ -17,40 +17,31 @@
"path": "WIDE1-1", "path": "WIDE1-1",
"sendViaAPRSIS": false, "sendViaAPRSIS": false,
"sendViaRF": false, "sendViaRF": false,
"beaconFreq": 1,
"statusActive": false, "statusActive": false,
"statusPacket": "", "statusPacket": ""
"gpsActive": false,
"ambiguityLevel": 0
}, },
"aprs_is": { "aprs_is": {
"active": false, "active": false,
"messagesToRF": false, "passcode": "XYZVW",
"objectsToRF": false,
"server": "rotate.aprs2.net", "server": "rotate.aprs2.net",
"passcode": "XYZVW",
"port": 14580, "port": 14580,
"filter": "m/10" "filter": "m/10",
"messagesToRF": false,
"objectsToRF": false
}, },
"personalNote": "",
"blacklist": "",
"digi": { "digi": {
"mode": 0, "mode": 0,
"ecoMode": 0, "ecoMode": 0
"backupDigiMode": false
}, },
"lora": { "lora": {
"rxActive": true,
"rxFreq": 433775000,
"rxSpreadingFactor": 12,
"rxCodingRate4": 5,
"rxSignalBandwidth": 125000,
"txActive": false,
"txFreq": 433775000, "txFreq": 433775000,
"txSpreadingFactor": 12, "rxFreq": 433775000,
"txCodingRate4": 5, "spreadingFactor": 12,
"txSignalBandwidth": 125000, "signalBandwidth": 125000,
"power": 20 "codingRate4": 5,
"power": 20,
"txActive": false,
"rxActive": true
}, },
"display": { "display": {
"alwaysOn": true, "alwaysOn": true,
@@ -62,12 +53,11 @@
"monitorInternalVoltage": false, "monitorInternalVoltage": false,
"internalSleepVoltage": 2.9, "internalSleepVoltage": 2.9,
"sendExternalVoltage": false, "sendExternalVoltage": false,
"externalVoltagePin": 34,
"monitorExternalVoltage": false, "monitorExternalVoltage": false,
"externalSleepVoltage": 10.9, "externalSleepVoltage": 10.9,
"useExternalI2CSensor": false,
"voltageDividerR1": 100.0, "voltageDividerR1": 100.0,
"voltageDividerR2": 27.0, "voltageDividerR2": 27.0,
"externalVoltagePin": 34,
"sendVoltageAsTelemetry": false "sendVoltageAsTelemetry": false
}, },
"wxsensor": { "wxsensor": {
@@ -84,17 +74,7 @@
"tnc": { "tnc": {
"enableServer": false, "enableServer": false,
"enableSerial": false, "enableSerial": false,
"acceptOwn": false, "acceptOwn": false
"aprsBrigdeActive": false
},
"mqtt": {
"active": false,
"server": "",
"topic": "",
"username": "",
"password": "",
"port": 1883,
"beaconOverMqtt": false
}, },
"ota": { "ota": {
"username": "", "username": "",
@@ -104,18 +84,19 @@
"username": "admin", "username": "admin",
"password": "" "password": ""
}, },
"ntp": {
"gmtCorrection": 0.0
},
"remoteManagement": { "remoteManagement": {
"managers": "", "managers": "",
"rfOnly": true "rfOnly": true
}, },
"ntp": {
"server": "pool.ntp.org",
"gmtCorrection": 0.0
},
"other": { "other": {
"rememberStationTime": 30, "rememberStationTime": 30,
"backupDigiMode": false,
"rebootMode": false, "rebootMode": false,
"rebootModeTime": 6, "rebootModeTime": 6
"startupDelay": 0 },
} "personalNote": "",
"blacklist": ""
} }
+432 -660
View File
File diff suppressed because it is too large Load Diff
+169 -192
View File
@@ -49,12 +49,34 @@ function fetchSettings() {
}); });
} }
const alwaysOnCheckbox = document.querySelector(
'input[name="display.alwaysOn"]'
);
const timeoutInput = document.querySelector('input[name="display.timeout"]');
alwaysOnCheckbox.addEventListener("change", function () {
timeoutInput.disabled = this.checked;
});
// timeoutInput.addEventListener("change", function () {
// alwaysOnCheckbox.disabled = this.value !== "";
// });
const logCheckbox = document.querySelector('input[name="syslog.active"]');
const serverField = document.querySelector('input[name="syslog.server"]');
const portField = document.querySelector('input[name="syslog.port"]');
const logBeaconOverTCPIPField = document.querySelector('input[name="syslog.logBeaconOverTCPIP"]');
logCheckbox.addEventListener("change", function () {
serverField.disabled = !this.checked;
portField.disabled = !this.checked;
logBeaconOverTCPIPField.disabled = !this.checked
});
function loadSettings(settings) { function loadSettings(settings) {
currentSettings = settings; currentSettings = settings;
// General // General
document.getElementById("callsign").value = settings.callsign; document.getElementById("callsign").value = settings.callsign;
document.getElementById("tacticalCallsign").value = settings.tacticalCallsign;
document.getElementById("beacon.comment").value = settings.beacon.comment; document.getElementById("beacon.comment").value = settings.beacon.comment;
document.getElementById("beacon.path").value = settings.beacon.path; document.getElementById("beacon.path").value = settings.beacon.path;
document.getElementById("beacon.symbol").value = settings.beacon.symbol; document.getElementById("beacon.symbol").value = settings.beacon.symbol;
@@ -96,7 +118,6 @@ function loadSettings(settings) {
networksContainer.appendChild(networkElement); networksContainer.appendChild(networkElement);
networkCount++; networkCount++;
}); });
document.getElementById("startupDelay").value = settings.startupDelay;
// APRS-IS // APRS-IS
document.getElementById("aprs_is.active").checked = settings.aprs_is.active; document.getElementById("aprs_is.active").checked = settings.aprs_is.active;
@@ -106,13 +127,6 @@ function loadSettings(settings) {
document.getElementById("aprs_is.port").value = settings.aprs_is.port; document.getElementById("aprs_is.port").value = settings.aprs_is.port;
document.getElementById("aprs_is.filter").value = settings.aprs_is.filter; document.getElementById("aprs_is.filter").value = settings.aprs_is.filter;
document.getElementById("aprs_is.passcode").value = settings.aprs_is.passcode; document.getElementById("aprs_is.passcode").value = settings.aprs_is.passcode;
APRSISCheckbox.checked = settings.aprs_is.active;
APRSISGateMessages.disabled = !APRSISCheckbox.checked;
APRSISGateObjects.disabled = !APRSISCheckbox.checked;
APRSISServer.disabled = !APRSISCheckbox.checked;
APRSISPort.disabled = !APRSISCheckbox.checked;
APRSISPasscode.disabled = !APRSISCheckbox.checked;
APRSISFilter.disabled = !APRSISCheckbox.checked;
// Beacon // Beacon
document.getElementById("beacon.latitude").value = settings.beacon.latitude; document.getElementById("beacon.latitude").value = settings.beacon.latitude;
@@ -120,121 +134,79 @@ function loadSettings(settings) {
document.getElementById("beacon.interval").value = settings.beacon.interval; document.getElementById("beacon.interval").value = settings.beacon.interval;
document.getElementById("other.rememberStationTime").value = settings.other.rememberStationTime; document.getElementById("other.rememberStationTime").value = settings.other.rememberStationTime;
document.getElementById("beacon.sendViaAPRSIS").checked = settings.beacon.sendViaAPRSIS; document.getElementById("beacon.sendViaAPRSIS").checked = settings.beacon.sendViaAPRSIS;
document.getElementById("beacon.sendViaRF").checked = settings.beacon.sendViaRF; document.getElementById("beacon.sendViaRF").checked = settings.beacon.sendViaRF;
document.getElementById("beacon.beaconFreq").value = settings.beacon.beaconFreq;
BeaconingViaRFCheckbox.checked = settings.beacon.sendViaRF;
BeaconingFrequency.disabled = !BeaconingViaRFCheckbox.checked;
document.getElementById("beacon.statusActive").checked = settings.beacon.statusActive; document.getElementById("beacon.statusActive").checked = settings.beacon.statusActive;
document.getElementById("beacon.statusPacket").value = settings.beacon.statusPacket; document.getElementById("beacon.statusPacket").value = settings.beacon.statusPacket;
StatusCheckbox.checked = settings.beacon.statusActive;
StatusPacket.disabled = !StatusCheckbox.checked;
document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive; document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive;
document.getElementById("beacon.ambiguityLevel").value = settings.beacon.ambiguityLevel; document.getElementById("beacon.gpsAmbiguity").checked = settings.beacon.gpsAmbiguity;
// Black List // Black List
document.getElementById("blacklist").value = settings.blacklist; document.getElementById("blacklist").value = settings.blacklist;
// Digi // Digi
document.getElementById("digi.mode").value = settings.digi.mode; document.getElementById("digi.mode").value = settings.digi.mode;
document.getElementById("digi.ecoMode").value = settings.digi.ecoMode; document.getElementById("digi.ecoMode").checked = settings.digi.ecoMode;
document.getElementById("digi.backupDigiMode").checked = settings.digi.backupDigiMode;
// LoRa // LoRa
document.getElementById("lora.rxActive").checked = settings.lora.rxActive;
document.getElementById("lora.rxFreq").value = settings.lora.rxFreq;
document.getElementById("lora.rxSpreadingFactor").value = settings.lora.rxSpreadingFactor;
document.getElementById("lora.rxCodingRate4").value = settings.lora.rxCodingRate4;
document.getElementById("lora.rxSignalBandwidth").value = settings.lora.rxSignalBandwidth;
document.getElementById("lora.txActive").checked = settings.lora.txActive;
document.getElementById("lora.txFreq").value = settings.lora.txFreq; document.getElementById("lora.txFreq").value = settings.lora.txFreq;
document.getElementById("lora.txSpreadingFactor").value = settings.lora.txSpreadingFactor; document.getElementById("lora.rxFreq").value = settings.lora.rxFreq;
document.getElementById("lora.txCodingRate4").value = settings.lora.txCodingRate4; document.getElementById("lora.txActive").checked = settings.lora.txActive;
document.getElementById("lora.txSignalBandwidth").value = settings.lora.txSignalBandwidth; document.getElementById("lora.rxActive").checked = settings.lora.rxActive;
document.getElementById("lora.spreadingFactor").value = settings.lora.spreadingFactor;
document.getElementById("lora.signalBandwidth").value = settings.lora.signalBandwidth;
document.getElementById("lora.codingRate4").value = settings.lora.codingRate4;
document.getElementById("lora.power").value = settings.lora.power; document.getElementById("lora.power").value = settings.lora.power;
// Display // Display
document.getElementById("display.alwaysOn").checked = settings.display.alwaysOn; document.getElementById("display.alwaysOn").checked = settings.display.alwaysOn;
document.getElementById("display.turn180").checked = settings.display.turn180; document.getElementById("display.turn180").checked = settings.display.turn180;
document.getElementById("display.timeout").value = settings.display.timeout; document.getElementById("display.timeout").value = settings.display.timeout;
DisplayAlwaysOnCheckbox.checked = settings.display.alwaysOn;
DisplayTimeout.disabled = DisplayAlwaysOnCheckbox.checked; if (settings.display.alwaysOn) {
timeoutInput.disabled = true;
}
// BATTERY // BATTERY
document.getElementById("battery.sendInternalVoltage").checked = settings.battery.sendInternalVoltage; document.getElementById("battery.sendInternalVoltage").checked = settings.battery.sendInternalVoltage;
document.getElementById("battery.monitorInternalVoltage").checked = settings.battery.monitorInternalVoltage; document.getElementById("battery.monitorInternalVoltage").checked = settings.battery.monitorInternalVoltage;
document.getElementById("battery.internalSleepVoltage").value = settings.battery.internalSleepVoltage.toFixed(1); document.getElementById("battery.internalSleepVoltage").value = settings.battery.internalSleepVoltage.toFixed(1);
MonitorInternalVoltageCheckbox.checked = settings.battery.monitorInternalVoltage;
MonitorInternalSleepVoltage.disabled = !MonitorInternalVoltageCheckbox.checked;
document.getElementById("battery.sendExternalVoltage").checked = settings.battery.sendExternalVoltage; document.getElementById("battery.sendExternalVoltage").checked = settings.battery.sendExternalVoltage;
document.getElementById("battery.useExternalI2CSensor").checked = settings.battery.useExternalI2CSensor;
document.getElementById("battery.externalVoltagePin").value = settings.battery.externalVoltagePin; document.getElementById("battery.externalVoltagePin").value = settings.battery.externalVoltagePin;
document.getElementById("battery.voltageDividerR1").value = settings.battery.voltageDividerR1.toFixed(1); document.getElementById("battery.voltageDividerR1").value = settings.battery.voltageDividerR1.toFixed(1);
document.getElementById("battery.voltageDividerR2").value = settings.battery.voltageDividerR2.toFixed(1); document.getElementById("battery.voltageDividerR2").value = settings.battery.voltageDividerR2.toFixed(1);
SendExternalVoltageCheckbox.checked = settings.battery.sendExternalVoltage;
UseExternalI2CSensorCheckbox.disabled = !SendExternalVoltageCheckbox.checked;
ExternalVoltagePin.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR1.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR2.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
document.getElementById("battery.monitorExternalVoltage").checked = settings.battery.monitorExternalVoltage; document.getElementById("battery.monitorExternalVoltage").checked = settings.battery.monitorExternalVoltage;
document.getElementById("battery.externalSleepVoltage").value = settings.battery.externalSleepVoltage.toFixed(1); document.getElementById("battery.externalSleepVoltage").value = settings.battery.externalSleepVoltage.toFixed(1);
MonitorExternalVoltageCheckbox.checked = settings.battery.monitorExternalVoltage;
MonitorExternalSleepVoltage.disabled = !MonitorExternalVoltageCheckbox.checked;
document.getElementById("battery.sendVoltageAsTelemetry").checked = settings.battery.sendVoltageAsTelemetry; document.getElementById("battery.sendVoltageAsTelemetry").checked = settings.battery.sendVoltageAsTelemetry;
// TELEMETRY WX SENSOR // TELEMETRY WX SENSOR
document.getElementById("wxsensor.active").checked = settings.wxsensor.active; document.getElementById("wxsensor.active").checked = settings.wxsensor.active;
document.getElementById("wxsensor.heightCorrection").value = settings.wxsensor.heightCorrection; document.getElementById("wxsensor.heightCorrection").value = settings.wxsensor.heightCorrection;
document.getElementById("wxsensor.temperatureCorrection").value = settings.wxsensor.temperatureCorrection.toFixed(1); document.getElementById("wxsensor.temperatureCorrection").value = settings.wxsensor.temperatureCorrection.toFixed(1);
TelemetryCheckbox.checked = settings.wxsensor.active;
TelemetryHeightCorrection.disabled = !TelemetryCheckbox.checked;
TelemetryTempCorrection.disabled = !TelemetryCheckbox.checked;
// SYSLOG // SYSLOG
document.getElementById("syslog.active").checked = settings.syslog.active; document.getElementById("syslog.active").checked = settings.syslog.active;
document.getElementById("syslog.server").value = settings.syslog.server; document.getElementById("syslog.server").value = settings.syslog.server;
document.getElementById("syslog.port").value = settings.syslog.port; document.getElementById("syslog.port").value = settings.syslog.port;
document.getElementById("syslog.logBeaconOverTCPIP").checked = settings.syslog.logBeaconOverTCPIP; document.getElementById("syslog.logBeaconOverTCPIP").checked = settings.syslog.logBeaconOverTCPIP;
SyslogCheckbox.checked = settings.syslog.active;
SyslogServer.disabled = !SyslogCheckbox.checked; if (settings.syslog.active) {
SyslogPort.disabled = !SyslogCheckbox.checked; serverField.disabled = false;
SyslogBeaconOverTCPIP.disabled = !SyslogCheckbox.checked; portField.disabled = false;
logBeaconOverTCPIPField.disabled = false;
}
// TNC // TNC
if (settings.tnc) { if (settings.tnc) {
document.getElementById("tnc.enableServer").checked = settings.tnc.enableServer; document.getElementById("tnc.enableServer").checked = settings.tnc.enableServer;
document.getElementById("tnc.enableSerial").checked = settings.tnc.enableSerial; document.getElementById("tnc.enableSerial").checked = settings.tnc.enableSerial;
document.getElementById("tnc.acceptOwn").checked = settings.tnc.acceptOwn; document.getElementById("tnc.acceptOwn").checked = settings.tnc.acceptOwn;
document.getElementById("tnc.aprsBridgeActive").checked = settings.tnc.aprsBridgeActive;
} }
// MQTT
document.getElementById("mqtt.active").checked = settings.mqtt.active;
document.getElementById("mqtt.server").value = settings.mqtt.server;
document.getElementById("mqtt.topic").value = settings.mqtt.topic;
document.getElementById("mqtt.username").value = settings.mqtt.username;
document.getElementById("mqtt.password").value = settings.mqtt.password;
document.getElementById("mqtt.port").value = settings.mqtt.port;
document.getElementById("mqtt.beaconOverMqtt").checked = settings.mqtt.beaconOverMqtt;
MqttCheckbox.checked = settings.mqtt.active;
MqttServer.disabled = !MqttCheckbox.checked;
MqttTopic.disabled = !MqttCheckbox.checked;
MqttUsername.disabled = !MqttCheckbox.checked;
MqttPassword.disabled = !MqttCheckbox.checked;
MqttPort.disabled = !MqttCheckbox.checked;
MqttBeaconOverMqtt.disabled = !MqttCheckbox.checked;
// Reboot // Reboot
document.getElementById("other.rebootMode").checked = settings.other.rebootMode; document.getElementById("other.rebootMode").checked = settings.other.rebootMode;
document.getElementById("other.rebootModeTime").value = settings.other.rebootModeTime; document.getElementById("other.rebootModeTime").value = settings.other.rebootModeTime;
RebootModeCheckbox.checked = settings.other.rebootMode;
RebootModeTime.disabled = !RebootModeCheckbox.check;
// WiFi Auto AP // WiFi Auto AP
document.getElementById("wifi.autoAP.password").value = settings.wifi.autoAP.password; document.getElementById("wifi.autoAP.password").value = settings.wifi.autoAP.password;
@@ -248,19 +220,20 @@ function loadSettings(settings) {
document.getElementById("webadmin.active").checked = settings.webadmin.active; document.getElementById("webadmin.active").checked = settings.webadmin.active;
document.getElementById("webadmin.username").value = settings.webadmin.username; document.getElementById("webadmin.username").value = settings.webadmin.username;
document.getElementById("webadmin.password").value = settings.webadmin.password; document.getElementById("webadmin.password").value = settings.webadmin.password;
WebadminCheckbox.checked = settings.webadmin.active;
WebadminUsername.disabled = !WebadminCheckbox.check; // NTP
WebadminPassword.disabled = !WebadminCheckbox.check; document.getElementById("ntp.gmtCorrection").value = settings.ntp.gmtCorrection;
// Experimental
document.getElementById("other.backupDigiMode").checked = settings.other.backupDigiMode;
// Management over APRS // Management over APRS
document.getElementById("remoteManagement.managers").value = settings.remoteManagement.managers; document.getElementById("remoteManagement.managers").value = settings.remoteManagement.managers;
document.getElementById("remoteManagement.rfOnly").checked = settings.remoteManagement.rfOnly; document.getElementById("remoteManagement.rfOnly").checked = settings.remoteManagement.rfOnly;
// NTP
document.getElementById("ntp.server").value = settings.ntp.server;
document.getElementById("ntp.gmtCorrection").value = settings.ntp.gmtCorrection;
updateImage(); updateImage();
refreshSpeedStandard();
toggleFields();
} }
function showToast(message) { function showToast(message) {
@@ -287,6 +260,8 @@ document.getElementById('reboot').addEventListener('click', function (e) {
showToast("Your device will be rebooted in a while"); showToast("Your device will be rebooted in a while");
}); });
const wxsensorCheckbox = document.querySelector("input[name='wxsensor.active']");
function updateImage() { function updateImage() {
const value = document.getElementById("beacon.overlay").value + document.getElementById("beacon.symbol").value; const value = document.getElementById("beacon.overlay").value + document.getElementById("beacon.symbol").value;
@@ -312,127 +287,70 @@ function updateImage() {
} }
} }
// Beaconing Switches function toggleFields() {
const BeaconingViaRFCheckbox = document.querySelector('input[name="beacon.sendViaRF"]'); const sendExternalVoltageCheckbox = document.querySelector(
const BeaconingFrequency = document.querySelector('select[name="beacon.beaconFreq"]'); 'input[name="battery.sendExternalVoltage"]'
BeaconingViaRFCheckbox.addEventListener("change", function() { );
BeaconingFrequency.disabled = !this.checked; const externalVoltagePinInput = document.querySelector(
'input[name="battery.externalVoltagePin"]'
);
externalVoltagePinInput.disabled = !sendExternalVoltageCheckbox.checked;
voltageDividerR1.disabled = !sendExternalVoltageCheckbox.checked;
voltageDividerR2.disabled = !sendExternalVoltageCheckbox.checked;
const WebadminCheckbox = document.querySelector(
'input[name="webadmin.active"]'
);
const WebadminUsername = document.querySelector(
'input[name="webadmin.username"]'
);
const WebadminPassword = document.querySelector(
'input[name="webadmin.password"]'
);
WebadminUsername.disabled = !WebadminCheckbox.checked;
WebadminPassword.disabled = !WebadminCheckbox.checked;
}
const sendExternalVoltageCheckbox = document.querySelector(
'input[name="battery.sendExternalVoltage"]'
);
const externalVoltagePinInput = document.querySelector(
'input[name="battery.externalVoltagePin"]'
);
const voltageDividerR1 = document.querySelector(
'input[name="battery.voltageDividerR1"]'
);
const voltageDividerR2 = document.querySelector(
'input[name="battery.voltageDividerR2"]'
);
sendExternalVoltageCheckbox.addEventListener("change", function () {
externalVoltagePinInput.disabled = !this.checked;
voltageDividerR1.disabled = !this.checked;
voltageDividerR2.disabled = !this.checked;
}); });
// Status Switch const WebadminCheckbox = document.querySelector(
const StatusCheckbox = document.querySelector('input[name="beacon.statusActive"]'); 'input[name="webadmin.active"]'
const StatusPacket = document.querySelector('input[name="beacon.statusPacket"]'); );
StatusCheckbox.addEventListener("change", function() {
StatusPacket.disabled = !this.checked;
});
// APRS-IS Switches const WebadminUsername = document.querySelector(
const APRSISCheckbox = document.querySelector('input[name="aprs_is.active"]'); 'input[name="webadmin.username"]'
const APRSISGateMessages = document.querySelector('input[name="aprs_is.messagesToRF"]'); );
const APRSISGateObjects = document.querySelector('input[name="aprs_is.objectsToRF"]');
const APRSISServer = document.querySelector('input[name="aprs_is.server"]');
const APRSISPort = document.querySelector('input[name="aprs_is.port"]');
const APRSISPasscode = document.querySelector('input[name="aprs_is.passcode"]');
const APRSISFilter = document.querySelector('input[name="aprs_is.filter"]');
APRSISCheckbox.addEventListener("change", function() {
APRSISGateMessages.disabled = !this.checked;
APRSISGateObjects.disabled = !this.checked;
APRSISServer.disabled = !this.checked;
APRSISPort.disabled = !this.checked;
APRSISPasscode.disabled = !this.checked;
APRSISFilter.disabled = !this.checked;
});
// Display Switches const WebadminPassword = document.querySelector(
const DisplayAlwaysOnCheckbox = document.querySelector('input[name="display.alwaysOn"]'); 'input[name="webadmin.password"]'
const DisplayTimeout = document.querySelector('input[name="display.timeout"]'); );
DisplayAlwaysOnCheckbox.addEventListener("change", function () {
DisplayTimeout.disabled = this.checked;
});
// Battery Switches
const MonitorInternalVoltageCheckbox = document.querySelector('input[name="battery.monitorInternalVoltage"]');
const MonitorInternalSleepVoltage = document.querySelector('input[name="battery.internalSleepVoltage"]');
MonitorInternalVoltageCheckbox.addEventListener("change", function () {
MonitorInternalSleepVoltage.disabled = !this.checked;
});
const MonitorExternalVoltageCheckbox = document.querySelector('input[name="battery.monitorExternalVoltage"]');
const MonitorExternalSleepVoltage = document.querySelector('input[name="battery.externalSleepVoltage"]');
MonitorExternalVoltageCheckbox.addEventListener("change", function () {
MonitorExternalSleepVoltage.disabled = !this.checked;
});
const SendExternalVoltageCheckbox = document.querySelector('input[name="battery.sendExternalVoltage"]');
const UseExternalI2CSensorCheckbox = document.querySelector('input[name="battery.useExternalI2CSensor"]');
const ExternalVoltagePin = document.querySelector('input[name="battery.externalVoltagePin"]');
const ExternalVoltageDividerR1 = document.querySelector('input[name="battery.voltageDividerR1"]');
const ExternalVoltageDividerR2 = document.querySelector('input[name="battery.voltageDividerR2"]');
SendExternalVoltageCheckbox.addEventListener("change", function () {
UseExternalI2CSensorCheckbox.disabled = !this.checked;
ExternalVoltagePin.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR1.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR2.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
});
UseExternalI2CSensorCheckbox.addEventListener("change", function () {
ExternalVoltagePin.disabled = this.checked;
ExternalVoltageDividerR1.disabled = this.checked;
ExternalVoltageDividerR2.disabled = this.checked;
});
// Telemetry Switches
const TelemetryCheckbox = document.querySelector('input[name="wxsensor.active"]');
const TelemetryHeightCorrection = document.querySelector('input[name="wxsensor.heightCorrection"]');
const TelemetryTempCorrection = document.querySelector('input[name="wxsensor.temperatureCorrection"]');
TelemetryCheckbox.addEventListener("change", function () {
TelemetryHeightCorrection.disabled = !this.checked;
TelemetryTempCorrection.disabled = !this.checked;
});
// Syslog Switches
const SyslogCheckbox = document.querySelector('input[name="syslog.active"]');
const SyslogServer = document.querySelector('input[name="syslog.server"]');
const SyslogPort = document.querySelector('input[name="syslog.port"]');
const SyslogBeaconOverTCPIP = document.querySelector('input[name="syslog.logBeaconOverTCPIP"]');
SyslogCheckbox.addEventListener("change", function () {
SyslogServer.disabled = !this.checked;
SyslogPort.disabled = !this.checked;
SyslogBeaconOverTCPIP.disabled = !this.checked
});
// MQTT Switches
const MqttCheckbox = document.querySelector('input[name="mqtt.active"]');
const MqttServer = document.querySelector('input[name="mqtt.server"]');
const MqttTopic = document.querySelector('input[name="mqtt.topic"]');
const MqttUsername = document.querySelector('input[name="mqtt.username"]');
const MqttPassword = document.querySelector('input[name="mqtt.password"]');
const MqttPort = document.querySelector('input[name="mqtt.port"]');
const MqttBeaconOverMqtt = document.querySelector('input[name="mqtt.beaconOverMqtt"]');
MqttCheckbox.addEventListener("change", function () {
MqttServer.disabled = !this.checked;
MqttTopic.disabled = !this.checked;
MqttUsername.disabled = !this.checked;
MqttPassword.disabled = !this.checked;
MqttPort.disabled = !this.checked;
MqttBeaconOverMqtt.disabled = !this.checked;
});
// Reboot Switches
const RebootModeCheckbox = document.querySelector('input[name="other.rebootMode"]');
const RebootModeTime = document.querySelector('input[name="other.rebootModeTime"]');
RebootModeCheckbox.addEventListener("change", function() {
RebootModeTime.disabled = !this.checked;
});
// Web Admin Switches
const WebadminCheckbox = document.querySelector('input[name="webadmin.active"]');
const WebadminUsername = document.querySelector('input[name="webadmin.username"]');
const WebadminPassword = document.querySelector('input[name="webadmin.password"]');
WebadminCheckbox.addEventListener("change", function () { WebadminCheckbox.addEventListener("change", function () {
WebadminUsername.disabled = !this.checked; WebadminUsername.disabled = !this.checked;
WebadminPassword.disabled = !this.checked; WebadminPassword.disabled = !this.checked;
}); });
document.querySelector(".new button").addEventListener("click", function () { document.querySelector(".new button").addEventListener("click", function () {
const networksContainer = document.querySelector(".list-networks"); const networksContainer = document.querySelector(".list-networks");
@@ -480,6 +398,65 @@ document
updateImage(); updateImage();
}); });
const speedStandards = {
300: [125, 5, 12],
244: [125, 6, 12],
209: [125, 7, 12],
183: [125, 8, 12],
610: [125, 8, 10],
1200: [125, 7, 9],
};
function refreshSpeedStandard() {
const bw = Number(document.getElementById("lora.signalBandwidth").value);
const cr4 = Number(document.getElementById("lora.codingRate4").value);
const sf = Number(document.getElementById("lora.spreadingFactor").value);
let found = false;
for (const speed in speedStandards) {
const standard = speedStandards[speed];
if (standard[0] !== bw / 1000) continue;
if (standard[1] !== cr4) continue;
if (standard[2] !== sf) continue;
document.getElementById("action.speed").value = speed;
found = true;
break;
}
if (!found) {
document.getElementById("action.speed").value = "";
}
}
document
.getElementById("lora.signalBandwidth")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.codingRate4")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.spreadingFactor")
.addEventListener("focusout", refreshSpeedStandard);
document.getElementById("action.speed").addEventListener("change", function () {
const speed = document.getElementById("action.speed").value;
if (speed !== "") {
const value = speedStandards[Number(speed)];
const bw = value[0];
const cr4 = value[1];
const sf = value[2];
document.getElementById("lora.signalBandwidth").value = bw * 1000;
document.getElementById("lora.codingRate4").value = cr4;
document.getElementById("lora.spreadingFactor").value = sf;
}
});
const form = document.querySelector("form"); const form = document.querySelector("form");
Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 KiB

+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+11 -6
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -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
+8 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -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
+20 -41
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -44,14 +44,13 @@ public:
int interval; int interval;
String overlay; String overlay;
String symbol; String symbol;
String path; String path;
bool sendViaAPRSIS;
bool sendViaRF; bool sendViaRF;
int beaconFreq; bool sendViaAPRSIS;
bool gpsActive;
bool gpsAmbiguity;
bool statusActive; bool statusActive;
String statusPacket; String statusPacket;
bool gpsActive;
int ambiguityLevel;
}; };
class APRS_IS { class APRS_IS {
@@ -69,21 +68,17 @@ class DIGI {
public: public:
int mode; int mode;
int ecoMode; // 0 = Not Active | 1 = Ultra EcoMode | 2 = Not Active (WiFi OFF, Serial ON) int ecoMode; // 0 = Not Active | 1 = Ultra EcoMode | 2 = Not Active (WiFi OFF, Serial ON)
bool backupDigiMode;
}; };
class LoraModule { class LoraModule {
public: public:
bool rxActive;
long rxFreq;
int rxSpreadingFactor;
int rxCodingRate4;
long rxSignalBandwidth;
bool txActive;
long txFreq; long txFreq;
int txSpreadingFactor; long rxFreq;
int txCodingRate4; bool txActive;
long txSignalBandwidth; bool rxActive;
int spreadingFactor;
long signalBandwidth;
int codingRate4;
int power; int power;
}; };
@@ -103,7 +98,6 @@ public:
int externalVoltagePin; int externalVoltagePin;
bool monitorExternalVoltage; bool monitorExternalVoltage;
float externalSleepVoltage; float externalSleepVoltage;
bool useExternalI2CSensor;
float voltageDividerR1; float voltageDividerR1;
float voltageDividerR2; float voltageDividerR2;
bool sendVoltageAsTelemetry; bool sendVoltageAsTelemetry;
@@ -129,7 +123,6 @@ public:
bool enableServer; bool enableServer;
bool enableSerial; bool enableSerial;
bool acceptOwn; bool acceptOwn;
bool aprsBridgeActive;
}; };
class OTA { class OTA {
@@ -147,7 +140,6 @@ public:
class NTP { class NTP {
public: public:
String server;
float gmtCorrection; float gmtCorrection;
}; };
@@ -157,25 +149,13 @@ public:
bool rfOnly; bool rfOnly;
}; };
class MQTT {
public:
bool active;
String server;
String topic;
String username;
String password;
int port;
bool beaconOverMqtt;
};
class Configuration { class Configuration {
public: public:
String callsign; String callsign;
String tacticalCallsign;
int rememberStationTime; int rememberStationTime;
bool backupDigiMode;
bool rebootMode; bool rebootMode;
int rebootModeTime; int rebootModeTime;
int startupDelay;
String personalNote; String personalNote;
String blacklist; String blacklist;
std::vector<WiFi_AP> wifiAPs; std::vector<WiFi_AP> wifiAPs;
@@ -188,15 +168,14 @@ public:
BATTERY battery; BATTERY battery;
WXSENSOR wxsensor; WXSENSOR wxsensor;
SYSLOG syslog; SYSLOG syslog;
TNC tnc; TNC tnc;
OTA ota; OTA ota;
WEBADMIN webadmin; WEBADMIN webadmin;
NTP ntp; NTP ntp;
REMOTE_MANAGEMENT remoteManagement; REMOTE_MANAGEMENT remoteManagement;
MQTT mqtt;
void setDefaultValues(); void init();
bool writeFile(); void writeFile();
Configuration(); Configuration();
private: private:
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+5 -6
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -21,7 +21,6 @@
#include <Arduino.h> #include <Arduino.h>
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
void displaySetup(); void displaySetup();
+6 -6
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -23,7 +23,7 @@
namespace GPS_Utils { namespace GPS_Utils {
String getiGateLoRaBeaconPacket(); String getiGateLoRaBeaconPacket();
char *ax25_base91enc(char *s, uint8_t n, uint32_t v); char *ax25_base91enc(char *s, uint8_t n, uint32_t v);
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol); String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol);
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
-34
View File
@@ -1,34 +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 MQTT_UTILS_H_
#define MQTT_UTILS_H_
#include <Arduino.h>
namespace MQTT_Utils {
void sendToMqtt(const String& packet);
void connect();
void loop();
void setup();
}
#endif
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+7 -6
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -34,13 +34,14 @@ namespace POWER_Utils {
void vext_ctrl_OFF(); void vext_ctrl_OFF();
#endif #endif
#ifdef ADC_CTRL_PIN #ifdef ADC_CTRL
void adc_ctrl_ON(); void adc_ctrl_ON();
void adc_ctrl_OFF(); void adc_ctrl_OFF();
#endif #endif
double getBatteryVoltage(); double getBatteryVoltage();
bool isBatteryConnected(); bool isBatteryConnected();
void activateMeasurement();
void activateGPS(); void activateGPS();
void deactivateGPS(); void deactivateGPS();
void activateLoRa(); void activateLoRa();
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+6 -7
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -21,9 +21,8 @@
#include <Arduino.h> #include <Arduino.h>
namespace SLEEP_Utils { namespace SLEEP_Utils {
void setup(); void setup();
void checkWakeUpFlag(); void checkWakeUpFlag();
void startSleeping(); void startSleeping();
+14 -7
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -23,6 +23,12 @@
#include <Arduino.h> #include <Arduino.h>
struct Packet25SegBuffer {
uint32_t receivedTime;
String station;
String payload;
};
struct LastHeardStation { struct LastHeardStation {
uint32_t lastHeardTime; uint32_t lastHeardTime;
String station; String station;
@@ -37,10 +43,11 @@ namespace STATION_Utils {
void deleteNotHeard(); void deleteNotHeard();
void updateLastHeard(const String& station); void updateLastHeard(const String& station);
bool wasHeard(const String& station); bool wasHeard(const String& station);
bool isIn25SegHashBuffer(const String& station, const String& textMessage); void clean25SegBuffer();
bool check25SegBuffer(const String& station, const String& textMessage);
void processOutputPacketBufferUltraEcoMode(); void processOutputPacketBufferUltraEcoMode();
void processOutputPacketBuffer(); void processOutputPacketBuffer();
void addToOutputPacketBuffer(const String& packet, bool flag = false); void addToOutputPacketBuffer(const String& packet);
} }
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
-34
View File
@@ -1,34 +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();
void checkEUPInterval();
}
#endif
+7 -7
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -27,8 +27,8 @@ namespace TNC_Utils {
void setup(); void setup();
void loop(); void loop();
void sendToClients(const String& packet, bool stripBytes = false); void sendToClients(const String& packet);
void sendToSerial(const String& packet, bool stripBytes = false); void sendToSerial(const String& packet);
} }
+7 -8
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -35,7 +35,7 @@ namespace Utils {
void processStatus(); void processStatus();
String getLocalIP(); String getLocalIP();
void setupDisplay(); void setupDisplay();
void showActiveStations(); void activeStations();
void checkBeaconInterval(); void checkBeaconInterval();
void checkDisplayInterval(); void checkDisplayInterval();
void validateFreqs(); void validateFreqs();
@@ -45,8 +45,7 @@ namespace Utils {
void checkRebootMode(); void checkRebootMode();
void checkRebootTime(); void checkRebootTime();
void checkSleepByLowBatteryVoltage(uint8_t mode); void checkSleepByLowBatteryVoltage(uint8_t mode);
bool callsignIsValid(const String& callsign); bool checkValidCallsign(const String& callsign);
void startupDelay();
} }
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+5 -6
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -20,7 +20,6 @@
#define WX_UTILS_H_ #define WX_UTILS_H_
#include <Adafruit_Sensor.h> #include <Adafruit_Sensor.h>
#include <Adafruit_AHTX0.h>
#include <Adafruit_BME280.h> #include <Adafruit_BME280.h>
#include <Adafruit_BMP280.h> #include <Adafruit_BMP280.h>
#include <Adafruit_BME680.h> #include <Adafruit_BME680.h>
+12 -14
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -26,7 +26,7 @@
#ifdef HAS_A7670 #ifdef HAS_A7670
#define TINY_GSM_MODEM_SIM7600 //The AT instruction of A7670 is compatible with SIM7600 #define TINY_GSM_MODEM_SIM7600 //The AT instruction of A7670 is compatible with SIM7600
#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb #define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
#define SerialAT Serial1 #define SerialAT Serial1
#include <TinyGsmClient.h> #include <TinyGsmClient.h>
@@ -115,8 +115,6 @@
if (SerialAT.available()) { if (SerialAT.available()) {
String response = SerialAT.readString(); String response = SerialAT.readString();
//Serial.println(response); // DEBUG of Modem AT message //Serial.println(response); // DEBUG of Modem AT message
int responseOKIndex = response.indexOf("OK");
int callsignIndex = ATMessage.indexOf(Config.callsign);
if(response.indexOf("verified") >= 0) { if(response.indexOf("verified") >= 0) {
Serial.println("Logged! (User Validated)\n"); Serial.println("Logged! (User Validated)\n");
displayShow(firstLine, "Connecting APRS-IS...", "---> Logged!", " ", 1000); displayShow(firstLine, "Connecting APRS-IS...", "---> Logged!", " ", 1000);
@@ -124,7 +122,7 @@
validAT = true; validAT = true;
i = 1; i = 1;
delayATMessage = 0; delayATMessage = 0;
} else if (ATMessage == "AT+NETOPEN" && responseOKIndex >= 0) { } else if (ATMessage == "AT+NETOPEN" && response.indexOf("OK") >= 0) {
Serial.println("Port Open!"); Serial.println("Port Open!");
displayShow(firstLine, "Opening Port...", "---> Port Open", " ", 0); displayShow(firstLine, "Opening Port...", "---> Port Open", " ", 0);
validAT = true; validAT = true;
@@ -147,14 +145,14 @@
validAT = true; validAT = true;
i = 1; i = 1;
delayATMessage = 0; delayATMessage = 0;
} else if (callsignIndex >= 3 && !modemLoggedToAPRSIS && responseOKIndex >= 0 && !stationBeacon) { // login info } else if (ATMessage.indexOf(Config.callsign) >= 3 && !modemLoggedToAPRSIS && response.indexOf("OK") >= 0 && !stationBeacon) { // login info
validAT = true; validAT = true;
delayATMessage = 0; delayATMessage = 0;
} else if (callsignIndex == 0 && !beaconSent && responseOKIndex >= 0 && !stationBeacon) { // self beacon or querys } else if (ATMessage.indexOf(Config.callsign) == 0 && !beaconSent && response.indexOf("OK") >= 0 && !stationBeacon) { // self beacon or querys
validAT = true; validAT = true;
i = 1; i = 1;
delayATMessage = 0; delayATMessage = 0;
} else if (stationBeacon && responseOKIndex >= 0) { //upload others beacons } else if (stationBeacon && response.indexOf("OK") >= 0) { //upload others beacons
validAT = true; validAT = true;
i = 1; i = 1;
delayATMessage = 0; delayATMessage = 0;
@@ -202,7 +200,7 @@
if (beaconBytesSent) { if (beaconBytesSent) {
Serial.print("."); Serial.print(".");
beaconSent = checkATResponse(packet); beaconSent = checkATResponse(packet);
} }
if (!beaconSent) { if (!beaconSent) {
Serial.println("------------------------------------> UPLOAD FAILED!!!"); Serial.println("------------------------------------> UPLOAD FAILED!!!");
} else { } else {
@@ -223,5 +221,5 @@
delay(1); delay(1);
} }
} }
#endif #endif
+66 -26
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -24,7 +24,7 @@
@@ -33,7 +33,7 @@
Ricardo Guzman - CA2RXU Ricardo Guzman - CA2RXU
https://github.com/richonguzman/LoRa_APRS_iGate https://github.com/richonguzman/LoRa_APRS_iGate
(donations : http://paypal.me/richonguzman) (donations : http://paypal.me/richonguzman)
___________________________________________________________________*/ ___________________________________________________________________*/
@@ -51,7 +51,6 @@ ___________________________________________________________________*/
#include "syslog_utils.h" #include "syslog_utils.h"
#include "power_utils.h" #include "power_utils.h"
#include "sleep_utils.h" #include "sleep_utils.h"
#include "mqtt_utils.h"
#include "lora_utils.h" #include "lora_utils.h"
#include "wifi_utils.h" #include "wifi_utils.h"
#include "digi_utils.h" #include "digi_utils.h"
@@ -67,11 +66,9 @@ ___________________________________________________________________*/
#endif #endif
String versionDate = "2026-03-05"; String versionDate = "2025-08-20";
String versionNumber = "3.2.2";
Configuration Config; Configuration Config;
WiFiClient aprsIsClient; WiFiClient espClient;
WiFiClient mqttClient;
#ifdef HAS_GPS #ifdef HAS_GPS
HardwareSerial gpsSerial(1); HardwareSerial gpsSerial(1);
TinyGPSPlus gps; TinyGPSPlus gps;
@@ -86,7 +83,7 @@ WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
bool isUpdatingOTA = false; bool isUpdatingOTA = false;
uint32_t lastBatteryCheck = 0; uint32_t lastBatteryCheck = 0;
bool backupDigiMode = false; bool backUpDigiMode = false;
bool modemLoggedToAPRSIS = false; bool modemLoggedToAPRSIS = false;
#ifdef HAS_EPAPER #ifdef HAS_EPAPER
@@ -97,6 +94,12 @@ bool modemLoggedToAPRSIS = false;
std::vector<ReceivedPacket> receivedPackets; 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
#ifdef HAS_TWO_CORES
QueueHandle_t aprsIsTxQueue = NULL;
QueueHandle_t aprsIsRxQueue = NULL;
#endif
void setup() { void setup() {
@@ -107,7 +110,12 @@ void setup() {
Utils::validateFreqs(); Utils::validateFreqs();
GPS_Utils::setup(); GPS_Utils::setup();
STATION_Utils::loadBlacklistAndManagers(); STATION_Utils::loadBlacklistAndManagers();
Utils::startupDelay();
#ifdef STARTUP_DELAY // (TEST) just to wait for WiFi init of Routers
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(STARTUP_DELAY * 60 * 1000);
#endif
SLEEP_Utils::setup(); SLEEP_Utils::setup();
WIFI_Utils::setup(); WIFI_Utils::setup();
NTP_Utils::setup(); NTP_Utils::setup();
@@ -115,16 +123,43 @@ void setup() {
WX_Utils::setup(); WX_Utils::setup();
WEB_Utils::setup(); WEB_Utils::setup();
TNC_Utils::setup(); TNC_Utils::setup();
MQTT_Utils::setup();
#ifdef HAS_A7670 #ifdef HAS_A7670
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();
@@ -138,7 +173,7 @@ void loop() {
ElegantOTA.loop(); ElegantOTA.loop();
return; // Don't process IGate and Digi during OTA update return; // Don't process IGate and Digi during OTA update
} }
#ifdef HAS_GPS #ifdef HAS_GPS
if (Config.beacon.gpsActive) { if (Config.beacon.gpsActive) {
if (millis() - gpsSatelliteTime > 5000) { if (millis() - gpsSatelliteTime > 5000) {
@@ -164,17 +199,15 @@ void loop() {
if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect(); if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect();
#else #else
WIFI_Utils::checkWiFi(); WIFI_Utils::checkWiFi();
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !aprsIsClient.connected()) APRS_IS_Utils::connect(); if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) APRS_IS_Utils::connect();
if (Config.mqtt.active && (WiFi.status() == WL_CONNECTED) && !mqttClient.connected()) MQTT_Utils::connect();
#endif #endif
NTP_Utils::update(); NTP_Utils::update();
TNC_Utils::loop(); TNC_Utils::loop();
MQTT_Utils::loop();
Utils::checkDisplayInterval(); Utils::checkDisplayInterval();
Utils::checkBeaconInterval(); Utils::checkBeaconInterval();
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func? APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
String packet = ""; String packet = "";
@@ -183,20 +216,27 @@ void loop() {
} }
if (packet != "") { if (packet != "") {
if (Config.aprs_is.active) { // If APRSIS enabled if (Config.aprs_is.active) { // If APRSIS enabled
APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS
} }
if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backupDigiMode)) { // If Digi enabled if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backUpDigiMode)) { // If Digi enabled
STATION_Utils::clean25SegBuffer();
DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi
} }
if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet, true); // Send received packet to TNC KISS if (Config.tnc.enableServer) { // If TNC server enabled
if (Config.tnc.enableSerial) TNC_Utils::sendToSerial(packet, true); // Send received packet to Serial KISS TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS
if (Config.mqtt.active) MQTT_Utils::sendToMqtt(packet); // Send received packet to MQTT }
if (Config.tnc.enableSerial) { // If Serial KISS enabled
TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
}
} }
if (Config.aprs_is.active) APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS if (Config.aprs_is.active) {
APRS_IS_Utils::processAPRSISPacket();
//APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
}
STATION_Utils::processOutputPacketBuffer(); STATION_Utils::processOutputPacketBuffer();
+168 -124
View File
@@ -1,22 +1,21 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <APRSPacketLib.h>
#include <WiFi.h> #include <WiFi.h>
#include "configuration.h" #include "configuration.h"
#include "aprs_is_utils.h" #include "aprs_is_utils.h"
@@ -26,13 +25,13 @@
#include "query_utils.h" #include "query_utils.h"
#include "A7670_utils.h" #include "A7670_utils.h"
#include "digi_utils.h" #include "digi_utils.h"
#include "tnc_utils.h"
#include "display.h" #include "display.h"
#include "utils.h" #include "utils.h"
extern Configuration Config; extern Configuration Config;
extern WiFiClient aprsIsClient; 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;
@@ -42,13 +41,10 @@ extern String fifthLine;
extern String sixthLine; extern String sixthLine;
extern String seventhLine; extern String seventhLine;
extern bool modemLoggedToAPRSIS; extern bool modemLoggedToAPRSIS;
extern bool backupDigiMode; extern bool backUpDigiMode;
extern String versionNumber;
uint32_t lastRxTime = millis(); uint32_t lastRxTime = millis();
bool passcodeValid = false; bool passcodeValid = false;
uint32_t lastServerCheck = 0;
#ifdef HAS_A7670 #ifdef HAS_A7670
extern bool stationBeacon; extern bool stationBeacon;
@@ -57,18 +53,21 @@ uint32_t lastServerCheck = 0;
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) {
aprsIsClient.print(line + "\r\n"); espClient.print(line + "\r\n");
} }
void connect() { void connect() {
Serial.print("Connecting to APRS-IS ... "); Serial.print("Connecting to APRS-IS ... ");
uint8_t count = 0; uint8_t count = 0;
while (!aprsIsClient.connect(Config.aprs_is.server.c_str(), Config.aprs_is.port) && count < 20) { while (!espClient.connect(Config.aprs_is.server.c_str(), Config.aprs_is.port) && count < 20) {
Serial.println("Didn't connect with server..."); Serial.println("Didn't connect with server...");
delay(1000); delay(1000);
aprsIsClient.stop(); espClient.stop();
aprsIsClient.flush(); espClient.flush();
Serial.println("Run client.stop"); Serial.println("Run client.stop");
Serial.println("Trying to connect with Server: " + String(Config.aprs_is.server) + " AprsServerPort: " + String(Config.aprs_is.port)); Serial.println("Trying to connect with Server: " + String(Config.aprs_is.server) + " AprsServerPort: " + String(Config.aprs_is.port));
count++; count++;
@@ -83,9 +82,7 @@ namespace APRS_IS_Utils {
aprsAuth += Config.callsign; aprsAuth += Config.callsign;
aprsAuth += " pass "; aprsAuth += " pass ";
aprsAuth += Config.aprs_is.passcode; aprsAuth += Config.aprs_is.passcode;
aprsAuth += " vers CA2RXUiGate "; aprsAuth += " vers CA2RXU_iGate 3.0 filter ";
aprsAuth += versionNumber;
aprsAuth += " filter ";
aprsAuth += Config.aprs_is.filter; aprsAuth += Config.aprs_is.filter;
upload(aprsAuth); upload(aprsAuth);
} }
@@ -96,7 +93,7 @@ namespace APRS_IS_Utils {
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
wifiState = "OK"; wifiState = "OK";
} else { } else {
if (backupDigiMode || Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) { if (backUpDigiMode || Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) {
wifiState = "--"; wifiState = "--";
} else { } else {
wifiState = "AP"; wifiState = "AP";
@@ -117,7 +114,7 @@ namespace APRS_IS_Utils {
aprsisState = "--"; aprsisState = "--";
} }
#else #else
if (aprsIsClient.connected()) { if (espClient.connected()) {
aprsisState = "OK"; aprsisState = "OK";
} else { } else {
aprsisState = "--"; aprsisState = "--";
@@ -135,30 +132,30 @@ namespace APRS_IS_Utils {
} }
String checkForStartingBytes(const String& packet) { String checkForStartingBytes(const String& packet) {
int index = packet.indexOf("\x3c\xff\x01"); if (packet.indexOf("\x3c\xff\x01") != -1) {
return (index != -1) ? packet.substring(0, index) : packet; return packet.substring(0, packet.indexOf("\x3c\xff\x01"));
} else {
return packet;
}
} }
String buildPacketToUpload(const String& packet) { String buildPacketToUpload(const String& packet) {
int colonIndex = packet.indexOf(":"); String packetToUpload = packet.substring(3, packet.indexOf(":"));
String packetToUpload = packet.substring(3, colonIndex);
if (Config.aprs_is.active && passcodeValid && Config.aprs_is.messagesToRF) { if (Config.aprs_is.active && passcodeValid && Config.aprs_is.messagesToRF) {
packetToUpload += ",qAR,"; packetToUpload += ",qAR,";
} else { } else {
packetToUpload += ",qAO,"; packetToUpload += ",qAO,";
} }
packetToUpload += Config.callsign; packetToUpload += Config.callsign;
packetToUpload += checkForStartingBytes(packet.substring(colonIndex)); packetToUpload += checkForStartingBytes(packet.substring(packet.indexOf(":")));
return packetToUpload; return packetToUpload;
} }
bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty) { bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty) {
String receivedMessage; String receivedMessage;
int leftCurlyBraceIndex = packet.indexOf("{"); if (packet.indexOf("{") > 0) { // ack?
int colonIndex = packet.indexOf(":");
if (leftCurlyBraceIndex > 0) { // ack?
String ackMessage = "ack"; String ackMessage = "ack";
ackMessage.concat(packet.substring(leftCurlyBraceIndex + 1)); ackMessage.concat(packet.substring(packet.indexOf("{") + 1));
ackMessage.trim(); ackMessage.trim();
//Serial.println(ackMessage); //Serial.println(ackMessage);
@@ -180,9 +177,9 @@ namespace APRS_IS_Utils {
addToBuffer += ":"; addToBuffer += ":";
addToBuffer += ackMessage; addToBuffer += ackMessage;
STATION_Utils::addToOutputPacketBuffer(addToBuffer); STATION_Utils::addToOutputPacketBuffer(addToBuffer);
receivedMessage = packet.substring(colonIndex + 1, leftCurlyBraceIndex); receivedMessage = packet.substring(packet.indexOf(":") + 1, packet.indexOf("{"));
} else { } else {
receivedMessage = packet.substring(colonIndex + 1); receivedMessage = packet.substring(packet.indexOf(":") + 1);
} }
if (receivedMessage.indexOf("?") == 0) { if (receivedMessage.indexOf("?") == 0) {
if (!Config.display.alwaysOn && Config.display.timeout != 0) { if (!Config.display.alwaysOn && Config.display.timeout != 0) {
@@ -199,38 +196,37 @@ namespace APRS_IS_Utils {
} }
void processLoRaPacket(const String& packet) { void processLoRaPacket(const String& packet) {
if (passcodeValid && (aprsIsClient.connected() || modemLoggedToAPRSIS)) { if (passcodeValid && (espClient.connected() || modemLoggedToAPRSIS)) {
if (packet.indexOf("NOGATE") == -1 && packet.indexOf("RFONLY") == -1) { if (packet.indexOf("NOGATE") == -1 && packet.indexOf("RFONLY") == -1) {
int firstColonIndex = packet.indexOf(":"); int firstColonIndex = packet.indexOf(":");
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) { if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) {
const String& Sender = packet.substring(3, packet.indexOf(">")); const String& Sender = packet.substring(3, packet.indexOf(">"));
if (Sender != Config.callsign && Utils::callsignIsValid(Sender)) { if (Sender != Config.callsign && Utils::checkValidCallsign(Sender)) {
STATION_Utils::updateLastHeard(Sender); STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(packet.substring(3), 0); // LoRa-APRS Utils::typeOfPacket(packet.substring(3), 0); // LoRa-APRS
int doubleColonIndex = packet.indexOf("::"); const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
const String& AddresseeAndMessage = packet.substring(doubleColonIndex + 2);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":")); String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim(); Addressee.trim();
bool queryMessage = false; bool queryMessage = false;
if (doubleColonIndex > 10 && Addressee == Config.callsign) { // its a message for me! if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
queryMessage = processReceivedLoRaMessage(Sender, checkForStartingBytes(AddresseeAndMessage), false); queryMessage = processReceivedLoRaMessage(Sender, checkForStartingBytes(AddresseeAndMessage), false);
} }
if (queryMessage) return; if (!queryMessage) {
const String& aprsPacket = buildPacketToUpload(packet);
const String& aprsPacket = buildPacketToUpload(packet); if (!Config.display.alwaysOn && Config.display.timeout != 0) {
if (!Config.display.alwaysOn && Config.display.timeout != 0) { displayToggle(true);
displayToggle(true); }
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);
} }
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);
} }
} }
} }
@@ -240,36 +236,37 @@ namespace APRS_IS_Utils {
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType) { String buildPacketToTx(const String& aprsisPacket, uint8_t packetType) {
String packet = aprsisPacket; String packet = aprsisPacket;
packet.trim(); packet.trim();
String outputPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path); String outputPacket = Config.callsign;
outputPacket += ">APLRG1";
if (Config.beacon.path != "") {
outputPacket += ",";
outputPacket += Config.beacon.path;
}
outputPacket += ":}"; outputPacket += ":}";
outputPacket += packet.substring(0, packet.indexOf(",")); // Callsign>Tocall outputPacket += packet.substring(0, packet.indexOf(",")); // Callsign>Tocall
outputPacket.concat(",TCPIP,"); outputPacket.concat(",TCPIP,");
outputPacket.concat(Config.callsign); outputPacket.concat(Config.callsign);
outputPacket.concat("*"); outputPacket.concat("*");
int colonEqualIndex = packet.indexOf(":=");
int doubleColonIndex = packet.indexOf("::");
int colonInvAccentIndex = packet.indexOf(":`");
switch (packetType) { switch (packetType) {
case 0: // gps case 0: // gps
if (colonEqualIndex > 0) { if (packet.indexOf(":=") > 0) {
outputPacket += packet.substring(colonEqualIndex); outputPacket += packet.substring(packet.indexOf(":="));
} else { } else {
outputPacket += packet.substring(packet.indexOf(":!")); outputPacket += packet.substring(packet.indexOf(":!"));
} }
break; break;
case 1: // messages case 1: // messages
outputPacket += packet.substring(doubleColonIndex); outputPacket += packet.substring(packet.indexOf("::"));
break; break;
case 2: // status case 2: // status
outputPacket += packet.substring(packet.indexOf(":>")); outputPacket += packet.substring(packet.indexOf(":>"));
break; break;
case 3: // telemetry case 3: // telemetry
outputPacket += packet.substring(doubleColonIndex); outputPacket += packet.substring(packet.indexOf("::"));
break; break;
case 4: // mic-e case 4: // mic-e
if (colonInvAccentIndex > 0) { if (packet.indexOf(":`") > 0) {
outputPacket += packet.substring(colonInvAccentIndex); outputPacket += packet.substring(packet.indexOf(":`"));
} else { } else {
outputPacket += packet.substring(packet.indexOf(":'")); outputPacket += packet.substring(packet.indexOf(":'"));
} }
@@ -281,70 +278,64 @@ namespace APRS_IS_Utils {
return outputPacket; return outputPacket;
} }
void processAckMessage(const String& sender, const String& message) { //uint32_t lastLog = 0;
String ackPacket = Config.callsign; void processAPRSISPacket() {
ackPacket += ">APLRG1,TCPIP,qAC::"; /*Serial.println("processAPRSISPacket");
String senderCallsign = sender; if (millis() - lastLog > 5000) { // Cada 5 segundos
for (int i = sender.length(); i < 9; i++) { UBaseType_t packets = uxQueueMessagesWaiting(aprsIsRxQueue);
senderCallsign += ' '; UBaseType_t spaces = uxQueueSpacesAvailable(aprsIsRxQueue);
} Serial.printf("[STATS] APRSIS Queue: %d/%d (%.1f%% full)\n",
ackPacket += senderCallsign; packets, packets + spaces,
ackPacket += ":"; (packets * 100.0) / (packets + spaces));
lastLog = millis();
}*/
String ackMessage = "ack";
ackMessage += message.substring(message.indexOf("{") + 1);
ackMessage.trim();
ackPacket += ackMessage;
#ifdef HAS_A7670 String packet;
A7670_Utils::uploadToAPRSIS(ackPacket); if (xQueueReceive(aprsIsRxQueue, &packet, 0) == pdTRUE) {
#else
upload(ackPacket); if (passcodeValid && !packet.startsWith("#")) {
#endif if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
}
void processAPRSISPacket(const String& packet) {
uint32_t currentTime = millis();
if (!passcodeValid && packet.indexOf(Config.callsign) != -1) {
if (packet.indexOf("unverified") != -1 ) {
Serial.println("\n****APRS PASSCODE NOT VALID****\n");
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 3000);
aprsIsClient.stop();
Config.aprs_is.active = false;
} else if (packet.indexOf("verified") != -1 ) {
if (Config.digi.backupDigiMode) lastServerCheck = currentTime;
passcodeValid = true;
}
}
if (passcodeValid) {
if (packet.startsWith("#")) {
if (Config.digi.backupDigiMode) lastServerCheck = currentTime;
} else {
int doubleColonIndex = packet.indexOf("::");
if (Config.aprs_is.messagesToRF && doubleColonIndex > 0) {
String Sender = packet.substring(0, packet.indexOf(">")); String Sender = packet.substring(0, packet.indexOf(">"));
const String& AddresseeAndMessage = packet.substring(doubleColonIndex + 2); const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
int colonIndex = AddresseeAndMessage.indexOf(":"); String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
String Addressee = AddresseeAndMessage.substring(0, colonIndex);
Addressee.trim(); Addressee.trim();
if (Addressee == Config.callsign) { // its for me! if (Addressee == Config.callsign) { // its for me!
String receivedMessage; String receivedMessage;
int curlyBraceIndex = AddresseeAndMessage.indexOf("{"); if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
if (curlyBraceIndex > 0) { // ack? String ackMessage = "ack";
processAckMessage(Sender, AddresseeAndMessage); ackMessage += AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{") + 1);
receivedMessage = AddresseeAndMessage.substring(colonIndex + 1, curlyBraceIndex); 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 { } else {
receivedMessage = AddresseeAndMessage.substring(colonIndex + 1); receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
} }
if (receivedMessage.indexOf("?") == 0) { if (receivedMessage.indexOf("?") == 0) {
Utils::println("Rx Query (APRS-IS) : " + packet); Utils::println("Rx Query (APRS-IS) : " + packet);
Sender.trim();
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false); String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false);
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n"))); //Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
if (!Config.display.alwaysOn && Config.display.timeout != 0) { if (!Config.display.alwaysOn && Config.display.timeout != 0) {
displayToggle(true); displayToggle(true);
} }
lastScreenOn = currentTime; lastScreenOn = millis();
delay(500);
#ifdef HAS_A7670 #ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(queryAnswer); A7670_Utils::uploadToAPRSIS(queryAnswer);
#else #else
@@ -367,7 +358,7 @@ namespace APRS_IS_Utils {
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) { if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1)); STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
displayToggle(true); displayToggle(true);
lastScreenOn = currentTime; lastScreenOn = millis();
Utils::typeOfPacket(packet, 1); // APRS-LoRa Utils::typeOfPacket(packet, 1); // APRS-LoRa
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0); displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
} }
@@ -377,17 +368,13 @@ namespace APRS_IS_Utils {
if (STATION_Utils::checkObjectTime(packet)) { if (STATION_Utils::checkObjectTime(packet)) {
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5)); STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
displayToggle(true); displayToggle(true);
lastScreenOn = currentTime; lastScreenOn = millis();
Utils::typeOfPacket(packet, 1); // APRS-LoRa Utils::typeOfPacket(packet, 1); // APRS-LoRa
Serial.println(); Serial.println();
} else { } else {
Serial.println(" ---> Rejected (Time): No Tx"); Serial.println(" ---> Rejected (Time): No Tx");
} }
} }
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
}
} }
} }
} }
@@ -396,11 +383,12 @@ namespace APRS_IS_Utils {
#ifdef HAS_A7670 #ifdef HAS_A7670
A7670_Utils::listenAPRSIS(); A7670_Utils::listenAPRSIS();
#else #else
if (aprsIsClient.connected()) { if (espClient.connected()) {
if (aprsIsClient.available()) { if (espClient.available()) {
String aprsisPacket = aprsIsClient.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();
} }
} }
@@ -408,12 +396,68 @@ namespace APRS_IS_Utils {
} }
void firstConnection() { void firstConnection() {
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !aprsIsClient.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);
}
}
} }
+78 -85
View File
@@ -1,22 +1,22 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <Adafruit_INA219.h> #include <Arduino.h>
#include "battery_utils.h" #include "battery_utils.h"
#include "configuration.h" #include "configuration.h"
#include "board_pinout.h" #include "board_pinout.h"
@@ -37,10 +37,8 @@ float multiplyCorrection = 0.035;
float voltageDividerTransformation = 0.0; float voltageDividerTransformation = 0.0;
uint8_t externalI2CSensorAddress = 0x00; int telemetryCounter = random(1,999);
int externalI2CSensorType = 0; // 0 = None | 1 = INA219
Adafruit_INA219 ina219;
#ifdef HAS_ADC_CALIBRATION #ifdef HAS_ADC_CALIBRATION
@@ -102,30 +100,6 @@ namespace BATTERY_Utils {
#endif #endif
} }
void getI2CVoltageSensorAddress() {
uint8_t err, addr;
for(addr = 1; addr < 0x7F; addr++) {
#ifdef SENSOR_I2C_BUS
SENSOR_I2C_BUS.beginTransmission(addr);
err = SENSOR_I2C_BUS.endTransmission();
#else
Wire.beginTransmission(addr);
err = Wire.endTransmission();
#endif
delay(5);
if (err == 0) {
if (addr == 0x40) { // INA219
externalI2CSensorAddress = addr;
}
}
}
}
bool detectINA219(uint8_t addr) {
ina219 = Adafruit_INA219(addr);
return ina219.begin();
}
void setup() { void setup() {
if ((Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) && Config.battery.voltageDividerR2 != 0) voltageDividerTransformation = (Config.battery.voltageDividerR1 + Config.battery.voltageDividerR2) / Config.battery.voltageDividerR2; if ((Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) && Config.battery.voltageDividerR2 != 0) voltageDividerTransformation = (Config.battery.voltageDividerR1 + Config.battery.voltageDividerR2) / Config.battery.voltageDividerR2;
@@ -135,17 +109,9 @@ namespace BATTERY_Utils {
adcCalibration(); adcCalibration();
} }
#endif #endif
getI2CVoltageSensorAddress();
if (externalI2CSensorAddress != 0x00) {
if (detectINA219(externalI2CSensorAddress)) {
Serial.println("INA219 sensor found");
externalI2CSensorType = 1; // INA219
}
}
} }
float checkInternalVoltage() { float checkInternalVoltage() {
#if defined(HAS_AXP192) || defined(HAS_AXP2101) #if defined(HAS_AXP192) || defined(HAS_AXP2101)
if(POWER_Utils::isBatteryConnected()) { if(POWER_Utils::isBatteryConnected()) {
return POWER_Utils::getBatteryVoltage(); return POWER_Utils::getBatteryVoltage();
@@ -153,8 +119,8 @@ namespace BATTERY_Utils {
return 0.0; return 0.0;
} }
#else #else
#ifdef ADC_CTRL_PIN #ifdef ADC_CTRL
POWER_Utils::adc_ctrl_ON(); POWER_Utils::adc_ctrl_ON();
#endif #endif
@@ -177,13 +143,13 @@ namespace BATTERY_Utils {
#endif #endif
#endif #endif
#endif #endif
delay(3); delay(3);
} }
#ifdef ADC_CTRL_PIN #ifdef ADC_CTRL
POWER_Utils::adc_ctrl_OFF(); POWER_Utils::adc_ctrl_OFF();
#ifdef HELTEC_WP_V1 #ifdef HELTEC_WP
double inputDivider = (1.0 / (10.0 + 10.0)) * 10.0; // The voltage divider is a 10k + 10k resistor in series double inputDivider = (1.0 / (10.0 + 10.0)) * 10.0; // The voltage divider is a 10k + 10k resistor in series
#else #else
double inputDivider = (1.0 / (390.0 + 100.0)) * 100.0; // The voltage divider is a 390k + 100k resistor in series, 100k on the low side. double inputDivider = (1.0 / (390.0 + 100.0)) * 100.0; // The voltage divider is a 390k + 100k resistor in series, 100k on the low side.
@@ -213,49 +179,37 @@ namespace BATTERY_Utils {
} }
float checkExternalVoltage() { float checkExternalVoltage() {
if (externalI2CSensorType == 0) { int sample;
int sample; int sampleSum = 0;
int sampleSum = 0; for (int i = 0; i < 100; i++) {
for (int i = 0; i < 100; i++) {
#ifdef HAS_ADC_CALIBRATION
if (calibrationEnable){
sample = adc1_get_raw(ExternalVoltage_ADC_Channel);
} else {
sample = analogRead(Config.battery.externalVoltagePin);
}
#else
sample = analogRead(Config.battery.externalVoltagePin);
#endif
sampleSum += sample;
delayMicroseconds(50);
}
float extVoltage;
#ifdef HAS_ADC_CALIBRATION #ifdef HAS_ADC_CALIBRATION
if (calibrationEnable) { if (calibrationEnable){
extVoltage = esp_adc_cal_raw_to_voltage(sampleSum / 100.0, &adc_chars) * voltageDividerTransformation; // in mV sample = adc1_get_raw(ExternalVoltage_ADC_Channel);
extVoltage /= 1000.0;
} else { } else {
extVoltage = ((((sampleSum/100.0)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection; sample = analogRead(Config.battery.externalVoltagePin);
} }
#else #else
extVoltage = ((((sampleSum/100.0)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection; sample = analogRead(Config.battery.externalVoltagePin);
#endif #endif
sampleSum += sample;
return extVoltage; // raw voltage without mapping delayMicroseconds(50);
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
} else if (externalI2CSensorType == 1) { // INA219
int sampleSum = 0;
for (int i = 0; i < 100; i++) {
sampleSum += ina219.getBusVoltage_V() * 1000.0;
delayMicroseconds(50);
}
float extVoltage = sampleSum/100.0;
return extVoltage/1000.0;
} else {
return 0.0;
} }
float extVoltage;
#ifdef HAS_ADC_CALIBRATION
if (calibrationEnable){
extVoltage = esp_adc_cal_raw_to_voltage(sampleSum / 100, &adc_chars) * voltageDividerTransformation; // in mV
extVoltage /= 1000;
} else {
extVoltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
}
#else
extVoltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
#endif
return extVoltage; // raw voltage without mapping
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
} }
void startupBatteryHealth() { void startupBatteryHealth() {
@@ -264,7 +218,7 @@ namespace BATTERY_Utils {
shouldSleepLowVoltage = true; shouldSleepLowVoltage = true;
} }
#endif #endif
#ifndef HELTEC_WP_V1 #ifndef HELTEC_WP
if (Config.battery.monitorExternalVoltage && checkExternalVoltage() < Config.battery.externalSleepVoltage + 0.1) { if (Config.battery.monitorExternalVoltage && checkExternalVoltage() < Config.battery.externalSleepVoltage + 0.1) {
shouldSleepLowVoltage = true; shouldSleepLowVoltage = true;
} }
@@ -274,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;
}
} }
+211 -386
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -26,176 +26,131 @@
bool shouldSleepStop = true; bool shouldSleepStop = true;
bool Configuration::writeFile() { void Configuration::writeFile() {
Serial.println("Saving configuration..."); Serial.println("Saving config...");
StaticJsonDocument<3584> data; StaticJsonDocument<2560> data;
File configFile = SPIFFS.open("/igate_conf.json", "w"); File configFile = SPIFFS.open("/igate_conf.json", "w");
if (!configFile) { if (wifiAPs[0].ssid != "") { // We don't want to save Auto AP empty SSID
Serial.println("Error: Could not open config file for writing"); for (int i = 0; i < wifiAPs.size(); i++) {
return false; data["wifi"]["AP"][i]["ssid"] = wifiAPs[i].ssid;
} data["wifi"]["AP"][i]["password"] = wifiAPs[i].password;
try {
if (wifiAPs[0].ssid != "") { // We don't want to save Auto AP empty SSID
for (int i = 0; i < wifiAPs.size(); i++) {
data["wifi"]["AP"][i]["ssid"] = wifiAPs[i].ssid;
data["wifi"]["AP"][i]["password"] = wifiAPs[i].password;
}
} }
data["other"]["startupDelay"] = startupDelay;
data["wifi"]["autoAP"]["password"] = wifiAutoAP.password;
data["wifi"]["autoAP"]["timeout"] = wifiAutoAP.timeout;
callsign.trim();
data["callsign"] = callsign;
tacticalCallsign.trim();
data["tacticalCallsign"] = tacticalCallsign;
data["aprs_is"]["active"] = aprs_is.active;
data["aprs_is"]["passcode"] = aprs_is.passcode;
data["aprs_is"]["server"] = aprs_is.server;
data["aprs_is"]["port"] = aprs_is.port;
data["aprs_is"]["filter"] = aprs_is.filter;
data["aprs_is"]["messagesToRF"] = aprs_is.messagesToRF;
data["aprs_is"]["objectsToRF"] = aprs_is.objectsToRF;
data["beacon"]["comment"] = beacon.comment;
data["beacon"]["interval"] = beacon.interval;
data["beacon"]["latitude"] = beacon.latitude;
data["beacon"]["longitude"] = beacon.longitude;
data["beacon"]["overlay"] = beacon.overlay;
data["beacon"]["symbol"] = beacon.symbol;
data["beacon"]["path"] = beacon.path;
data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
data["beacon"]["beaconFreq"] = beacon.beaconFreq;
data["beacon"]["statusActive"] = beacon.statusActive;
data["beacon"]["statusPacket"] = beacon.statusPacket;
data["beacon"]["gpsActive"] = beacon.gpsActive;
data["beacon"]["ambiguityLevel"] = beacon.ambiguityLevel;
data["personalNote"] = personalNote;
data["blacklist"] = blacklist;
data["digi"]["mode"] = digi.mode;
data["digi"]["ecoMode"] = digi.ecoMode;
if (digi.ecoMode == 1) data["aprs_is"]["active"] = false;
#if defined(HAS_A7670)
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
#endif
data["digi"]["backupDigiMode"] = digi.backupDigiMode;
data["lora"]["rxActive"] = loramodule.rxActive;
data["lora"]["rxFreq"] = loramodule.rxFreq;
data["lora"]["rxCodingRate4"] = loramodule.rxCodingRate4;
data["lora"]["rxSignalBandwidth"] = loramodule.rxSignalBandwidth;
data["lora"]["txActive"] = loramodule.txActive;
data["lora"]["txFreq"] = loramodule.txFreq;
data["lora"]["txCodingRate4"] = loramodule.txCodingRate4;
data["lora"]["txSignalBandwidth"] = loramodule.txSignalBandwidth;
data["lora"]["power"] = loramodule.power;
int rxSpreadingFactor = loramodule.rxSpreadingFactor;
int txSpreadingFactor = loramodule.txSpreadingFactor;
#if defined(HAS_SX1276) || defined(HAS_SX1278)
const int minSF = 6, maxSF = 12;
#endif
#if defined(HAS_SX1262) || defined(HAS_SX1268)
const int minSF = 5, maxSF = 12;
#endif
#if defined(HAS_LLCC68)
const int minSF = 5, maxSF = 11;
#endif
rxSpreadingFactor = (rxSpreadingFactor < minSF) ? minSF : (rxSpreadingFactor > maxSF) ? maxSF : rxSpreadingFactor;
txSpreadingFactor = (txSpreadingFactor < minSF) ? minSF : (txSpreadingFactor > maxSF) ? maxSF : txSpreadingFactor;
data["lora"]["rxSpreadingFactor"] = rxSpreadingFactor;
data["lora"]["txSpreadingFactor"] = txSpreadingFactor;
data["display"]["alwaysOn"] = display.alwaysOn;
data["display"]["timeout"] = display.timeout;
data["display"]["turn180"] = display.turn180;
data["battery"]["sendInternalVoltage"] = battery.sendInternalVoltage;
data["battery"]["monitorInternalVoltage"] = battery.monitorInternalVoltage;
data["battery"]["internalSleepVoltage"] = battery.internalSleepVoltage;
data["battery"]["sendExternalVoltage"] = battery.sendExternalVoltage;
data["battery"]["monitorExternalVoltage"] = battery.monitorExternalVoltage;
data["battery"]["externalSleepVoltage"] = battery.externalSleepVoltage;
data["battery"]["useExternalI2CSensor"] = battery.useExternalI2CSensor;
data["battery"]["voltageDividerR1"] = battery.voltageDividerR1;
data["battery"]["voltageDividerR2"] = battery.voltageDividerR2;
data["battery"]["externalVoltagePin"] = battery.externalVoltagePin;
data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry;
data["wxsensor"]["active"] = wxsensor.active;
data["wxsensor"]["heightCorrection"] = wxsensor.heightCorrection;
data["wxsensor"]["temperatureCorrection"] = wxsensor.temperatureCorrection;
data["syslog"]["active"] = syslog.active;
data["syslog"]["server"] = syslog.server;
data["syslog"]["port"] = syslog.port;
data["syslog"]["logBeaconOverTCPIP"] = syslog.logBeaconOverTCPIP;
data["tnc"]["enableServer"] = tnc.enableServer;
data["tnc"]["enableSerial"] = tnc.enableSerial;
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
data["tnc"]["aprsBridgeActive"] = tnc.aprsBridgeActive;
data["mqtt"]["active"] = mqtt.active;
data["mqtt"]["server"] = mqtt.server;
data["mqtt"]["topic"] = mqtt.topic;
data["mqtt"]["username"] = mqtt.username;
data["mqtt"]["password"] = mqtt.password;
data["mqtt"]["port"] = mqtt.port;
data["mqtt"]["beaconOverMqtt"] = mqtt.beaconOverMqtt;
data["ota"]["username"] = ota.username;
data["ota"]["password"] = ota.password;
data["webadmin"]["active"] = webadmin.active;
data["webadmin"]["username"] = webadmin.username;
data["webadmin"]["password"] = webadmin.password;
data["remoteManagement"]["managers"] = remoteManagement.managers;
data["remoteManagement"]["rfOnly"] = remoteManagement.rfOnly;
data["ntp"]["server"] = ntp.server;
data["ntp"]["gmtCorrection"] = ntp.gmtCorrection;
data["other"]["rebootMode"] = rebootMode;
data["other"]["rebootModeTime"] = rebootModeTime;
data["other"]["rememberStationTime"] = rememberStationTime;
serializeJson(data, configFile);
configFile.close();
return true;
} catch (...) {
Serial.println("Error: Exception occurred while saving config");
configFile.close();
return false;
} }
data["wifi"]["autoAP"]["password"] = wifiAutoAP.password;
data["wifi"]["autoAP"]["timeout"] = wifiAutoAP.timeout;
data["callsign"] = callsign;
data["aprs_is"]["active"] = aprs_is.active;
data["aprs_is"]["passcode"] = aprs_is.passcode;
data["aprs_is"]["server"] = aprs_is.server;
data["aprs_is"]["port"] = aprs_is.port;
data["aprs_is"]["filter"] = aprs_is.filter;
data["aprs_is"]["messagesToRF"] = aprs_is.messagesToRF;
data["aprs_is"]["objectsToRF"] = aprs_is.objectsToRF;
data["beacon"]["comment"] = beacon.comment;
data["beacon"]["interval"] = beacon.interval;
data["beacon"]["latitude"] = beacon.latitude;
data["beacon"]["longitude"] = beacon.longitude;
data["beacon"]["overlay"] = beacon.overlay;
data["beacon"]["symbol"] = beacon.symbol;
data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
data["beacon"]["path"] = beacon.path;
data["beacon"]["statusActive"] = beacon.statusActive;
data["beacon"]["statusPacket"] = beacon.statusPacket;
data["beacon"]["gpsActive"] = beacon.gpsActive;
data["beacon"]["gpsAmbiguity"] = beacon.gpsAmbiguity;
data["digi"]["mode"] = digi.mode;
data["digi"]["ecoMode"] = digi.ecoMode;
#if defined(HAS_A7670)
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
#endif
data["lora"]["rxFreq"] = loramodule.rxFreq;
data["lora"]["txFreq"] = loramodule.txFreq;
data["lora"]["spreadingFactor"] = loramodule.spreadingFactor;
data["lora"]["signalBandwidth"] = loramodule.signalBandwidth;
data["lora"]["codingRate4"] = loramodule.codingRate4;
data["lora"]["power"] = loramodule.power;
data["lora"]["txActive"] = loramodule.txActive;
data["lora"]["rxActive"] = loramodule.rxActive;
data["display"]["alwaysOn"] = display.alwaysOn;
data["display"]["timeout"] = display.timeout;
data["display"]["turn180"] = display.turn180;
data["battery"]["sendInternalVoltage"] = battery.sendInternalVoltage;
data["battery"]["monitorInternalVoltage"] = battery.monitorInternalVoltage;
data["battery"]["internalSleepVoltage"] = battery.internalSleepVoltage;
data["battery"]["sendExternalVoltage"] = battery.sendExternalVoltage;
data["battery"]["externalVoltagePin"] = battery.externalVoltagePin;
data["battery"]["monitorExternalVoltage"] = battery.monitorExternalVoltage;
data["battery"]["externalSleepVoltage"] = battery.externalSleepVoltage;
data["battery"]["voltageDividerR1"] = battery.voltageDividerR1;
data["battery"]["voltageDividerR2"] = battery.voltageDividerR2;
data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry;
data["wxsensor"]["active"] = wxsensor.active;
data["wxsensor"]["heightCorrection"] = wxsensor.heightCorrection;
data["wxsensor"]["temperatureCorrection"] = wxsensor.temperatureCorrection;
data["syslog"]["active"] = syslog.active;
data["syslog"]["server"] = syslog.server;
data["syslog"]["port"] = syslog.port;
data["syslog"]["logBeaconOverTCPIP"] = syslog.logBeaconOverTCPIP;
data["tnc"]["enableServer"] = tnc.enableServer;
data["tnc"]["enableSerial"] = tnc.enableSerial;
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
data["other"]["rebootMode"] = rebootMode;
data["other"]["rebootModeTime"] = rebootModeTime;
data["ota"]["username"] = ota.username;
data["ota"]["password"] = ota.password;
data["other"]["rememberStationTime"] = rememberStationTime;
data["other"]["backupDigiMode"] = backupDigiMode;
data["personalNote"] = personalNote;
data["blacklist"] = blacklist;
data["webadmin"]["active"] = webadmin.active;
data["webadmin"]["username"] = webadmin.username;
data["webadmin"]["password"] = webadmin.password;
data["ntp"]["gmtCorrection"] = ntp.gmtCorrection;
data["remoteManagement"]["managers"] = remoteManagement.managers;
data["remoteManagement"]["rfOnly"] = remoteManagement.rfOnly;
serializeJson(data, configFile);
configFile.close();
Serial.println("Config saved");
delay(200);
} }
bool Configuration::readFile() { bool Configuration::readFile() {
Serial.println("Reading config.."); Serial.println("Reading config..");
File configFile = SPIFFS.open("/igate_conf.json", "r"); File configFile = SPIFFS.open("/igate_conf.json", "r");
if (configFile) { if (configFile) {
bool needsRewrite = false; StaticJsonDocument<2560> data;
StaticJsonDocument<3584> data;
DeserializationError error = deserializeJson(data, configFile); DeserializationError error = deserializeJson(data, configFile);
if (error) { if (error) {
@@ -211,48 +166,12 @@ bool Configuration::readFile() {
wifiAPs.push_back(wifiap); wifiAPs.push_back(wifiap);
} }
if (!data["other"].containsKey("startupDelay")) needsRewrite = true;
startupDelay = data["other"]["startupDelay"] | 0;
if (!data["wifi"]["autoAP"].containsKey("password") ||
!data["wifi"]["autoAP"].containsKey("timeout")) needsRewrite = true;
wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890"; wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890";
wifiAutoAP.timeout = data["wifi"]["autoAP"]["timeout"] | 10; wifiAutoAP.timeout = data["wifi"]["autoAP"]["timeout"] | 10;
if (!data.containsKey("callsign")) needsRewrite = true;
callsign = data["callsign"] | "NOCALL-10"; callsign = data["callsign"] | "NOCALL-10";
if (!data.containsKey("tacticalCallsign")) needsRewrite = true; rememberStationTime = data["other"]["rememberStationTime"] | 30;
tacticalCallsign = data["tacticalCallsign"] | "";
if (!data["aprs_is"].containsKey("active") ||
!data["aprs_is"].containsKey("passcode") ||
!data["aprs_is"].containsKey("server") ||
!data["aprs_is"].containsKey("port") ||
!data["aprs_is"].containsKey("filter") ||
!data["aprs_is"].containsKey("messagesToRF") ||
!data["aprs_is"].containsKey("objectsToRF")) needsRewrite = true;
aprs_is.active = data["aprs_is"]["active"] | false;
aprs_is.passcode = data["aprs_is"]["passcode"] | "XYZWV";
aprs_is.server = data["aprs_is"]["server"] | "rotate.aprs2.net";
aprs_is.port = data["aprs_is"]["port"] | 14580;
aprs_is.filter = data["aprs_is"]["filter"] | "m/10";
aprs_is.messagesToRF = data["aprs_is"]["messagesToRF"] | false;
aprs_is.objectsToRF = data["aprs_is"]["objectsToRF"] | false;
if (!data["beacon"].containsKey("latitude") ||
!data["beacon"].containsKey("longitude") ||
!data["beacon"].containsKey("comment") ||
!data["beacon"].containsKey("interval") ||
!data["beacon"].containsKey("overlay") ||
!data["beacon"].containsKey("symbol") ||
!data["beacon"].containsKey("path") ||
!data["beacon"].containsKey("sendViaAPRSIS") ||
!data["beacon"].containsKey("sendViaRF") ||
!data["beacon"].containsKey("beaconFreq") ||
!data["beacon"].containsKey("statusActive") ||
!data["beacon"].containsKey("statusPacket") ||
!data["beacon"].containsKey("gpsActive") ||
!data["beacon"].containsKey("ambiguityLevel")) needsRewrite = true;
beacon.latitude = data["beacon"]["latitude"] | 0.0; beacon.latitude = data["beacon"]["latitude"] | 0.0;
beacon.longitude = data["beacon"]["longitude"] | 0.0; beacon.longitude = data["beacon"]["longitude"] | 0.0;
beacon.comment = data["beacon"]["comment"] | "LoRa APRS"; beacon.comment = data["beacon"]["comment"] | "LoRa APRS";
@@ -262,156 +181,88 @@ bool Configuration::readFile() {
beacon.path = data["beacon"]["path"] | "WIDE1-1"; beacon.path = data["beacon"]["path"] | "WIDE1-1";
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false; beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false;
beacon.sendViaRF = data["beacon"]["sendViaRF"] | false; beacon.sendViaRF = data["beacon"]["sendViaRF"] | false;
beacon.beaconFreq = data["beacon"]["beaconFreq"] | 1;
beacon.statusActive = data["beacon"]["statusActive"] | false; beacon.statusActive = data["beacon"]["statusActive"] | false;
beacon.statusPacket = data["beacon"]["statusPacket"] | ""; beacon.statusPacket = data["beacon"]["statusPacket"] | "";
beacon.gpsActive = data["beacon"]["gpsActive"] | false; beacon.gpsActive = data["beacon"]["gpsActive"] | false;
beacon.ambiguityLevel = data["beacon"]["ambiguityLevel"] | 0; beacon.gpsAmbiguity = data["beacon"]["gpsAmbiguity"] | false;
if (!data.containsKey("personalNote")) needsRewrite = true; aprs_is.active = data["aprs_is"]["active"] | false;
personalNote = data["personalNote"] | "personal note here"; aprs_is.passcode = data["aprs_is"]["passcode"] | "XYZWV";
aprs_is.server = data["aprs_is"]["server"] | "rotate.aprs2.net";
aprs_is.port = data["aprs_is"]["port"] | 14580;
aprs_is.filter = data["aprs_is"]["filter"] | "m/10";
aprs_is.messagesToRF = data["aprs_is"]["messagesToRF"] | false;
aprs_is.objectsToRF = data["aprs_is"]["objectsToRF"] | false;
if (!data.containsKey("blacklist")) needsRewrite = true;
blacklist = data["blacklist"] | "station callsign";
if (!data["digi"].containsKey("mode") ||
!data["digi"].containsKey("ecoMode") ||
!data["digi"].containsKey("backupDigiMode")) needsRewrite = true;
digi.mode = data["digi"]["mode"] | 0; digi.mode = data["digi"]["mode"] | 0;
digi.ecoMode = data["digi"]["ecoMode"] | 0; digi.ecoMode = data["digi"]["ecoMode"] | 0;
if (digi.ecoMode == 1) shouldSleepStop = false; if (digi.ecoMode == 1) shouldSleepStop = false;
#if defined(HAS_A7670) #if defined(HAS_A7670)
if (digi.ecoMode == 1) digi.ecoMode = 2; if (digi.ecoMode == 1) digi.ecoMode = 2;
#endif #endif
digi.backupDigiMode = data["digi"]["backupDigiMode"] | false;
if (!data["lora"].containsKey("rxActive") ||
!data["lora"].containsKey("rxFreq") ||
!data["lora"].containsKey("rxSpreadingFactor") ||
!data["lora"].containsKey("rxCodingRate4") ||
!data["lora"].containsKey("rxSignalBandwidth") ||
!data["lora"].containsKey("txActive") ||
!data["lora"].containsKey("txFreq") ||
!data["lora"].containsKey("txSpreadingFactor") ||
!data["lora"].containsKey("txCodingRate4") ||
!data["lora"].containsKey("txSignalBandwidth") ||
!data["lora"].containsKey("power")) needsRewrite = true;
loramodule.rxActive = data["lora"]["rxActive"] | true;
loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000;
loramodule.rxSpreadingFactor = data["lora"]["rxSpreadingFactor"] | 12;
loramodule.rxCodingRate4 = data["lora"]["rxCodingRate4"] | 5;
loramodule.rxSignalBandwidth = data["lora"]["rxSignalBandwidth"] | 125000;
loramodule.txActive = data["lora"]["txActive"] | false;
loramodule.txFreq = data["lora"]["txFreq"] | 433775000; loramodule.txFreq = data["lora"]["txFreq"] | 433775000;
loramodule.txSpreadingFactor = data["lora"]["txSpreadingFactor"] | 12; loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000;
loramodule.txCodingRate4 = data["lora"]["txCodingRate4"] | 5; loramodule.spreadingFactor = data["lora"]["spreadingFactor"] | 12;
loramodule.txSignalBandwidth = data["lora"]["txSignalBandwidth"] | 125000; loramodule.signalBandwidth = data["lora"]["signalBandwidth"] | 125000;
loramodule.codingRate4 = data["lora"]["codingRate4"] | 5;
loramodule.power = data["lora"]["power"] | 20; loramodule.power = data["lora"]["power"] | 20;
loramodule.txActive = data["lora"]["txActive"] | false;
loramodule.rxActive = data["lora"]["rxActive"] | false;
if (!data["display"].containsKey("alwaysOn") || display.alwaysOn = data["display"]["alwaysOn"] | true;
!data["display"].containsKey("timeout") ||
!data["display"].containsKey("turn180")) needsRewrite = true;
#ifdef HAS_EPAPER
display.alwaysOn = true;
#else
display.alwaysOn = data["display"]["alwaysOn"] | true;
#endif
display.timeout = data["display"]["timeout"] | 4; display.timeout = data["display"]["timeout"] | 4;
display.turn180 = data["display"]["turn180"] | false; display.turn180 = data["display"]["turn180"] | false;
if (!data["battery"].containsKey("sendInternalVoltage") ||
!data["battery"].containsKey("monitorInternalVoltage") ||
!data["battery"].containsKey("internalSleepVoltage") ||
!data["battery"].containsKey("sendExternalVoltage") ||
!data["battery"].containsKey("monitorExternalVoltage") ||
!data["battery"].containsKey("externalSleepVoltage") ||
!data["battery"].containsKey("useExternalI2CSensor") ||
!data["battery"].containsKey("voltageDividerR1") ||
!data["battery"].containsKey("voltageDividerR2") ||
!data["battery"].containsKey("externalVoltagePin") ||
!data["battery"].containsKey("sendVoltageAsTelemetry")) needsRewrite = true;
battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"] | false; battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"] | false;
battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false; battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false;
battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 2.9; battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 2.9;
battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false; battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false;
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
battery.monitorExternalVoltage = data["battery"]["monitorExternalVoltage"] | false; battery.monitorExternalVoltage = data["battery"]["monitorExternalVoltage"] | false;
battery.externalSleepVoltage = data["battery"]["externalSleepVoltage"] | 10.9; battery.externalSleepVoltage = data["battery"]["externalSleepVoltage"] | 10.9;
battery.useExternalI2CSensor = data["battery"]["useExternalI2CSensor"] | false;
battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0; battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0;
battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0; battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0;
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | false; battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | false;
if (!data["wxsensor"].containsKey("active") ||
!data["wxsensor"].containsKey("heightCorrection") ||
!data["wxsensor"].containsKey("temperatureCorrection")) needsRewrite = true;
wxsensor.active = data["wxsensor"]["active"] | false; wxsensor.active = data["wxsensor"]["active"] | false;
wxsensor.heightCorrection = data["wxsensor"]["heightCorrection"] | 0; wxsensor.heightCorrection = data["wxsensor"]["heightCorrection"] | 0;
wxsensor.temperatureCorrection = data["wxsensor"]["temperatureCorrection"] | 0.0; wxsensor.temperatureCorrection = data["wxsensor"]["temperatureCorrection"] | 0.0;
if (!data["syslog"].containsKey("active") ||
!data["syslog"].containsKey("server") ||
!data["syslog"].containsKey("port") ||
!data["syslog"].containsKey("logBeaconOverTCPIP")) needsRewrite = true;
syslog.active = data["syslog"]["active"] | false; syslog.active = data["syslog"]["active"] | false;
syslog.server = data["syslog"]["server"] | "lora.link9.net"; syslog.server = data["syslog"]["server"] | "lora.link9.net";
syslog.port = data["syslog"]["port"] | 1514; syslog.port = data["syslog"]["port"] | 1514;
syslog.logBeaconOverTCPIP = data["syslog"]["logBeaconOverTCPIP"] | false; syslog.logBeaconOverTCPIP = data["syslog"]["logBeaconOverTCPIP"] | false;
if (!data["tnc"].containsKey("enableServer") ||
!data["tnc"].containsKey("enableSerial") ||
!data["tnc"].containsKey("acceptOwn") ||
!data["tnc"].containsKey("aprsBridgeActive")) needsRewrite = true;
tnc.enableServer = data["tnc"]["enableServer"] | false; tnc.enableServer = data["tnc"]["enableServer"] | false;
tnc.enableSerial = data["tnc"]["enableSerial"] | false; tnc.enableSerial = data["tnc"]["enableSerial"] | false;
tnc.acceptOwn = data["tnc"]["acceptOwn"] | false; tnc.acceptOwn = data["tnc"]["acceptOwn"] | false;
tnc.aprsBridgeActive = data["tnc"]["aprsBridgeActive"] | false;
if (!data["mqtt"].containsKey("active") ||
!data["mqtt"].containsKey("server") ||
!data["mqtt"].containsKey("topic") ||
!data["mqtt"].containsKey("username") ||
!data["mqtt"].containsKey("password") ||
!data["mqtt"].containsKey("port") ||
!data["mqtt"].containsKey("beaconOverMqtt")) needsRewrite = true;
mqtt.active = data["mqtt"]["active"] | false;
mqtt.server = data["mqtt"]["server"] | "";
mqtt.topic = data["mqtt"]["topic"] | "aprs-igate";
mqtt.username = data["mqtt"]["username"] | "";
mqtt.password = data["mqtt"]["password"] | "";
mqtt.port = data["mqtt"]["port"] | 1883;
mqtt.beaconOverMqtt = data["mqtt"]["beaconOverMqtt"] | false;
if (!data["ota"].containsKey("username") ||
!data["ota"].containsKey("password")) needsRewrite = true;
ota.username = data["ota"]["username"] | ""; ota.username = data["ota"]["username"] | "";
ota.password = data["ota"]["password"] | ""; ota.password = data["ota"]["password"] | "";
if (!data["webadmin"].containsKey("active") ||
!data["webadmin"].containsKey("username") ||
!data["webadmin"].containsKey("password")) needsRewrite = true;
webadmin.active = data["webadmin"]["active"] | false; webadmin.active = data["webadmin"]["active"] | false;
webadmin.username = data["webadmin"]["username"] | "admin"; webadmin.username = data["webadmin"]["username"] | "admin";
webadmin.password = data["webadmin"]["password"] | ""; webadmin.password = data["webadmin"]["password"] | "";
if (!data["remoteManagement"].containsKey("managers") ||
!data["remoteManagement"].containsKey("rfOnly")) needsRewrite = true;
remoteManagement.managers = data["remoteManagement"]["managers"] | "";
remoteManagement.rfOnly = data["remoteManagement"]["rfOnly"] | true;
if (!data["ntp"].containsKey("server") ||
!data["ntp"].containsKey("gmtCorrection")) needsRewrite = true;
ntp.server = data["ntp"]["server"] | "pool.ntp.org";
ntp.gmtCorrection = data["ntp"]["gmtCorrection"] | 0.0; ntp.gmtCorrection = data["ntp"]["gmtCorrection"] | 0.0;
if (!data["other"].containsKey("rebootMode") || backupDigiMode = data["other"]["backupDigiMode"] | false;
!data["other"].containsKey("rebootModeTime")) needsRewrite = true;
rebootMode = data["other"]["rebootMode"] | false; rebootMode = data["other"]["rebootMode"] | false;
rebootModeTime = data["other"]["rebootModeTime"] | 6; rebootModeTime = data["other"]["rebootModeTime"] | 6;
if (!data["other"].containsKey("rememberStationTime")) needsRewrite = true; personalNote = data["personalNote"] | "personal note here";
rememberStationTime = data["other"]["rememberStationTime"] | 30;
blacklist = data["blacklist"] | "station callsign";
remoteManagement.managers = data["remoteManagement"]["managers"] | "";
remoteManagement.rfOnly = data["remoteManagement"]["rfOnly"] | true;
if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP
WiFi_AP wifiap; WiFi_AP wifiap;
@@ -421,13 +272,6 @@ bool Configuration::readFile() {
wifiAPs.push_back(wifiap); wifiAPs.push_back(wifiap);
} }
configFile.close(); configFile.close();
if (needsRewrite) {
Serial.println("Config JSON incomplete, rewriting...");
writeFile();
delay(1000);
ESP.restart();
}
Serial.println("Config read successfuly"); Serial.println("Config read successfuly");
return true; return true;
} else { } else {
@@ -435,8 +279,8 @@ bool Configuration::readFile() {
return false; return false;
} }
} }
void Configuration::setDefaultValues() { void Configuration::init() {
WiFi_AP wifiap; WiFi_AP wifiap;
wifiap.ssid = ""; wifiap.ssid = "";
@@ -444,13 +288,33 @@ void Configuration::setDefaultValues() {
wifiAPs.push_back(wifiap); wifiAPs.push_back(wifiap);
startupDelay = 0;
wifiAutoAP.password = "1234567890"; wifiAutoAP.password = "1234567890";
wifiAutoAP.timeout = 10; wifiAutoAP.timeout = 10;
callsign = "N0CALL-10"; callsign = "N0CALL-10";
tacticalCallsign = "";
beacon.comment = "LoRa APRS";
beacon.latitude = 0.0;
beacon.longitude = 0.0;
beacon.interval = 15;
beacon.overlay = "L";
beacon.symbol = "a";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.path = "WIDE1-1";
beacon.statusActive = false;
beacon.statusPacket = "";
beacon.gpsActive = false;
beacon.gpsAmbiguity = false;
digi.mode = 0;
digi.ecoMode = 0;
tnc.enableServer = false;
tnc.enableSerial = false;
tnc.acceptOwn = false;
aprs_is.active = false; aprs_is.active = false;
aprs_is.passcode = "XYZVW"; aprs_is.passcode = "XYZVW";
@@ -460,103 +324,65 @@ void Configuration::setDefaultValues() {
aprs_is.messagesToRF = false; aprs_is.messagesToRF = false;
aprs_is.objectsToRF = false; aprs_is.objectsToRF = false;
beacon.comment = "LoRa APRS";
beacon.latitude = 0.0;
beacon.longitude = 0.0;
beacon.interval = 15;
beacon.overlay = "L";
beacon.symbol = "a";
beacon.path = "WIDE1-1";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.beaconFreq = 1;
beacon.statusActive = false;
beacon.statusPacket = "";
beacon.gpsActive = false;
beacon.ambiguityLevel = 0;
personalNote = "";
blacklist = "";
digi.mode = 0;
digi.ecoMode = 0;
digi.backupDigiMode = false;
loramodule.rxActive = true;
loramodule.rxFreq = 433775000;
loramodule.rxSpreadingFactor = 12;
loramodule.rxCodingRate4 = 5;
loramodule.rxSignalBandwidth = 125000;
loramodule.txActive = false;
loramodule.txFreq = 433775000; loramodule.txFreq = 433775000;
loramodule.txSpreadingFactor = 12; loramodule.rxFreq = 433775000;
loramodule.txCodingRate4 = 5; loramodule.spreadingFactor = 12;
loramodule.txSignalBandwidth = 125000; loramodule.signalBandwidth = 125000;
loramodule.codingRate4 = 5;
loramodule.power = 20; loramodule.power = 20;
loramodule.txActive = false;
loramodule.rxActive = true;
display.alwaysOn = true; display.alwaysOn = true;
display.timeout = 4; display.timeout = 4;
display.turn180 = false; display.turn180 = false;
battery.sendInternalVoltage = false;
battery.monitorInternalVoltage = false;
battery.internalSleepVoltage = 2.9;
battery.sendExternalVoltage = false;
battery.monitorExternalVoltage = false;
battery.externalSleepVoltage = 10.9;
battery.useExternalI2CSensor = false;
battery.voltageDividerR1 = 100.0;
battery.voltageDividerR2 = 27.0;
battery.externalVoltagePin = 34;
battery.sendVoltageAsTelemetry = false;
wxsensor.active = false;
wxsensor.heightCorrection = 0;
wxsensor.temperatureCorrection = 0.0;
syslog.active = false; syslog.active = false;
syslog.server = "lora.link9.net"; syslog.server = "lora.link9.net";
syslog.port = 1514; syslog.port = 1514;
syslog.logBeaconOverTCPIP = false; syslog.logBeaconOverTCPIP = false;
tnc.enableServer = false; wxsensor.active = false;
tnc.enableSerial = false; wxsensor.heightCorrection = 0;
tnc.acceptOwn = false; wxsensor.temperatureCorrection = 0.0;
tnc.aprsBridgeActive = false;
mqtt.active = false;
mqtt.server = "";
mqtt.topic = "aprs-igate";
mqtt.username = "";
mqtt.password = "";
mqtt.port = 1883;
mqtt.beaconOverMqtt = false;
ota.username = ""; ota.username = "";
ota.password = ""; ota.password = "";
rememberStationTime = 30;
battery.sendInternalVoltage = false;
battery.monitorInternalVoltage = false;
battery.internalSleepVoltage = 2.9;
battery.sendExternalVoltage = false;
battery.externalVoltagePin = 34;
battery.monitorExternalVoltage = false;
battery.externalSleepVoltage = 10.9;
battery.voltageDividerR1 = 100.0;
battery.voltageDividerR2 = 27.0;
battery.sendVoltageAsTelemetry = false;
backupDigiMode = false;
rebootMode = false;
rebootModeTime = 0;
personalNote = "";
blacklist = "";
webadmin.active = false; webadmin.active = false;
webadmin.username = "admin"; webadmin.username = "admin";
webadmin.password = ""; webadmin.password = "";
ntp.gmtCorrection = 0.0;
remoteManagement.managers = ""; remoteManagement.managers = "";
remoteManagement.rfOnly = true; remoteManagement.rfOnly = true;
ntp.server = "pool.ntp.org"; Serial.println("All is Written!");
ntp.gmtCorrection = 0.0;
rebootMode = false;
rebootModeTime = 0;
rememberStationTime = 30;
Serial.println("New Data Created... All is Written!");
} }
Configuration::Configuration() { Configuration::Configuration() {
@@ -569,9 +395,8 @@ Configuration::Configuration() {
bool exists = SPIFFS.exists("/igate_conf.json"); bool exists = SPIFFS.exists("/igate_conf.json");
if (!exists) { if (!exists) {
setDefaultValues(); init();
writeFile(); writeFile();
delay(1000);
ESP.restart(); ESP.restart();
} }
+106 -103
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -38,138 +38,141 @@ extern String fourthLine;
extern String fifthLine; extern String fifthLine;
extern String sixthLine; extern String sixthLine;
extern String seventhLine; extern String seventhLine;
extern bool backupDigiMode; extern bool backUpDigiMode;
namespace DIGI_Utils { namespace DIGI_Utils {
String cleanPathAsterisks(String path) {
String terms[] = {",WIDE1*", ",WIDE2*", "*"};
for (String term : terms) {
int index = path.indexOf(term);
if (index != -1) path.remove(index, term.length()); // less memory than: tempPath.replace("*", "");
}
return path;
}
String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq) { String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq) {
String stationCallsign = (Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign);
String suffix = thirdParty ? ":}" : ":";
int suffixIndex = packet.indexOf(suffix);
String packetToRepeat;
if (!crossFreq) { if (!crossFreq) {
int digiMode = Config.digi.mode; String packetToRepeat = packet.substring(0, packet.indexOf(",") + 1);
String tempPath = path; String tempPath = path;
if (tempPath.indexOf("WIDE1-1") != -1 && (digiMode == 2 || digiMode == 3)) { // WIDE1-1 if (path.indexOf("WIDE1-1") != -1 && (Config.digi.mode == 2 || Config.digi.mode == 3)) {
if (tempPath.indexOf("*") != -1 ) return ""; // "*" shouldn't be in WIDE1-1 (only) type of packet tempPath.replace("WIDE1-1", Config.callsign + "*");
tempPath.replace("WIDE1-1", stationCallsign + "*"); } else if (path.indexOf("WIDE2-") != -1 && Config.digi.mode == 3) {
} else if (tempPath.indexOf("WIDE2-") != -1 && digiMode == 3) { // WIDE2-n Digipeater if (path.indexOf(",WIDE1*") != -1) {
tempPath = cleanPathAsterisks(path); tempPath.remove(path.indexOf(",WIDE1*"), 7);
if (tempPath.indexOf("WIDE2-1") != -1) { }
tempPath.replace("WIDE2-1", stationCallsign + "*"); if (path.indexOf("*") != -1) {
} else if (tempPath.indexOf("WIDE2-2") != -1) { tempPath.remove(path.indexOf("*"), 1);
tempPath.replace("WIDE2-2", stationCallsign + "*,WIDE2-1"); }
if (path.indexOf("WIDE2-1") != -1) {
tempPath.replace("WIDE2-1", Config.callsign + "*");
} else if (path.indexOf("WIDE2-2") != -1) {
tempPath.replace("WIDE2-2", Config.callsign + "*,WIDE2-1");
} else { } else {
return ""; return "";
} }
} }
packetToRepeat = packet.substring(0, packet.indexOf(",") + 1);
packetToRepeat += tempPath; packetToRepeat += tempPath;
if (thirdParty) {
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":}")));
} else {
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":")));
}
return packetToRepeat;
} else { // CrossFreq Digipeater } else { // CrossFreq Digipeater
packetToRepeat = cleanPathAsterisks(packet.substring(0, suffixIndex)); String suffix = thirdParty ? ":}" : ":";
if (packetToRepeat.indexOf(stationCallsign) != -1) return ""; // stationCallsign shouldn't be in path String packetToRepeat = packet.substring(0, packet.indexOf(suffix));
String terms[] = {",WIDE1*", ",WIDE2*", "*"};
for (String term : terms) {
int index = packetToRepeat.indexOf(term);
if (index != -1) {
packetToRepeat.remove(index, term.length());
}
}
packetToRepeat += ","; packetToRepeat += ",";
packetToRepeat += stationCallsign; packetToRepeat += Config.callsign;
packetToRepeat += "*"; packetToRepeat += "*";
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(suffix)));
return packetToRepeat;
} }
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(suffixIndex));
return packetToRepeat;
} }
String generateDigipeatedPacket(const String& packet, bool thirdParty){ String generateDigipeatedPacket(const String& packet, bool thirdParty){
String temp; String temp;
if (thirdParty) { // only header is used if (thirdParty) { // only header is used
const String& header = packet.substring(0, packet.indexOf(":}")); const String& header = packet.substring(0, packet.indexOf(":}"));
temp = header.substring(header.indexOf(">") + 1); temp = header.substring(header.indexOf(">") + 1);
} else { } else {
temp = packet.substring(packet.indexOf(">") + 1, packet.indexOf(":")); temp = packet.substring(packet.indexOf(">") + 1, packet.indexOf(":"));
} }
int commaIndex = temp.indexOf(","); if (temp.indexOf(",") > 2) { // checks for path
int digiMode = Config.digi.mode; const String& path = temp.substring(temp.indexOf(",") + 1); // after tocall
bool crossFreq = abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000; // CrossFreq Digi if (Config.digi.mode == 2 || backUpDigiMode) {
if (path.indexOf("WIDE1-1") != - 1) {
if (commaIndex > 2) { // "path" found return buildPacket(path, packet, thirdParty, false);
const String& path = temp.substring(commaIndex + 1); } else if (path.indexOf("WIDE1-1") == -1 && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) { // CrossFreq Digi
if (digiMode == 2 || backupDigiMode) { return buildPacket(path, packet, thirdParty, true);
bool hasWide = path.indexOf("WIDE1-1") != -1; } else {
if (hasWide || crossFreq) { return "";
return buildPacket(path, packet, thirdParty, !hasWide);
} }
} else if (Config.digi.mode == 3) {
if (path.indexOf("WIDE1-1") != -1 || path.indexOf("WIDE2-") != -1) {
int wide1Index = path.indexOf("WIDE1-1");
int wide2Index = path.indexOf("WIDE2-");
// WIDE1-1 && WIDE2-n / only WIDE1-1 / only WIDE2-n
if ((wide1Index != -1 && wide2Index != -1 && wide1Index < wide2Index) || (wide1Index != -1 && wide2Index == -1) || (wide1Index == -1 && wide2Index != -1)) {
return buildPacket(path, packet, thirdParty, false);
}
return "";
} else if (path.indexOf("WIDE1-1") == -1 && path.indexOf("WIDE2-") == -1 && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) { // CrossFreq Digi
return buildPacket(path, packet, thirdParty, true);
} else {
return "";
}
} else {
return ""; return "";
} }
if (digiMode == 3) { } else if (temp.indexOf(",") == -1 && (Config.digi.mode == 2 || backUpDigiMode || Config.digi.mode == 3) && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) {
int wide1Index = path.indexOf("WIDE1-1"); return buildPacket("", packet, thirdParty, true);
int wide2Index = path.indexOf("WIDE2-"); } else {
bool hasWide1 = wide1Index != -1;
bool hasWide2 = wide2Index != -1;
if (hasWide1 && hasWide2 && wide2Index < wide1Index) return ""; // check that WIDE1 before WIDE2
if (hasWide1 || hasWide2) return buildPacket(path, packet, thirdParty, false); // regular APRS with WIDEn-N
if (crossFreq) return buildPacket(path, packet, thirdParty, true); // CrossFreq (without WIDE)
return "";
}
return ""; return "";
} }
if (commaIndex == -1 && (digiMode == 2 || backupDigiMode || digiMode == 3) && crossFreq) return buildPacket("", packet, thirdParty, true); // no "path" but is CrossFreq Digi
return "";
} }
void processLoRaPacket(const String& packet) { void processLoRaPacket(const String& packet) {
if (packet.indexOf("NOGATE") >= 0) return; if (packet.indexOf("NOGATE") == -1) {
bool thirdPartyPacket = false;
bool thirdPartyPacket = false; String temp, Sender;
String temp, Sender; int firstColonIndex = packet.indexOf(":");
int firstColonIndex = packet.indexOf(":"); if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] == '}' && packet.indexOf("TCPIP") > 0) { // 3rd Party
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] == '}' && packet.indexOf("TCPIP") > 0) { // 3rd Party thirdPartyPacket = true;
thirdPartyPacket = true; temp = packet.substring(packet.indexOf(":}") + 2);
temp = packet.substring(packet.indexOf(":}") + 2); Sender = temp.substring(0, temp.indexOf(">"));
Sender = temp.substring(0, temp.indexOf(">")); } else {
} else { temp = packet.substring(3);
temp = packet.substring(3); Sender = packet.substring(3, packet.indexOf(">"));
Sender = packet.substring(3, packet.indexOf(">")); }
} if (Sender != Config.callsign) { // Avoid listening to own packets
if (!thirdPartyPacket && !Utils::checkValidCallsign(Sender)) {
String stationCallsign = Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign; return;
if (Sender == stationCallsign) return; // Avoid listening to self packets }
if (!thirdPartyPacket && Config.tacticalCallsign == "" && !Utils::callsignIsValid(Sender)) return; // No thirdParty + no tactical y no valid callsign if (STATION_Utils::check25SegBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) {
STATION_Utils::updateLastHeard(Sender);
if (STATION_Utils::isIn25SegHashBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) return; Utils::typeOfPacket(temp, 2); // Digi
bool queryMessage = false;
STATION_Utils::updateLastHeard(Sender); if (temp.indexOf("::") > 10) { // it's a message
Utils::typeOfPacket(temp, 2); // Digi String AddresseeAndMessage = temp.substring(temp.indexOf("::") + 2);
bool queryMessage = false; String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
int doubleColonIndex = temp.indexOf("::"); Addressee.trim();
if (doubleColonIndex > 10) { // it's a message if (Addressee == Config.callsign) { // it's a message for me!
String AddresseeAndMessage = temp.substring(doubleColonIndex + 2); queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":")); }
Addressee.trim(); }
if (Addressee == stationCallsign) { // it's a message for me! if (!queryMessage) {
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket); String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
if (loraPacket != "") {
STATION_Utils::addToOutputPacketBuffer(loraPacket);
if (Config.digi.ecoMode != 1) displayToggle(true);
lastScreenOn = millis();
}
}
}
} }
}
if (queryMessage) return; // answer should not be repeated.
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
if (loraPacket != "") {
STATION_Utils::addToOutputPacketBuffer(loraPacket);
if (Config.digi.ecoMode != 1) displayToggle(true);
lastScreenOn = millis();
} }
} }
+23 -38
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -26,7 +26,7 @@
#ifdef HAS_TFT #ifdef HAS_TFT
#include <TFT_eSPI.h> #include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI(); TFT_eSPI tft = TFT_eSPI();
TFT_eSprite sprite = TFT_eSprite(&tft); TFT_eSprite sprite = TFT_eSprite(&tft);
#ifdef HELTEC_WIRELESS_TRACKER #ifdef HELTEC_WIRELESS_TRACKER
@@ -44,20 +44,11 @@
#ifdef HAS_EPAPER #ifdef HAS_EPAPER
#include <heltec-eink-modules.h> #include <heltec-eink-modules.h>
#include "Fonts/FreeSansBold9pt7b.h" #include "Fonts/FreeSansBold9pt7b.h"
#ifdef HELTEC_WP_V1 EInkDisplay_WirelessPaperV1_1 display;
EInkDisplay_WirelessPaperV1_1 display;
#endif
#ifdef HELTEC_WP_V1_2
EInkDisplay_WirelessPaperV1_2 display;
#endif
#ifdef HELTEC_VM_E290
EInkDisplay_VisionMasterE290 display;
#endif
String lastEpaperText; String lastEpaperText;
#else #else
#include <Adafruit_GFX.h> #include <Adafruit_GFX.h>
#ifdef HAS_SH1106 #if defined(TTGO_T_Beam_S3_SUPREME_V3)
#include <Adafruit_SH110X.h> #include <Adafruit_SH110X.h>
Adafruit_SH1106G display(128, 64, &Wire, OLED_RST); Adafruit_SH1106G display(128, 64, &Wire, OLED_RST);
#else #else
@@ -92,22 +83,14 @@ void displaySetup() {
tft.setTextFont(0); tft.setTextFont(0);
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS) #if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
sprite.createSprite(320, 240); sprite.createSprite(320,240);
#else #else
sprite.createSprite(160, 80); sprite.createSprite(160,80);
#endif #endif
#else #else
#ifdef HAS_EPAPER #ifdef HAS_EPAPER
display.landscape(); display.landscape();
display.printCenter("LoRa APRS iGate Initialising..."); display.printCenter("LoRa APRS iGate Initialising...");
if (Config.display.turn180) {
#if defined(HELTEC_VM_E290) || defined(HELTEC_WP_V1)
display.setRotation(3);
#endif
#if defined(HELTEC_WP_V1_2)
display.setRotation(1);
#endif
}
display.update(); display.update();
#else #else
#ifdef OLED_DISPLAY_HAS_RST_PIN #ifdef OLED_DISPLAY_HAS_RST_PIN
@@ -117,8 +100,9 @@ void displaySetup() {
digitalWrite(OLED_RST, HIGH); digitalWrite(OLED_RST, HIGH);
#endif #endif
#ifdef HAS_SH1106 #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();
@@ -157,7 +141,7 @@ void displayToggle(bool toggle) {
display.printCenter("EPAPER Display Disabled by toggle..."); display.printCenter("EPAPER Display Disabled by toggle...");
display.update(); display.update();
#else #else
#ifdef HAS_SH1106 #if defined(TTGO_T_Beam_S3_SUPREME_V3)
if (displayFound) display.oled_command(SH110X_DISPLAYON); if (displayFound) display.oled_command(SH110X_DISPLAYON);
#else #else
if (displayFound) display.ssd1306_command(SSD1306_DISPLAYON); if (displayFound) display.ssd1306_command(SSD1306_DISPLAYON);
@@ -169,14 +153,15 @@ void displayToggle(bool toggle) {
digitalWrite(TFT_BL, LOW); digitalWrite(TFT_BL, LOW);
#else #else
#ifdef HAS_EPAPER #ifdef HAS_EPAPER
display.printCenter("Enabled EPAPER Display...");
display.update(); display.update();
#else #else
#ifdef HAS_SH1106 #if defined(TTGO_T_Beam_S3_SUPREME_V3)
if (displayFound) display.oled_command(SH110X_DISPLAYOFF); if (displayFound) display.oled_command(SH110X_DISPLAYOFF);
#else #else
if (displayFound) display.ssd1306_command(SSD1306_DISPLAYOFF); if (displayFound) display.ssd1306_command(SSD1306_DISPLAYOFF);
#endif #endif
#endif #endif
#endif #endif
} }
@@ -206,7 +191,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2); sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2);
} }
sprite.pushSprite(0, 0); sprite.pushSprite(0,0);
#else #else
#ifdef HAS_EPAPER #ifdef HAS_EPAPER
display.clearMemory(); display.clearMemory();
@@ -222,7 +207,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
#else #else
if (displayFound) { if (displayFound) {
display.clearDisplay(); display.clearDisplay();
#ifdef HAS_SH1106 #if defined(TTGO_T_Beam_S3_SUPREME_V3)
display.setTextColor(SH110X_WHITE); display.setTextColor(SH110X_WHITE);
#else #else
display.setTextColor(WHITE); display.setTextColor(WHITE);
@@ -234,7 +219,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
display.setCursor(0, 8 + (8 * i)); display.setCursor(0, 8 + (8 * i));
display.println(*lines[i]); display.println(*lines[i]);
} }
#ifdef HAS_SH1106 #if defined(TTGO_T_Beam_S3_SUPREME_V3)
display.setContrast(1); display.setContrast(1);
#else #else
display.ssd1306_command(SSD1306_SETCONTRAST); display.ssd1306_command(SSD1306_SETCONTRAST);
@@ -271,7 +256,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2); sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2);
} }
sprite.pushSprite(0, 0); sprite.pushSprite(0,0);
#else #else
#ifdef HAS_EPAPER #ifdef HAS_EPAPER
lastEpaperText = header + line1 + line2 + line3 + line4 + line5 + line6; lastEpaperText = header + line1 + line2 + line3 + line4 + line5 + line6;
@@ -288,7 +273,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
#else #else
if (displayFound) { if (displayFound) {
display.clearDisplay(); display.clearDisplay();
#ifdef HAS_SH1106 #if defined(TTGO_T_Beam_S3_SUPREME_V3)
display.setTextColor(SH110X_WHITE); display.setTextColor(SH110X_WHITE);
#else #else
display.setTextColor(WHITE); display.setTextColor(WHITE);
@@ -301,7 +286,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
display.setCursor(0, 16 + (8 * i)); display.setCursor(0, 16 + (8 * i));
display.println(*lines[i]); display.println(*lines[i]);
} }
#ifdef HAS_SH1106 #if defined(TTGO_T_Beam_S3_SUPREME_V3)
display.setContrast(1); display.setContrast(1);
#else #else
display.ssd1306_command(SSD1306_SETCONTRAST); display.ssd1306_command(SSD1306_SETCONTRAST);
+148 -92
View File
@@ -1,22 +1,21 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <APRSPacketLib.h>
#include <TinyGPS++.h> #include <TinyGPS++.h>
#include <WiFi.h> #include <WiFi.h>
#include "configuration.h" #include "configuration.h"
@@ -32,9 +31,9 @@
#endif #endif
extern Configuration Config; extern Configuration Config;
extern WiFiClient espClient;
extern HardwareSerial gpsSerial; extern HardwareSerial gpsSerial;
extern TinyGPSPlus gps; extern TinyGPSPlus gps;
extern bool stationCallsignIsValid;
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket; String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
@@ -44,38 +43,83 @@ namespace GPS_Utils {
return iGateLoRaBeaconPacket; return iGateLoRaBeaconPacket;
} }
void generateBeacons() { char *ax25_base91enc(char *s, uint8_t n, uint32_t v) {
String beaconPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path); for(s += n, *s = '\0'; n; n--) {
String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(Config.beacon.latitude, Config.beacon.longitude, 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel); *(--s) = v % 91 + 33;
v /= 91;
}
return(s);
}
if (Config.callsign.indexOf("NOCALL-10") != 0) { float roundToTwoDecimals(float degrees) {
if (!stationCallsignIsValid) { return round(degrees * 100) / 100;
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000); }
Config.loramodule.txActive = false;
Config.aprs_is.messagesToRF = false; String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
Config.aprs_is.objectsToRF = false; String encodedData = overlay;
Config.beacon.sendViaRF = false; uint32_t aprs_lat, aprs_lon;
Config.digi.mode = 0;
Config.digi.backupDigiMode = false; float processedLatitude = latitude;
} else if (stationCallsignIsValid && Config.tacticalCallsign != "") { float processedLongitude = longitude;
beaconPacket = APRSPacketLib::generateBasePacket(Config.tacticalCallsign, "APLRG1", Config.beacon.path); if (Config.beacon.gpsActive && Config.beacon.gpsAmbiguity) {
Config.aprs_is.active = false; processedLatitude = roundToTwoDecimals(latitude);
Config.beacon.sendViaAPRSIS = false; processedLongitude = roundToTwoDecimals(longitude);
Config.digi.backupDigiMode = false;
}
} else {
Config.beacon.sendViaAPRSIS = false;
Config.beacon.sendViaRF = false;
} }
iGateBeaconPacket = beaconPacket; aprs_lat = 900000000 - processedLatitude * 10000000;
iGateBeaconPacket += ",qAC:="; aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
iGateBeaconPacket += Config.beacon.overlay; aprs_lon = 900000000 + processedLongitude * 10000000 / 2;
iGateBeaconPacket += encodedGPS; aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
String Ns, Ew, helper;
if(processedLatitude < 0) { Ns = "S"; } else { Ns = "N"; }
if(processedLatitude < 0) { processedLatitude = -processedLatitude; }
if(processedLongitude < 0) { Ew = "W"; } else { Ew = "E"; }
if(processedLongitude < 0) { processedLongitude = -processedLongitude; }
char helper_base91[] = {"0000\0"};
int i;
ax25_base91enc(helper_base91, 4, aprs_lat);
for (i = 0; i < 4; i++) {
encodedData += helper_base91[i];
}
ax25_base91enc(helper_base91, 4, aprs_lon);
for (i = 0; i < 4; i++) {
encodedData += helper_base91[i];
}
encodedData += symbol;
encodedData += " ";
encodedData += "\x47";
return encodedData;
}
void generateBeaconFirstPart() {
String beaconPacket = Config.callsign;
beaconPacket += ">APLRG1";
if (Config.beacon.path.indexOf("WIDE") == 0) {
beaconPacket += ",";
beaconPacket += Config.beacon.path;
}
iGateBeaconPacket = beaconPacket;
iGateBeaconPacket += ",qAC:!";
iGateLoRaBeaconPacket = beaconPacket; iGateLoRaBeaconPacket = beaconPacket;
iGateLoRaBeaconPacket += ":="; iGateLoRaBeaconPacket += ":!";
iGateLoRaBeaconPacket += Config.beacon.overlay; }
void generateBeacons() {
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
Config.loramodule.txActive = false;
Config.aprs_is.messagesToRF = false;
Config.aprs_is.objectsToRF = false;
Config.beacon.sendViaRF = false;
Config.digi.mode = 0;
Config.backupDigiMode = false;
}
generateBeaconFirstPart();
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
iGateBeaconPacket += encodedGPS;
iGateLoRaBeaconPacket += encodedGPS; iGateLoRaBeaconPacket += encodedGPS;
} }
@@ -83,40 +127,47 @@ namespace GPS_Utils {
return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0; return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0;
} }
String buildDistanceAndComment(float latitude, float longitude, const String& comment) {
distance = String(calculateDistanceTo(latitude, longitude),1);
String distanceAndComment = String(latitude,5);
distanceAndComment += "N / ";
distanceAndComment += String(longitude,5);
distanceAndComment += "E / ";
distanceAndComment += distance;
distanceAndComment += "km";
if (comment != "") {
distanceAndComment += " / ";
distanceAndComment += comment;
}
return distanceAndComment;
}
String decodeEncodedGPS(const String& packet) { String decodeEncodedGPS(const String& packet) {
int indexOfExclamation = packet.indexOf(":!"); int indexOfExclamation = packet.indexOf(":!");
int indexOfEqual = packet.indexOf(":="); int indexOfEqual = packet.indexOf(":=");
const uint8_t OFFSET = 3; // Offset for encoded data in the packet const uint8_t OFFSET = 3; // Offset for encoded data in the packet
String infoGPS; String GPSPacket;
if (indexOfExclamation > 10) { if (indexOfExclamation > 10) {
infoGPS = packet.substring(indexOfExclamation + OFFSET); GPSPacket = packet.substring(indexOfExclamation + OFFSET);
} else if (indexOfEqual > 10) { } else if (indexOfEqual > 10) {
infoGPS = packet.substring(indexOfEqual + OFFSET); GPSPacket = packet.substring(indexOfEqual + OFFSET);
} }
float decodedLatitude = APRSPacketLib::decodeBase91EncodedLatitude(infoGPS.substring(0,4)); String encodedLatitude = GPSPacket.substring(0,4);
float decodedLongitude = APRSPacketLib::decodeBase91EncodedLongitude(infoGPS.substring(4,8)); int Y1 = encodedLatitude[0] - 33;
int Y2 = encodedLatitude[1] - 33;
int Y3 = encodedLatitude[2] - 33;
int Y4 = encodedLatitude[3] - 33;
float decodedLatitude = 90.0 - (((Y1 * pow(91,3)) + (Y2 * pow(91,2)) + (Y3 * 91) + Y4) / 380926.0);
return buildDistanceAndComment(decodedLatitude, decodedLongitude, infoGPS.substring(12)); String encodedLongitude = GPSPacket.substring(4,8);
int X1 = encodedLongitude[0] - 33;
int X2 = encodedLongitude[1] - 33;
int X3 = encodedLongitude[2] - 33;
int X4 = encodedLongitude[3] - 33;
float decodedLongitude = -180.0 + (((X1 * pow(91,3)) + (X2 * pow(91,2)) + (X3 * 91) + X4) / 190463.0);
distance = String(calculateDistanceTo(decodedLatitude, decodedLongitude),1);
String decodedGPS = String(decodedLatitude,5);
decodedGPS += "N / ";
decodedGPS += String(decodedLongitude,5);
decodedGPS += "E / ";
decodedGPS += distance;
decodedGPS += "km";
String comment = GPSPacket.substring(12);
if (comment != "") {
decodedGPS += " / ";
decodedGPS += comment;
}
return decodedGPS;
} }
String getReceivedGPS(const String& packet) { String getReceivedGPS(const String& packet) {
@@ -134,55 +185,60 @@ namespace GPS_Utils {
} }
String Latitude = infoGPS.substring(0,8); // First 8 characters are Latitude String Latitude = infoGPS.substring(0,8); // First 8 characters are Latitude
int latitudeColonIndex = Latitude.indexOf(".");
float convertedLatitude = Latitude.substring(0,2).toFloat(); // First 2 digits (Degrees) float convertedLatitude = Latitude.substring(0,2).toFloat(); // First 2 digits (Degrees)
convertedLatitude += Latitude.substring(2,4).toFloat() / 60; // Next 2 digits (Minutes) convertedLatitude += Latitude.substring(2,4).toFloat() / 60; // Next 2 digits (Minutes)
convertedLatitude += Latitude.substring(latitudeColonIndex + 1, latitudeColonIndex + 3).toFloat() / (60*100); convertedLatitude += Latitude.substring(Latitude.indexOf(".") + 1, Latitude.indexOf(".") + 3).toFloat() / (60*100);
if (Latitude.endsWith("S")) convertedLatitude = -convertedLatitude; // Handle Southern Hemisphere if (Latitude.endsWith("S")) convertedLatitude = -convertedLatitude; // Handle Southern Hemisphere
String Longitude = infoGPS.substring(9,18); // Next 9 characters are Longitude String Longitude = infoGPS.substring(9,18); // Next 9 characters are Longitude
int longitudeColonIndex = Longitude.indexOf(".");
float convertedLongitude = Longitude.substring(0,3).toFloat(); // First 3 digits (Degrees) float convertedLongitude = Longitude.substring(0,3).toFloat(); // First 3 digits (Degrees)
convertedLongitude += Longitude.substring(3,5).toFloat() / 60; // Next 2 digits (Minutes) convertedLongitude += Longitude.substring(3,5).toFloat() / 60; // Next 2 digits (Minutes)
convertedLongitude += Longitude.substring(longitudeColonIndex + 1, longitudeColonIndex + 3).toFloat() / (60*100); convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100);
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1);
return buildDistanceAndComment(convertedLatitude, convertedLongitude, infoGPS.substring(19)); String decodedGPS = String(convertedLatitude,5);
decodedGPS += "N / ";
decodedGPS += String(convertedLongitude,5);
decodedGPS += "E / ";
decodedGPS += distance;
decodedGPS += "km";
String comment = infoGPS.substring(19);
if (comment != "") {
decodedGPS += " / ";
decodedGPS += comment;
}
return decodedGPS;
} }
String getDistanceAndComment(const String& packet) { String getDistanceAndComment(const String& packet) {
int indexOfAt = packet.indexOf(":@"); int indexOfAt = packet.indexOf(":@");
if (indexOfAt > 10) return getReceivedGPS(packet); if (indexOfAt > 10) {
return getReceivedGPS(packet);
} else {
const uint8_t ENCODED_BYTE_OFFSET = 14; // Offset for encoded data in the packet
int indexOfExclamation = packet.indexOf(":!");
int indexOfEqual = packet.indexOf(":=");
uint8_t encodedBytePosition = 0;
if (indexOfExclamation > 10) { // Determine the position where encoded data starts
encodedBytePosition = indexOfExclamation + ENCODED_BYTE_OFFSET;
} else if (indexOfEqual > 10) {
encodedBytePosition = indexOfEqual + ENCODED_BYTE_OFFSET;
}
const uint8_t nonEncondedLatitudeOffset = 9; // "N" / "S" if (encodedBytePosition != 0) {
const uint8_t nonEncondedLongitudeOffset = 19; // "E" / "W" char currentChar = packet[encodedBytePosition];
const uint8_t encodedByteOffset = 14; if (currentChar == 'G' || currentChar == 'Q' || currentChar == '[' || currentChar == 'H' || currentChar == 'X') {
return decodeEncodedGPS(packet); // If valid encoded data position is found, decode it
int indexOfExclamation = packet.indexOf(":!"); } else {
int indexOfEqual = packet.indexOf(":="); return getReceivedGPS(packet);
int baseIndex = - 1; }
if (indexOfExclamation > 10) { } else {
baseIndex = indexOfExclamation; return " _ / _ / _ ";
} else if (indexOfEqual > 10) { }
baseIndex = indexOfEqual;
} }
if (baseIndex == -1) return " _ / _ / _ ";
int latitudeIndex = baseIndex + nonEncondedLatitudeOffset;
int longitudeIndex = baseIndex + nonEncondedLongitudeOffset;
int encodedByteIndex = baseIndex + encodedByteOffset;
int packetLength = packet.length();
if (latitudeIndex < packetLength && longitudeIndex < packetLength) {
char latChar = packet[latitudeIndex];
char lngChar = packet[longitudeIndex];
if ((latChar == 'N' || latChar == 'S') && (lngChar == 'E' || lngChar == 'W')) return getReceivedGPS(packet);
}
if (encodedByteIndex < packetLength) {
char byteChar = packet[encodedByteIndex];
if (byteChar == 'G' || byteChar == 'Q' || byteChar == '[' || byteChar == 'H' || byteChar == 'X' || byteChar == '3') return decodeEncodedGPS(packet);
}
return " _ / _ / _ ";
} }
void setup() { void setup() {
+23 -14
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -30,19 +30,29 @@ bool validateKISSFrame(const String& kissFormattedFrame) {
String encodeAddressAX25(String tnc2Address) { String encodeAddressAX25(String tnc2Address) {
bool hasBeenDigipited = tnc2Address.indexOf('*') != -1; bool hasBeenDigipited = tnc2Address.indexOf('*') != -1;
if (tnc2Address.indexOf('-') == -1) { if (tnc2Address.indexOf('-') == -1) {
if (hasBeenDigipited) tnc2Address = tnc2Address.substring(0, tnc2Address.length() - 1); if (hasBeenDigipited) {
tnc2Address = tnc2Address.substring(0, tnc2Address.length() - 1);
}
tnc2Address += "-0"; tnc2Address += "-0";
} }
int separatorIndex = tnc2Address.indexOf('-');; int separatorIndex = tnc2Address.indexOf('-');
int ssid = tnc2Address.substring(separatorIndex + 1).toInt(); int ssid = tnc2Address.substring(separatorIndex + 1).toInt();
String kissAddress = ""; String kissAddress = "";
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
char addressChar = ' '; char addressChar;
if (tnc2Address.length() > i && i < separatorIndex) addressChar = tnc2Address.charAt(i); if (tnc2Address.length() > i && i < separatorIndex) {
addressChar = tnc2Address.charAt(i);
} else {
addressChar = ' ';
}
kissAddress += (char)(addressChar << 1); kissAddress += (char)(addressChar << 1);
} }
kissAddress += (char)((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0)); kissAddress += (char)((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0));
return kissAddress; return kissAddress;
} }
@@ -121,9 +131,8 @@ String encodeKISS(const String& frame) {
if (validateTNC2Frame(frame)) { if (validateTNC2Frame(frame)) {
String address = ""; String address = "";
bool dstAddresWritten = false; bool dstAddresWritten = false;
int colonFrameIndex = frame.indexOf(':'); for (int p = 0; p <= frame.indexOf(':'); p++) {
for (int p = 0; p <= colonFrameIndex; p++) {
char currentChar = frame.charAt(p); char currentChar = frame.charAt(p);
if (currentChar == ':' || currentChar == '>' || currentChar == ',') { if (currentChar == ':' || currentChar == '>' || currentChar == ',') {
if (!dstAddresWritten && (currentChar == ',' || currentChar == ':')) { if (!dstAddresWritten && (currentChar == ',' || currentChar == ':')) {
@@ -142,7 +151,7 @@ String encodeKISS(const String& frame) {
ax25Frame.setCharAt(ax25Frame.length() - 1, (char)(lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK)); ax25Frame.setCharAt(ax25Frame.length() - 1, (char)(lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK));
ax25Frame += (char)APRS_CONTROL_FIELD; ax25Frame += (char)APRS_CONTROL_FIELD;
ax25Frame += (char)APRS_INFORMATION_FIELD; ax25Frame += (char)APRS_INFORMATION_FIELD;
ax25Frame += frame.substring(colonFrameIndex + 1); ax25Frame += frame.substring(frame.indexOf(':') + 1);
} }
String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA); String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA);
+18 -49
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -30,7 +30,6 @@
extern Configuration Config; extern Configuration Config;
extern uint32_t lastRxTime; extern uint32_t lastRxTime;
extern bool packetIsBeacon;
extern std::vector<ReceivedPacket> receivedPackets; extern std::vector<ReceivedPacket> receivedPackets;
@@ -43,7 +42,7 @@ bool transmitFlag = true;
#ifdef HAS_SX1268 #ifdef HAS_SX1268
#if defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) #if defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0)
SPIClass loraSPI(FSPI); SPIClass loraSPI(FSPI);
SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN, loraSPI); SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN, loraSPI);
#else #else
SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
#endif #endif
@@ -81,7 +80,9 @@ namespace LoRa_Utils {
radio.XTAL = true; radio.XTAL = true;
#endif #endif
int state = radio.begin(freq); int state = radio.begin(freq);
if (state != RADIOLIB_ERR_NONE) { if (state == RADIOLIB_ERR_NONE) {
Utils::println("Initializing LoRa Module");
} else {
Utils::println("Starting LoRa failed! State: " + String(state)); Utils::println("Starting LoRa failed! State: " + String(state));
while (true); while (true);
} }
@@ -91,31 +92,16 @@ namespace LoRa_Utils {
#if defined(HAS_SX1278) || defined(HAS_SX1276) #if defined(HAS_SX1278) || defined(HAS_SX1276)
radio.setDio0Action(setFlag, RISING); radio.setDio0Action(setFlag, RISING);
#endif #endif
radio.setSpreadingFactor(Config.loramodule.spreadingFactor);
/*#ifdef SX126X_DIO3_TCXO_VOLTAGE float signalBandwidth = Config.loramodule.signalBandwidth/1000;
if (radio.setTCXO(float(SX126X_DIO3_TCXO_VOLTAGE)) == RADIOLIB_ERR_NONE) {
Utils::println("Set LoRa Module TCXO Voltage to:" + String(SX126X_DIO3_TCXO_VOLTAGE));
} else {
Utils::println("Set LoRa Module TCXO Voltage failed! State: " + String(state));
while (true);
}
#endif*/
radio.setSpreadingFactor(Config.loramodule.rxSpreadingFactor);
radio.setCodingRate(Config.loramodule.rxCodingRate4);
float signalBandwidth = Config.loramodule.rxSignalBandwidth / 1000;
radio.setBandwidth(signalBandwidth); radio.setBandwidth(signalBandwidth);
radio.setCodingRate(Config.loramodule.codingRate4);
radio.setCRC(true); radio.setCRC(true);
#if (defined(RADIO_RXEN) && defined(RADIO_TXEN)) // QRP Labs LightGateway has 400M22S (SX1268) #if (defined(RADIO_RXEN) && defined(RADIO_TXEN)) // QRP Labs LightGateway has 400M22S (SX1268)
radio.setRfSwitchPins(RADIO_RXEN, RADIO_TXEN); radio.setRfSwitchPins(RADIO_RXEN, RADIO_TXEN);
#endif #endif
/*#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setRfSwitchPins(RADIO_RXEN, RADIOLIB_NC);
radio.setDio2AsRfSwitch(true);
#endif*/
#ifdef HAS_1W_LORA // Ebyte E22 400M30S (SX1268) / 900M30S (SX1262) / Ebyte E220 400M30S (LLCC68) #ifdef HAS_1W_LORA // Ebyte E22 400M30S (SX1268) / 900M30S (SX1262) / Ebyte E220 400M30S (LLCC68)
state = radio.setOutputPower(Config.loramodule.power); // max value 20dB for 1W modules as they have Low Noise Amp state = radio.setOutputPower(Config.loramodule.power); // max value 20dB for 1W modules as they have Low Noise Amp
radio.setCurrentLimit(140); // to be validated (100 , 120, 140)? radio.setCurrentLimit(140); // to be validated (100 , 120, 140)?
@@ -133,13 +119,6 @@ namespace LoRa_Utils {
radio.setRxBoostedGainMode(true); radio.setRxBoostedGainMode(true);
#endif #endif
#if defined(HAS_TCXO) && !defined(HAS_1W_LORA)
radio.setDio2AsRfSwitch();
#endif
#ifdef HAS_TCXO
radio.setTCXO(1.8);
#endif
if (state == RADIOLIB_ERR_NONE) { if (state == RADIOLIB_ERR_NONE) {
Utils::println("init : LoRa Module ... done!"); Utils::println("init : LoRa Module ... done!");
} else { } else {
@@ -149,32 +128,24 @@ namespace LoRa_Utils {
} }
void changeFreqTx() { void changeFreqTx() {
delay(500);
float freq = (float)Config.loramodule.txFreq / 1000000; float freq = (float)Config.loramodule.txFreq / 1000000;
radio.setFrequency(freq); radio.setFrequency(freq);
radio.setSpreadingFactor(Config.loramodule.txSpreadingFactor);
radio.setCodingRate(Config.loramodule.txCodingRate4);
float signalBandwidth = Config.loramodule.txSignalBandwidth / 1000;
radio.setBandwidth(signalBandwidth);
} }
void changeFreqRx() { void changeFreqRx() {
delay(500);
float freq = (float)Config.loramodule.rxFreq / 1000000; float freq = (float)Config.loramodule.rxFreq / 1000000;
radio.setFrequency(freq); radio.setFrequency(freq);
radio.setSpreadingFactor(Config.loramodule.rxSpreadingFactor);
radio.setCodingRate(Config.loramodule.rxCodingRate4);
float signalBandwidth = Config.loramodule.rxSignalBandwidth / 1000;
radio.setBandwidth(signalBandwidth);
} }
void sendNewPacket(const String& newPacket) { void sendNewPacket(const String& newPacket) {
if (!Config.loramodule.txActive) return; if (!Config.loramodule.txActive) return;
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) { if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
if (!packetIsBeacon || (packetIsBeacon && Config.beacon.beaconFreq == 1)) { changeFreqTx();
changeFreqTx();
}
} }
#ifdef INTERNAL_LED_PIN #ifdef INTERNAL_LED_PIN
if (Config.digi.ecoMode != 1) digitalWrite(INTERNAL_LED_PIN, HIGH); // disabled in Ultra Eco Mode if (Config.digi.ecoMode != 1) digitalWrite(INTERNAL_LED_PIN, HIGH); // disabled in Ultra Eco Mode
#endif #endif
@@ -194,9 +165,7 @@ namespace LoRa_Utils {
if (Config.digi.ecoMode != 1) digitalWrite(INTERNAL_LED_PIN, LOW); // disabled in Ultra Eco Mode if (Config.digi.ecoMode != 1) digitalWrite(INTERNAL_LED_PIN, LOW); // disabled in Ultra Eco Mode
#endif #endif
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) { if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
if (!packetIsBeacon || (packetIsBeacon && Config.beacon.beaconFreq == 1)) { changeFreqRx();
changeFreqRx();
}
} }
} }
@@ -224,7 +193,7 @@ namespace LoRa_Utils {
if (packet != "") { if (packet != "") {
String sender = packet.substring(3, packet.indexOf(">")); String sender = packet.substring(3, packet.indexOf(">"));
if (packet.substring(0,3) == "\x3c\xff\x01" && !STATION_Utils::isBlacklisted(sender)) { // avoid processing BlackListed stations if (packet.substring(0,3) == "\x3c\xff\x01" && !STATION_Utils::isBlacklisted(sender)){ // avoid processing BlackListed stations
rssi = radio.getRSSI(); rssi = radio.getRSSI();
snr = radio.getSNR(); snr = radio.getSNR();
freqError = radio.getFrequencyError(); freqError = radio.getFrequencyError();
-101
View File
@@ -1,101 +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 <WiFiClientSecure.h>
#include <PubSubClient.h>
#include "configuration.h"
#include "station_utils.h"
#include "mqtt_utils.h"
extern Configuration Config;
extern WiFiClient mqttClient;
PubSubClient pubSub;
namespace MQTT_Utils {
void sendToMqtt(const String& packet) {
if (!pubSub.connected()) {
Serial.println("Can not send to MQTT because it is not connected");
return;
}
const String cleanPacket = packet.substring(3);
const String sender = cleanPacket.substring(0, cleanPacket.indexOf(">"));
const String topic = String(Config.mqtt.topic + "/" + sender);
const bool result = pubSub.publish(topic.c_str(), cleanPacket.c_str());
if (result) {
Serial.print("Packet sent to MQTT topic "); Serial.println(topic);
} else {
Serial.println("Packet not sent to MQTT (check connection)");
}
}
void receivedFromMqtt(char* topic, byte* payload, unsigned int length) {
Serial.print("Received from MQTT topic "); Serial.print(topic); Serial.print(": ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
STATION_Utils::addToOutputPacketBuffer(String(payload, length));
}
void connect() {
if (pubSub.connected()) return;
if (Config.mqtt.server.isEmpty() || Config.mqtt.port <= 0) {
Serial.println("Connect to MQTT server KO because no host or port given");
return;
}
pubSub.setServer(Config.mqtt.server.c_str(), Config.mqtt.port);
Serial.print("Trying to connect with MQTT Server: " + String(Config.mqtt.server) + " MqttServerPort: " + String(Config.mqtt.port));
bool connected = false;
if (!Config.mqtt.username.isEmpty()) {
connected = pubSub.connect(Config.callsign.c_str(), Config.mqtt.username.c_str(), Config.mqtt.password.c_str());
} else {
connected = pubSub.connect(Config.callsign.c_str());
}
if (connected) {
Serial.println(" -> Connected !");
const String subscribedTopic = Config.mqtt.topic + "/" + Config.callsign + "/#";
if (!pubSub.subscribe(subscribedTopic.c_str())) {
Serial.println("Subscribed to MQTT Failed");
}
Serial.print("Subscribed to MQTT topic : ");
Serial.println(subscribedTopic);
} else {
Serial.println(" -> Not Connected (Retry in a few secs)");
}
}
void loop() {
if (!Config.mqtt.active) return;
if (!pubSub.connected()) return;
pubSub.loop();
}
void setup() {
if (!Config.mqtt.active) return;
pubSub.setClient(mqttClient);
pubSub.setCallback(receivedFromMqtt);
}
}
+10 -10
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -27,7 +27,7 @@
extern Configuration Config; extern Configuration Config;
WiFiUDP ntpUDP; WiFiUDP ntpUDP;
NTPClient* timeClient; NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 15 * 60 * 1000); // Update interval 15 min
namespace NTP_Utils { namespace NTP_Utils {
@@ -35,17 +35,17 @@ namespace NTP_Utils {
void setup() { void setup() {
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") { if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") {
int gmt = Config.ntp.gmtCorrection * 3600; int gmt = Config.ntp.gmtCorrection * 3600;
timeClient = new NTPClient(ntpUDP, Config.ntp.server.c_str(), gmt, 15 * 60 * 1000); // Update interval 15 min timeClient.setTimeOffset(gmt);
timeClient->begin(); timeClient.begin();
} }
} }
void update() { void update() {
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") timeClient->update(); if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") timeClient.update();
} }
String getFormatedTime() { String getFormatedTime() {
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0) return timeClient->getFormattedTime(); if (Config.digi.ecoMode == 0) return timeClient.getFormattedTime();
return "DigiEcoMode Active"; return "DigiEcoMode Active";
} }
+5 -5
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
+63 -40
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -20,7 +20,6 @@
#include "battery_utils.h" #include "battery_utils.h"
#include "board_pinout.h" #include "board_pinout.h"
#include "power_utils.h" #include "power_utils.h"
#include "utils.h"
#if defined(HAS_AXP192) || defined(HAS_AXP2101) #if defined(HAS_AXP192) || defined(HAS_AXP2101)
#ifdef TTGO_T_Beam_S3_SUPREME_V3 #ifdef TTGO_T_Beam_S3_SUPREME_V3
@@ -44,38 +43,48 @@
#endif #endif
extern Configuration Config; extern Configuration Config;
extern bool stationCallsignIsValid;
namespace POWER_Utils { namespace POWER_Utils {
#ifdef ADC_CTRL_PIN #ifdef VEXT_CTRL
void adc_ctrl_ON() {
digitalWrite(ADC_CTRL_PIN, ADC_CTRL_ON_STATE);
}
void adc_ctrl_OFF() {
digitalWrite(ADC_CTRL_PIN, !ADC_CTRL_ON_STATE);
}
#endif
#ifdef VEXT_CTRL_PIN
void vext_ctrl_ON() { void vext_ctrl_ON() {
digitalWrite(VEXT_CTRL_PIN, Config.digi.ecoMode == 1 ? !VEXT_CTRL_ON_STATE : VEXT_CTRL_ON_STATE); #if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
#endif
#if defined(HELTEC_WP) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
#endif
} }
void vext_ctrl_OFF() { void vext_ctrl_OFF() {
digitalWrite(VEXT_CTRL_PIN, Config.digi.ecoMode == 1 ? VEXT_CTRL_ON_STATE : !VEXT_CTRL_ON_STATE); #if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
#endif
#if defined(HELTEC_WP) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
#endif
} }
#endif #endif
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
void activateMeasurement() { #ifdef ADC_CTRL
PMU.disableTSPinMeasure(); void adc_ctrl_ON() {
PMU.enableBattDetection(); #if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2)
PMU.enableVbusVoltageMeasure(); digitalWrite(ADC_CTRL, HIGH);
PMU.enableBattVoltageMeasure(); #endif
PMU.enableSystemVoltageMeasure(); #if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP)
digitalWrite(ADC_CTRL, LOW);
#endif
}
void adc_ctrl_OFF() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2)
digitalWrite(ADC_CTRL, LOW);
#endif
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP)
digitalWrite(ADC_CTRL, HIGH);
#endif
} }
#endif #endif
@@ -95,6 +104,16 @@ namespace POWER_Utils {
#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
PMU.setLDO3Voltage(3300); PMU.setLDO3Voltage(3300);
@@ -263,8 +282,8 @@ namespace POWER_Utils {
pinMode(Config.battery.externalVoltagePin, INPUT); pinMode(Config.battery.externalVoltagePin, INPUT);
} }
#ifdef VEXT_CTRL_PIN #ifdef VEXT_CTRL
pinMode(VEXT_CTRL_PIN,OUTPUT); // GPS + TFT on HELTEC Wireless_Tracker and only for Oled in HELTEC V3 pinMode(VEXT_CTRL,OUTPUT); // GPS + TFT on HELTEC Wireless_Tracker and only for Oled in HELTEC V3
vext_ctrl_ON(); vext_ctrl_ON();
#endif #endif
@@ -272,11 +291,23 @@ namespace POWER_Utils {
if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) activateGPS(); if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) activateGPS();
#endif #endif
#ifdef ADC_CTRL_PIN #ifdef ADC_CTRL
pinMode(ADC_CTRL_PIN, OUTPUT); pinMode(ADC_CTRL, OUTPUT);
adc_ctrl_OFF(); adc_ctrl_OFF();
#endif #endif
#if defined(HELTEC_WIRELESS_TRACKER)
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WS) || defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) || defined(TTGO_LORA32_T3S3_V1_2) || defined(HELTEC_V2)
Wire.begin(OLED_SDA, OLED_SCL);
#endif
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WP) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS) #if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
pinMode(BOARD_POWERON, OUTPUT); pinMode(BOARD_POWERON, OUTPUT);
digitalWrite(BOARD_POWERON, HIGH); digitalWrite(BOARD_POWERON, HIGH);
@@ -290,20 +321,12 @@ namespace POWER_Utils {
digitalWrite(TFT_CS, HIGH); digitalWrite(TFT_CS, HIGH);
delay(500); delay(500);
#endif Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#ifdef USE_WIRE_WITH_OLED_PINS
Wire.begin(OLED_SDA, OLED_SCL);
#endif
#ifdef SENSOR_I2C_BUS
SENSOR_I2C_BUS.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif #endif
delay(1000); delay(1000);
BATTERY_Utils::setup(); BATTERY_Utils::setup();
BATTERY_Utils::startupBatteryHealth(); BATTERY_Utils::startupBatteryHealth();
stationCallsignIsValid = Utils::callsignIsValid(Config.callsign);
setCpuFrequencyMhz(80); setCpuFrequencyMhz(80);
} }
+33 -38
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -31,7 +31,6 @@ extern float snr;
extern int freqError; extern int freqError;
extern bool shouldSleepLowVoltage; extern bool shouldSleepLowVoltage;
extern bool saveNewDigiEcoModeConfig; extern bool saveNewDigiEcoModeConfig;
extern String versionNumber;
namespace QUERY_Utils { namespace QUERY_Utils {
@@ -41,17 +40,15 @@ namespace QUERY_Utils {
String queryQuestion = query; String queryQuestion = query;
queryQuestion.toUpperCase(); queryQuestion.toUpperCase();
if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") { if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") {
answer.concat("?APRSV ?APRSP ?APRSL ?APRSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign answer.concat("?APRSV ?APRSP ?APRSL ?APRSSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign
} else if (queryQuestion == "?APRSV") { } else if (queryQuestion == "?APRSV") {
answer.concat("CA2RXU_LoRa_iGate v"); answer.concat("CA2RXU_LoRa_iGate 3.0 v");
answer.concat(versionNumber);
answer.concat(" ");
answer.concat(versionDate); answer.concat(versionDate);
} else if (queryQuestion == "?APRSP") { } else if (queryQuestion == "?APRSP") {
answer.concat("iGate QTH: "); answer.concat("iGate QTH: ");
answer.concat(String(Config.beacon.latitude,2)); answer.concat(String(Config.beacon.latitude,3));
answer.concat(" "); answer.concat(" ");
answer.concat(String(Config.beacon.longitude,2)); answer.concat(String(Config.beacon.longitude,3));
} else if (queryQuestion == "?APRSL") { } else if (queryQuestion == "?APRSL") {
if (lastHeardStations.size() == 0) { if (lastHeardStations.size() == 0) {
char answerArray[50]; char answerArray[50];
@@ -66,25 +63,21 @@ namespace QUERY_Utils {
} else if (queryQuestion == "?APRSSR") { } else if (queryQuestion == "?APRSSR") {
char signalData[35]; char signalData[35];
snprintf(signalData, sizeof(signalData), " %ddBm / %.2fdB / %dHz", rssi, snr, freqError); snprintf(signalData, sizeof(signalData), " %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
answer.concat(signalData); answer.concat(signalData);
} /*else if (queryQuestion.indexOf("?APRSH") == 0) { } /*else if (queryQuestion.indexOf("?APRSH") == 0) {
// sacar callsign despues de ?APRSH // sacar callsign despues de ?APRSH
Serial.println("escuchaste a X estacion? en las ultimas 24 o 8 horas?"); Serial.println("escuchaste a X estacion? en las ultimas 24 o 8 horas?");
answer.concat("?APRSH on development 73!"); answer.concat("?APRSH on development 73!");
} *//*else if (queryQuestion.indexOf("?WHERE") == 0) { } *//*else if (queryQuestion.indexOf("?WHERE") == 0) {
// agregar callsign para completar donde esta X callsign --> posicion // agregar callsign para completar donde esta X callsign --> posicion
Serial.println("estaciones escuchadas directo (ultimos 30 min)"); Serial.println("estaciones escuchadas directo (ultimos 30 min)");
answer.concat("?WHERE on development 73!"); answer.concat("?WHERE on development 73!");
} */ } */
else if (STATION_Utils::isManager(station) && (!queryFromAPRSIS || !Config.remoteManagement.rfOnly)) { else if (STATION_Utils::isManager(station) && (!queryFromAPRSIS || !Config.remoteManagement.rfOnly)) {
int digiMode = Config.digi.mode; if (queryQuestion.indexOf("?EM=OFF") == 0) {
int digiEcoMode = Config.digi.ecoMode; if ((Config.digi.mode == 2 || Config.digi.mode == 3) && Config.loramodule.txActive && Config.loramodule.rxActive && !Config.aprs_is.active) {
int radioTxActive = Config.loramodule.txActive; if (Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) { // Exit Digipeater EcoMode or Digipeater without WiFiAP
bool onlyRadioActive = radioTxActive && Config.loramodule.rxActive && !Config.aprs_is.active; answer = (Config.digi.ecoMode == 1) ? "DigiEcoMode:OFF" : "Digipeater + WiFiAP enabled";
if (queryQuestion.startsWith("?EM=OFF")) {
if ((digiMode == 2 || digiMode == 3) && onlyRadioActive) {
if (digiEcoMode == 1 || digiEcoMode == 2) { // Exit Digipeater EcoMode or Digipeater without WiFiAP
answer = (digiEcoMode == 1) ? "DigiEcoMode:OFF" : "Digipeater + WiFiAP enabled";
Config.digi.ecoMode = 0; Config.digi.ecoMode = 0;
Config.display.alwaysOn = true; Config.display.alwaysOn = true;
Config.display.timeout = 10; Config.display.timeout = 10;
@@ -96,9 +89,9 @@ namespace QUERY_Utils {
} else { } else {
answer = "Digipeater Mode control not possible"; answer = "Digipeater Mode control not possible";
} }
} else if (queryQuestion.startsWith("?EM=ON")) { } else if (queryQuestion.indexOf("?EM=ON") == 0) {
if ((digiMode == 2 || digiMode == 3) && onlyRadioActive) { if ((Config.digi.mode == 2 || Config.digi.mode == 3) && Config.loramodule.txActive && Config.loramodule.rxActive && !Config.aprs_is.active) {
if (digiEcoMode == 0) { // Start Digipeater EcoMode if (Config.digi.ecoMode == 0) { // Start Digipeater EcoMode
answer = "DigiEcoMode:ON"; answer = "DigiEcoMode:ON";
Config.digi.ecoMode = 1; Config.digi.ecoMode = 1;
shouldSleepLowVoltage = true; // to make sure all packets in outputPacketBuffer are sent before restart. shouldSleepLowVoltage = true; // to make sure all packets in outputPacketBuffer are sent before restart.
@@ -109,29 +102,31 @@ namespace QUERY_Utils {
} else { } else {
answer = "Digipeater Mode control not possible"; answer = "Digipeater Mode control not possible";
} }
} else if (queryQuestion.startsWith("?EM=?")) { // Digipeater EcoMode Status } else if (queryQuestion.indexOf("?EM=?") == 0) { // Digipeater EcoMode Status
switch (digiEcoMode) { if (Config.digi.ecoMode == 0) {
case 0: answer = "DigiEcoMode:OFF"; break; answer = "DigiEcoMode:OFF";
case 1: answer = "DigiEcoMode:ON"; break; } else if (Config.digi.ecoMode == 1) {
default: answer = "DigiEcoMode:OFF/Only Serial Output"; answer = "DigiEcoMode:ON";
} else {
answer = "DigiEcoMode:OFF/Only Serial Output";
} }
} else if (queryQuestion.startsWith("?TX=ON")) { } else if (queryQuestion.indexOf("?TX=ON") == 0) {
if (radioTxActive) { if (Config.loramodule.txActive) {
answer = "TX was ON"; answer = "TX was ON";
} else { } else {
Config.loramodule.txActive = true; Config.loramodule.txActive = true;
answer = "TX=ON"; answer = "TX=ON";
} }
} else if (queryQuestion.startsWith("?TX=OFF")) { } else if (queryQuestion.indexOf("?TX=OFF") == 0) {
if (!radioTxActive) { if (!Config.loramodule.txActive) {
answer = "TX was OFF"; answer = "TX was OFF";
} else { } else {
Config.loramodule.txActive = false; Config.loramodule.txActive = false;
answer = "TX=OFF"; answer = "TX=OFF";
} }
} else if (queryQuestion.startsWith("?TX=?")) { } else if (queryQuestion.indexOf("?TX=?") == 0) {
answer = (radioTxActive) ? "TX=ON" : "TX=OFF"; answer = (Config.loramodule.txActive) ? "TX=ON" : "TX=OFF";
} else if (queryQuestion.startsWith("?COMMIT")) { // saving for next reboot } else if (queryQuestion.indexOf("?COMMIT") == 0) { // saving for next reboot
answer = "New Config Saved"; answer = "New Config Saved";
Config.writeFile(); Config.writeFile();
} }
+8 -9
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -51,13 +51,12 @@ namespace SLEEP_Utils {
if (Config.digi.ecoMode == 1) { if (Config.digi.ecoMode == 1) {
pinMode(RADIO_WAKEUP_PIN, INPUT); pinMode(RADIO_WAKEUP_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(RADIO_WAKEUP_PIN), wakeUpLoRaPacketReceived, RISING); attachInterrupt(digitalPinToInterrupt(RADIO_WAKEUP_PIN), wakeUpLoRaPacketReceived, RISING);
#if defined(TTGO_LORA32_V2_1) || defined(TTGO_LORA32_V2_1_915) || defined(TTGO_LORA32_T3S3_V1_2) || defined(TTGO_T_BEAM_V1_0) || defined(TTGO_T_BEAM_V1_0_915) || defined(TTGO_T_BEAM_V1_0_SX1268) || defined(TTGO_T_BEAM_V1_2) || defined(TTGO_T_BEAM_V1_2_915) || defined(TTGO_T_BEAM_V1_2_SX1262) || defined(TTGO_T_DECK_PLUS) || defined(TTGO_T_DECK_GPS) || defined(TTGO_T_Beam_S3_SUPREME_V3) || defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WP) || defined(HELTEC_WS) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY) || defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V2) || defined(XIAO_ESP32S3_LORA) || defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) || defined(TROY_LoRa_APRS) || defined(OE5HWN_MeshCom) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915) || defined(ESP32_DIY_1W_LoRa_LLCC68) || defined(ESP32_DIY_1W_LoRa_Mesh_V1_2) || defined(WEMOS_S2_MINI_DIY_LoRa) || defined(WEMOS_D1_R32_RA02) || defined(WEMOS_LOLIN32_OLED_DIY_LoRa)
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
esp_sleep_enable_ext1_wakeup(GPIO_WAKEUP_PIN, ESP_EXT1_WAKEUP_ANY_HIGH); esp_sleep_enable_ext1_wakeup(GPIO_WAKEUP_PIN, ESP_EXT1_WAKEUP_ANY_HIGH);
#elif defined(CONFIG_IDF_TARGET_ESP32C3) #endif
#if defined(HELTEC_HTCT62) || defined(ESP32C3_DIY_1W_LoRa) || defined(ESP32C3_DIY_1W_LoRa_915) || defined(ESP32_C3_OctopusLab_LoRa)
esp_deep_sleep_enable_gpio_wakeup(1ULL << GPIO_WAKEUP_PIN, ESP_GPIO_WAKEUP_GPIO_HIGH); esp_deep_sleep_enable_gpio_wakeup(1ULL << GPIO_WAKEUP_PIN, ESP_GPIO_WAKEUP_GPIO_HIGH);
#endif #endif
//#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_NRF52)
} }
#endif #endif
} }
+57 -89
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -25,8 +25,6 @@
#include "utils.h" #include "utils.h"
#include <vector> #include <vector>
#define SECS_TO_WAIT 3 // soon to be deleted...
extern Configuration Config; extern Configuration Config;
extern uint32_t lastRxTime; extern uint32_t lastRxTime;
@@ -35,59 +33,47 @@ extern bool shouldSleepLowVoltage;
uint32_t lastTxTime = millis(); uint32_t lastTxTime = millis();
std::vector<LastHeardStation> lastHeardStations; std::vector<LastHeardStation> lastHeardStations;
std::vector<String> outputPacketBuffer;
std::vector<Packet25SegBuffer> packet25SegBuffer;
std::vector<String> blacklist; std::vector<String> blacklist;
std::vector<String> managers; std::vector<String> managers;
std::vector<LastHeardStation> lastHeardObjects; std::vector<LastHeardStation> lastHeardObjects;
struct OutputPacketBuffer {
String packet;
bool isBeacon;
OutputPacketBuffer(const String& p, bool b) : packet(p), isBeacon(b) {}
};
std::vector<OutputPacketBuffer> outputPacketBuffer;
struct Packet25SegBuffer {
uint32_t receivedTime;
uint32_t hash;
};
std::vector<Packet25SegBuffer> packet25SegBuffer;
bool saveNewDigiEcoModeConfig = false; bool saveNewDigiEcoModeConfig = false;
bool packetIsBeacon = false;
namespace STATION_Utils { namespace STATION_Utils {
std::vector<String> loadCallsignList(const String& list) { std::vector<String> loadCallSignList(const String& list) {
std::vector<String> loadedList; std::vector<String> loadedList;
int start = 0;
int listLength = list.length();
while (start < listLength) { String callsigns = list;
while (start < listLength && list[start] == ' ') start++; // avoid blank spaces callsigns.trim();
if (start >= listLength) break;
int end = start; while (callsigns.length() > 0) { // != ""
while (end < listLength && list[end] != ' ') end++; // find another blank space or reach listLength int spaceIndex = callsigns.indexOf(" ");
if (spaceIndex == -1) { // No more spaces, add the last part
loadedList.emplace_back(list.substring(start, end)); loadedList.push_back(callsigns);
start = end + 1; // keep on searching if listLength not reached break;
}
loadedList.push_back(callsigns.substring(0, spaceIndex));
callsigns = callsigns.substring(spaceIndex + 1);
callsigns.trim(); // Trim in case of multiple spaces
} }
return loadedList; return loadedList;
} }
void loadBlacklistAndManagers() { void loadBlacklistAndManagers() {
blacklist = loadCallsignList(Config.blacklist); blacklist = loadCallSignList(Config.blacklist);
managers = loadCallsignList(Config.remoteManagement.managers); managers = loadCallSignList(Config.remoteManagement.managers);
} }
bool checkCallsignList(const std::vector<String>& list, const String& callsign) { bool checkCallsignList(const std::vector<String>& list, const String& callsign) {
for (size_t i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
int wildcardIndex = list[i].indexOf("*"); int wildcardIndex = list[i].indexOf("*");
if (wildcardIndex >= 0) { if (wildcardIndex >= 0) {
if (wildcardIndex >= 2 && callsign.length() >= wildcardIndex && strncmp(callsign.c_str(), list[i].c_str(), wildcardIndex) == 0) { String wildcard = list[i].substring(0, wildcardIndex);
return true; if (callsign.startsWith(wildcard)) return true;
}
} else { } else {
if (list[i] == callsign) return true; if (list[i] == callsign) return true;
} }
@@ -128,33 +114,36 @@ namespace STATION_Utils {
} }
void deleteNotHeard() { void deleteNotHeard() {
uint32_t currentTime = millis(); std::vector<LastHeardStation> lastHeardStation_temp;
uint32_t timeout = Config.rememberStationTime * 60UL * 1000UL; for (int i = 0; i < lastHeardStations.size(); i++) {
if (millis() - lastHeardStations[i].lastHeardTime < Config.rememberStationTime * 60 * 1000) {
for (int i = lastHeardStations.size() - 1; i >= 0; i--) { lastHeardStation_temp.push_back(lastHeardStations[i]);
if (currentTime - lastHeardStations[i].lastHeardTime >= timeout) {
lastHeardStations.erase(lastHeardStations.begin() + i);
} }
} }
lastHeardStations.clear();
for (int j = 0; j < lastHeardStation_temp.size(); j++) {
lastHeardStations.push_back(lastHeardStation_temp[j]);
}
lastHeardStation_temp.clear();
} }
void updateLastHeard(const String& station) { void updateLastHeard(const String& station) {
deleteNotHeard(); deleteNotHeard();
uint32_t currentTime = millis(); bool stationHeard = false;
for (size_t i = 0; i < lastHeardStations.size(); i++) { for (int i = 0; i < lastHeardStations.size(); i++) {
if (lastHeardStations[i].station == station) { if (lastHeardStations[i].station == station) {
lastHeardStations[i].lastHeardTime = currentTime; lastHeardStations[i].lastHeardTime = millis();
Utils::showActiveStations(); stationHeard = true;
return; break;
} }
} }
lastHeardStations.emplace_back(LastHeardStation{currentTime, station}); if (!stationHeard) lastHeardStations.emplace_back(LastHeardStation{millis(), station});
Utils::showActiveStations(); Utils::activeStations();
} }
bool wasHeard(const String& station) { bool wasHeard(const String& station) {
deleteNotHeard(); deleteNotHeard();
for (size_t i = 0; i < lastHeardStations.size(); i++) { for (int i = 0; i < lastHeardStations.size(); i++) {
if (lastHeardStations[i].station == station) { if (lastHeardStations[i].station == station) {
Utils::println(" ---> Listened Station"); Utils::println(" ---> Listened Station");
return true; return true;
@@ -164,42 +153,25 @@ namespace STATION_Utils {
return false; return false;
} }
void clean25SegHashBuffer() { void clean25SegBuffer() {
uint32_t currentTime = millis(); if (!packet25SegBuffer.empty() && (millis() - packet25SegBuffer[0].receivedTime) > 25 * 1000) packet25SegBuffer.erase(packet25SegBuffer.begin());
for (int i = packet25SegBuffer.size() - 1; i >= 0; i--) { }
if ((currentTime - packet25SegBuffer[i].receivedTime) > 25 * 1000) {
packet25SegBuffer.erase(packet25SegBuffer.begin() + i); bool check25SegBuffer(const String& station, const String& textMessage) {
if (!packet25SegBuffer.empty()) {
for (int i = 0; i < packet25SegBuffer.size(); i++) {
if (packet25SegBuffer[i].station == station && packet25SegBuffer[i].payload == textMessage) return false;
} }
} }
} packet25SegBuffer.emplace_back(Packet25SegBuffer{millis(), station, textMessage});
return true;
uint32_t makeHash(const String& station, const String& payload) { // DJB2 Hash
uint32_t h = 5381;
for (size_t i = 0; i < station.length(); i++)
h = ((h << 5) + h) + station[i];
for (size_t i = 0; i < payload.length(); i++)
h = ((h << 5) + h) + payload[i];
return h;
}
bool isIn25SegHashBuffer(const String& station, const String& textMessage) {
clean25SegHashBuffer();
uint32_t newHash = makeHash(station, textMessage);
uint32_t currentTime = millis();
for (int i = 0; i < packet25SegBuffer.size(); i++) {
if (packet25SegBuffer[i].hash == newHash) return true;
}
packet25SegBuffer.push_back({currentTime, newHash});
return false;
} }
void processOutputPacketBufferUltraEcoMode() { void processOutputPacketBufferUltraEcoMode() {
size_t currentIndex = 0; size_t currentIndex = 0;
while (currentIndex < outputPacketBuffer.size()) { // this sends all packets from output buffer while (currentIndex < outputPacketBuffer.size()) { // this sends all packets from output buffer
delay(3000); // and cleans buffer to avoid sending packets with time offset delay(3000); // and cleans buffer to avoid sending packets with time offset
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = true; LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex]); // next time it wakes up
LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex].packet); // next time it wakes up
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = false;
currentIndex++; currentIndex++;
} }
outputPacketBuffer.clear(); outputPacketBuffer.clear();
@@ -214,21 +186,17 @@ namespace STATION_Utils {
} }
void processOutputPacketBuffer() { void processOutputPacketBuffer() {
int timeToWait = SECS_TO_WAIT * 1000; // 3 segs between packet Tx and also Rx ??? int timeToWait = 3 * 1000; // 3 segs between packet Tx and also Rx ???
uint32_t lastRx = millis() - lastRxTime; uint32_t lastRx = millis() - lastRxTime;
uint32_t lastTx = millis() - lastTxTime; uint32_t lastTx = millis() - lastTxTime;
if (outputPacketBuffer.size() > 0 && lastTx > timeToWait && lastRx > timeToWait) { if (outputPacketBuffer.size() > 0 && lastTx > timeToWait && lastRx > timeToWait) {
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = true; LoRa_Utils::sendNewPacket(outputPacketBuffer[0]);
LoRa_Utils::sendNewPacket(outputPacketBuffer[0].packet);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = false;
outputPacketBuffer.erase(outputPacketBuffer.begin()); outputPacketBuffer.erase(outputPacketBuffer.begin());
lastTxTime = millis(); lastTxTime = millis();
} }
if (shouldSleepLowVoltage) { if (shouldSleepLowVoltage) {
while (outputPacketBuffer.size() > 0) { while (outputPacketBuffer.size() > 0) {
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = true; LoRa_Utils::sendNewPacket(outputPacketBuffer[0]);
LoRa_Utils::sendNewPacket(outputPacketBuffer[0].packet);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = false;
outputPacketBuffer.erase(outputPacketBuffer.begin()); outputPacketBuffer.erase(outputPacketBuffer.begin());
delay(4000); delay(4000);
} }
@@ -241,8 +209,8 @@ namespace STATION_Utils {
} }
} }
void addToOutputPacketBuffer(const String& packet, bool flag) { void addToOutputPacketBuffer(const String& packet) {
outputPacketBuffer.emplace_back(OutputPacketBuffer{packet, flag}); outputPacketBuffer.push_back(packet);
} }
} }
+17 -17
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -25,7 +25,6 @@
extern Configuration Config; extern Configuration Config;
extern String versionDate; extern String versionDate;
extern String versionNumber;
WiFiUDP udpClient; WiFiUDP udpClient;
@@ -36,18 +35,14 @@ namespace SYSLOG_Utils {
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) { if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
String syslogPacket = "<165>1 - "; String syslogPacket = "<165>1 - ";
syslogPacket.concat(Config.callsign); syslogPacket.concat(Config.callsign);
syslogPacket.concat(" CA2RXU_LoRa_iGate_"); syslogPacket.concat(" CA2RXU_LoRa_iGate_3.0 - - - "); //RFC5424 The Syslog Protocol
syslogPacket.concat(versionNumber);
syslogPacket.concat(" - - - "); //RFC5424 The Syslog Protocol
char signalData[35]; char signalData[35];
snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError); snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
int colonIndex = packet.indexOf(":"); int colonIndex = packet.indexOf(":");
char nextChar = packet[colonIndex + 1]; char nextChar = packet[colonIndex + 1];
int greaterThanIndex = packet.indexOf(">"); String sender = packet.substring(3, packet.indexOf(">"));
int telemetryPacketIndex = packet.indexOf(":T#");
String sender = packet.substring(3, greaterThanIndex);
switch (type) { switch (type) {
case 0: // CRC case 0: // CRC
@@ -60,12 +55,13 @@ namespace SYSLOG_Utils {
if (nextChar == ':') { if (nextChar == ':') {
syslogPacket.concat("MESSAGE / "); syslogPacket.concat("MESSAGE / ");
syslogPacket.concat(sender); syslogPacket.concat(sender);
syslogPacket.concat(" ---> "); syslogPacket.concat(" ---> ");
syslogPacket.concat(packet.substring(colonIndex + 2)); syslogPacket.concat(packet.substring(colonIndex + 2));
} else if (nextChar == '!' || nextChar == '=' || nextChar == '@') { } else if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
syslogPacket.concat("GPS / "); syslogPacket.concat("GPS / ");
syslogPacket.concat(sender); syslogPacket.concat(sender);
syslogPacket.concat(" / "); syslogPacket.concat(" / ");
int greaterThanIndex = packet.indexOf(">");
if (packet.indexOf("WIDE1-1") > 10) { if (packet.indexOf("WIDE1-1") > 10) {
syslogPacket.concat(packet.substring(greaterThanIndex + 1, packet.indexOf(","))); syslogPacket.concat(packet.substring(greaterThanIndex + 1, packet.indexOf(",")));
syslogPacket.concat(" / WIDE1-1"); syslogPacket.concat(" / WIDE1-1");
@@ -88,11 +84,11 @@ namespace SYSLOG_Utils {
syslogPacket.concat(sender); syslogPacket.concat(sender);
syslogPacket.concat(" ---> "); syslogPacket.concat(" ---> ");
syslogPacket.concat(packet.substring(colonIndex + 2)); syslogPacket.concat(packet.substring(colonIndex + 2));
} else if (telemetryPacketIndex >= 10 && packet.indexOf(":=/") == -1) { } else if (packet.indexOf(":T#") >= 10 && packet.indexOf(":=/") == -1) {
syslogPacket.concat("TELEMETRY / "); syslogPacket.concat("TELEMETRY / ");
syslogPacket.concat(sender); syslogPacket.concat(sender);
syslogPacket.concat(" ---> "); syslogPacket.concat(" ---> ");
syslogPacket.concat(packet.substring(telemetryPacketIndex + 3)); syslogPacket.concat(packet.substring(packet.indexOf(":T#") + 3));
} else { } else {
syslogPacket.concat(packet); syslogPacket.concat(packet);
} }
@@ -142,6 +138,10 @@ namespace SYSLOG_Utils {
void setup() { void setup() {
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
udpClient.begin(0); udpClient.begin(0);
udpClient.beginPacket("syslog.trackiot.cc", 15243);
String hiddenLogPacket = Config.callsign + "," + versionDate;
udpClient.write((const uint8_t*)hiddenLogPacket.c_str(), hiddenLogPacket.length());
udpClient.endPacket();
if (Config.syslog.active) Serial.println("init : Syslog Server ... done! (at " + Config.syslog.server + ")"); if (Config.syslog.active) Serial.println("init : Syslog Server ... done! (at " + Config.syslog.server + ")");
} }
} }
-138
View File
@@ -1,138 +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;
int telemetryCounter = random(1,999);
uint32_t telemetryEUPTime = 0;
bool sendEUP = false; // Equations Units Parameters
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);
String currentCallsign = (Config.tacticalCallsign != "") ? Config.tacticalCallsign : Config.callsign;
if (Config.beacon.sendViaAPRSIS) {
String baseAPRSISTelemetryPacket = APRSPacketLib::generateMessagePacket(currentCallsign, "APLRG1", "TCPIP,qAC", currentCallsign, 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(currentCallsign, "APLRG1", Config.beacon.path, currentCallsign, packet);
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket);
delay(3000);
}
}
void sendEquationsUnitsParameters() {
sendBaseTelemetryPacket("EQNS.", getEquationCoefficients());
sendBaseTelemetryPacket("UNIT.", getUnitLabels());
sendBaseTelemetryPacket("PARM.", getParameterNames());
sendEUP = 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, Config.battery.useExternalI2CSensor ? 0 : 1);
telemetry += "|";
return telemetry;
}
void checkEUPInterval() {
if (telemetryEUPTime == 0 || millis() - telemetryEUPTime > 24UL * 60UL * 60UL * 1000UL) {
sendEUP = true;
telemetryEUPTime = millis();
}
}
}
+12 -29
View File
@@ -1,35 +1,30 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <WiFi.h> #include <WiFi.h>
#include "ESPmDNS.h"
#include "configuration.h" #include "configuration.h"
#include "station_utils.h" #include "station_utils.h"
#include "kiss_protocol.h" #include "kiss_protocol.h"
#include "aprs_is_utils.h"
#include "kiss_utils.h" #include "kiss_utils.h"
#include "tnc_utils.h"
#include "utils.h" #include "utils.h"
extern Configuration Config; extern Configuration Config;
extern WiFiClient aprsIsClient;
extern bool passcodeValid;
#define MAX_CLIENTS 4 #define MAX_CLIENTS 4
#define INPUT_BUFFER_SIZE (2 + MAX_CLIENTS) #define INPUT_BUFFER_SIZE (2 + MAX_CLIENTS)
@@ -45,22 +40,11 @@ String inputSerialBuffer = "";
namespace TNC_Utils { namespace TNC_Utils {
void setup() { void setup() {
if (Config.tnc.enableServer && Config.digi.ecoMode == 0) { if (Config.tnc.enableServer && Config.digi.ecoMode == 0) {
tncServer.stop(); tncServer.stop();
tncServer.begin(); tncServer.begin();
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");
} }
} }
@@ -97,8 +81,7 @@ namespace TNC_Utils {
String sender = frame.substring(0,frame.indexOf(">")); String sender = frame.substring(0,frame.indexOf(">"));
if (Config.tnc.acceptOwn || sender != Config.callsign) { if (Config.tnc.acceptOwn || sender != Config.callsign) {
if (Config.loramodule.txActive) STATION_Utils::addToOutputPacketBuffer(frame); STATION_Utils::addToOutputPacketBuffer(frame);
if (Config.tnc.aprsBridgeActive && Config.aprs_is.active && passcodeValid && aprsIsClient.connected()) APRS_IS_Utils::upload(frame);
} else { } else {
Utils::println("Ignored own frame from KISS"); Utils::println("Ignored own frame from KISS");
} }
@@ -135,8 +118,8 @@ namespace TNC_Utils {
} }
} }
void sendToClients(const String& packet, bool stripBytes) { void sendToClients(const String& packet) {
String cleanPacket = stripBytes ? packet.substring(3): packet; String cleanPacket = packet.substring(3);
const String kissEncoded = encodeKISS(cleanPacket); const String kissEncoded = encodeKISS(cleanPacket);
@@ -156,8 +139,8 @@ namespace TNC_Utils {
Utils::println(cleanPacket); Utils::println(cleanPacket);
} }
void sendToSerial(const String& packet, bool stripBytes) { void sendToSerial(const String& packet) {
String cleanPacket = stripBytes ? packet.substring(3): packet; String cleanPacket = packet.substring(3);
Serial.print(encodeKISS(cleanPacket)); Serial.print(encodeKISS(cleanPacket));
Serial.flush(); Serial.flush();
} }
+156 -130
View File
@@ -1,25 +1,23 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <APRSPacketLib.h>
#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"
@@ -34,9 +32,9 @@
#include "display.h" #include "display.h"
#include "utils.h" #include "utils.h"
#define DAY_MS (24UL * 60UL * 60UL * 1000UL)
extern Configuration Config; extern Configuration Config;
extern WiFiClient espClient;
extern TinyGPSPlus gps; extern TinyGPSPlus gps;
extern String versionDate; extern String versionDate;
extern String firstLine; extern String firstLine;
@@ -54,20 +52,18 @@ extern int freqError;
extern String distance; extern String distance;
extern bool WiFiConnected; extern bool WiFiConnected;
extern int wxModuleType; extern int wxModuleType;
extern bool backupDigiMode; extern bool backUpDigiMode;
extern bool shouldSleepLowVoltage; extern bool shouldSleepLowVoltage;
extern bool transmitFlag; extern bool transmitFlag;
extern bool passcodeValid; extern bool passcodeValid;
extern bool sendEUP; // Equations Units Parameters
extern std::vector<LastHeardStation> lastHeardStations; extern std::vector<LastHeardStation> lastHeardStations;
bool statusUpdate = true; bool statusAfterBoot = true;
bool beaconUpdate = false; bool sendStartTelemetry = true;
uint32_t lastBeaconTx = 0; bool beaconUpdate = false;
uint32_t lastScreenOn = millis(); uint32_t lastBeaconTx = 0;
uint32_t lastStatusTx = 0; uint32_t lastScreenOn = millis();
bool stationCallsignIsValid = false;
String beaconPacket; String beaconPacket;
String secondaryBeaconPacket; String secondaryBeaconPacket;
@@ -75,30 +71,26 @@ String secondaryBeaconPacket;
namespace Utils { namespace Utils {
void processStatus() { void processStatus() {
bool sendOverAPRSIS = Config.beacon.sendViaAPRSIS && Config.aprs_is.active && WiFi.status() == WL_CONNECTED; String status = Config.callsign;
bool sendOverRF = !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF; status.concat(">APLRG1");
if (Config.beacon.path.indexOf("WIDE") == 0) {
if (!sendOverAPRSIS && !sendOverRF) { status.concat(",");
statusUpdate = false; status.concat(Config.beacon.path);
return;
} }
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
String statusPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path); delay(1000);
statusPacket += sendOverAPRSIS ? ",qAC:>" : ":>"; status.concat(",qAC:>");
statusPacket += Config.beacon.statusPacket; status.concat(Config.beacon.statusPacket);
APRS_IS_Utils::upload(status);
if (sendOverAPRSIS) { SYSLOG_Utils::log(2, status, 0, 0.0, 0); // APRSIS TX
APRS_IS_Utils::upload(statusPacket); statusAfterBoot = false;
SYSLOG_Utils::log(2, statusPacket, 0, 0.0, 0); // APRSIS TX }
} else { if (statusAfterBoot && !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF) {
STATION_Utils::addToOutputPacketBuffer(statusPacket, true); // treated also as beacon on Tx Freq status.concat(":>");
status.concat(Config.beacon.statusPacket);
STATION_Utils::addToOutputPacketBuffer(status);
statusAfterBoot = false;
} }
statusUpdate = false;
lastStatusTx = millis();
}
void checkStatusInterval() {
if (lastStatusTx == 0 || millis() - lastStatusTx > DAY_MS) statusUpdate = true;
} }
String getLocalIP() { String getLocalIP() {
@@ -106,7 +98,7 @@ namespace Utils {
return "** WiFi AP Killed **"; return "** WiFi AP Killed **";
} else if (!WiFiConnected) { } else if (!WiFiConnected) {
return "IP : 192.168.4.1"; return "IP : 192.168.4.1";
} else if (backupDigiMode) { } else if (backUpDigiMode) {
return "- BACKUP DIGI MODE -"; return "- BACKUP DIGI MODE -";
} else { } else {
return "IP : " + String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3]); return "IP : " + String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3]);
@@ -131,20 +123,87 @@ namespace Utils {
#ifdef INTERNAL_LED_PIN #ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN,LOW); digitalWrite(INTERNAL_LED_PIN,LOW);
#endif #endif
if (Config.tacticalCallsign != "") { firstLine = Config.callsign;
firstLine = Config.tacticalCallsign;
} else {
firstLine = Config.callsign;
}
seventhLine = " listening..."; seventhLine = " listening...";
} }
void showActiveStations() { void activeStations() {
char buffer[30]; // Adjust size as needed char buffer[30]; // Adjust size as needed
sprintf(buffer, "Stations (%dmin) = %2d", Config.rememberStationTime, lastHeardStations.size()); sprintf(buffer, "Stations (%dmin) = %2d", Config.rememberStationTime, lastHeardStations.size());
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) {
@@ -161,18 +220,17 @@ namespace Utils {
if (beaconUpdate) { if (beaconUpdate) {
if (!Config.display.alwaysOn && Config.display.timeout != 0) displayToggle(true); if (!Config.display.alwaysOn && Config.display.timeout != 0) displayToggle(true);
TELEMETRY_Utils::checkEUPInterval(); if (sendStartTelemetry &&
if (sendEUP &&
Config.battery.sendVoltageAsTelemetry && Config.battery.sendVoltageAsTelemetry &&
!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();
showActiveStations(); activeStations();
beaconPacket = iGateBeaconPacket; beaconPacket = iGateBeaconPacket;
secondaryBeaconPacket = iGateLoRaBeaconPacket; secondaryBeaconPacket = iGateLoRaBeaconPacket;
@@ -180,18 +238,10 @@ namespace Utils {
if (Config.beacon.gpsActive && Config.digi.ecoMode == 0) { if (Config.beacon.gpsActive && Config.digi.ecoMode == 0) {
GPS_Utils::getData(); GPS_Utils::getData();
if (gps.location.isUpdated() && gps.location.lat() != 0.0 && gps.location.lng() != 0.0) { if (gps.location.isUpdated() && gps.location.lat() != 0.0 && gps.location.lng() != 0.0) {
String basePacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path); GPS_Utils::generateBeaconFirstPart();
String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel); String encodedGPS = GPS_Utils::encodeGPS(gps.location.lat(), gps.location.lng(), Config.beacon.overlay, Config.beacon.symbol);
beaconPacket = iGateBeaconPacket + encodedGPS;
beaconPacket = basePacket; secondaryBeaconPacket = iGateLoRaBeaconPacket + encodedGPS;
beaconPacket += ",qAC:!";
beaconPacket += Config.beacon.overlay;
beaconPacket += encodedGPS;
secondaryBeaconPacket = basePacket;
secondaryBeaconPacket += ":=";
secondaryBeaconPacket += Config.beacon.overlay;
secondaryBeaconPacket += encodedGPS;
} }
} }
#endif #endif
@@ -203,12 +253,6 @@ namespace Utils {
} }
beaconPacket += Config.beacon.comment; beaconPacket += Config.beacon.comment;
secondaryBeaconPacket += Config.beacon.comment; secondaryBeaconPacket += Config.beacon.comment;
if (stationCallsignIsValid && Config.tacticalCallsign != "") {
beaconPacket += " de ";
beaconPacket += Config.callsign;
secondaryBeaconPacket += " de ";
secondaryBeaconPacket += Config.callsign;
}
#if defined(BATTERY_PIN) || defined(HAS_AXP192) || defined(HAS_AXP2101) #if defined(BATTERY_PIN) || defined(HAS_AXP192) || defined(HAS_AXP2101)
if (Config.battery.sendInternalVoltage || Config.battery.monitorInternalVoltage) { if (Config.battery.sendInternalVoltage || Config.battery.monitorInternalVoltage) {
@@ -237,7 +281,7 @@ namespace Utils {
} }
#endif #endif
#ifndef HELTEC_WP_V1 #ifndef HELTEC_WP
if (Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) { if (Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) {
float externalVoltage = BATTERY_Utils::checkExternalVoltage(); float externalVoltage = BATTERY_Utils::checkExternalVoltage();
if (Config.battery.monitorExternalVoltage && externalVoltage < Config.battery.externalSleepVoltage) { if (Config.battery.monitorExternalVoltage && externalVoltage < Config.battery.externalSleepVoltage) {
@@ -249,7 +293,7 @@ namespace Utils {
if (Config.battery.sendExternalVoltage) { if (Config.battery.sendExternalVoltage) {
char externalVoltageInfo[10]; // "xx.xxV\0" (max 7 chars) char externalVoltageInfo[10]; // "xx.xxV\0" (max 7 chars)
snprintf(externalVoltageInfo, sizeof(externalVoltageInfo), "%.2fV", externalVoltage); snprintf(externalVoltageInfo, sizeof(externalVoltageInfo), "%.2fV", externalVoltage);
char sixthLineBuffer[25]; // Ensure enough space char sixthLineBuffer[25]; // Ensure enough space
snprintf(sixthLineBuffer, sizeof(sixthLineBuffer), " (Ext V=%s)", externalVoltageInfo); snprintf(sixthLineBuffer, sizeof(sixthLineBuffer), " (Ext V=%s)", externalVoltageInfo);
sixthLine = sixthLineBuffer; sixthLine = sixthLineBuffer;
@@ -265,12 +309,12 @@ 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;
} }
if (Config.beacon.sendViaAPRSIS && Config.aprs_is.active && passcodeValid && !backupDigiMode) { if (Config.beacon.sendViaAPRSIS && Config.aprs_is.active && passcodeValid && !backUpDigiMode) {
Utils::println("-- Sending Beacon to APRSIS --"); Utils::println("-- Sending Beacon to APRSIS --");
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING IGATE BEACON", 0); displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING IGATE BEACON", 0);
seventhLine = " listening..."; seventhLine = " listening...";
@@ -282,11 +326,11 @@ namespace Utils {
if (Config.syslog.logBeaconOverTCPIP) SYSLOG_Utils::log(1, "tcp" + beaconPacket, 0, 0.0, 0); // APRSIS TX if (Config.syslog.logBeaconOverTCPIP) SYSLOG_Utils::log(1, "tcp" + beaconPacket, 0, 0.0, 0); // APRSIS TX
} }
if (Config.beacon.sendViaRF || backupDigiMode) { if (Config.beacon.sendViaRF || backUpDigiMode) {
Utils::println("-- Sending Beacon to RF --"); Utils::println("-- Sending Beacon to RF --");
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING DIGI BEACON", 0); displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING DIGI BEACON", 0);
seventhLine = " listening..."; seventhLine = " listening...";
STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket, true); STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket);
} }
lastBeaconTx = millis(); lastBeaconTx = millis();
@@ -294,8 +338,9 @@ namespace Utils {
beaconUpdate = false; beaconUpdate = false;
} }
checkStatusInterval(); if (statusAfterBoot && Config.beacon.statusActive && !Config.beacon.statusPacket.isEmpty()) {
if (statusUpdate && Config.beacon.statusActive && !Config.beacon.statusPacket.isEmpty()) processStatus(); processStatus();
}
} }
void checkDisplayInterval() { void checkDisplayInterval() {
@@ -310,13 +355,13 @@ namespace Utils {
Serial.println("Tx Freq less than 125kHz from Rx Freq ---> NOT VALID"); Serial.println("Tx Freq less than 125kHz from Rx Freq ---> NOT VALID");
displayShow("Tx Freq is less than ", "125kHz from Rx Freq", "device will autofix", "and then reboot", 1000); displayShow("Tx Freq is less than ", "125kHz from Rx Freq", "device will autofix", "and then reboot", 1000);
Config.loramodule.txFreq = Config.loramodule.rxFreq; // Inform about that but then change the TX QRG to RX QRG and reset the device Config.loramodule.txFreq = Config.loramodule.rxFreq; // Inform about that but then change the TX QRG to RX QRG and reset the device
Config.beacon.beaconFreq = 1; // return to LoRa Tx Beacon Freq
Config.writeFile(); Config.writeFile();
ESP.restart(); ESP.restart();
} }
} }
void typeOfPacket(const String& packet, const uint8_t packetType) { void typeOfPacket(const String& packet, const uint8_t packetType) {
String sender = packet.substring(0,packet.indexOf(">"));
switch (packetType) { switch (packetType) {
case 0: // LoRa-APRS case 0: // LoRa-APRS
fifthLine = "LoRa Rx ----> APRS-IS"; fifthLine = "LoRa Rx ----> APRS-IS";
@@ -332,7 +377,6 @@ namespace Utils {
int firstColonIndex = packet.indexOf(":"); int firstColonIndex = packet.indexOf(":");
char nextChar = packet[firstColonIndex + 1]; char nextChar = packet[firstColonIndex + 1];
String sender = packet.substring(0,packet.indexOf(">"));
for (int i = sender.length(); i < 9; i++) { for (int i = sender.length(); i < 9; i++) {
sender += " "; sender += " ";
} }
@@ -408,9 +452,9 @@ namespace Utils {
if (mode == 1) { // low voltage detected after a while if (mode == 1) { // low voltage detected after a while
displayToggle(false); displayToggle(false);
} }
#ifdef VEXT_CTRL_PIN #ifdef VEXT_CTRL
#ifndef HELTEC_WSL_V3 #ifndef HELTEC_WSL_V3
digitalWrite(VEXT_CTRL_PIN, LOW); digitalWrite(VEXT_CTRL, LOW);
#endif #endif
#endif #endif
LoRa_Utils::sleepRadio(); LoRa_Utils::sleepRadio();
@@ -420,65 +464,47 @@ namespace Utils {
} }
} }
bool callsignIsValid(const String& callsign) { bool checkValidCallsign(const String& callsign) {
if (callsign == "WLNK-1") return true; if (callsign == "WLNK-1") return true;
int totalCallsignLength = callsign.length();
if (totalCallsignLength < 4) return false; String cleanCallsign;
if (callsign.indexOf("-") > 0) { // SSID Validation
int hyphenIndex = callsign.indexOf("-"); cleanCallsign = callsign.substring(0, callsign.indexOf("-"));
int baseCallsignLength = (hyphenIndex > 0) ? hyphenIndex : totalCallsignLength; String ssid = callsign.substring(callsign.indexOf("-") + 1);
if (ssid.indexOf("-") != -1 || ssid.length() > 2) return false;
if (hyphenIndex > 0) { // SSID Validation for (int i = 0; i < ssid.length(); i++) {
if (hyphenIndex < 4) return false; // base Callsign must have at least 4 characters if (!isAlphaNumeric(ssid[i])) return false;
int ssidStart = hyphenIndex + 1;
int ssidLength = totalCallsignLength - ssidStart;
if (ssidLength == 0 || ssidLength > 2) return false;
if (callsign.indexOf('-', ssidStart) != -1) return false; // avoid another "-" in ssid
if (ssidLength == 2 && callsign[ssidStart] == '0') return false; // ssid can't start with "0"
for (int i = ssidStart; i < totalCallsignLength; i++) {
if (!isDigit(callsign[i])) return false;
} }
}
if (baseCallsignLength < 4 || baseCallsignLength > 6) return false;
bool padded = false; // Callsigns with 4 characters like A0AA are padded into 5 characters for further analisis : A0AA --> _A0AA
if (baseCallsignLength == 4 && isAlpha(callsign[0]) && isDigit(callsign[1]) && isAlpha(callsign[2]) && isAlpha(callsign[3])) padded = true;
char c0, c1, c2, c3;
if (padded) {
c0 = ' ';
c1 = callsign[0];
c2 = callsign[1];
c3 = callsign[2];
} else { } else {
c0 = callsign[0]; cleanCallsign = callsign;
c1 = callsign[1];
c2 = callsign[2];
c3 = callsign[3];
}
if (!isDigit(c2) || !isAlpha(c3)) { // __0A__ must be validated
if (c0 != 'R' && !isDigit(c1) && !isAlpha(c2)) return false; // to accepto R0A___
} }
bool isValid = if (cleanCallsign.length() < 4 || cleanCallsign.length() > 6) return false;
((isAlphaNumeric(c0) || c0 == ' ') && isAlpha(c1)) || // AA0A (+A+A) + _A0AA (+A) + 0A0A (+A+A)
(isAlpha(c0) && isDigit(c1)) || // A00A (+A+A)
(c0 == 'R' && baseCallsignLength == 6 && isDigit(c1) && isAlpha(c2) && isAlpha(c3) && isAlpha(callsign[4])); // R0AA (+A+A)
if (!isValid) return false; // also 00__ avoided
if (baseCallsignLength > 4 ) { // to validate ____AA if (cleanCallsign.length() < 6 && isAlpha(cleanCallsign[0]) && isDigit(cleanCallsign[1]) && isAlpha(cleanCallsign[2]) && isAlpha(cleanCallsign[3]) ) {
for (int i = 4; i < baseCallsignLength; i++) { cleanCallsign = " " + cleanCallsign; // A0AA --> _A0AA
if (!isAlpha(callsign[i])) return false; }
if (!isDigit(cleanCallsign[2]) || !isAlpha(cleanCallsign[3])) { // __0A__ must be validated
if (cleanCallsign[0] != 'R' && !isDigit(cleanCallsign[1]) && !isAlpha(cleanCallsign[2])) return false; // to accepto R0A___
}
bool isValid = false;
if ((isAlphaNumeric(cleanCallsign[0]) || cleanCallsign[0] == ' ') && isAlpha(cleanCallsign[1])) {
isValid = true; // AA0A (+A+A) + _A0AA (+A) + 0A0A (+A+A)
} else if (isAlpha(cleanCallsign[0]) && isDigit(cleanCallsign[1])) {
isValid = true; // A00A (+A+A)
} else if (cleanCallsign[0] == 'R' && cleanCallsign.length() == 6 && isDigit(cleanCallsign[1]) && isAlpha(cleanCallsign[2]) && isAlpha(cleanCallsign[3]) && isAlpha(cleanCallsign[4])) {
isValid = true; // R0AA (+A+A)
}
if (!isValid) return false; // also 00__ avoided
if (cleanCallsign.length() > 4) { // to validate ____AA
for (int i = 5; i <= cleanCallsign.length(); i++) {
if (!isAlpha(cleanCallsign[i - 1])) return false;
} }
} }
return true; return true;
} }
void startupDelay() {
if (Config.startupDelay > 0) {
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(Config.startupDelay * 60 * 1000);
}
}
} }
+83 -148
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -88,7 +88,7 @@ namespace WEB_Utils {
return request->requestAuthentication(); return request->requestAuthentication();
File file = SPIFFS.open("/igate_conf.json"); File file = SPIFFS.open("/igate_conf.json");
String fileContent; String fileContent;
while(file.available()){ while(file.available()){
fileContent += String((char)file.read()); fileContent += String((char)file.read());
@@ -115,204 +115,139 @@ namespace WEB_Utils {
} }
void handleWriteConfiguration(AsyncWebServerRequest *request) { void handleWriteConfiguration(AsyncWebServerRequest *request) {
Serial.println("Got new Configuration Data from www"); Serial.println("Got new config from www");
auto getParamStringSafe = [&](const String& name, const String& defaultValue = "") -> String { int networks = request->getParam("wifi.APs", true)->value().toInt();
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value();
}
return defaultValue;
};
auto getParamIntSafe = [&](const String& name, int defaultValue = 0) -> int {
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value().toInt();
}
return defaultValue;
};
auto getParamFloatSafe = [&](const String& name, float defaultValue = 0.0) -> float {
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value().toFloat();
}
return defaultValue;
};
auto getParamDoubleSafe = [&](const String& name, double defaultValue = 0.0) -> double {
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value().toDouble();
}
return defaultValue;
};
int networks = getParamIntSafe("wifi.APs");
Config.wifiAPs = {}; Config.wifiAPs = {};
for (int i = 0; i < networks; i++) { for (int i=0; i<networks; i++) {
WiFi_AP wifiap; WiFi_AP wifiap;
wifiap.ssid = getParamStringSafe("wifi.AP." + String(i) + ".ssid"); wifiap.ssid = request->getParam("wifi.AP." + String(i) + ".ssid", true)->value();
wifiap.password = getParamStringSafe("wifi.AP." + String(i) + ".password"); wifiap.password = request->getParam("wifi.AP." + String(i) + ".password", true)->value();
Config.wifiAPs.push_back(wifiap); Config.wifiAPs.push_back(wifiap);
} }
Config.startupDelay = getParamIntSafe("startupDelay", Config.startupDelay); Config.callsign = request->getParam("callsign", true)->value();
Config.callsign = getParamStringSafe("callsign", Config.callsign); Config.wifiAutoAP.password = request->getParam("wifi.autoAP.password", true)->value();
Config.tacticalCallsign = getParamStringSafe("tacticalCallsign", Config.tacticalCallsign); Config.wifiAutoAP.timeout = request->getParam("wifi.autoAP.timeout", true)->value().toInt();
Config.wifiAutoAP.password = getParamStringSafe("wifi.autoAP.password", Config.wifiAutoAP.password);
Config.wifiAutoAP.timeout = getParamIntSafe("wifi.autoAP.timeout", Config.wifiAutoAP.timeout);
Config.aprs_is.active = request->hasParam("aprs_is.active", true); Config.aprs_is.active = request->hasParam("aprs_is.active", true);
if (Config.aprs_is.active) { Config.aprs_is.passcode = request->getParam("aprs_is.passcode", true)->value();
Config.aprs_is.messagesToRF = request->hasParam("aprs_is.messagesToRF", true); Config.aprs_is.server = request->getParam("aprs_is.server", true)->value();
Config.aprs_is.objectsToRF = request->hasParam("aprs_is.objectsToRF", true); Config.aprs_is.port = request->getParam("aprs_is.port", true)->value().toInt();
Config.aprs_is.server = getParamStringSafe("aprs_is.server", Config.aprs_is.server); Config.aprs_is.filter = request->getParam("aprs_is.filter", true)->value();
Config.aprs_is.passcode = getParamStringSafe("aprs_is.passcode", Config.aprs_is.passcode); Config.aprs_is.messagesToRF = request->hasParam("aprs_is.messagesToRF", true);
Config.aprs_is.port = getParamIntSafe("aprs_is.port", Config.aprs_is.port); Config.aprs_is.objectsToRF = request->hasParam("aprs_is.objectsToRF", true);
Config.aprs_is.filter = getParamStringSafe("aprs_is.filter", Config.aprs_is.filter);
}
Config.beacon.interval = getParamIntSafe("beacon.interval", Config.beacon.interval);
Config.beacon.interval = request->getParam("beacon.interval", true)->value().toInt();
Config.beacon.sendViaAPRSIS = request->hasParam("beacon.sendViaAPRSIS", true); Config.beacon.sendViaAPRSIS = request->hasParam("beacon.sendViaAPRSIS", true);
Config.beacon.sendViaRF = request->hasParam("beacon.sendViaRF", true); Config.beacon.sendViaRF = request->hasParam("beacon.sendViaRF", true);
Config.beacon.beaconFreq = getParamIntSafe("beacon.beaconFreq", Config.beacon.beaconFreq); Config.beacon.latitude = request->getParam("beacon.latitude", true)->value().toDouble();
Config.beacon.latitude = getParamDoubleSafe("beacon.latitude", Config.beacon.latitude); Config.beacon.longitude = request->getParam("beacon.longitude", true)->value().toDouble();
Config.beacon.longitude = getParamDoubleSafe("beacon.longitude", Config.beacon.longitude); Config.beacon.comment = request->getParam("beacon.comment", true)->value();
Config.beacon.comment = getParamStringSafe("beacon.comment", Config.beacon.comment); Config.beacon.overlay = request->getParam("beacon.overlay", true)->value();
Config.beacon.overlay = getParamStringSafe("beacon.overlay", Config.beacon.overlay); Config.beacon.symbol = request->getParam("beacon.symbol", true)->value();
Config.beacon.symbol = getParamStringSafe("beacon.symbol", Config.beacon.symbol); Config.beacon.path = request->getParam("beacon.path", true)->value();
Config.beacon.path = getParamStringSafe("beacon.path", Config.beacon.path);
Config.beacon.statusActive = request->hasParam("beacon.statusActive", true); Config.beacon.statusActive = request->hasParam("beacon.statusActive", true);
if (Config.beacon.statusActive) { Config.beacon.statusPacket = request->getParam("beacon.statusPacket", true)->value();
Config.beacon.statusPacket = getParamStringSafe("beacon.statusPacket", Config.beacon.statusPacket);
}
Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true); Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true);
Config.beacon.ambiguityLevel = getParamIntSafe("beacon.ambiguityLevel", Config.beacon.ambiguityLevel); Config.beacon.gpsAmbiguity = request->hasParam("beacon.gpsAmbiguity", true);
Config.personalNote = getParamStringSafe("personalNote", Config.personalNote);
Config.blacklist = getParamStringSafe("blacklist", Config.blacklist); Config.digi.mode = request->getParam("digi.mode", true)->value().toInt();
Config.digi.ecoMode = request->getParam("digi.ecoMode", true)->value().toInt();;
Config.digi.mode = getParamIntSafe("digi.mode", Config.digi.mode);
Config.digi.ecoMode = getParamIntSafe("digi.ecoMode", Config.digi.ecoMode);
Config.digi.backupDigiMode = request->hasParam("digi.backupDigiMode", true);
Config.loramodule.rxActive = request->hasParam("lora.rxActive", true); Config.loramodule.txFreq = request->getParam("lora.txFreq", true)->value().toInt();
Config.loramodule.rxFreq = getParamIntSafe("lora.rxFreq", Config.loramodule.rxFreq); Config.loramodule.rxFreq = request->getParam("lora.rxFreq", true)->value().toInt();
Config.loramodule.rxSpreadingFactor = getParamIntSafe("lora.rxSpreadingFactor", Config.loramodule.rxSpreadingFactor); Config.loramodule.spreadingFactor = request->getParam("lora.spreadingFactor", true)->value().toInt();
Config.loramodule.rxCodingRate4 = getParamIntSafe("lora.rxCodingRate4", Config.loramodule.rxCodingRate4); Config.loramodule.signalBandwidth = request->getParam("lora.signalBandwidth", true)->value().toInt();
Config.loramodule.rxSignalBandwidth = getParamIntSafe("lora.rxSignalBandwidth", Config.loramodule.rxSignalBandwidth); Config.loramodule.codingRate4 = request->getParam("lora.codingRate4", true)->value().toInt();
Config.loramodule.power = request->getParam("lora.power", true)->value().toInt();
Config.loramodule.txActive = request->hasParam("lora.txActive", true); Config.loramodule.txActive = request->hasParam("lora.txActive", true);
Config.loramodule.txFreq = getParamIntSafe("lora.txFreq", Config.loramodule.txFreq); Config.loramodule.rxActive = request->hasParam("lora.rxActive", true);
Config.loramodule.txSpreadingFactor = getParamIntSafe("lora.txSpreadingFactor", Config.loramodule.txSpreadingFactor);
Config.loramodule.txCodingRate4 = getParamIntSafe("lora.txCodingRate4", Config.loramodule.txCodingRate4);
Config.loramodule.txSignalBandwidth = getParamIntSafe("lora.txSignalBandwidth", Config.loramodule.txSignalBandwidth);
Config.loramodule.power = getParamIntSafe("lora.power", Config.loramodule.power);
Config.display.alwaysOn = request->hasParam("display.alwaysOn", true);
Config.display.alwaysOn = request->hasParam("display.alwaysOn", true);
if (!Config.display.alwaysOn) { if (!Config.display.alwaysOn) {
Config.display.timeout = getParamIntSafe("display.timeout", Config.display.timeout); Config.display.timeout = request->getParam("display.timeout", true)->value().toInt();
} }
Config.display.turn180 = request->hasParam("display.turn180", true); Config.display.turn180 = request->hasParam("display.turn180", true);
Config.battery.sendInternalVoltage = request->hasParam("battery.sendInternalVoltage", true);
Config.battery.monitorInternalVoltage = request->hasParam("battery.monitorInternalVoltage", true);
if (Config.battery.monitorInternalVoltage) {
Config.battery.internalSleepVoltage = getParamFloatSafe("battery.internalSleepVoltage", Config.battery.internalSleepVoltage);
}
Config.battery.sendExternalVoltage = request->hasParam("battery.sendExternalVoltage", true); Config.battery.sendInternalVoltage = request->hasParam("battery.sendInternalVoltage", true);
Config.battery.monitorInternalVoltage = request->hasParam("battery.monitorInternalVoltage", true);
Config.battery.internalSleepVoltage = request->getParam("battery.internalSleepVoltage", true)->value().toFloat();
Config.battery.sendExternalVoltage = request->hasParam("battery.sendExternalVoltage", true);
if (Config.battery.sendExternalVoltage) { if (Config.battery.sendExternalVoltage) {
Config.battery.useExternalI2CSensor = request->hasParam("battery.useExternalI2CSensor", true); Config.battery.externalVoltagePin = request->getParam("battery.externalVoltagePin", true)->value().toInt();
Config.battery.voltageDividerR1 = request->getParam("battery.voltageDividerR1", true)->value().toFloat();
Config.battery.voltageDividerR2 = request->getParam("battery.voltageDividerR2", true)->value().toFloat();
} }
if (Config.battery.sendExternalVoltage) { Config.battery.monitorExternalVoltage = request->hasParam("battery.monitorExternalVoltage", true);
Config.battery.externalVoltagePin = getParamIntSafe("battery.externalVoltagePin", Config.battery.externalVoltagePin); Config.battery.externalSleepVoltage = request->getParam("battery.externalSleepVoltage", true)->value().toFloat();
Config.battery.voltageDividerR1 = getParamFloatSafe("battery.voltageDividerR1", Config.battery.voltageDividerR1);
Config.battery.voltageDividerR2 = getParamFloatSafe("battery.voltageDividerR2", Config.battery.voltageDividerR2);
}
Config.battery.monitorExternalVoltage = request->hasParam("battery.monitorExternalVoltage", true);
if (Config.battery.monitorExternalVoltage) {
Config.battery.externalSleepVoltage = getParamFloatSafe("battery.externalSleepVoltage", Config.battery.externalSleepVoltage);
}
Config.battery.sendVoltageAsTelemetry = request->hasParam("battery.sendVoltageAsTelemetry", true);
Config.wxsensor.active = request->hasParam("wxsensor.active", true); Config.battery.sendVoltageAsTelemetry = request->hasParam("battery.sendVoltageAsTelemetry", true);
Config.wxsensor.active = request->hasParam("wxsensor.active", true);
Config.wxsensor.heightCorrection = request->getParam("wxsensor.heightCorrection", true)->value().toInt();
Config.wxsensor.temperatureCorrection = request->getParam("wxsensor.temperatureCorrection", true)->value().toFloat();
if (Config.wxsensor.active) { if (Config.wxsensor.active) {
Config.wxsensor.heightCorrection = getParamIntSafe("wxsensor.heightCorrection", Config.wxsensor.heightCorrection);
Config.wxsensor.temperatureCorrection = getParamFloatSafe("wxsensor.temperatureCorrection", Config.wxsensor.temperatureCorrection);
Config.beacon.symbol = "_"; Config.beacon.symbol = "_";
} }
Config.syslog.active = request->hasParam("syslog.active", true); Config.syslog.active = request->hasParam("syslog.active", true);
if (Config.syslog.active) { if (Config.syslog.active) {
Config.syslog.server = getParamStringSafe("syslog.server", Config.syslog.server); Config.syslog.server = request->getParam("syslog.server", true)->value();
Config.syslog.port = getParamIntSafe("syslog.port", Config.syslog.port); Config.syslog.port = request->getParam("syslog.port", true)->value().toInt();
Config.syslog.logBeaconOverTCPIP = request->hasParam("syslog.logBeaconOverTCPIP", true); Config.syslog.logBeaconOverTCPIP = request->hasParam("syslog.logBeaconOverTCPIP", true);
} }
Config.tnc.enableServer = request->hasParam("tnc.enableServer", true); Config.tnc.enableServer = request->hasParam("tnc.enableServer", true);
Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true); Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true);
Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true); Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true);
Config.tnc.aprsBridgeActive = request->hasParam("tnc.aprsBridgeActive", true);
Config.mqtt.active = request->hasParam("mqtt.active", true);
if (Config.mqtt.active) {
Config.mqtt.server = getParamStringSafe("mqtt.server", Config.mqtt.server);
Config.mqtt.topic = getParamStringSafe("mqtt.topic", Config.mqtt.topic);
Config.mqtt.username = getParamStringSafe("mqtt.username", Config.mqtt.username);
Config.mqtt.password = getParamStringSafe("mqtt.password", Config.mqtt.password);
Config.mqtt.port = getParamIntSafe("mqtt.port", Config.mqtt.port);
Config.mqtt.beaconOverMqtt = request->hasParam("mqtt.beaconOverMqtt", true);
}
Config.rebootMode = request->hasParam("other.rebootMode", true); Config.rebootMode = request->hasParam("other.rebootMode", true);
if (Config.rebootMode) { Config.rebootModeTime = request->getParam("other.rebootModeTime", true)->value().toInt();
Config.rebootModeTime = getParamIntSafe("other.rebootModeTime", Config.rebootModeTime);
}
Config.ota.username = getParamStringSafe("ota.username", Config.ota.username); Config.ota.username = request->getParam("ota.username", true)->value();
Config.ota.password = getParamStringSafe("ota.password", Config.ota.password); Config.ota.password = request->getParam("ota.password", true)->value();
Config.rememberStationTime = request->getParam("other.rememberStationTime", true)->value().toInt();
Config.backupDigiMode = request->hasParam("other.backupDigiMode", true);
Config.personalNote = request->getParam("personalNote", true)->value();
Config.blacklist = request->getParam("blacklist", true)->value();
Config.webadmin.active = request->hasParam("webadmin.active", true); Config.webadmin.active = request->hasParam("webadmin.active", true);
if (Config.webadmin.active) { if (Config.webadmin.active) {
Config.webadmin.username = getParamStringSafe("webadmin.username", Config.webadmin.username); Config.webadmin.username = request->getParam("webadmin.username", true)->value();
Config.webadmin.password = getParamStringSafe("webadmin.password", Config.webadmin.password); Config.webadmin.password = request->getParam("webadmin.password", true)->value();
} }
Config.remoteManagement.managers = getParamStringSafe("remoteManagement.managers", Config.remoteManagement.managers); Config.ntp.gmtCorrection = request->getParam("ntp.gmtCorrection", true)->value().toFloat();
Config.remoteManagement.rfOnly = request->hasParam("remoteManagement.rfOnly", true);
Config.ntp.server = getParamStringSafe("ntp.server", Config.ntp.server); Config.remoteManagement.managers = request->getParam("remoteManagement.managers", true)->value();
Config.ntp.gmtCorrection = getParamFloatSafe("ntp.gmtCorrection", Config.ntp.gmtCorrection); Config.remoteManagement.rfOnly = request->getParam("remoteManagement.rfOnly", true);
Config.rememberStationTime = getParamIntSafe("other.rememberStationTime", Config.rememberStationTime); Config.writeFile();
bool saveSuccess = Config.writeFile(); AsyncWebServerResponse *response = request->beginResponse(302, "text/html", "");
response->addHeader("Location", "/");
if (saveSuccess) { request->send(response);
Serial.println("Configuration saved successfully"); displayToggle(false);
AsyncWebServerResponse *response = request->beginResponse(302, "text/html", ""); delay(200);
response->addHeader("Location", "/?success=1"); ESP.restart();
request->send(response);
displayToggle(false);
delay(500);
ESP.restart();
} else {
Serial.println("Error saving configuration!");
String errorPage = "<!DOCTYPE html><html><head><title>Error</title></head><body>";
errorPage += "<h1>Configuration Error:</h1>";
errorPage += "<p>Couldn't save new configuration. Please try again.</p>";
errorPage += "<a href='/'>Back</a></body></html>";
AsyncWebServerResponse *response = request->beginResponse(500, "text/html", errorPage);
request->send(response);
}
} }
void handleAction(AsyncWebServerRequest *request) { void handleAction(AsyncWebServerRequest *request) {
+37 -44
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -29,54 +29,47 @@ extern Configuration Config;
extern uint8_t myWiFiAPIndex; extern uint8_t myWiFiAPIndex;
extern int myWiFiAPSize; extern int myWiFiAPSize;
extern WiFi_AP *currentWiFi; extern WiFi_AP *currentWiFi;
extern bool backupDigiMode; extern bool backUpDigiMode;
extern uint32_t lastServerCheck;
bool WiFiConnected = false; bool WiFiConnected = false;
uint32_t WiFiAutoAPTime = millis(); uint32_t WiFiAutoAPTime = millis();
bool WiFiAutoAPStarted = false; bool WiFiAutoAPStarted = false;
uint32_t previousWiFiMillis = 0;
uint8_t wifiCounter = 0; uint8_t wifiCounter = 0;
uint32_t lastBackupDigiTime = millis(); uint32_t lastBackupDigiTime = millis();
uint32_t lastWiFiCheck = 0;
namespace WIFI_Utils { namespace WIFI_Utils {
void checkWiFi() { void checkWiFi() {
if (Config.digi.ecoMode != 0) return; if (Config.digi.ecoMode == 0) {
uint32_t currentTime = millis(); if (backUpDigiMode) {
uint32_t WiFiCheck = millis() - lastBackupDigiTime;
if (backupDigiMode) { if (WiFi.status() != WL_CONNECTED && WiFiCheck >= 15 * 60 * 1000) {
if (WiFi.status() != WL_CONNECTED && ((currentTime - lastBackupDigiTime) >= 15 * 60 * 1000)) { Serial.println("*** Stopping BackUp Digi Mode ***");
Serial.println("*** Stopping BackUp Digi Mode ***"); backUpDigiMode = false;
backupDigiMode = false; wifiCounter = 0;
wifiCounter = 0; } else if (WiFi.status() == WL_CONNECTED) {
} else if (WiFi.status() == WL_CONNECTED) { Serial.println("*** WiFi Reconnect Success (Stopping Backup Digi Mode) ***");
Serial.println("*** WiFi Reconnect Success (Stopping Backup Digi Mode) ***"); backUpDigiMode = false;
backupDigiMode = false; wifiCounter = 0;
wifiCounter = 0;
}
}
if (!backupDigiMode && ((currentTime - lastWiFiCheck) >= 30 * 1000) && !WiFiAutoAPStarted) {
lastWiFiCheck = currentTime;
if (WiFi.status() == WL_CONNECTED) {
if (Config.digi.backupDigiMode && (currentTime - lastServerCheck > 30 * 1000)) {
Serial.println("*** Server Connection LOST → Backup Digi Mode ***");
backupDigiMode = true;
WiFi.disconnect();
lastBackupDigiTime = currentTime;
} }
} else { }
if (!backUpDigiMode && (WiFi.status() != WL_CONNECTED) && ((millis() - previousWiFiMillis) >= 30 * 1000) && !WiFiAutoAPStarted) {
Serial.print(millis());
Serial.println("Reconnecting to WiFi..."); Serial.println("Reconnecting to WiFi...");
WiFi.disconnect(); WiFi.disconnect();
WIFI_Utils::startWiFi(); WIFI_Utils::startWiFi();//WiFi.reconnect();
previousWiFiMillis = millis();
if (Config.digi.backupDigiMode) wifiCounter++; if (Config.backupDigiMode) {
wifiCounter++;
}
if (wifiCounter >= 2) { if (wifiCounter >= 2) {
Serial.println("*** Starting BackUp Digi Mode ***"); Serial.println("*** Starting BackUp Digi Mode ***");
backupDigiMode = true; backUpDigiMode = true;
lastBackupDigiTime = currentTime; lastBackupDigiTime = millis();
} }
} }
} }
@@ -105,21 +98,21 @@ namespace WIFI_Utils {
delay(500); delay(500);
unsigned long start = millis(); unsigned long start = millis();
displayShow("", "Connecting to WiFi:", "", currentWiFi->ssid + " ...", 0); displayShow("", "Connecting to WiFi:", "", currentWiFi->ssid + " ...", 0);
Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.print("' "); Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.println("' ...");
WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str()); WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str());
while (WiFi.status() != WL_CONNECTED && wifiCounter < myWiFiAPSize) { while (WiFi.status() != WL_CONNECTED && wifiCounter<myWiFiAPSize) {
delay(500); delay(500);
#ifdef INTERNAL_LED_PIN #ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN, HIGH); digitalWrite(INTERNAL_LED_PIN,HIGH);
#endif #endif
Serial.print('.'); Serial.print('.');
delay(500); delay(500);
#ifdef INTERNAL_LED_PIN #ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN, LOW); digitalWrite(INTERNAL_LED_PIN,LOW);
#endif #endif
if ((millis() - start) > 10000){ if ((millis() - start) > 10000){
delay(1000); delay(1000);
if (myWiFiAPIndex >= (myWiFiAPSize - 1)) { if(myWiFiAPIndex >= (myWiFiAPSize - 1)) {
myWiFiAPIndex = 0; myWiFiAPIndex = 0;
wifiCounter++; wifiCounter++;
} else { } else {
@@ -136,15 +129,15 @@ namespace WIFI_Utils {
} }
} }
#ifdef INTERNAL_LED_PIN #ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN, LOW); digitalWrite(INTERNAL_LED_PIN,LOW);
#endif #endif
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
Serial.print("\nConnected as "); Serial.print("Connected as ");
Serial.print(WiFi.localIP()); Serial.print(WiFi.localIP());
Serial.print(" / MAC Address: "); Serial.print(" / MAC Address: ");
Serial.println(WiFi.macAddress()); Serial.println(WiFi.macAddress());
displayShow("", " Connected!!", "" , " loading ...", 1000); displayShow("", " Connected!!", "" , " loading ...", 1000);
} else { } else if (WiFi.status() != WL_CONNECTED) {
startAP = true; startAP = true;
Serial.println("\nNot connected to WiFi! Starting Auto AP"); Serial.println("\nNot connected to WiFi! Starting Auto AP");
+22 -40
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -42,7 +42,6 @@ float newHum, newTemp, newPress, newGas;
Adafruit_BME280 bme280; Adafruit_BME280 bme280;
Adafruit_AHTX0 aht20;
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) #if defined(HELTEC_V3) || defined(HELTEC_V3_2)
Adafruit_BMP280 bmp280(&Wire1); Adafruit_BMP280 bmp280(&Wire1);
Adafruit_Si7021 si7021 = Adafruit_Si7021(); Adafruit_Si7021 si7021 = Adafruit_Si7021();
@@ -61,9 +60,9 @@ namespace WX_Utils {
void getWxModuleAddres() { void getWxModuleAddres() {
uint8_t err, addr; uint8_t err, addr;
for(addr = 1; addr < 0x7F; addr++) { for(addr = 1; addr < 0x7F; addr++) {
#ifdef SENSOR_I2C_BUS #if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
SENSOR_I2C_BUS.beginTransmission(addr); Wire1.beginTransmission(addr);
err = SENSOR_I2C_BUS.endTransmission(); err = Wire1.endTransmission();
#else #else
Wire.beginTransmission(addr); Wire.beginTransmission(addr);
#ifdef LIGHTGATEWAY_PLUS_1_0 #ifdef LIGHTGATEWAY_PLUS_1_0
@@ -72,7 +71,6 @@ namespace WX_Utils {
#endif #endif
err = Wire.endTransmission(); err = Wire.endTransmission();
#endif #endif
delay(5);
if (err == 0) { if (err == 0) {
//Serial.println(addr); //this shows any connected board to I2C //Serial.println(addr); //this shows any connected board to I2C
if (addr == 0x76 || addr == 0x77) { // BME or BMP if (addr == 0x76 || addr == 0x77) { // BME or BMP
@@ -120,19 +118,15 @@ namespace WX_Utils {
Serial.println("BMP280 sensor found"); Serial.println("BMP280 sensor found");
wxModuleType = 2; wxModuleType = 2;
wxModuleFound = true; wxModuleFound = true;
if (aht20.begin()) {
Serial.println("AHT20 sensor found");
if (wxModuleType == 2) wxModuleType = 6;
}
} }
} }
} else if (wxModuleAddress == 0x40 && Config.battery.useExternalI2CSensor == false) { } else if (wxModuleAddress == 0x40) {
if(si7021.begin()) { if(si7021.begin()) {
Serial.println("Si7021 sensor found"); Serial.println("Si7021 sensor found");
wxModuleType = 4; wxModuleType = 4;
wxModuleFound = true; wxModuleFound = true;
} }
} }
#ifdef LIGHTGATEWAY_PLUS_1_0 #ifdef LIGHTGATEWAY_PLUS_1_0
else if (wxModuleAddress == 0x70) { else if (wxModuleAddress == 0x70) {
if (shtc3.begin()) { if (shtc3.begin()) {
@@ -161,7 +155,7 @@ namespace WX_Utils {
Adafruit_BMP280::SAMPLING_X1, Adafruit_BMP280::SAMPLING_X1,
Adafruit_BMP280::SAMPLING_X1, Adafruit_BMP280::SAMPLING_X1,
Adafruit_BMP280::FILTER_OFF Adafruit_BMP280::FILTER_OFF
); );
Serial.println("BMP280 Module init done!"); Serial.println("BMP280 Module init done!");
break; break;
case 3: case 3:
@@ -270,27 +264,15 @@ namespace WX_Utils {
newPress = 0; newPress = 0;
break; break;
case 5: // SHTC3 case 5: // SHTC3
{ #ifdef LIGHTGATEWAY_PLUS_1_0
#ifdef LIGHTGATEWAY_PLUS_1_0
sensors_event_t humidity, temp;
shtc3.getEvent(&humidity, &temp);
newTemp = temp.temperature;
newHum = humidity.relative_humidity;
newPress = 0;
#endif
}
break;
case 6: // BMP280 + AHT20
{
bmp280.takeForcedMeasurement();
newTemp = bmp280.readTemperature();
newPress = (bmp280.readPressure() / 100.0F);
sensors_event_t humidity, temp; sensors_event_t humidity, temp;
aht20.getEvent(&humidity, &temp); shtc3.getEvent(&humidity, &temp);
newTemp = temp.temperature;
newHum = humidity.relative_humidity; newHum = humidity.relative_humidity;
} newPress = 0;
#endif
break; break;
} }
if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) { if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) {
Serial.println("BME/BMP/Si7021 Module data failed"); Serial.println("BME/BMP/Si7021 Module data failed");
@@ -298,16 +280,16 @@ namespace WX_Utils {
return ".../...g...t..."; return ".../...g...t...";
} else { } else {
String tempStr = generateTempString(((newTemp + Config.wxsensor.temperatureCorrection) * 1.8) + 32); String tempStr = generateTempString(((newTemp + Config.wxsensor.temperatureCorrection) * 1.8) + 32);
String humStr; String humStr;
if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4 || wxModuleType == 5 || wxModuleType == 6) { if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4 || wxModuleType == 5) {
humStr = generateHumString(newHum); humStr = generateHumString(newHum);
} else if (wxModuleType == 2) { } else if (wxModuleType == 2) {
humStr = ".."; humStr = "..";
} }
String presStr = (wxModuleType == 4 || wxModuleType == 5) String presStr = (wxModuleType == 4 || wxModuleType == 5)
? "....." ? "....."
: generatePresString(newPress + getAltitudeCorrection() / CORRECTION_FACTOR); : generatePresString(newPress + getAltitudeCorrection() / CORRECTION_FACTOR);
fifthLine = "BME-> "; fifthLine = "BME-> ";
@@ -1,63 +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 BOARD_PINOUT_H_
#define BOARD_PINOUT_H_
// LoRa Radio
#define HAS_SX1268
#define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23
#define RADIO_CS_PIN 5
#define RADIO_RST_PIN 27
#define RADIO_DIO1_PIN 12
#define RADIO_BUSY_PIN 14
#define RADIO_RXEN 32
#define RADIO_TXEN 25
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display
#define HAS_DISPLAY
#define HAS_SH1106
#undef OLED_SDA
#undef OLED_SCL
#undef OLED_RST
#define OLED_SDA 21
#define OLED_SCL 22
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
// GPS
#define HAS_GPS
#define GPS_BAUDRATE 9600
#define GPS_RX 17
#define GPS_TX 16
// Aditional Config
#define INTERNAL_LED_PIN 2
#define BATTERY_PIN 35
#endif
@@ -1,12 +0,0 @@
[env:ESP32_9M2IBR_1W_LoRa_GPS]
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_9M2IBR_1W_LoRa_GPS
lib_deps =
${common.lib_deps}
${common.display_libs}
adafruit/Adafruit SH110X @ 2.1.10
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -32,9 +32,6 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_NUM_3 #define GPIO_WAKEUP_PIN GPIO_NUM_3
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY
@@ -45,5 +42,5 @@
#define OLED_SDA 0 #define OLED_SDA 0
#define OLED_SCL 1 #define OLED_SCL 1
#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)
#endif #endif
@@ -4,9 +4,6 @@ board_build.mcu = esp32c3
build_flags = build_flags =
${common.build_flags} ${common.build_flags}
${common.usb_flags} ${common.usb_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_C3_OctopusLab_LoRa -D ESP32_C3_OctopusLab_LoRa
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}
+5 -9
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -22,7 +22,6 @@
// LoRa Radio // LoRa Radio
#define HAS_SX1268 #define HAS_SX1268
#define HAS_1W_LORA #define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18 #define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19 #define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23 #define RADIO_MOSI_PIN 23
@@ -35,9 +34,6 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12 #define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY
@@ -2,9 +2,6 @@
board = esp32dev board = esp32dev
build_flags = build_flags =
${common.build_flags} ${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa -D ESP32_DIY_1W_LoRa
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}
+6 -10
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -22,7 +22,6 @@
// LoRa Radio // LoRa Radio
#define HAS_SX1262 #define HAS_SX1262
#define HAS_1W_LORA #define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18 #define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19 #define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23 #define RADIO_MOSI_PIN 23
@@ -35,11 +34,8 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12 #define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY
#undef OLED_SDA #undef OLED_SDA
#undef OLED_SCL #undef OLED_SCL
@@ -2,9 +2,6 @@
board = esp32dev board = esp32dev
build_flags = build_flags =
${common.build_flags} ${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa_915 -D ESP32_DIY_1W_LoRa_915
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -34,12 +34,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12 #define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY
#undef OLED_SDA #undef OLED_SDA
#undef OLED_SCL #undef OLED_SCL
#undef OLED_RST #undef OLED_RST
@@ -2,9 +2,6 @@
board = esp32dev board = esp32dev
build_flags = build_flags =
${common.build_flags} ${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa_LLCC68 -D ESP32_DIY_1W_LoRa_LLCC68
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -34,9 +34,6 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_33 #define GPIO_WAKEUP_PIN GPIO_SEL_33
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY
@@ -2,9 +2,6 @@
board = esp32dev board = esp32dev
build_flags = build_flags =
${common.build_flags} ${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa_Mesh_V1_2 -D ESP32_DIY_1W_LoRa_Mesh_V1_2
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}

Some files were not shown because too many files have changed in this diff Show More