mirror of
https://github.com/richonguzman/LoRa_APRS_iGate.git
synced 2026-07-13 21:31:14 +02:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a4c0ef01a | |||
| 0bd3201ef7 | |||
| b5d9103ea5 | |||
| 20a5029da8 | |||
| b3cc8af180 | |||
| ca16761f68 | |||
| e593b2c670 | |||
| 08a7e0aac1 | |||
| 9b258c42ab | |||
| 8712122d33 | |||
| 9e34684627 | |||
| e4aa52241d | |||
| c840ef01fa | |||
| 98f85725f8 | |||
| 10bde884b1 | |||
| 546e4f63a4 | |||
| 87f2ec2b7b | |||
| c9c7e24aae |
@@ -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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
__(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)
|
||||||
|
|
||||||
____________________________________________________
|
____________________________________________________
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@ ____________________________________________________
|
|||||||
<br />
|
<br />
|
||||||
|
|
||||||
# Timeline (Versions):
|
# Timeline (Versions):
|
||||||
|
- 2025-12-01 APRSPacketLib updates, AHT20 sensor added.
|
||||||
- 2025-10-15 APRS Bridge for TNC added.
|
- 2025-10-15 APRS Bridge for TNC added.
|
||||||
- 2025-10-13 Rx and Tx Frequencies are now with fully configurable.
|
- 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-13 Startup Delay to allow the Router/Modem to start WiFiAP before connecting.
|
||||||
|
|||||||
+2
-1
@@ -25,6 +25,7 @@ build_flags =
|
|||||||
-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
|
||||||
@@ -37,7 +38,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
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"statusActive": false,
|
"statusActive": false,
|
||||||
"statusPacket": "",
|
"statusPacket": "",
|
||||||
"gpsActive": false,
|
"gpsActive": false,
|
||||||
"gpsAmbiguity": false
|
"ambiguityLevel": 0
|
||||||
},
|
},
|
||||||
"aprs_is": {
|
"aprs_is": {
|
||||||
"active": false,
|
"active": false,
|
||||||
|
|||||||
+31
-54
@@ -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
|
||||||
@@ -583,12 +574,8 @@
|
|||||||
name="beacon.beaconFreq"
|
name="beacon.beaconFreq"
|
||||||
id="beacon.beaconFreq"
|
id="beacon.beaconFreq"
|
||||||
>
|
>
|
||||||
<option value="0">
|
<option value="0">Beacon on Rx Freq</option>
|
||||||
Beacon on Rx Freq
|
<option value="1">Beacon on Tx Freq</option>
|
||||||
</option>
|
|
||||||
<option value="1">
|
|
||||||
Beacon on Tx Freq
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -615,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">
|
||||||
@@ -631,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>
|
||||||
@@ -667,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">
|
||||||
@@ -723,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">
|
||||||
@@ -748,15 +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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -131,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;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public:
|
|||||||
bool statusActive;
|
bool statusActive;
|
||||||
String statusPacket;
|
String statusPacket;
|
||||||
bool gpsActive;
|
bool gpsActive;
|
||||||
bool gpsAmbiguity;
|
int ambiguityLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
class APRS_IS {
|
class APRS_IS {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#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>
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ ___________________________________________________________________*/
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
String versionDate = "2025-10-21";
|
String versionDate = "2025-12-01";
|
||||||
String versionNumber = "3.1.4";
|
String versionNumber = "3.1.5";
|
||||||
Configuration Config;
|
Configuration Config;
|
||||||
WiFiClient aprsIsClient;
|
WiFiClient aprsIsClient;
|
||||||
WiFiClient mqttClient;
|
WiFiClient mqttClient;
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -236,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,");
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ bool Configuration::writeFile() {
|
|||||||
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;
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ bool Configuration::readFile() {
|
|||||||
!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";
|
||||||
@@ -246,7 +246,7 @@ bool Configuration::readFile() {
|
|||||||
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";
|
||||||
@@ -449,7 +449,7 @@ void Configuration::setDefaultValues() {
|
|||||||
beacon.statusPacket = "";
|
beacon.statusPacket = "";
|
||||||
|
|
||||||
beacon.gpsActive = false;
|
beacon.gpsActive = false;
|
||||||
beacon.gpsAmbiguity = false;
|
beacon.ambiguityLevel = 0;
|
||||||
|
|
||||||
personalNote = "";
|
personalNote = "";
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -107,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
@@ -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) {
|
||||||
|
|||||||
+1
-1
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
+19
-12
@@ -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) {
|
||||||
@@ -402,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;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -191,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);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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) {
|
||||||
|
|||||||
+19
-6
@@ -42,6 +42,7 @@ 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();
|
||||||
@@ -119,6 +120,10 @@ 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) {
|
} else if (wxModuleAddress == 0x40) {
|
||||||
@@ -127,7 +132,7 @@ namespace WX_Utils {
|
|||||||
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()) {
|
||||||
@@ -273,7 +278,15 @@ namespace WX_Utils {
|
|||||||
newPress = 0;
|
newPress = 0;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
case 6: // BMP280 + AHT20
|
||||||
|
bmp280.takeForcedMeasurement();
|
||||||
|
newTemp = bmp280.readTemperature();
|
||||||
|
newPress = (bmp280.readPressure() / 100.0F);
|
||||||
|
sensors_event_t humidity, temp;
|
||||||
|
aht20.getEvent(&humidity, &temp);
|
||||||
|
newHum = humidity.relative_humidity;
|
||||||
|
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");
|
||||||
@@ -281,16 +294,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) {
|
if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4 || wxModuleType == 5 || wxModuleType == 6) {
|
||||||
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-> ";
|
||||||
|
|||||||
Reference in New Issue
Block a user