Compare commits

..

32 Commits

Author SHA1 Message Date
Ricardo Guzman (Richonguzman) b5d9103ea5 gps beacon fix 2025-11-30 10:47:12 -03:00
Ricardo Guzman (Richonguzman) 20a5029da8 index fix 2025-11-30 10:37:23 -03:00
Ricardo Guzman (Richonguzman) b3cc8af180 test update aprspacketlib 2025-11-30 10:15:01 -03:00
Ricardo Guzman (Richonguzman) ca16761f68 loadCallsignList update 2025-11-29 14:11:43 -03:00
Ricardo Guzman (Richonguzman) e593b2c670 version number update 2025-11-08 21:16:03 -03:00
Ricardo Guzman (Richonguzman) 08a7e0aac1 T-Beam Supreme Screen Fix 2025-11-08 21:13:57 -03:00
Ricardo Guzman (Richonguzman) 9b258c42ab checkValidCallsign update 2025-11-06 10:11:55 -03:00
richonguzman 8712122d33 raw images 2025-10-29 10:34:43 -03:00
richonguzman 9e34684627 update pdf y blob 2025-10-29 10:28:25 -03:00
richonguzman e4aa52241d sin gitattributes ahora 2025-10-29 10:11:18 -03:00
richonguzman c840ef01fa sin manual en github 2025-10-29 10:10:58 -03:00
richonguzman 98f85725f8 test con Vista Previa 2025-10-29 09:13:48 -03:00
richonguzman 10bde884b1 new gitattributes 2025-10-28 22:54:22 -03:00
richonguzman 546e4f63a4 update pdf to v1.4 2025-10-28 22:46:44 -03:00
Ricardo Guzman (Richonguzman) 87f2ec2b7b test new version pdf 20250128 2025-10-28 22:40:15 -03:00
Ricardo Guzman (Richonguzman) c9c7e24aae Merge pull request #356 from richonguzman/richonguzman-patch-15
Delete manual/LoRa_APRS_iGate_CA2RXU_Firmware_Manual.pdf
2025-10-28 22:38:23 -03:00
Ricardo Guzman (Richonguzman) b86133223d Delete manual/LoRa_APRS_iGate_CA2RXU_Firmware_Manual.pdf 2025-10-28 22:38:02 -03:00
richonguzman 880a41bfd8 update manual 20251028 2025-10-28 22:34:06 -03:00
richonguzman d610b3a90f mqtt beacon fix 2025-10-21 14:33:58 -03:00
richonguzman 23a33ed27c new message warning 2025-10-19 14:20:11 -03:00
richonguzman 72ef827900 new update pdf manual v314 2025-10-18 11:42:18 -03:00
richonguzman b756f97f55 v314 manual update 2025-10-18 10:50:54 -03:00
richonguzman 2625e7b91d update readme v314 2025-10-15 17:29:26 -03:00
richonguzman 75337d6c29 update to Tnc strip bytes 2025-10-15 17:12:38 -03:00
richonguzman a618383617 APRS Bridge for TNC 2025-10-15 15:11:07 -03:00
richonguzman 8add599838 NTP server change fix 2025-10-15 14:31:02 -03:00
richonguzman 83ec2265c6 Rx and Tx Freq selection fix 2025-10-15 14:18:43 -03:00
richonguzman bec4f4f473 beaconOnRxFreq into beacon config 2025-10-15 10:58:30 -03:00
richonguzman 87a67cd2c9 readmeupdate otro mas 2025-10-13 13:29:56 -03:00
richonguzman 4e48b2d02d manual update 2025-10-13 13:11:51 -03:00
richonguzman 607277bfe9 readme update2 2025-10-13 12:44:47 -03:00
richonguzman c2f8596667 readme update 2025-10-13 12:44:15 -03:00
54 changed files with 238 additions and 289 deletions
+9 -6
View File
@@ -2,7 +2,7 @@
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/blob/main/images/iGateOledScreen.jpeg) ![Screenshot](https://github.com/richonguzman/LoRa_APRS_iGate/raw/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 />
@@ -12,13 +12,13 @@ ____________________________________________________
# <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">WEB FLASHER/INSTALLER</a> # <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">WEB FLASHER/INSTALLER</a>
# <a href="https://github.com/richonguzman/LoRa_APRS_iGate/blob/main/manual/LoRa_APRS_iGate_CA2RXU_Firmware_Manual.pdf" target="_blank">LoRa APRS iGate CA2RXU Firmware Manual</a> # <a href="https://drive.google.com/file/d/1-jafzOWis_qOaNUl4WGVcFKjh_uCJtB6/view?usp=share_link" 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/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) [<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)
____________________________________________________ ____________________________________________________
@@ -32,7 +32,7 @@ ____________________________________________________
- 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, HT-CT62, Wireless Tracker, Wireless Paper. - HELTEC V2, V3, V3.2, T114, Wireless Stick, Wireless Stick Lite V3/V3.2, HT-CT62, Wireless Tracker, Wireless Paper.
- RAK Wireless 4631 + 19007(or 19003) - RAK Wireless 4631 + 19007(or 19003)
@@ -51,9 +51,12 @@ ____________________________________________________
<br /> <br />
# Timeline (Versions): # Timeline (Versions):
- 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-11 User defined NTP server and send beacon over MQTT added.
- 2025-10-10 Changed Wiki into a pdf manual. - 2025-10-10 Converted the Wiki into a PDF manual.
- 2025-09-26 Heltec Wireless Bridge support added. - 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-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).
+1 -1
View File
@@ -37,7 +37,7 @@ lib_deps =
mathieucarbou/AsyncTCP @ 3.2.5 mathieucarbou/AsyncTCP @ 3.2.5
mathieucarbou/ESPAsyncWebServer @ 3.2.3 mathieucarbou/ESPAsyncWebServer @ 3.2.3
mikalhart/TinyGPSPlus @ 1.0.3 mikalhart/TinyGPSPlus @ 1.0.3
richonguzman/APRSPacketLib @1.0.0 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
+5 -4
View File
@@ -17,10 +17,11 @@
"path": "WIDE1-1", "path": "WIDE1-1",
"sendViaAPRSIS": false, "sendViaAPRSIS": false,
"sendViaRF": false, "sendViaRF": false,
"beaconFreq": 1,
"statusActive": false, "statusActive": false,
"statusPacket": "", "statusPacket": "",
"gpsActive": false, "gpsActive": false,
"gpsAmbiguity": false "ambiguityLevel": 0
}, },
"aprs_is": { "aprs_is": {
"active": false, "active": false,
@@ -35,8 +36,7 @@
"blacklist": "", "blacklist": "",
"digi": { "digi": {
"mode": 0, "mode": 0,
"ecoMode": 0, "ecoMode": 0
"beaconOnRxFreq": false
}, },
"lora": { "lora": {
"rxActive": true, "rxActive": true,
@@ -82,7 +82,8 @@
"tnc": { "tnc": {
"enableServer": false, "enableServer": false,
"enableSerial": false, "enableSerial": false,
"acceptOwn": false "acceptOwn": false,
"aprsBrigdeActive": false
}, },
"mqtt": { "mqtt": {
"active": false, "active": false,
+83 -93
View File
@@ -144,8 +144,7 @@
name="beacon.comment" name="beacon.comment"
id="beacon.comment" id="beacon.comment"
class="form-control" class="form-control"
placeholder="LoRa APRS" placeholder=""
required=""
/> />
</div> </div>
<div class="col-12 mt-3"> <div class="col-12 mt-3">
@@ -189,18 +188,10 @@
name="action.symbol" name="action.symbol"
id="action.symbol" id="action.symbol"
> >
<option value="L#"> <option value="L#">Green star with L</option>
Green star with L <option value="L_">Blue circle with L</option>
</option> <option value="L&">Black diamond with L</option>
<option value="L_"> <option value="La" selected>Red diamond with L</option>
Blue circle with L
</option>
<option value="L&">
Black diamond with L
</option>
<option value="La">
Red diamond with L
</option>
</select> </select>
</div> </div>
<div <div
@@ -567,11 +558,28 @@
</label> </label>
</div> </div>
</div> </div>
<div class="col-6 d-grid gap-2"> </div>
<button class="btn btn-primary" id="send-beacon">Send beacon now</button> <div class="row mt-3">
<div class="col-6">
<button class="btn btn-primary form-control h-100" id="send-beacon">Send beacon now</button>
</div>
<div class="col-6">
<label
for="beacon.beaconFreq"
class="form-label">
<small>(Select LoRa Beacon Frequency)</small>
</label>
<select
class="form-select form-select"
name="beacon.beaconFreq"
id="beacon.beaconFreq"
>
<option value="0">Beacon on Rx Freq</option>
<option value="1">Beacon on Tx Freq</option>
</select>
</div> </div>
</div> </div>
<div class="row mt-4"> <div class="row mt-3">
<div class="col-6"> <div class="col-6">
<label <label
for="beacon.interval" for="beacon.interval"
@@ -594,6 +602,23 @@
</span> </span>
</div> </div>
</div> </div>
<div class="col-6">
<label
for="beacon.ambiguityLevel"
class="form-label"
>APRS Position Ambiguity</label
>
<select
class="form-select form-select"
name="beacon.ambiguityLevel"
id="beacon.ambiguityLevel"
>
<option value="0" selected>No Ambiguity</option>
<option value="1">~110 m forced Ambiguity</option>
<option value="2">~1.1 km forced Ambiguity</option>
<option value="3">~11 km forced Ambiguity</option>
</select>
</div>
</div> </div>
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<div class="form-check form-switch"> <div class="form-check form-switch">
@@ -610,21 +635,6 @@
</label> </label>
</div> </div>
</div> </div>
<div class="col-12">
<div class="form-check form-switch">
<input
type="checkbox"
name="beacon.gpsAmbiguity"
id="beacon.gpsAmbiguity"
class="form-check-input"
/>
<label
for="beacon.gpsAmbiguity"
class="form-label"
>Send Real-GPS Beacon with Ambiguity <small>(~ 1 Km of Random Error)</small>
</label>
</div>
</div>
</div> </div>
</div> </div>
<hr> <hr>
@@ -646,7 +656,7 @@
</svg> </svg>
Black List Black List
</h5> </h5>
<small>Add Callsigns with space between them to Blacklist them (* wild card allowed)</small> <small>Add Callsigns with space between them to Blacklist (* wildcard allowed)</small>
</div> </div>
<div class="col-9"> <div class="col-9">
<div class="row"> <div class="row">
@@ -702,15 +712,9 @@
name="digi.mode" name="digi.mode"
id="digi.mode" id="digi.mode"
> >
<option value="0"> <option value="0" selected>OFF</option>
OFF <option value="2">WIDE1 (fill-in) Digi</option>
</option> <option value="3">WIDE2 (+WIDE1) Digi</option>
<option value="2">
WIDE1 (fill-in) Digi
</option>
<option value="3">
WIDE2 (+WIDE1) Digi
</option>
</select> </select>
</div> </div>
<div class="col-12 mt-3"> <div class="col-12 mt-3">
@@ -727,37 +731,9 @@
name="digi.ecoMode" name="digi.ecoMode"
id="digi.ecoMode" id="digi.ecoMode"
> >
<option value="0"> <option value="0" selected>OFF (Normal Mode - WiFiAP and Serial Output enabled)</option>
OFF (Normal Mode - WiFiAP and Serial Output enabled) <option value="1">Ultra Eco Mode (Sleep till Packet Rx (WiFiAP/WebUI & Display disabled))</option>
</option> <option value="2">OFF (Normal Mode - WiFiAP disabled but Serial Output still enabled)</option>
<option value="1">
Ultra Eco Mode (Sleep till Packet Rx (WiFiAP/WebUI & Display disabled))
</option>
<option value="2">
OFF (Normal Mode - WiFiAP disabled but Serial Output still enabled)
</option>
</select>
</div>
<div class="col-12 mt-3">
<label
for="digi.beaconOnRxFreq"
class="form-label"
>Digipeater Beacon Frequency
<small
>(If Rx Freq different from Tx Freq).</small
></label
>
<select
class="form-select form-select"
name="digi.beaconOnRxFreq"
id="digi.beaconOnRxFreq"
>
<option value="false">
Beacon on Tx Freq
</option>
<option value="true">
Beacon on Rx Freq
</option>
</select> </select>
</div> </div>
</div> </div>
@@ -1512,25 +1488,7 @@
</div> </div>
<div class="col-lg-9 col-sm-12"> <div class="col-lg-9 col-sm-12">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-6">
<div class="form-check form-switch">
<div class="form-text">
Server will be available at port <strong>8001</strong>
</div>
<input
type="checkbox"
name="tnc.enableServer"
id="tnc.enableServer"
class="form-check-input"
/>
<label
for="tnc.enableServer"
class="form-label"
>Enable TNC server</label
>
</div>
</div>
<div class="col-12">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input <input
type="checkbox" type="checkbox"
@@ -1545,7 +1503,24 @@
> >
</div> </div>
</div> </div>
<div class="col-12"> <div class="col-6">
<div class="form-check form-switch">
<input
type="checkbox"
name="tnc.enableServer"
id="tnc.enableServer"
class="form-check-input"
/>
<label
for="tnc.enableServer"
class="form-label"
>Enable TNC server <small><strong>(Port 8001)</strong></small></label
>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input <input
type="checkbox" type="checkbox"
@@ -1560,6 +1535,21 @@
> >
</div> </div>
</div> </div>
<div class="col-6">
<div class="form-check form-switch">
<input
type="checkbox"
name="tnc.aprsBridgeActive"
id="tnc.aprsBridgeActive"
class="form-check-input"
/>
<label
for="tnc.aprsBridgeActive"
class="form-label"
>Enable APRS Bridge</label
>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
+13 -3
View File
@@ -119,7 +119,11 @@ 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;
@@ -127,7 +131,7 @@ function loadSettings(settings) {
StatusPacket.disabled = !StatusCheckbox.checked; StatusPacket.disabled = !StatusCheckbox.checked;
document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive; document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive;
document.getElementById("beacon.gpsAmbiguity").checked = settings.beacon.gpsAmbiguity; document.getElementById("beacon.ambiguityLevel").value = settings.beacon.ambiguityLevel;
// Black List // Black List
document.getElementById("blacklist").value = settings.blacklist; document.getElementById("blacklist").value = settings.blacklist;
@@ -135,7 +139,6 @@ function loadSettings(settings) {
// 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").value = settings.digi.ecoMode;
document.getElementById("digi.beaconOnRxFreq").value = settings.digi.beaconOnRxFreq;
// LoRa // LoRa
document.getElementById("lora.rxActive").checked = settings.lora.rxActive; document.getElementById("lora.rxActive").checked = settings.lora.rxActive;
@@ -203,6 +206,7 @@ function loadSettings(settings) {
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 // MQTT
@@ -212,7 +216,7 @@ function loadSettings(settings) {
document.getElementById("mqtt.username").value = settings.mqtt.username; document.getElementById("mqtt.username").value = settings.mqtt.username;
document.getElementById("mqtt.password").value = settings.mqtt.password; document.getElementById("mqtt.password").value = settings.mqtt.password;
document.getElementById("mqtt.port").value = settings.mqtt.port; document.getElementById("mqtt.port").value = settings.mqtt.port;
document.getElementById("mqtt.beaconOverMqtt").value = settings.mqtt.beaconOverMqtt; document.getElementById("mqtt.beaconOverMqtt").checked = settings.mqtt.beaconOverMqtt;
MqttCheckbox.checked = settings.mqtt.active; MqttCheckbox.checked = settings.mqtt.active;
MqttServer.disabled = !MqttCheckbox.check; MqttServer.disabled = !MqttCheckbox.check;
MqttTopic.disabled = !MqttCheckbox.check; MqttTopic.disabled = !MqttCheckbox.check;
@@ -306,6 +310,12 @@ function updateImage() {
} }
} }
// Beaconing Switches
const BeaconingViaRFCheckbox = document.querySelector('input[name="beacon.sendViaRF"]');
const BeaconingFrequency = document.querySelector('select[name="beacon.beaconFreq"]');
BeaconingViaRFCheckbox.addEventListener("change", function() {
BeaconingFrequency.disabled = !this.checked;
});
// Status Switch // Status Switch
const StatusCheckbox = document.querySelector('input[name="beacon.statusActive"]'); const StatusCheckbox = document.querySelector('input[name="beacon.statusActive"]');
Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 KiB

+6 -5
View File
@@ -45,12 +45,13 @@ public:
String overlay; String overlay;
String symbol; String symbol;
String path; String path;
bool sendViaRF;
bool sendViaAPRSIS; bool sendViaAPRSIS;
bool gpsActive; bool sendViaRF;
bool gpsAmbiguity; int beaconFreq;
bool statusActive; bool statusActive;
String statusPacket; String statusPacket;
bool gpsActive;
int ambiguityLevel;
}; };
class APRS_IS { class APRS_IS {
@@ -67,8 +68,7 @@ public:
class DIGI { 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 beaconOnRxFreq;
}; };
class LoraModule { class LoraModule {
@@ -127,6 +127,7 @@ public:
bool enableServer; bool enableServer;
bool enableSerial; bool enableSerial;
bool acceptOwn; bool acceptOwn;
bool aprsBridgeActive;
}; };
class OTA { class OTA {
+2 -2
View File
@@ -27,8 +27,8 @@ namespace TNC_Utils {
void setup(); void setup();
void loop(); void loop();
void sendToClients(const String& packet); void sendToClients(const String& packet, bool stripBytes = false);
void sendToSerial(const String& packet); void sendToSerial(const String& packet, bool stripBytes = false);
} }
Binary file not shown.
+5 -5
View File
@@ -67,8 +67,8 @@ ___________________________________________________________________*/
#endif #endif
String versionDate = "2025-10-13"; String versionDate = "2025-11-30";
String versionNumber = "3.1.3"; String versionNumber = "3.1.5";
Configuration Config; Configuration Config;
WiFiClient aprsIsClient; WiFiClient aprsIsClient;
WiFiClient mqttClient; WiFiClient mqttClient;
@@ -192,9 +192,9 @@ void loop() {
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); // Send received packet to TNC KISS if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet, true); // Send received packet to TNC KISS
if (Config.tnc.enableSerial) TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS if (Config.tnc.enableSerial) TNC_Utils::sendToSerial(packet, true); // Send received packet to Serial KISS
if (Config.mqtt.active) MQTT_Utils::sendToMqtt(packet); // Send received packet to MQTT if (Config.mqtt.active) MQTT_Utils::sendToMqtt(packet); // Send received packet to MQTT
} }
if (Config.aprs_is.active) APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS if (Config.aprs_is.active) APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
+7 -6
View File
@@ -16,6 +16,7 @@
* 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"
@@ -25,6 +26,7 @@
#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"
@@ -235,12 +237,7 @@ 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 = Config.callsign; String outputPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
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,");
@@ -367,6 +364,10 @@ namespace APRS_IS_Utils {
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
}
} }
} }
+20 -15
View File
@@ -68,15 +68,17 @@ bool Configuration::writeFile() {
data["beacon"]["longitude"] = beacon.longitude; data["beacon"]["longitude"] = beacon.longitude;
data["beacon"]["overlay"] = beacon.overlay; data["beacon"]["overlay"] = beacon.overlay;
data["beacon"]["symbol"] = beacon.symbol; data["beacon"]["symbol"] = beacon.symbol;
data["beacon"]["path"] = beacon.path;
data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS; data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
data["beacon"]["sendViaRF"] = beacon.sendViaRF; data["beacon"]["sendViaRF"] = beacon.sendViaRF;
data["beacon"]["path"] = beacon.path; data["beacon"]["beaconFreq"] = beacon.beaconFreq;
data["beacon"]["statusActive"] = beacon.statusActive; data["beacon"]["statusActive"] = beacon.statusActive;
data["beacon"]["statusPacket"] = beacon.statusPacket; data["beacon"]["statusPacket"] = beacon.statusPacket;
data["beacon"]["gpsActive"] = beacon.gpsActive; data["beacon"]["gpsActive"] = beacon.gpsActive;
data["beacon"]["gpsAmbiguity"] = beacon.gpsAmbiguity; data["beacon"]["ambiguityLevel"] = beacon.ambiguityLevel;
data["personalNote"] = personalNote; data["personalNote"] = personalNote;
@@ -87,7 +89,6 @@ bool Configuration::writeFile() {
#if defined(HAS_A7670) #if defined(HAS_A7670)
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2; if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
#endif #endif
data["digi"]["beaconOnRxFreq"] = digi.beaconOnRxFreq;
data["lora"]["rxActive"] = loramodule.rxActive; data["lora"]["rxActive"] = loramodule.rxActive;
data["lora"]["rxFreq"] = loramodule.rxFreq; data["lora"]["rxFreq"] = loramodule.rxFreq;
@@ -130,6 +131,7 @@ bool Configuration::writeFile() {
data["tnc"]["enableServer"] = tnc.enableServer; data["tnc"]["enableServer"] = tnc.enableServer;
data["tnc"]["enableSerial"] = tnc.enableSerial; data["tnc"]["enableSerial"] = tnc.enableSerial;
data["tnc"]["acceptOwn"] = tnc.acceptOwn; data["tnc"]["acceptOwn"] = tnc.acceptOwn;
data["tnc"]["aprsBridgeActive"] = tnc.aprsBridgeActive;
data["mqtt"]["active"] = mqtt.active; data["mqtt"]["active"] = mqtt.active;
data["mqtt"]["server"] = mqtt.server; data["mqtt"]["server"] = mqtt.server;
@@ -226,10 +228,11 @@ bool Configuration::readFile() {
!data["beacon"].containsKey("path") || !data["beacon"].containsKey("path") ||
!data["beacon"].containsKey("sendViaAPRSIS") || !data["beacon"].containsKey("sendViaAPRSIS") ||
!data["beacon"].containsKey("sendViaRF") || !data["beacon"].containsKey("sendViaRF") ||
!data["beacon"].containsKey("beaconFreq") ||
!data["beacon"].containsKey("statusActive") || !data["beacon"].containsKey("statusActive") ||
!data["beacon"].containsKey("statusPacket") || !data["beacon"].containsKey("statusPacket") ||
!data["beacon"].containsKey("gpsActive") || !data["beacon"].containsKey("gpsActive") ||
!data["beacon"].containsKey("gpsAmbiguity")) needsRewrite = true; !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";
@@ -239,10 +242,11 @@ 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.gpsAmbiguity = data["beacon"]["gpsAmbiguity"] | false; beacon.ambiguityLevel = data["beacon"]["ambiguityLevel"] | 0;
if (!data.containsKey("personalNote")) needsRewrite = true; if (!data.containsKey("personalNote")) needsRewrite = true;
personalNote = data["personalNote"] | "personal note here"; personalNote = data["personalNote"] | "personal note here";
@@ -251,13 +255,10 @@ bool Configuration::readFile() {
blacklist = data["blacklist"] | "station callsign"; blacklist = data["blacklist"] | "station callsign";
if (!data["digi"].containsKey("mode") || if (!data["digi"].containsKey("mode") ||
!data["digi"].containsKey("ecoMode") || !data["digi"].containsKey("ecoMode")) needsRewrite = true;
!data["digi"].containsKey("beaconOnRxFreq")) 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;
digi.beaconOnRxFreq = data["digi"]["beaconOnRxFreq"] | 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
@@ -331,10 +332,12 @@ bool Configuration::readFile() {
if (!data["tnc"].containsKey("enableServer") || if (!data["tnc"].containsKey("enableServer") ||
!data["tnc"].containsKey("enableSerial") || !data["tnc"].containsKey("enableSerial") ||
!data["tnc"].containsKey("acceptOwn")) needsRewrite = true; !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") || if (!data["mqtt"].containsKey("active") ||
!data["mqtt"].containsKey("server") || !data["mqtt"].containsKey("server") ||
@@ -436,15 +439,17 @@ void Configuration::setDefaultValues() {
beacon.interval = 15; beacon.interval = 15;
beacon.overlay = "L"; beacon.overlay = "L";
beacon.symbol = "a"; beacon.symbol = "a";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.path = "WIDE1-1"; beacon.path = "WIDE1-1";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.beaconFreq = 1;
beacon.statusActive = false; beacon.statusActive = false;
beacon.statusPacket = ""; beacon.statusPacket = "";
beacon.gpsActive = false; beacon.gpsActive = false;
beacon.gpsAmbiguity = false; beacon.ambiguityLevel = 0;
personalNote = ""; personalNote = "";
@@ -452,7 +457,6 @@ void Configuration::setDefaultValues() {
digi.mode = 0; digi.mode = 0;
digi.ecoMode = 0; digi.ecoMode = 0;
digi.beaconOnRxFreq = false;
loramodule.rxActive = true; loramodule.rxActive = true;
loramodule.rxFreq = 433775000; loramodule.rxFreq = 433775000;
@@ -495,6 +499,7 @@ void Configuration::setDefaultValues() {
tnc.enableServer = false; tnc.enableServer = false;
tnc.enableSerial = false; tnc.enableSerial = false;
tnc.acceptOwn = false; tnc.acceptOwn = false;
tnc.aprsBridgeActive = false;
mqtt.active = false; mqtt.active = false;
mqtt.server = ""; mqtt.server = "";
@@ -524,7 +529,7 @@ void Configuration::setDefaultValues() {
backupDigiMode = false; backupDigiMode = false;
Serial.println("All is Written!"); Serial.println("New Data Created... All is Written!");
} }
Configuration::Configuration() { Configuration::Configuration() {
+2 -1
View File
@@ -96,6 +96,7 @@ void displaySetup() {
#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) display.setRotation(2);
display.update(); display.update();
#else #else
#ifdef OLED_DISPLAY_HAS_RST_PIN #ifdef OLED_DISPLAY_HAS_RST_PIN
@@ -106,7 +107,7 @@ void displaySetup() {
#endif #endif
#if defined(TTGO_T_Beam_S3_SUPREME_V3) #if defined(TTGO_T_Beam_S3_SUPREME_V3)
if (!display.begin(0x3c, false)) { if (display.begin(0x3c, false)) {
displayFound = true; displayFound = true;
if (Config.display.turn180) display.setRotation(2); if (Config.display.turn180) display.setRotation(2);
display.clearDisplay(); display.clearDisplay();
+36 -112
View File
@@ -16,6 +16,7 @@
* 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"
@@ -42,70 +43,6 @@ namespace GPS_Utils {
return iGateLoRaBeaconPacket; return iGateLoRaBeaconPacket;
} }
char *ax25_base91enc(char *s, uint8_t n, uint32_t v) {
for(s += n, *s = '\0'; n; n--) {
*(--s) = v % 91 + 33;
v /= 91;
}
return(s);
}
float roundToTwoDecimals(float degrees) {
return round(degrees * 100) / 100;
}
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
String encodedData = overlay;
uint32_t aprs_lat, aprs_lon;
float processedLatitude = latitude;
float processedLongitude = longitude;
if (Config.beacon.gpsActive && Config.beacon.gpsAmbiguity) {
processedLatitude = roundToTwoDecimals(latitude);
processedLongitude = roundToTwoDecimals(longitude);
}
aprs_lat = 900000000 - processedLatitude * 10000000;
aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
aprs_lon = 900000000 + processedLongitude * 10000000 / 2;
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 += ":!";
}
void generateBeacons() { void generateBeacons() {
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) { if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000); displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
@@ -116,9 +53,17 @@ namespace GPS_Utils {
Config.digi.mode = 0; Config.digi.mode = 0;
Config.backupDigiMode = false; Config.backupDigiMode = false;
} }
generateBeaconFirstPart(); String beaconPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol); String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(Config.beacon.latitude, Config.beacon.longitude, 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
iGateBeaconPacket = beaconPacket;
iGateBeaconPacket += ",qAC:!";
iGateBeaconPacket += Config.beacon.overlay;
iGateBeaconPacket += encodedGPS; iGateBeaconPacket += encodedGPS;
iGateLoRaBeaconPacket = beaconPacket;
iGateLoRaBeaconPacket += ":=";
iGateLoRaBeaconPacket += Config.beacon.overlay;
iGateLoRaBeaconPacket += encodedGPS; iGateLoRaBeaconPacket += encodedGPS;
} }
@@ -126,47 +71,40 @@ 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 GPSPacket; String infoGPS;
if (indexOfExclamation > 10) { if (indexOfExclamation > 10) {
GPSPacket = packet.substring(indexOfExclamation + OFFSET); infoGPS = packet.substring(indexOfExclamation + OFFSET);
} else if (indexOfEqual > 10) { } else if (indexOfEqual > 10) {
GPSPacket = packet.substring(indexOfEqual + OFFSET); infoGPS = packet.substring(indexOfEqual + OFFSET);
} }
String encodedLatitude = GPSPacket.substring(0,4); float decodedLatitude = APRSPacketLib::decodeBase91EncodedLatitude(infoGPS.substring(0,4));
int Y1 = encodedLatitude[0] - 33; float decodedLongitude = APRSPacketLib::decodeBase91EncodedLongitude(infoGPS.substring(4,8));
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);
String encodedLongitude = GPSPacket.substring(4,8); return buildDistanceAndComment(decodedLatitude, decodedLongitude, infoGPS.substring(12));
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) {
@@ -195,21 +133,7 @@ namespace GPS_Utils {
convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 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) {
+5 -5
View File
@@ -129,7 +129,7 @@ namespace LoRa_Utils {
} }
void changeFreqTx() { void changeFreqTx() {
delay(500); delay(300);
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.setSpreadingFactor(Config.loramodule.txSpreadingFactor);
@@ -138,7 +138,7 @@ namespace LoRa_Utils {
} }
void changeFreqRx() { void changeFreqRx() {
delay(500); delay(300);
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.setSpreadingFactor(Config.loramodule.rxSpreadingFactor);
@@ -150,7 +150,7 @@ namespace LoRa_Utils {
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.digi.beaconOnRxFreq)) { if (!packetIsBeacon || (packetIsBeacon && Config.beacon.beaconFreq == 1)) {
changeFreqTx(); changeFreqTx();
} }
} }
@@ -174,7 +174,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.digi.beaconOnRxFreq)) { if (!packetIsBeacon || (packetIsBeacon && Config.beacon.beaconFreq == 1)) {
changeFreqRx(); changeFreqRx();
} }
} }
@@ -204,7 +204,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();
+5 -5
View File
@@ -27,7 +27,7 @@
extern Configuration Config; extern Configuration Config;
WiFiUDP ntpUDP; WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, Config.ntp.server.c_str(), 0, 15 * 60 * 1000); // Update interval 15 min NTPClient* timeClient;
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.setTimeOffset(gmt); timeClient = new NTPClient(ntpUDP, Config.ntp.server.c_str(), gmt, 15 * 60 * 1000); // Update interval 15 min
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 (Config.digi.ecoMode == 0) return timeClient.getFormattedTime(); if (Config.digi.ecoMode == 0) return timeClient->getFormattedTime();
return "DigiEcoMode Active"; return "DigiEcoMode Active";
} }
+4 -4
View File
@@ -57,7 +57,7 @@ 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;
String callsigns = list; String callsigns = list;
@@ -77,12 +77,12 @@ namespace STATION_Utils {
} }
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 (int i = 0; i < list.size(); i++) { for (size_t i = 0; i < list.size(); i++) {
int wildcardIndex = list[i].indexOf("*"); int wildcardIndex = list[i].indexOf("*");
if (wildcardIndex >= 0) { if (wildcardIndex >= 0) {
String wildcard = list[i].substring(0, wildcardIndex); String wildcard = list[i].substring(0, wildcardIndex);
+10 -6
View File
@@ -21,12 +21,15 @@
#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 "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)
@@ -94,7 +97,8 @@ 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) {
STATION_Utils::addToOutputPacketBuffer(frame); if (Config.loramodule.txActive) 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");
} }
@@ -131,8 +135,8 @@ namespace TNC_Utils {
} }
} }
void sendToClients(const String& packet) { void sendToClients(const String& packet, bool stripBytes) {
String cleanPacket = packet.substring(3); String cleanPacket = stripBytes ? packet.substring(3): packet;
const String kissEncoded = encodeKISS(cleanPacket); const String kissEncoded = encodeKISS(cleanPacket);
@@ -152,8 +156,8 @@ namespace TNC_Utils {
Utils::println(cleanPacket); Utils::println(cleanPacket);
} }
void sendToSerial(const String& packet) { void sendToSerial(const String& packet, bool stripBytes) {
String cleanPacket = packet.substring(3); String cleanPacket = stripBytes ? packet.substring(3): packet;
Serial.print(encodeKISS(cleanPacket)); Serial.print(encodeKISS(cleanPacket));
Serial.flush(); Serial.flush();
} }
+20 -12
View File
@@ -16,6 +16,7 @@
* 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 "telemetry_utils.h"
@@ -71,12 +72,8 @@ String secondaryBeaconPacket;
namespace Utils { namespace Utils {
void processStatus() { void processStatus() {
String status = Config.callsign; String status = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
status.concat(">APLRG1");
if (Config.beacon.path.indexOf("WIDE") == 0) {
status.concat(",");
status.concat(Config.beacon.path);
}
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) { if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
delay(1000); delay(1000);
status.concat(",qAC:>"); status.concat(",qAC:>");
@@ -161,18 +158,27 @@ namespace Utils {
showActiveStations(); showActiveStations();
beaconPacket = iGateBeaconPacket;
secondaryBeaconPacket = iGateLoRaBeaconPacket;
#ifdef HAS_GPS #ifdef HAS_GPS
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) {
GPS_Utils::generateBeaconFirstPart(); String basePacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
String encodedGPS = GPS_Utils::encodeGPS(gps.location.lat(), gps.location.lng(), Config.beacon.overlay, Config.beacon.symbol); String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
beaconPacket = iGateBeaconPacket + encodedGPS;
secondaryBeaconPacket = iGateLoRaBeaconPacket + encodedGPS; beaconPacket = basePacket;
beaconPacket += ",qAC:!";
beaconPacket += Config.beacon.overlay;
beaconPacket += encodedGPS;
secondaryBeaconPacket = basePacket;
secondaryBeaconPacket += ":=";
secondaryBeaconPacket += Config.beacon.overlay;
secondaryBeaconPacket += encodedGPS;
} }
} }
#else
beaconPacket = iGateBeaconPacket;
secondaryBeaconPacket = iGateLoRaBeaconPacket;
#endif #endif
if (Config.wxsensor.active) { if (Config.wxsensor.active) {
@@ -284,6 +290,7 @@ 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();
} }
@@ -401,6 +408,7 @@ namespace Utils {
cleanCallsign = callsign.substring(0, callsign.indexOf("-")); cleanCallsign = callsign.substring(0, callsign.indexOf("-"));
String ssid = callsign.substring(callsign.indexOf("-") + 1); String ssid = callsign.substring(callsign.indexOf("-") + 1);
if (ssid.indexOf("-") != -1 || ssid.length() > 2) return false; if (ssid.indexOf("-") != -1 || ssid.length() > 2) return false;
if (ssid.length() == 2 && ssid[0] == '0') return false;
for (int i = 0; i < ssid.length(); i++) { for (int i = 0; i < ssid.length(); i++) {
if (!isAlphaNumeric(ssid[i])) return false; if (!isAlphaNumeric(ssid[i])) return false;
} }
+4 -3
View File
@@ -177,6 +177,7 @@ namespace WEB_Utils {
Config.beacon.interval = getParamIntSafe("beacon.interval", Config.beacon.interval); Config.beacon.interval = getParamIntSafe("beacon.interval", Config.beacon.interval);
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 = getParamDoubleSafe("beacon.latitude", Config.beacon.latitude); Config.beacon.latitude = getParamDoubleSafe("beacon.latitude", Config.beacon.latitude);
Config.beacon.longitude = getParamDoubleSafe("beacon.longitude", Config.beacon.longitude); Config.beacon.longitude = getParamDoubleSafe("beacon.longitude", Config.beacon.longitude);
Config.beacon.comment = getParamStringSafe("beacon.comment", Config.beacon.comment); Config.beacon.comment = getParamStringSafe("beacon.comment", Config.beacon.comment);
@@ -190,7 +191,7 @@ namespace WEB_Utils {
} }
Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true); Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true);
Config.beacon.gpsAmbiguity = request->hasParam("beacon.gpsAmbiguity", true); Config.beacon.ambiguityLevel = getParamIntSafe("beacon.ambiguityLevel", Config.beacon.ambiguityLevel);
Config.personalNote = getParamStringSafe("personalNote", Config.personalNote); Config.personalNote = getParamStringSafe("personalNote", Config.personalNote);
@@ -198,9 +199,8 @@ namespace WEB_Utils {
Config.digi.mode = getParamIntSafe("digi.mode", Config.digi.mode); Config.digi.mode = getParamIntSafe("digi.mode", Config.digi.mode);
Config.digi.ecoMode = getParamIntSafe("digi.ecoMode", Config.digi.ecoMode); Config.digi.ecoMode = getParamIntSafe("digi.ecoMode", Config.digi.ecoMode);
Config.digi.beaconOnRxFreq = request->hasParam("digi.beaconOnRxFreq", true);
Config.loramodule.rxActive = request->hasParam("lora.rxActive", true); Config.loramodule.rxActive = request->hasParam("lora.rxActive", true);
Config.loramodule.rxFreq = getParamIntSafe("lora.rxFreq", Config.loramodule.rxFreq); Config.loramodule.rxFreq = getParamIntSafe("lora.rxFreq", Config.loramodule.rxFreq);
Config.loramodule.rxSpreadingFactor = getParamIntSafe("lora.rxSpreadingFactor", Config.loramodule.rxSpreadingFactor); Config.loramodule.rxSpreadingFactor = getParamIntSafe("lora.rxSpreadingFactor", Config.loramodule.rxSpreadingFactor);
@@ -259,6 +259,7 @@ namespace WEB_Utils {
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); Config.mqtt.active = request->hasParam("mqtt.active", true);
+1 -1
View File
@@ -60,7 +60,7 @@ namespace WIFI_Utils {
Serial.print(millis()); Serial.print(millis());
Serial.println("Reconnecting to WiFi..."); Serial.println("Reconnecting to WiFi...");
WiFi.disconnect(); WiFi.disconnect();
WIFI_Utils::startWiFi();//WiFi.reconnect(); WIFI_Utils::startWiFi();
previousWiFiMillis = millis(); previousWiFiMillis = millis();
if (Config.backupDigiMode) { if (Config.backupDigiMode) {