Compare commits

...

128 Commits

Author SHA1 Message Date
Ricardo Guzman (Richonguzman)
0eec028c5d update SENSOR BUS 2026-02-23 12:00:55 -03:00
Ricardo Guzman (Richonguzman)
c48dd15bd6 better ADC and VEXT control 2026-02-23 11:14:42 -03:00
Ricardo Guzman (Richonguzman)
f26ded5b5c less memory fixes too 2026-02-20 17:36:26 -03:00
Ricardo Guzman (Richonguzman)
c887689406 indexOf transformation 2026-02-20 17:03:39 -03:00
Ricardo Guzman (Richonguzman)
775e08a10a stationCallsignIsValid 2026-02-20 10:03:01 -03:00
Ricardo Guzman (Richonguzman)
f9291821d2 2 decimales for QTH query 2026-02-18 11:03:06 -03:00
Ricardo Guzman (Richonguzman)
12d0bb760e version update 2026-02-16 23:46:03 -03:00
Ricardo Guzman (Richonguzman)
f020eb7491 update a nombre CPU 2026-02-16 23:01:05 -03:00
Ricardo Guzman (Richonguzman)
63f4660ef6 power utils update con cpu 2026-02-16 22:48:28 -03:00
Ricardo Guzman (Richonguzman)
efabe9b1f7 start Heltec V2 915 2026-02-16 21:37:54 -03:00
Ricardo Guzman (Richonguzman)
beae88d557 less time 2026-02-11 01:03:54 -03:00
Ricardo Guzman (Richonguzman)
8b4d8c2d1d day reset of status tx 2026-02-10 23:01:49 -03:00
Ricardo Guzman (Richonguzman)
08ae1e322b Symbol Explanation added to index.html 2026-02-03 17:09:27 -03:00
Ricardo Guzman (Richonguzman)
d576be0f2b fix telemetry tactical callsign 2026-01-31 11:47:27 -03:00
Ricardo Guzman (Richonguzman)
7c9903ac12 WEBUI update 2026-01-20 22:44:03 -03:00
Ricardo Guzman (Richonguzman)
0d7cff14f5 lastServerCheck update 2026-01-20 21:19:53 -03:00
Ricardo Guzman (Richonguzman)
5c89635a23 lastServerCheck 2026-01-20 21:08:28 -03:00
Ricardo Guzman (Richonguzman)
7a35f9e32a Server Alive mod 2026-01-20 20:56:20 -03:00
Ricardo Guzman (Richonguzman)
7695675a7c check server 2026-01-20 09:50:09 -03:00
Ricardo Guzman (Richonguzman)
c8c0be636d check internet connection 2026-01-20 00:16:24 -03:00
Ricardo Guzman (Richonguzman)
76fe27a0ab APRSSSR into APRSSR fix 2026-01-16 09:41:20 -03:00
Ricardo Guzman (Richonguzman)
da80391921 change Freq with bandWidth fix 2026-01-13 01:50:12 -03:00
Ricardo Guzman (Richonguzman)
d638093dbf Tactical 6 update 2026-01-11 11:46:23 -03:00
Ricardo Guzman (Richonguzman)
66a5f03c12 de update for tactical 2026-01-09 00:02:29 -03:00
Ricardo Guzman (Richonguzman)
ce8cf3a2fe minimal interval updated 2026-01-08 14:08:07 -03:00
Ricardo Guzman (Richonguzman)
4b45b90c88 minor path updates 2026-01-08 12:21:19 -03:00
Ricardo Guzman (Richonguzman)
dbe980a081 tactical callsign ready for testing 2026-01-07 23:18:14 -03:00
Ricardo Guzman (Richonguzman)
bb3d59a20d digi tactical changes 2026-01-07 17:25:45 -03:00
Ricardo Guzman (Richonguzman)
fe590b41b0 tactical callsign displayShow 2026-01-07 14:30:32 -03:00
Ricardo Guzman (Richonguzman)
3f76005949 few delays killed 2026-01-07 12:19:51 -03:00
Ricardo Guzman (Richonguzman)
6d95231b9c web mods for tactical 2026-01-07 11:53:10 -03:00
Ricardo Guzman (Richonguzman)
81692010cf tactical validation 2026-01-07 11:44:00 -03:00
Ricardo Guzman (Richonguzman)
50b738d04b Spreading Factor update 2026-01-07 10:37:11 -03:00
Ricardo Guzman (Richonguzman)
026d6b2eeb starting Heltec V4 2026-01-07 10:14:42 -03:00
Ricardo Guzman (Richonguzman)
fe705519cb V3.1.7 OTA and mode 2025-12-29 09:10:17 -03:00
Ricardo Guzman (Richonguzman)
1fa74b8697 GPS encoded payload comment fix 2025-12-28 13:45:29 -03:00
Ricardo Guzman (Richonguzman)
a3794085b4 OTA update fixed 2025-12-28 11:59:05 -03:00
Ricardo Guzman (Richonguzman)
0a898a40e6 gps payload decoding fix2 2025-12-28 11:46:43 -03:00
Ricardo Guzman (Richonguzman)
ff8c7581fa gps payload decoding fix 2025-12-28 10:06:34 -03:00
Ricardo Guzman (Richonguzman)
449a8557d2 definition fix for VisionMaster 2025-12-23 08:32:06 -03:00
Ricardo Guzman (Richonguzman)
4419c98920 Merge pull request #375 from richonguzman/richonguzman-patch-16
added Heltec VisionMaster E290 and WirelessPaper V1.2
2025-12-22 23:02:46 -03:00
Ricardo Guzman (Richonguzman)
45bf90817b added Heltec VisionMaster E290 and WirelessPaper V1.2 2025-12-22 22:59:21 -03:00
Ricardo Guzman (Richonguzman)
ef30a1bf58 readme update 2025-12-22 21:34:58 -03:00
Ricardo Guzman (Richonguzman)
9a705d3dfa Heltec Wireless Paper V1.2 added 2025-12-22 21:29:50 -03:00
Ricardo Guzman (Richonguzman)
63f74396d2 Heltec Vision Master update 2025-12-22 21:15:03 -03:00
Ricardo Guzman (Richonguzman)
400b77c2d3 date update 2025-12-22 19:57:18 -03:00
Ricardo Guzman (Richonguzman)
31daddf917 invalid Passcode non blocking fix 2025-12-22 19:56:15 -03:00
Ricardo Guzman (Richonguzman)
529a44018f readme and version update 2025-12-18 09:47:56 -03:00
Ricardo Guzman (Richonguzman)
5ba9c5b382 test 2025-12-18 09:28:56 -03:00
Ricardo Guzman (Richonguzman)
c7a0e3773b ina bme update 2025-12-14 05:53:43 -03:00
Ricardo Guzman (Richonguzman)
a5f9e5b844 typo TCXO fix 2025-12-12 12:25:41 -03:00
Ricardo Guzman (Richonguzman)
24f407d51c TCXO add 2025-12-12 12:24:53 -03:00
Ricardo Guzman (Richonguzman)
2c6665b557 FIX for boards with GPS onboard 2025-12-11 13:44:06 -03:00
Ricardo Guzman (Richonguzman)
38f52564f1 lib def fix 2025-12-11 10:27:29 -03:00
Ricardo Guzman (Richonguzman)
49e92c622f README UPDATE for v3.1.5 manual 2025-12-07 08:46:45 -03:00
Ricardo Guzman (Richonguzman)
5370850ae1 update to fix ntp without wifi 2025-12-03 12:31:36 -03:00
Ricardo Guzman (Richonguzman)
984e9f9561 encoded telemetry fix for INA219 2025-12-01 19:53:14 -03:00
Ricardo Guzman (Richonguzman)
f60252ef94 fix for same declaration of variables 2025-12-01 16:02:26 -03:00
Ricardo Guzman (Richonguzman)
63257d6329 readme update 2025-12-01 13:30:37 -03:00
Ricardo Guzman (Richonguzman)
18929ad379 INA219 final test 2025-12-01 13:29:54 -03:00
Ricardo Guzman (Richonguzman)
cda901142d INA219 3 2025-12-01 12:45:14 -03:00
Ricardo Guzman (Richonguzman)
8008143267 configuration INA219 add 2025-12-01 10:55:31 -03:00
Ricardo Guzman (Richonguzman)
3416e21a8c ready radiolib excludes 2025-12-01 10:45:14 -03:00
Ricardo Guzman (Richonguzman)
5882b54c62 radiolib exclude start 2025-12-01 10:11:15 -03:00
Ricardo Guzman (Richonguzman)
6a4c0ef01a readme update 2025-12-01 09:38:37 -03:00
Ricardo Guzman (Richonguzman)
0bd3201ef7 AHT20 add 2025-12-01 09:37:02 -03:00
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
richonguzman
ba7ff2a2d9 WebUI change 2025-10-13 12:38:52 -03:00
richonguzman
b44eb1028d no more speed standards on index 2025-10-13 02:03:18 -03:00
richonguzman
d7602268f2 changes for Rx and Tx Lora Freq 2025-10-13 02:00:05 -03:00
richonguzman
2f6f9be28e Lora Config separated for Rx and Tx 2025-10-13 01:33:41 -03:00
richonguzman
92572245cd beacon Freq on digi mod 2025-10-12 21:45:23 -03:00
richonguzman
152217e71c startup delay added 2025-10-12 14:21:51 -03:00
richonguzman
d334164b6f Beacon Freq on Digi Mode 2025-10-11 18:27:06 -03:00
richonguzman
61409ce683 index correction 2025-10-11 15:57:29 -03:00
richonguzman
742a6b6477 readme update 2025-10-11 13:23:39 -03:00
richonguzman
dd2fce3908 igate beacon over mqtt 2025-10-11 13:22:08 -03:00
richonguzman
90b29b66d5 callsign trim 2025-10-11 12:50:53 -03:00
richonguzman
2a86cdb0d8 Op ntp server 2025-10-11 12:40:07 -03:00
richonguzman
1abaa299e0 update readme 2025-10-10 12:44:15 -03:00
Ricardo Guzman (Richonguzman)
9cbd7c22fd Merge pull request #342 from richonguzman/richonguzman-patch-14
Update README.md
2025-10-10 12:15:18 -03:00
Ricardo Guzman (Richonguzman)
e15e6def91 Update README.md 2025-10-10 12:14:56 -03:00
richonguzman
33c8be4f48 added manual for 3.1.2 2025-10-10 12:11:24 -03:00
Ricardo Guzman (Richonguzman)
c2a7c26d78 Merge pull request #341 from richonguzman/richonguzman-patch-13
Update README.md
2025-10-10 12:08:41 -03:00
Ricardo Guzman (Richonguzman)
387e8a988a Update README.md 2025-10-10 12:08:28 -03:00
Ricardo Guzman (Richonguzman)
48b075a554 Merge pull request #340 from richonguzman/richonguzman-patch-12
Update README.md
2025-10-10 12:06:38 -03:00
Ricardo Guzman (Richonguzman)
7e76e43817 Update README.md 2025-10-10 12:06:25 -03:00
Ricardo Guzman (Richonguzman)
9b7b4e1838 Merge pull request #339 from richonguzman/richonguzman-patch-11
Update README.md
2025-10-10 12:04:17 -03:00
Ricardo Guzman (Richonguzman)
5b9d56843b Update README.md 2025-10-10 12:04:04 -03:00
Ricardo Guzman (Richonguzman)
30eb1023dc Merge pull request #338 from richonguzman/richonguzman-patch-10
Update README.md
2025-10-10 12:03:09 -03:00
Ricardo Guzman (Richonguzman)
1ba86af61b Update README.md 2025-10-10 12:02:54 -03:00
Ricardo Guzman (Richonguzman)
1d475bc8a2 Merge pull request #337 from richonguzman/richonguzman-patch-9
Update README.md
2025-10-10 11:58:13 -03:00
Ricardo Guzman (Richonguzman)
70f9a6fa04 Update README.md 2025-10-10 11:57:50 -03:00
Ricardo Guzman (Richonguzman)
6747511850 Merge pull request #336 from richonguzman/richonguzman-patch-8
Update README.md
2025-10-10 11:56:25 -03:00
Ricardo Guzman (Richonguzman)
96a4394e89 Update README.md 2025-10-10 11:56:10 -03:00
Ricardo Guzman (Richonguzman)
4e009bc14c Merge pull request #332 from richonguzman/richonguzman-patch-7
adding heltec wireless bridge
2025-09-29 15:52:00 -03:00
Ricardo Guzman (Richonguzman)
4c11c66a1c adding heltec wireless bridge 2025-09-29 15:51:41 -03:00
159 changed files with 2304 additions and 1579 deletions

View File

@@ -29,6 +29,8 @@ jobs:
chip: esp32s3
- name: heltec_wireless_stick_lite_v3_display
chip: esp32s3
- name: heltec_wireless_bridge
chip: esp32
- name: ESP32_DIY_LoRa
chip: esp32
- name: ESP32_DIY_LoRa_915
@@ -69,6 +71,10 @@ jobs:
chip: esp32c3
- name: heltec_wireless_paper_v1
chip: esp32s3
- name: heltec_wireless_paper_v1_2
chip: esp32s3
- name: heltec_vision_master_e290
chip: esp32s3
- name: OE5HWN_MeshCom
chip: esp32
- name: WEMOS-LOLIN32-OLED-DIY

View File

@@ -2,53 +2,45 @@
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>))__
<br />
____________________________________________________
## You can support this project to continue to grow:
# <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">WEB FLASHER/INSTALLER</a>
[<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/blob/main/images/github-sponsors.png">](https://github.com/sponsors/richonguzman) [<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/blob/main/images/paypalme.png">](http://paypal.me/richonguzman)
<br />
# WEB FLASHER/INSTALLER is <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">here</a>
# <a href="https://drive.google.com/file/d/1Hff_Szd7ks8RC7_RiV6POxPJlclbO05M/view?usp=sharing" target="_blank">LoRa APRS iGate CA2RXU Firmware Manual</a>
____________________________________________________
# WIKI
## You can support this project to continue to grow:
### FAQ, BME280, TNC and more --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/00.-FAQ-(frequently-asked-questions)" target="_blank">here</a>.
[<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)
### Installation Guide --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/01.-Installation-Guide" target="_blank">here</a>.
<br />
____________________________________________________
# SUPPORTED BOARDS
### Buying links --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/108.-Supported-Boards-and-Buying-Links" target="_blank">here</a>.
## SUPPORTED BOARDS (<a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/Supported-Boards-and-Buying-Links" target="_blank">Buying links</a>).
(NOTE: all boards with 433-868-915 MHz versions)
- TTGO Lilygo LoRa32 T3S3 V1.2 and LoRa32 V2.1 (V1.6 is the same).
- TTGO T-Beam V1.0 , V1.1, V1.2 (also variations with SX1262 and SX1268 LoRa Modules).
- TTGO T-Beam V1.0 , V1.1, V1.2 (also variations with SX1262 and SX1268 LoRa Modules) and Supreme V3.
- 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(19003)
- RAK Wireless 4631 + 19007(or 19003)
- Faketec (NRF52840 + Heltec HTRA62(SX1262))
- Faketec V3 (NRF52840 + Heltec HTRA62(SX1262))
- QRP Labs LightGateway 1.0 and Plus 1.0.
- Faketec V3 (NRF52840 + Heltec HTRA62 SX1262)
- ESP32 Wroom + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
- ESP32 + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
- ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version.
@@ -58,8 +50,18 @@ ____________________________________________________
<br />
## Timeline (Versions):
# Timeline (Versions):
- 2026-01-07 Tactical Callsign added.
- 2026-01-05 Heltec V4 support added.
- 2025-12-22 Heltec Wireless Paper V1.2 and VisionMaster E290 Added. Thanks HA5SZI.
- 2025-12-18 TCXO and packet decoding updates.
- 2025-12-01 APRSPacketLib updates, AHT20 sensor added, INA219 support added.
- 2025-10-15 APRS Bridge for TNC added.
- 2025-10-13 Rx and Tx Frequencies are now with fully configurable.
- 2025-10-13 Startup Delay to allow the Router/Modem to start WiFiAP before connecting.
- 2025-10-12 Choose to send Beacon on Rx or Tx frequency.
- 2025-10-11 User defined NTP server and send beacon over MQTT added.
- 2025-10-10 Converted the Wiki into a PDF manual.
- 2025-09-26 Heltec Wireless Bridge support added.
- 2025-09-09 MQTT added (pub+sub), Status defined by Op now and many fixes more.
- 2025-06-20 Digipeaters now with updated EcoMode (Board Sleeps until packet Rx reducing current consumption to almost 10% at idle).

View File

@@ -2,42 +2,43 @@
build_flags =
-Werror -Wall
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
-DRADIOLIB_EXCLUDE_CC1101=1
-DRADIOLIB_EXCLUDE_NRF24=1
-DRADIOLIB_EXCLUDE_RF69=1
-DRADIOLIB_EXCLUDE_SX1231=1
-DRADIOLIB_EXCLUDE_SX1233=1
-DRADIOLIB_EXCLUDE_SI443X=1
-DRADIOLIB_EXCLUDE_RFM2X=1
-DRADIOLIB_EXCLUDE_AFSK=1
-DRADIOLIB_EXCLUDE_BELL=1
-DRADIOLIB_EXCLUDE_HELLSCHREIBER=1
-DRADIOLIB_EXCLUDE_MORSE=1
-DRADIOLIB_EXCLUDE_RTTY=1
-DRADIOLIB_EXCLUDE_SSTV=1
-DRADIOLIB_EXCLUDE_AX25=1
-DRADIOLIB_EXCLUDE_DIRECT_RECEIVE=1
-DRADIOLIB_EXCLUDE_BELL=1
-DRADIOLIB_EXCLUDE_PAGER=1
-DRADIOLIB_EXCLUDE_FSK4=1
-DRADIOLIB_EXCLUDE_APRS=1
-DRADIOLIB_EXCLUDE_LORAWAN=1
-D RADIOLIB_EXCLUDE_CC1101=1
-D RADIOLIB_EXCLUDE_RF69=1
-D RADIOLIB_EXCLUDE_RFM2X=1
-D RADIOLIB_EXCLUDE_SX1231=1
-D RADIOLIB_EXCLUDE_SX1233=1
-D RADIOLIB_EXCLUDE_SI443X=1
-D RADIOLIB_EXCLUDE_NRF24=1
-D RADIOLIB_EXCLUDE_AFSK=1
-D RADIOLIB_EXCLUDE_APRS=1
-D RADIOLIB_EXCLUDE_AX25=1
-D RADIOLIB_EXCLUDE_BELL=1
-D RADIOLIB_EXCLUDE_FSK4=1
-D RADIOLIB_EXCLUDE_HELLSCHREIBER=1
-D RADIOLIB_EXCLUDE_LORAWAN=1
-D RADIOLIB_EXCLUDE_MORSE=1
-D RADIOLIB_EXCLUDE_PAGER=1
-D RADIOLIB_EXCLUDE_DIRECT_RECEIVE=1
-D RADIOLIB_EXCLUDE_RTTY=1
-D RADIOLIB_EXCLUDE_SSTV=1
-I variants/${PIOENV}
lib_deps =
adafruit/Adafruit Unified Sensor @ 1.1.14
adafruit/Adafruit AHTX0 @ 2.0.5
adafruit/Adafruit BME280 Library @ 2.2.4
adafruit/Adafruit BMP280 Library @ 2.6.8
adafruit/Adafruit BME680 Library @ 2.0.4
adafruit/Adafruit INA219 @ 1.2.3
adafruit/Adafruit Si7021 Library @ 1.5.3
arduino-libraries/NTPClient @ 3.2.1
ayushsharma82/ElegantOTA @ 3.1.5
ayushsharma82/ElegantOTA @ 3.1.7
bblanchon/ArduinoJson @ 6.21.3
jgromes/RadioLib @ 7.1.0
knolleary/PubSubClient @ 2.8
mathieucarbou/AsyncTCP @ 3.2.5
mathieucarbou/ESPAsyncWebServer @ 3.2.3
ESP32Async/AsyncTCP @ 3.4.9
ESP32Async/ESPAsyncWebServer @ 3.9.3
mikalhart/TinyGPSPlus @ 1.0.3
richonguzman/APRSPacketLib @1.0.0
richonguzman/APRSPacketLib @ 1.0.4
display_libs =
adafruit/Adafruit GFX Library @ 1.11.9
adafruit/Adafruit SSD1306 @ 2.5.10

View File

@@ -17,10 +17,11 @@
"path": "WIDE1-1",
"sendViaAPRSIS": false,
"sendViaRF": false,
"beaconFreq": 1,
"statusActive": false,
"statusPacket": "",
"gpsActive": false,
"gpsAmbiguity": false
"ambiguityLevel": 0
},
"aprs_is": {
"active": false,
@@ -35,17 +36,21 @@
"blacklist": "",
"digi": {
"mode": 0,
"ecoMode": 0
"ecoMode": 0,
"backupDigiMode": false
},
"lora": {
"txFreq": 433775000,
"rxActive": true,
"rxFreq": 433775000,
"spreadingFactor": 12,
"signalBandwidth": 125000,
"codingRate4": 5,
"power": 20,
"rxSpreadingFactor": 12,
"rxCodingRate4": 5,
"rxSignalBandwidth": 125000,
"txActive": false,
"rxActive": true
"txFreq": 433775000,
"txSpreadingFactor": 12,
"txCodingRate4": 5,
"txSignalBandwidth": 125000,
"power": 20
},
"display": {
"alwaysOn": true,
@@ -57,11 +62,12 @@
"monitorInternalVoltage": false,
"internalSleepVoltage": 2.9,
"sendExternalVoltage": false,
"externalVoltagePin": 34,
"monitorExternalVoltage": false,
"externalSleepVoltage": 10.9,
"useExternalI2CSensor": false,
"voltageDividerR1": 100.0,
"voltageDividerR2": 27.0,
"externalVoltagePin": 34,
"sendVoltageAsTelemetry": false
},
"wxsensor": {
@@ -78,7 +84,8 @@
"tnc": {
"enableServer": false,
"enableSerial": false,
"acceptOwn": false
"acceptOwn": false,
"aprsBrigdeActive": false
},
"mqtt": {
"active": false,
@@ -86,7 +93,8 @@
"topic": "",
"username": "",
"password": "",
"port": 1883
"port": 1883,
"beaconOverMqtt": false
},
"ota": {
"username": "",
@@ -101,12 +109,13 @@
"rfOnly": true
},
"ntp": {
"server": "pool.ntp.org",
"gmtCorrection": 0.0
},
"other": {
"rememberStationTime": 30,
"backupDigiMode": false,
"rebootMode": false,
"rebootModeTime": 6
"rebootModeTime": 6,
"startupDelay": 0
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -54,6 +54,7 @@ function loadSettings(settings) {
currentSettings = settings;
// General
document.getElementById("callsign").value = settings.callsign;
document.getElementById("tacticalCallsign").value = settings.tacticalCallsign;
document.getElementById("beacon.comment").value = settings.beacon.comment;
document.getElementById("beacon.path").value = settings.beacon.path;
document.getElementById("beacon.symbol").value = settings.beacon.symbol;
@@ -95,6 +96,7 @@ function loadSettings(settings) {
networksContainer.appendChild(networkElement);
networkCount++;
});
document.getElementById("startupDelay").value = settings.startupDelay;
// APRS-IS
document.getElementById("aprs_is.active").checked = settings.aprs_is.active;
@@ -118,7 +120,11 @@ function loadSettings(settings) {
document.getElementById("beacon.interval").value = settings.beacon.interval;
document.getElementById("other.rememberStationTime").value = settings.other.rememberStationTime;
document.getElementById("beacon.sendViaAPRSIS").checked = settings.beacon.sendViaAPRSIS;
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.statusPacket").value = settings.beacon.statusPacket;
@@ -126,7 +132,7 @@ function loadSettings(settings) {
StatusPacket.disabled = !StatusCheckbox.checked;
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
document.getElementById("blacklist").value = settings.blacklist;
@@ -134,15 +140,19 @@ function loadSettings(settings) {
// Digi
document.getElementById("digi.mode").value = settings.digi.mode;
document.getElementById("digi.ecoMode").value = settings.digi.ecoMode;
document.getElementById("digi.backupDigiMode").checked = settings.digi.backupDigiMode;
// LoRa
document.getElementById("lora.txFreq").value = settings.lora.txFreq;
document.getElementById("lora.rxFreq").value = settings.lora.rxFreq;
document.getElementById("lora.txActive").checked = settings.lora.txActive;
document.getElementById("lora.rxActive").checked = settings.lora.rxActive;
document.getElementById("lora.spreadingFactor").value = settings.lora.spreadingFactor;
document.getElementById("lora.signalBandwidth").value = settings.lora.signalBandwidth;
document.getElementById("lora.codingRate4").value = settings.lora.codingRate4;
document.getElementById("lora.rxFreq").value = settings.lora.rxFreq;
document.getElementById("lora.rxSpreadingFactor").value = settings.lora.rxSpreadingFactor;
document.getElementById("lora.rxCodingRate4").value = settings.lora.rxCodingRate4;
document.getElementById("lora.rxSignalBandwidth").value = settings.lora.rxSignalBandwidth;
document.getElementById("lora.txActive").checked = settings.lora.txActive;
document.getElementById("lora.txFreq").value = settings.lora.txFreq;
document.getElementById("lora.txSpreadingFactor").value = settings.lora.txSpreadingFactor;
document.getElementById("lora.txCodingRate4").value = settings.lora.txCodingRate4;
document.getElementById("lora.txSignalBandwidth").value = settings.lora.txSignalBandwidth;
document.getElementById("lora.power").value = settings.lora.power;
// Display
@@ -160,13 +170,16 @@ function loadSettings(settings) {
MonitorInternalSleepVoltage.disabled = !MonitorInternalVoltageCheckbox.checked;
document.getElementById("battery.sendExternalVoltage").checked = settings.battery.sendExternalVoltage;
document.getElementById("battery.useExternalI2CSensor").checked = settings.battery.useExternalI2CSensor;
document.getElementById("battery.externalVoltagePin").value = settings.battery.externalVoltagePin;
document.getElementById("battery.voltageDividerR1").value = settings.battery.voltageDividerR1.toFixed(1);
document.getElementById("battery.voltageDividerR2").value = settings.battery.voltageDividerR2.toFixed(1);
SendExternalVoltageCheckbox.checked = settings.battery.sendExternalVoltage;
ExternalVoltagePin.disabled = !SendExternalVoltageCheckbox.checked;
ExternalVoltageDividerR1.disabled = !SendExternalVoltageCheckbox.checked;
ExternalVoltageDividerR2.disabled = !SendExternalVoltageCheckbox.checked;
UseExternalI2CSensorCheckbox.disabled = !SendExternalVoltageCheckbox.checked;
ExternalVoltagePin.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR1.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR2.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
document.getElementById("battery.monitorExternalVoltage").checked = settings.battery.monitorExternalVoltage;
document.getElementById("battery.externalSleepVoltage").value = settings.battery.externalSleepVoltage.toFixed(1);
@@ -198,6 +211,7 @@ function loadSettings(settings) {
document.getElementById("tnc.enableServer").checked = settings.tnc.enableServer;
document.getElementById("tnc.enableSerial").checked = settings.tnc.enableSerial;
document.getElementById("tnc.acceptOwn").checked = settings.tnc.acceptOwn;
document.getElementById("tnc.aprsBridgeActive").checked = settings.tnc.aprsBridgeActive;
}
// MQTT
@@ -207,12 +221,14 @@ function loadSettings(settings) {
document.getElementById("mqtt.username").value = settings.mqtt.username;
document.getElementById("mqtt.password").value = settings.mqtt.password;
document.getElementById("mqtt.port").value = settings.mqtt.port;
MqttCheckbox.checked = settings.mqtt.active;
MqttServer.disabled = !MqttCheckbox.check;
MqttTopic.disabled = !MqttCheckbox.check;
MqttUsername.disabled = !MqttCheckbox.check;
MqttPassword.disabled = !MqttCheckbox.check;
MqttPort.disabled = !MqttCheckbox.check;
document.getElementById("mqtt.beaconOverMqtt").checked = settings.mqtt.beaconOverMqtt;
MqttCheckbox.checked = settings.mqtt.active;
MqttServer.disabled = !MqttCheckbox.checked;
MqttTopic.disabled = !MqttCheckbox.checked;
MqttUsername.disabled = !MqttCheckbox.checked;
MqttPassword.disabled = !MqttCheckbox.checked;
MqttPort.disabled = !MqttCheckbox.checked;
MqttBeaconOverMqtt.disabled = !MqttCheckbox.checked;
// Reboot
document.getElementById("other.rebootMode").checked = settings.other.rebootMode;
@@ -241,13 +257,10 @@ function loadSettings(settings) {
document.getElementById("remoteManagement.rfOnly").checked = settings.remoteManagement.rfOnly;
// NTP
document.getElementById("ntp.server").value = settings.ntp.server;
document.getElementById("ntp.gmtCorrection").value = settings.ntp.gmtCorrection;
// Experimental
document.getElementById("other.backupDigiMode").checked = settings.other.backupDigiMode;
updateImage();
refreshSpeedStandard();
}
function showToast(message) {
@@ -299,6 +312,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
const StatusCheckbox = document.querySelector('input[name="beacon.statusActive"]');
@@ -343,13 +362,21 @@ MonitorExternalVoltageCheckbox.addEventListener("change", function () {
MonitorExternalSleepVoltage.disabled = !this.checked;
});
const SendExternalVoltageCheckbox = document.querySelector('input[name="battery.sendExternalVoltage"]');
const UseExternalI2CSensorCheckbox = document.querySelector('input[name="battery.useExternalI2CSensor"]');
const ExternalVoltagePin = document.querySelector('input[name="battery.externalVoltagePin"]');
const ExternalVoltageDividerR1 = document.querySelector('input[name="battery.voltageDividerR1"]');
const ExternalVoltageDividerR2 = document.querySelector('input[name="battery.voltageDividerR2"]');
SendExternalVoltageCheckbox.addEventListener("change", function () {
ExternalVoltagePin.disabled = !this.checked;
ExternalVoltageDividerR1.disabled = !this.checked;
ExternalVoltageDividerR2.disabled = !this.checked;
UseExternalI2CSensorCheckbox.disabled = !this.checked;
ExternalVoltagePin.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR1.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR2.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
});
UseExternalI2CSensorCheckbox.addEventListener("change", function () {
ExternalVoltagePin.disabled = this.checked;
ExternalVoltageDividerR1.disabled = this.checked;
ExternalVoltageDividerR2.disabled = this.checked;
});
// Telemetry Switches
@@ -379,12 +406,14 @@ const MqttTopic = document.querySelector('input[name="mqtt.topic
const MqttUsername = document.querySelector('input[name="mqtt.username"]');
const MqttPassword = document.querySelector('input[name="mqtt.password"]');
const MqttPort = document.querySelector('input[name="mqtt.port"]');
const MqttBeaconOverMqtt = document.querySelector('input[name="mqtt.beaconOverMqtt"]');
MqttCheckbox.addEventListener("change", function () {
MqttServer.disabled = !this.checked;
MqttTopic.disabled = !this.checked;
MqttUsername.disabled = !this.checked;
MqttPassword.disabled = !this.checked;
MqttPort.disabled = !this.checked;
MqttBeaconOverMqtt.disabled = !this.checked;
});
// Reboot Switches
@@ -451,65 +480,6 @@ document
updateImage();
});
const speedStandards = {
300: [125, 5, 12],
244: [125, 6, 12],
209: [125, 7, 12],
183: [125, 8, 12],
610: [125, 8, 10],
1200: [125, 7, 9],
};
function refreshSpeedStandard() {
const bw = Number(document.getElementById("lora.signalBandwidth").value);
const cr4 = Number(document.getElementById("lora.codingRate4").value);
const sf = Number(document.getElementById("lora.spreadingFactor").value);
let found = false;
for (const speed in speedStandards) {
const standard = speedStandards[speed];
if (standard[0] !== bw / 1000) continue;
if (standard[1] !== cr4) continue;
if (standard[2] !== sf) continue;
document.getElementById("action.speed").value = speed;
found = true;
break;
}
if (!found) {
document.getElementById("action.speed").value = "";
}
}
document
.getElementById("lora.signalBandwidth")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.codingRate4")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.spreadingFactor")
.addEventListener("focusout", refreshSpeedStandard);
document.getElementById("action.speed").addEventListener("change", function () {
const speed = document.getElementById("action.speed").value;
if (speed !== "") {
const value = speedStandards[Number(speed)];
const bw = value[0];
const cr4 = value[1];
const sf = value[2];
document.getElementById("lora.signalBandwidth").value = bw * 1000;
document.getElementById("lora.codingRate4").value = cr4;
document.getElementById("lora.spreadingFactor").value = sf;
}
});
const form = document.querySelector("form");

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

View File

@@ -45,12 +45,13 @@ public:
String overlay;
String symbol;
String path;
bool sendViaRF;
bool sendViaAPRSIS;
bool gpsActive;
bool gpsAmbiguity;
bool sendViaRF;
int beaconFreq;
bool statusActive;
String statusPacket;
bool gpsActive;
int ambiguityLevel;
};
class APRS_IS {
@@ -68,17 +69,21 @@ class DIGI {
public:
int mode;
int ecoMode; // 0 = Not Active | 1 = Ultra EcoMode | 2 = Not Active (WiFi OFF, Serial ON)
bool backupDigiMode;
};
class LoraModule {
public:
long txFreq;
long rxFreq;
bool txActive;
bool rxActive;
int spreadingFactor;
long signalBandwidth;
int codingRate4;
long rxFreq;
int rxSpreadingFactor;
int rxCodingRate4;
long rxSignalBandwidth;
bool txActive;
long txFreq;
int txSpreadingFactor;
int txCodingRate4;
long txSignalBandwidth;
int power;
};
@@ -98,6 +103,7 @@ public:
int externalVoltagePin;
bool monitorExternalVoltage;
float externalSleepVoltage;
bool useExternalI2CSensor;
float voltageDividerR1;
float voltageDividerR2;
bool sendVoltageAsTelemetry;
@@ -123,6 +129,7 @@ public:
bool enableServer;
bool enableSerial;
bool acceptOwn;
bool aprsBridgeActive;
};
class OTA {
@@ -140,6 +147,7 @@ public:
class NTP {
public:
String server;
float gmtCorrection;
};
@@ -157,15 +165,17 @@ public:
String username;
String password;
int port;
bool beaconOverMqtt;
};
class Configuration {
public:
String callsign;
String tacticalCallsign;
int rememberStationTime;
bool backupDigiMode;
bool rebootMode;
int rebootModeTime;
int startupDelay;
String personalNote;
String blacklist;
std::vector<WiFi_AP> wifiAPs;

View File

@@ -23,12 +23,6 @@
#include <Arduino.h>
struct Packet25SegBuffer {
uint32_t receivedTime;
String station;
String payload;
};
struct LastHeardStation {
uint32_t lastHeardTime;
String station;
@@ -47,7 +41,7 @@ namespace STATION_Utils {
bool check25SegBuffer(const String& station, const String& textMessage);
void processOutputPacketBufferUltraEcoMode();
void processOutputPacketBuffer();
void addToOutputPacketBuffer(const String& packet);
void addToOutputPacketBuffer(const String& packet, bool flag = false);
}

View File

@@ -27,8 +27,8 @@ namespace TNC_Utils {
void setup();
void loop();
void sendToClients(const String& packet);
void sendToSerial(const String& packet);
void sendToClients(const String& packet, bool stripBytes = false);
void sendToSerial(const String& packet, bool stripBytes = false);
}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -35,7 +35,7 @@ namespace Utils {
void processStatus();
String getLocalIP();
void setupDisplay();
void activeStations();
void showActiveStations();
void checkBeaconInterval();
void checkDisplayInterval();
void validateFreqs();
@@ -45,7 +45,8 @@ namespace Utils {
void checkRebootMode();
void checkRebootTime();
void checkSleepByLowBatteryVoltage(uint8_t mode);
bool checkValidCallsign(const String& callsign);
bool callsignIsValid(const String& callsign);
void startupDelay();
}

View File

@@ -20,6 +20,7 @@
#define WX_UTILS_H_
#include <Adafruit_Sensor.h>
#include <Adafruit_AHTX0.h>
#include <Adafruit_BME280.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_BME680.h>

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -26,7 +26,7 @@
#ifdef HAS_A7670
#define TINY_GSM_MODEM_SIM7600 //The AT instruction of A7670 is compatible with SIM7600
#define TINY_GSM_MODEM_SIM7600 //The AT instruction of A7670 is compatible with SIM7600
#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
#define SerialAT Serial1
#include <TinyGsmClient.h>
@@ -115,6 +115,8 @@
if (SerialAT.available()) {
String response = SerialAT.readString();
//Serial.println(response); // DEBUG of Modem AT message
int responseOKIndex = response.indexOf("OK");
int callsignIndex = ATMessage.indexOf(Config.callsign);
if(response.indexOf("verified") >= 0) {
Serial.println("Logged! (User Validated)\n");
displayShow(firstLine, "Connecting APRS-IS...", "---> Logged!", " ", 1000);
@@ -122,7 +124,7 @@
validAT = true;
i = 1;
delayATMessage = 0;
} else if (ATMessage == "AT+NETOPEN" && response.indexOf("OK") >= 0) {
} else if (ATMessage == "AT+NETOPEN" && responseOKIndex >= 0) {
Serial.println("Port Open!");
displayShow(firstLine, "Opening Port...", "---> Port Open", " ", 0);
validAT = true;
@@ -145,14 +147,14 @@
validAT = true;
i = 1;
delayATMessage = 0;
} else if (ATMessage.indexOf(Config.callsign) >= 3 && !modemLoggedToAPRSIS && response.indexOf("OK") >= 0 && !stationBeacon) { // login info
} else if (callsignIndex >= 3 && !modemLoggedToAPRSIS && responseOKIndex >= 0 && !stationBeacon) { // login info
validAT = true;
delayATMessage = 0;
} else if (ATMessage.indexOf(Config.callsign) == 0 && !beaconSent && response.indexOf("OK") >= 0 && !stationBeacon) { // self beacon or querys
} else if (callsignIndex == 0 && !beaconSent && responseOKIndex >= 0 && !stationBeacon) { // self beacon or querys
validAT = true;
i = 1;
delayATMessage = 0;
} else if (stationBeacon && response.indexOf("OK") >= 0) { //upload others beacons
} else if (stationBeacon && responseOKIndex >= 0) { //upload others beacons
validAT = true;
i = 1;
delayATMessage = 0;
@@ -200,7 +202,7 @@
if (beaconBytesSent) {
Serial.print(".");
beaconSent = checkATResponse(packet);
}
}
if (!beaconSent) {
Serial.println("------------------------------------> UPLOAD FAILED!!!");
} else {
@@ -221,5 +223,5 @@
delay(1);
}
}
#endif

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -24,7 +24,7 @@
██║ ██║ ██║██╔══██╗██╔══██║ ██╔══██║██╔═══╝ ██╔══██╗╚════██║
███████╗╚██████╔╝██║ ██║██║ ██║ ██║ ██║██║ ██║ ██║███████║
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝
██╗ ██████╗ █████╗ ████████╗███████╗
██║██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝
██║██║ ███╗███████║ ██║ █████╗
@@ -33,7 +33,7 @@
╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝
Ricardo Guzman - CA2RXU
Ricardo Guzman - CA2RXU
https://github.com/richonguzman/LoRa_APRS_iGate
(donations : http://paypal.me/richonguzman)
___________________________________________________________________*/
@@ -67,8 +67,8 @@ ___________________________________________________________________*/
#endif
String versionDate = "2025-09-26";
String versionNumber = "3.1.2.1";
String versionDate = "2026-02-20";
String versionNumber = "3.2";
Configuration Config;
WiFiClient aprsIsClient;
WiFiClient mqttClient;
@@ -86,7 +86,7 @@ WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
bool isUpdatingOTA = false;
uint32_t lastBatteryCheck = 0;
bool backUpDigiMode = false;
bool backupDigiMode = false;
bool modemLoggedToAPRSIS = false;
#ifdef HAS_EPAPER
@@ -97,7 +97,6 @@ bool modemLoggedToAPRSIS = false;
std::vector<ReceivedPacket> receivedPackets;
String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine;
//#define STARTUP_DELAY 5 //min
void setup() {
@@ -108,12 +107,7 @@ void setup() {
Utils::validateFreqs();
GPS_Utils::setup();
STATION_Utils::loadBlacklistAndManagers();
#ifdef STARTUP_DELAY // (TEST) just to wait for WiFi init of Routers
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(STARTUP_DELAY * 60 * 1000);
#endif
Utils::startupDelay();
SLEEP_Utils::setup();
WIFI_Utils::setup();
NTP_Utils::setup();
@@ -144,7 +138,7 @@ void loop() {
ElegantOTA.loop();
return; // Don't process IGate and Digi during OTA update
}
#ifdef HAS_GPS
if (Config.beacon.gpsActive) {
if (millis() - gpsSatelliteTime > 5000) {
@@ -180,7 +174,7 @@ void loop() {
Utils::checkDisplayInterval();
Utils::checkBeaconInterval();
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
String packet = "";
@@ -193,14 +187,14 @@ void loop() {
APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS
}
if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backUpDigiMode)) { // If Digi enabled
if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backupDigiMode)) { // If Digi enabled
STATION_Utils::clean25SegBuffer();
DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi
}
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
if (Config.mqtt.active) MQTT_Utils::sendToMqtt(packet); // Send received packet to MQTT
if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet, true); // Send received packet to TNC 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.aprs_is.active) APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS

View File

@@ -1,21 +1,22 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#include <APRSPacketLib.h>
#include <WiFi.h>
#include "configuration.h"
#include "aprs_is_utils.h"
@@ -25,6 +26,7 @@
#include "query_utils.h"
#include "A7670_utils.h"
#include "digi_utils.h"
#include "tnc_utils.h"
#include "display.h"
#include "utils.h"
@@ -40,11 +42,13 @@ extern String fifthLine;
extern String sixthLine;
extern String seventhLine;
extern bool modemLoggedToAPRSIS;
extern bool backUpDigiMode;
extern bool backupDigiMode;
extern String versionNumber;
uint32_t lastRxTime = millis();
bool passcodeValid = false;
uint32_t lastServerCheck = 0;
#ifdef HAS_A7670
extern bool stationBeacon;
@@ -92,7 +96,7 @@ namespace APRS_IS_Utils {
if (WiFi.status() == WL_CONNECTED) {
wifiState = "OK";
} else {
if (backUpDigiMode || Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) {
if (backupDigiMode || Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) {
wifiState = "--";
} else {
wifiState = "AP";
@@ -200,7 +204,7 @@ namespace APRS_IS_Utils {
int firstColonIndex = packet.indexOf(":");
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) {
const String& Sender = packet.substring(3, packet.indexOf(">"));
if (Sender != Config.callsign && Utils::checkValidCallsign(Sender)) {
if (Sender != Config.callsign && Utils::callsignIsValid(Sender)) {
STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(packet.substring(3), 0); // LoRa-APRS
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
@@ -235,12 +239,7 @@ namespace APRS_IS_Utils {
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType) {
String packet = aprsisPacket;
packet.trim();
String outputPacket = Config.callsign;
outputPacket += ">APLRG1";
if (Config.beacon.path != "") {
outputPacket += ",";
outputPacket += Config.beacon.path;
}
String outputPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
outputPacket += ":}";
outputPacket += packet.substring(0, packet.indexOf(",")); // Callsign>Tocall
outputPacket.concat(",TCPIP,");
@@ -277,94 +276,109 @@ namespace APRS_IS_Utils {
return outputPacket;
}
void processAckMessage(const String& sender, const String& message) {
String ackPacket = Config.callsign;
ackPacket += ">APLRG1,TCPIP,qAC::";
String senderCallsign = sender;
for (int i = sender.length(); i < 9; i++) {
senderCallsign += ' ';
}
ackPacket += senderCallsign;
ackPacket += ":";
String ackMessage = "ack";
ackMessage += message.substring(message.indexOf("{") + 1);
ackMessage.trim();
ackPacket += ackMessage;
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(ackPacket);
#else
upload(ackPacket);
#endif
}
void processAPRSISPacket(const String& packet) {
uint32_t currentTime = millis();
if (!passcodeValid && packet.indexOf(Config.callsign) != -1) {
if (packet.indexOf("unverified") != -1 ) {
Serial.println("\n****APRS PASSCODE NOT VALID****\n");
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 0);
while (1) {};
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 3000);
aprsIsClient.stop();
Config.aprs_is.active = false;
} else if (packet.indexOf("verified") != -1 ) {
if (Config.digi.backupDigiMode) lastServerCheck = currentTime;
passcodeValid = true;
}
}
if (passcodeValid && !packet.startsWith("#")) {
if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
String Sender = packet.substring(0, packet.indexOf(">"));
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (Addressee == Config.callsign) { // its for me!
String receivedMessage;
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
String ackMessage = "ack";
ackMessage += AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{") + 1);
ackMessage.trim();
delay(4000);
for (int i = Sender.length(); i < 9; i++) {
Sender += ' ';
if (passcodeValid) {
if (packet.startsWith("#")) {
if (Config.digi.backupDigiMode) lastServerCheck = currentTime;
} else {
if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
String Sender = packet.substring(0, packet.indexOf(">"));
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (Addressee == Config.callsign) { // its for me!
String receivedMessage;
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
processAckMessage(Sender, AddresseeAndMessage);
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
} else {
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
}
String ackPacket = Config.callsign;
ackPacket += ">APLRG1,TCPIP,qAC::";
ackPacket += Sender;
ackPacket += ":";
ackPacket += ackMessage;
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(ackPacket);
#else
upload(ackPacket);
#endif
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
} else {
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
}
if (receivedMessage.indexOf("?") == 0) {
Utils::println("Rx Query (APRS-IS) : " + packet);
Sender.trim();
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false);
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
displayToggle(true);
if (receivedMessage.indexOf("?") == 0) {
Utils::println("Rx Query (APRS-IS) : " + packet);
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false);
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
displayToggle(true);
}
lastScreenOn = currentTime;
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(queryAnswer);
#else
upload(queryAnswer);
#endif
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
fifthLine = "APRS-IS ----> APRS-IS";
sixthLine = Config.callsign;
for (int j = sixthLine.length();j < 9;j++) {
sixthLine += " ";
}
sixthLine += "> ";
sixthLine += Sender;
seventhLine = "QUERY = ";
seventhLine += receivedMessage;
}
lastScreenOn = millis();
delay(500);
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(queryAnswer);
#else
upload(queryAnswer);
#endif
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
fifthLine = "APRS-IS ----> APRS-IS";
sixthLine = Config.callsign;
for (int j = sixthLine.length();j < 9;j++) {
sixthLine += " ";
}
sixthLine += "> ";
sixthLine += Sender;
seventhLine = "QUERY = ";
seventhLine += receivedMessage;
}
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
} else {
Utils::print("Rx Message (APRS-IS): " + packet);
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
displayToggle(true);
lastScreenOn = millis();
Utils::typeOfPacket(packet, 1); // APRS-LoRa
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
} else {
Utils::print("Rx Message (APRS-IS): " + packet);
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
displayToggle(true);
lastScreenOn = currentTime;
Utils::typeOfPacket(packet, 1); // APRS-LoRa
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
}
}
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
Utils::print("Rx Object (APRS-IS) : " + packet);
if (STATION_Utils::checkObjectTime(packet)) {
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
displayToggle(true);
lastScreenOn = currentTime;
Utils::typeOfPacket(packet, 1); // APRS-LoRa
Serial.println();
} else {
Serial.println(" ---> Rejected (Time): No Tx");
}
}
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
Utils::print("Rx Object (APRS-IS) : " + packet);
if (STATION_Utils::checkObjectTime(packet)) {
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
displayToggle(true);
lastScreenOn = millis();
Utils::typeOfPacket(packet, 1); // APRS-LoRa
Serial.println();
} else {
Serial.println(" ---> Rejected (Time): No Tx");
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
}
}
}

View File

@@ -1,22 +1,22 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#include <Arduino.h>
#include <Adafruit_INA219.h>
#include "battery_utils.h"
#include "configuration.h"
#include "board_pinout.h"
@@ -37,6 +37,10 @@ float multiplyCorrection = 0.035;
float voltageDividerTransformation = 0.0;
uint8_t externalI2CSensorAddress = 0x00;
int externalI2CSensorType = 0; // 0 = None | 1 = INA219
Adafruit_INA219 ina219;
#ifdef HAS_ADC_CALIBRATION
@@ -98,6 +102,30 @@ namespace BATTERY_Utils {
#endif
}
void getI2CVoltageSensorAddress() {
uint8_t err, addr;
for(addr = 1; addr < 0x7F; addr++) {
#ifdef SENSOR_I2C_BUS
SENSOR_I2C_BUS.beginTransmission(addr);
err = SENSOR_I2C_BUS.endTransmission();
#else
Wire.beginTransmission(addr);
err = Wire.endTransmission();
#endif
delay(5);
if (err == 0) {
if (addr == 0x40) { // INA219
externalI2CSensorAddress = addr;
}
}
}
}
bool detectINA219(uint8_t addr) {
ina219 = Adafruit_INA219(addr);
return ina219.begin();
}
void setup() {
if ((Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) && Config.battery.voltageDividerR2 != 0) voltageDividerTransformation = (Config.battery.voltageDividerR1 + Config.battery.voltageDividerR2) / Config.battery.voltageDividerR2;
@@ -107,9 +135,17 @@ namespace BATTERY_Utils {
adcCalibration();
}
#endif
getI2CVoltageSensorAddress();
if (externalI2CSensorAddress != 0x00) {
if (detectINA219(externalI2CSensorAddress)) {
Serial.println("INA219 sensor found");
externalI2CSensorType = 1; // INA219
}
}
}
float checkInternalVoltage() {
float checkInternalVoltage() {
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
if(POWER_Utils::isBatteryConnected()) {
return POWER_Utils::getBatteryVoltage();
@@ -117,7 +153,7 @@ namespace BATTERY_Utils {
return 0.0;
}
#else
#ifdef ADC_CTRL
POWER_Utils::adc_ctrl_ON();
#endif
@@ -141,7 +177,7 @@ namespace BATTERY_Utils {
#endif
#endif
#endif
delay(3);
delay(3);
}
#ifdef ADC_CTRL
@@ -177,37 +213,49 @@ namespace BATTERY_Utils {
}
float checkExternalVoltage() {
int sample;
int sampleSum = 0;
for (int i = 0; i < 100; i++) {
#ifdef HAS_ADC_CALIBRATION
if (calibrationEnable){
sample = adc1_get_raw(ExternalVoltage_ADC_Channel);
} else {
if (externalI2CSensorType == 0) {
int sample;
int sampleSum = 0;
for (int i = 0; i < 100; i++) {
#ifdef HAS_ADC_CALIBRATION
if (calibrationEnable){
sample = adc1_get_raw(ExternalVoltage_ADC_Channel);
} else {
sample = analogRead(Config.battery.externalVoltagePin);
}
#else
sample = analogRead(Config.battery.externalVoltagePin);
#endif
sampleSum += sample;
delayMicroseconds(50);
}
float extVoltage;
#ifdef HAS_ADC_CALIBRATION
if (calibrationEnable) {
extVoltage = esp_adc_cal_raw_to_voltage(sampleSum / 100.0, &adc_chars) * voltageDividerTransformation; // in mV
extVoltage /= 1000.0;
} else {
extVoltage = ((((sampleSum/100.0)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
}
#else
sample = analogRead(Config.battery.externalVoltagePin);
extVoltage = ((((sampleSum/100.0)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
#endif
sampleSum += sample;
delayMicroseconds(50);
}
float extVoltage;
#ifdef HAS_ADC_CALIBRATION
if (calibrationEnable){
extVoltage = esp_adc_cal_raw_to_voltage(sampleSum / 100, &adc_chars) * voltageDividerTransformation; // in mV
extVoltage /= 1000;
} else {
extVoltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
return extVoltage; // raw voltage without mapping
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
} else if (externalI2CSensorType == 1) { // INA219
int sampleSum = 0;
for (int i = 0; i < 100; i++) {
sampleSum += ina219.getBusVoltage_V() * 1000.0;
delayMicroseconds(50);
}
#else
extVoltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
#endif
return extVoltage; // raw voltage without mapping
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
float extVoltage = sampleSum/100.0;
return extVoltage/1000.0;
} else {
return 0.0;
}
}
void startupBatteryHealth() {

View File

@@ -45,10 +45,15 @@ bool Configuration::writeFile() {
}
}
data["other"]["startupDelay"] = startupDelay;
data["wifi"]["autoAP"]["password"] = wifiAutoAP.password;
data["wifi"]["autoAP"]["timeout"] = wifiAutoAP.timeout;
callsign.trim();
data["callsign"] = callsign;
tacticalCallsign.trim();
data["tacticalCallsign"] = tacticalCallsign;
data["aprs_is"]["active"] = aprs_is.active;
data["aprs_is"]["passcode"] = aprs_is.passcode;
@@ -64,15 +69,17 @@ bool Configuration::writeFile() {
data["beacon"]["longitude"] = beacon.longitude;
data["beacon"]["overlay"] = beacon.overlay;
data["beacon"]["symbol"] = beacon.symbol;
data["beacon"]["path"] = beacon.path;
data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
data["beacon"]["path"] = beacon.path;
data["beacon"]["beaconFreq"] = beacon.beaconFreq;
data["beacon"]["statusActive"] = beacon.statusActive;
data["beacon"]["statusPacket"] = beacon.statusPacket;
data["beacon"]["gpsActive"] = beacon.gpsActive;
data["beacon"]["gpsAmbiguity"] = beacon.gpsAmbiguity;
data["beacon"]["ambiguityLevel"] = beacon.ambiguityLevel;
data["personalNote"] = personalNote;
@@ -83,15 +90,35 @@ bool Configuration::writeFile() {
#if defined(HAS_A7670)
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
#endif
data["digi"]["backupDigiMode"] = digi.backupDigiMode;
data["lora"]["rxFreq"] = loramodule.rxFreq;
data["lora"]["txFreq"] = loramodule.txFreq;
data["lora"]["spreadingFactor"] = loramodule.spreadingFactor;
data["lora"]["signalBandwidth"] = loramodule.signalBandwidth;
data["lora"]["codingRate4"] = loramodule.codingRate4;
data["lora"]["power"] = loramodule.power;
data["lora"]["txActive"] = loramodule.txActive;
data["lora"]["rxActive"] = loramodule.rxActive;
data["lora"]["rxFreq"] = loramodule.rxFreq;
data["lora"]["rxCodingRate4"] = loramodule.rxCodingRate4;
data["lora"]["rxSignalBandwidth"] = loramodule.rxSignalBandwidth;
data["lora"]["txActive"] = loramodule.txActive;
data["lora"]["txFreq"] = loramodule.txFreq;
data["lora"]["txCodingRate4"] = loramodule.txCodingRate4;
data["lora"]["txSignalBandwidth"] = loramodule.txSignalBandwidth;
data["lora"]["power"] = loramodule.power;
int rxSpreadingFactor = loramodule.rxSpreadingFactor;
int txSpreadingFactor = loramodule.txSpreadingFactor;
#if defined(HAS_SX1276) || defined(HAS_SX1278)
const int minSF = 6, maxSF = 12;
#endif
#if defined(HAS_SX1262) || defined(HAS_SX1268)
const int minSF = 5, maxSF = 12;
#endif
#if defined(HAS_LLCC68)
const int minSF = 5, maxSF = 11;
#endif
rxSpreadingFactor = (rxSpreadingFactor < minSF) ? minSF : (rxSpreadingFactor > maxSF) ? maxSF : rxSpreadingFactor;
txSpreadingFactor = (txSpreadingFactor < minSF) ? minSF : (txSpreadingFactor > maxSF) ? maxSF : txSpreadingFactor;
data["lora"]["rxSpreadingFactor"] = rxSpreadingFactor;
data["lora"]["txSpreadingFactor"] = txSpreadingFactor;
data["display"]["alwaysOn"] = display.alwaysOn;
data["display"]["timeout"] = display.timeout;
@@ -102,11 +129,12 @@ bool Configuration::writeFile() {
data["battery"]["internalSleepVoltage"] = battery.internalSleepVoltage;
data["battery"]["sendExternalVoltage"] = battery.sendExternalVoltage;
data["battery"]["externalVoltagePin"] = battery.externalVoltagePin;
data["battery"]["monitorExternalVoltage"] = battery.monitorExternalVoltage;
data["battery"]["externalSleepVoltage"] = battery.externalSleepVoltage;
data["battery"]["useExternalI2CSensor"] = battery.useExternalI2CSensor;
data["battery"]["voltageDividerR1"] = battery.voltageDividerR1;
data["battery"]["voltageDividerR2"] = battery.voltageDividerR2;
data["battery"]["externalVoltagePin"] = battery.externalVoltagePin;
data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry;
@@ -122,6 +150,7 @@ bool Configuration::writeFile() {
data["tnc"]["enableServer"] = tnc.enableServer;
data["tnc"]["enableSerial"] = tnc.enableSerial;
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
data["tnc"]["aprsBridgeActive"] = tnc.aprsBridgeActive;
data["mqtt"]["active"] = mqtt.active;
data["mqtt"]["server"] = mqtt.server;
@@ -129,6 +158,7 @@ bool Configuration::writeFile() {
data["mqtt"]["username"] = mqtt.username;
data["mqtt"]["password"] = mqtt.password;
data["mqtt"]["port"] = mqtt.port;
data["mqtt"]["beaconOverMqtt"] = mqtt.beaconOverMqtt;
data["ota"]["username"] = ota.username;
data["ota"]["password"] = ota.password;
@@ -140,6 +170,7 @@ bool Configuration::writeFile() {
data["remoteManagement"]["managers"] = remoteManagement.managers;
data["remoteManagement"]["rfOnly"] = remoteManagement.rfOnly;
data["ntp"]["server"] = ntp.server;
data["ntp"]["gmtCorrection"] = ntp.gmtCorrection;
data["other"]["rebootMode"] = rebootMode;
@@ -147,8 +178,6 @@ bool Configuration::writeFile() {
data["other"]["rememberStationTime"] = rememberStationTime;
data["other"]["backupDigiMode"] = backupDigiMode;
serializeJson(data, configFile);
configFile.close();
return true;
@@ -181,6 +210,9 @@ bool Configuration::readFile() {
wifiAPs.push_back(wifiap);
}
if (!data["other"].containsKey("startupDelay")) needsRewrite = true;
startupDelay = data["other"]["startupDelay"] | 0;
if (!data["wifi"]["autoAP"].containsKey("password") ||
!data["wifi"]["autoAP"].containsKey("timeout")) needsRewrite = true;
wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890";
@@ -188,6 +220,8 @@ bool Configuration::readFile() {
if (!data.containsKey("callsign")) needsRewrite = true;
callsign = data["callsign"] | "NOCALL-10";
if (!data.containsKey("tacticalCallsign")) needsRewrite = true;
tacticalCallsign = data["tacticalCallsign"] | "";
if (!data["aprs_is"].containsKey("active") ||
!data["aprs_is"].containsKey("passcode") ||
@@ -213,10 +247,11 @@ bool Configuration::readFile() {
!data["beacon"].containsKey("path") ||
!data["beacon"].containsKey("sendViaAPRSIS") ||
!data["beacon"].containsKey("sendViaRF") ||
!data["beacon"].containsKey("beaconFreq") ||
!data["beacon"].containsKey("statusActive") ||
!data["beacon"].containsKey("statusPacket") ||
!data["beacon"].containsKey("gpsActive") ||
!data["beacon"].containsKey("gpsAmbiguity")) needsRewrite = true;
!data["beacon"].containsKey("ambiguityLevel")) needsRewrite = true;
beacon.latitude = data["beacon"]["latitude"] | 0.0;
beacon.longitude = data["beacon"]["longitude"] | 0.0;
beacon.comment = data["beacon"]["comment"] | "LoRa APRS";
@@ -226,10 +261,11 @@ bool Configuration::readFile() {
beacon.path = data["beacon"]["path"] | "WIDE1-1";
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false;
beacon.sendViaRF = data["beacon"]["sendViaRF"] | false;
beacon.beaconFreq = data["beacon"]["beaconFreq"] | 1;
beacon.statusActive = data["beacon"]["statusActive"] | false;
beacon.statusPacket = data["beacon"]["statusPacket"] | "";
beacon.gpsActive = data["beacon"]["gpsActive"] | false;
beacon.gpsAmbiguity = data["beacon"]["gpsAmbiguity"] | false;
beacon.ambiguityLevel = data["beacon"]["ambiguityLevel"] | 0;
if (!data.containsKey("personalNote")) needsRewrite = true;
personalNote = data["personalNote"] | "personal note here";
@@ -238,36 +274,48 @@ bool Configuration::readFile() {
blacklist = data["blacklist"] | "station callsign";
if (!data["digi"].containsKey("mode") ||
!data["digi"].containsKey("ecoMode")) needsRewrite = true;
!data["digi"].containsKey("ecoMode") ||
!data["digi"].containsKey("backupDigiMode")) needsRewrite = true;
digi.mode = data["digi"]["mode"] | 0;
digi.ecoMode = data["digi"]["ecoMode"] | 0;
if (digi.ecoMode == 1) shouldSleepStop = false;
#if defined(HAS_A7670)
if (digi.ecoMode == 1) digi.ecoMode = 2;
#endif
digi.backupDigiMode = data["digi"]["backupDigiMode"] | false;
if (!data["lora"].containsKey("txFreq") ||
if (!data["lora"].containsKey("rxActive") ||
!data["lora"].containsKey("rxFreq") ||
!data["lora"].containsKey("spreadingFactor") ||
!data["lora"].containsKey("signalBandwidth") ||
!data["lora"].containsKey("codingRate4") ||
!data["lora"].containsKey("power") ||
!data["lora"].containsKey("rxSpreadingFactor") ||
!data["lora"].containsKey("rxCodingRate4") ||
!data["lora"].containsKey("rxSignalBandwidth") ||
!data["lora"].containsKey("txActive") ||
!data["lora"].containsKey("rxActive")) needsRewrite = true;
loramodule.txFreq = data["lora"]["txFreq"] | 433775000;
!data["lora"].containsKey("txFreq") ||
!data["lora"].containsKey("txSpreadingFactor") ||
!data["lora"].containsKey("txCodingRate4") ||
!data["lora"].containsKey("txSignalBandwidth") ||
!data["lora"].containsKey("power")) needsRewrite = true;
loramodule.rxActive = data["lora"]["rxActive"] | true;
loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000;
loramodule.spreadingFactor = data["lora"]["spreadingFactor"] | 12;
loramodule.signalBandwidth = data["lora"]["signalBandwidth"] | 125000;
loramodule.codingRate4 = data["lora"]["codingRate4"] | 5;
loramodule.power = data["lora"]["power"] | 20;
loramodule.rxSpreadingFactor = data["lora"]["rxSpreadingFactor"] | 12;
loramodule.rxCodingRate4 = data["lora"]["rxCodingRate4"] | 5;
loramodule.rxSignalBandwidth = data["lora"]["rxSignalBandwidth"] | 125000;
loramodule.txActive = data["lora"]["txActive"] | false;
loramodule.rxActive = data["lora"]["rxActive"] | false;
loramodule.txFreq = data["lora"]["txFreq"] | 433775000;
loramodule.txSpreadingFactor = data["lora"]["txSpreadingFactor"] | 12;
loramodule.txCodingRate4 = data["lora"]["txCodingRate4"] | 5;
loramodule.txSignalBandwidth = data["lora"]["txSignalBandwidth"] | 125000;
loramodule.power = data["lora"]["power"] | 20;
if (!data["display"].containsKey("alwaysOn") ||
!data["display"].containsKey("timeout") ||
!data["display"].containsKey("turn180")) needsRewrite = true;
display.alwaysOn = data["display"]["alwaysOn"] | true;
#ifdef HAS_EPAPER
display.alwaysOn = true;
#else
display.alwaysOn = data["display"]["alwaysOn"] | true;
#endif
display.timeout = data["display"]["timeout"] | 4;
display.turn180 = data["display"]["turn180"] | false;
@@ -275,21 +323,23 @@ bool Configuration::readFile() {
!data["battery"].containsKey("monitorInternalVoltage") ||
!data["battery"].containsKey("internalSleepVoltage") ||
!data["battery"].containsKey("sendExternalVoltage") ||
!data["battery"].containsKey("externalVoltagePin") ||
!data["battery"].containsKey("monitorExternalVoltage") ||
!data["battery"].containsKey("externalSleepVoltage") ||
!data["battery"].containsKey("useExternalI2CSensor") ||
!data["battery"].containsKey("voltageDividerR1") ||
!data["battery"].containsKey("voltageDividerR2") ||
!data["battery"].containsKey("externalVoltagePin") ||
!data["battery"].containsKey("sendVoltageAsTelemetry")) needsRewrite = true;
battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"] | false;
battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false;
battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 2.9;
battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false;
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
battery.monitorExternalVoltage = data["battery"]["monitorExternalVoltage"] | false;
battery.externalSleepVoltage = data["battery"]["externalSleepVoltage"] | 10.9;
battery.useExternalI2CSensor = data["battery"]["useExternalI2CSensor"] | false;
battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0;
battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0;
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | false;
if (!data["wxsensor"].containsKey("active") ||
@@ -310,23 +360,27 @@ bool Configuration::readFile() {
if (!data["tnc"].containsKey("enableServer") ||
!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.enableSerial = data["tnc"]["enableSerial"] | false;
tnc.acceptOwn = data["tnc"]["acceptOwn"] | false;
tnc.aprsBridgeActive = data["tnc"]["aprsBridgeActive"] | false;
if (!data["mqtt"].containsKey("active") ||
!data["mqtt"].containsKey("server") ||
!data["mqtt"].containsKey("topic") ||
!data["mqtt"].containsKey("username") ||
!data["mqtt"].containsKey("password") ||
!data["mqtt"].containsKey("port")) needsRewrite = true;
!data["mqtt"].containsKey("port") ||
!data["mqtt"].containsKey("beaconOverMqtt")) needsRewrite = true;
mqtt.active = data["mqtt"]["active"] | false;
mqtt.server = data["mqtt"]["server"] | "";
mqtt.topic = data["mqtt"]["topic"] | "aprs-igate";
mqtt.username = data["mqtt"]["username"] | "";
mqtt.password = data["mqtt"]["password"] | "";
mqtt.port = data["mqtt"]["port"] | 1883;
mqtt.beaconOverMqtt = data["mqtt"]["beaconOverMqtt"] | false;
if (!data["ota"].containsKey("username") ||
!data["ota"].containsKey("password")) needsRewrite = true;
@@ -345,7 +399,9 @@ bool Configuration::readFile() {
remoteManagement.managers = data["remoteManagement"]["managers"] | "";
remoteManagement.rfOnly = data["remoteManagement"]["rfOnly"] | true;
if (!data["ntp"].containsKey("gmtCorrection")) needsRewrite = true;
if (!data["ntp"].containsKey("server") ||
!data["ntp"].containsKey("gmtCorrection")) needsRewrite = true;
ntp.server = data["ntp"]["server"] | "pool.ntp.org";
ntp.gmtCorrection = data["ntp"]["gmtCorrection"] | 0.0;
if (!data["other"].containsKey("rebootMode") ||
@@ -356,9 +412,6 @@ bool Configuration::readFile() {
if (!data["other"].containsKey("rememberStationTime")) needsRewrite = true;
rememberStationTime = data["other"]["rememberStationTime"] | 30;
if (!data["other"].containsKey("backupDigiMode")) needsRewrite = true;
backupDigiMode = data["other"]["backupDigiMode"] | false;
if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP
WiFi_AP wifiap;
wifiap.ssid = "";
@@ -371,7 +424,7 @@ bool Configuration::readFile() {
if (needsRewrite) {
Serial.println("Config JSON incomplete, rewriting...");
writeFile();
delay(500);
delay(1000);
ESP.restart();
}
Serial.println("Config read successfuly");
@@ -390,10 +443,13 @@ void Configuration::setDefaultValues() {
wifiAPs.push_back(wifiap);
startupDelay = 0;
wifiAutoAP.password = "1234567890";
wifiAutoAP.timeout = 10;
callsign = "N0CALL-10";
tacticalCallsign = "";
aprs_is.active = false;
aprs_is.passcode = "XYZVW";
@@ -409,15 +465,17 @@ void Configuration::setDefaultValues() {
beacon.interval = 15;
beacon.overlay = "L";
beacon.symbol = "a";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.path = "WIDE1-1";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.beaconFreq = 1;
beacon.statusActive = false;
beacon.statusPacket = "";
beacon.gpsActive = false;
beacon.gpsAmbiguity = false;
beacon.ambiguityLevel = 0;
personalNote = "";
@@ -425,15 +483,19 @@ void Configuration::setDefaultValues() {
digi.mode = 0;
digi.ecoMode = 0;
digi.backupDigiMode = false;
loramodule.txFreq = 433775000;
loramodule.rxFreq = 433775000;
loramodule.spreadingFactor = 12;
loramodule.signalBandwidth = 125000;
loramodule.codingRate4 = 5;
loramodule.power = 20;
loramodule.txActive = false;
loramodule.rxActive = true;
loramodule.rxFreq = 433775000;
loramodule.rxSpreadingFactor = 12;
loramodule.rxCodingRate4 = 5;
loramodule.rxSignalBandwidth = 125000;
loramodule.txActive = false;
loramodule.txFreq = 433775000;
loramodule.txSpreadingFactor = 12;
loramodule.txCodingRate4 = 5;
loramodule.txSignalBandwidth = 125000;
loramodule.power = 20;
display.alwaysOn = true;
display.timeout = 4;
@@ -444,11 +506,12 @@ void Configuration::setDefaultValues() {
battery.internalSleepVoltage = 2.9;
battery.sendExternalVoltage = false;
battery.externalVoltagePin = 34;
battery.monitorExternalVoltage = false;
battery.externalSleepVoltage = 10.9;
battery.useExternalI2CSensor = false;
battery.voltageDividerR1 = 100.0;
battery.voltageDividerR2 = 27.0;
battery.externalVoltagePin = 34;
battery.sendVoltageAsTelemetry = false;
@@ -464,6 +527,7 @@ void Configuration::setDefaultValues() {
tnc.enableServer = false;
tnc.enableSerial = false;
tnc.acceptOwn = false;
tnc.aprsBridgeActive = false;
mqtt.active = false;
mqtt.server = "";
@@ -471,6 +535,7 @@ void Configuration::setDefaultValues() {
mqtt.username = "";
mqtt.password = "";
mqtt.port = 1883;
mqtt.beaconOverMqtt = false;
ota.username = "";
ota.password = "";
@@ -482,6 +547,7 @@ void Configuration::setDefaultValues() {
remoteManagement.managers = "";
remoteManagement.rfOnly = true;
ntp.server = "pool.ntp.org";
ntp.gmtCorrection = 0.0;
rebootMode = false;
@@ -489,9 +555,7 @@ void Configuration::setDefaultValues() {
rememberStationTime = 30;
backupDigiMode = false;
Serial.println("All is Written!");
Serial.println("New Data Created... All is Written!");
}
Configuration::Configuration() {
@@ -506,7 +570,7 @@ Configuration::Configuration() {
if (!exists) {
setDefaultValues();
writeFile();
delay(500);
delay(1000);
ESP.restart();
}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -38,44 +38,45 @@ extern String fourthLine;
extern String fifthLine;
extern String sixthLine;
extern String seventhLine;
extern bool backUpDigiMode;
extern bool backupDigiMode;
namespace DIGI_Utils {
String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq) {
String stationCallsign = (Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign);
if (!crossFreq) {
String packetToRepeat = packet.substring(0, packet.indexOf(",") + 1);
String tempPath = path;
int digiMode = Config.digi.mode;
if (path.indexOf("WIDE1-1") != -1 && (Config.digi.mode == 2 || Config.digi.mode == 3)) {
tempPath.replace("WIDE1-1", Config.callsign + "*");
} else if (path.indexOf("WIDE2-") != -1 && Config.digi.mode == 3) {
if (path.indexOf(",WIDE1*") != -1) {
tempPath.remove(path.indexOf(",WIDE1*"), 7);
if (path.indexOf("WIDE1-1") != -1 && (digiMode == 2 || digiMode == 3)) {
tempPath.replace("WIDE1-1", stationCallsign + "*");
} else if (path.indexOf("WIDE2-") != -1 && digiMode == 3) {
int wide1AsteriskIndex = path.indexOf(",WIDE1*"); // less memory than: tempPath.replace(",WIDE1*", "");
if (wide1AsteriskIndex != -1) {
tempPath.remove(wide1AsteriskIndex, 7);
}
if (path.indexOf("*") != -1) {
tempPath.remove(path.indexOf("*"), 1);
int asteriskIndex = path.indexOf("*"); // less memory than: tempPath.replace("*", "");
if (asteriskIndex != -1) {
tempPath.remove(asteriskIndex, 1);
}
if (path.indexOf("WIDE2-1") != -1) {
tempPath.replace("WIDE2-1", Config.callsign + "*");
tempPath.replace("WIDE2-1", stationCallsign + "*");
} else if (path.indexOf("WIDE2-2") != -1) {
tempPath.replace("WIDE2-2", Config.callsign + "*,WIDE2-1");
tempPath.replace("WIDE2-2", stationCallsign + "*,WIDE2-1");
} else {
return "";
}
}
packetToRepeat += tempPath;
if (thirdParty) {
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":}")));
} else {
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":")));
}
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(thirdParty ? ":}" : ":")));
return packetToRepeat;
} else { // CrossFreq Digipeater
String suffix = thirdParty ? ":}" : ":";
String packetToRepeat = packet.substring(0, packet.indexOf(suffix));
String suffix = thirdParty ? ":}" : ":";
int suffixIndex = packet.indexOf(suffix);
String packetToRepeat = packet.substring(0, suffixIndex);
String terms[] = {",WIDE1*", ",WIDE2*", "*"};
for (String term : terms) {
int index = packetToRepeat.indexOf(term);
@@ -84,9 +85,9 @@ namespace DIGI_Utils {
}
}
packetToRepeat += ",";
packetToRepeat += Config.callsign;
packetToRepeat += stationCallsign;
packetToRepeat += "*";
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(suffix)));
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(suffixIndex));
return packetToRepeat;
}
}
@@ -99,78 +100,79 @@ namespace DIGI_Utils {
} else {
temp = packet.substring(packet.indexOf(">") + 1, packet.indexOf(":"));
}
if (temp.indexOf(",") > 2) { // checks for path
const String& path = temp.substring(temp.indexOf(",") + 1); // after tocall
if (Config.digi.mode == 2 || backUpDigiMode) {
if (path.indexOf("WIDE1-1") != - 1) {
return buildPacket(path, packet, thirdParty, false);
} else if (path.indexOf("WIDE1-1") == -1 && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) { // CrossFreq Digi
return buildPacket(path, packet, thirdParty, true);
} else {
return "";
}
} else if (Config.digi.mode == 3) {
if (path.indexOf("WIDE1-1") != -1 || path.indexOf("WIDE2-") != -1) {
int wide1Index = path.indexOf("WIDE1-1");
int wide2Index = path.indexOf("WIDE2-");
int commaIndex = temp.indexOf(",");
int digiMode = Config.digi.mode;
bool crossFreq = abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000; // CrossFreq Digi
// WIDE1-1 && WIDE2-n / only WIDE1-1 / only WIDE2-n
if ((wide1Index != -1 && wide2Index != -1 && wide1Index < wide2Index) || (wide1Index != -1 && wide2Index == -1) || (wide1Index == -1 && wide2Index != -1)) {
return buildPacket(path, packet, thirdParty, false);
}
return "";
} else if (path.indexOf("WIDE1-1") == -1 && path.indexOf("WIDE2-") == -1 && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) { // CrossFreq Digi
return buildPacket(path, packet, thirdParty, true);
} else {
return "";
if (commaIndex > 2) { // Packet has "path"
const String& path = temp.substring(commaIndex + 1);
if (digiMode == 2 || backupDigiMode) {
bool hasWide = path.indexOf("WIDE1-1") != -1;
if (hasWide || crossFreq) {
return buildPacket(path, packet, thirdParty, !hasWide);
}
} else {
return "";
}
} else if (temp.indexOf(",") == -1 && (Config.digi.mode == 2 || backUpDigiMode || Config.digi.mode == 3) && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) {
return buildPacket("", packet, thirdParty, true);
} else {
if (digiMode == 3) {
int wide1Index = path.indexOf("WIDE1-1");
int wide2Index = path.indexOf("WIDE2-");
bool hasWide1 = wide1Index != -1;
bool hasWide2 = wide2Index != -1;
if (hasWide1 && hasWide2 && wide2Index < wide1Index) return ""; // check that WIDE1 before WIDE2
if (hasWide1 || hasWide2) return buildPacket(path, packet, thirdParty, false); // regular APRS with WIDEn-N
if (crossFreq) return buildPacket(path, packet, thirdParty, true); // CrossFreq (without WIDE)
return "";
}
return "";
}
if (commaIndex == -1 && (digiMode == 2 || backupDigiMode || digiMode == 3) && crossFreq) return buildPacket("", packet, thirdParty, true); // no "path" but is CrossFreq Digi
return "";
}
void processLoRaPacket(const String& packet) {
if (packet.indexOf("NOGATE") == -1) {
bool thirdPartyPacket = false;
String temp, Sender;
int firstColonIndex = packet.indexOf(":");
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] == '}' && packet.indexOf("TCPIP") > 0) { // 3rd Party
thirdPartyPacket = true;
temp = packet.substring(packet.indexOf(":}") + 2);
Sender = temp.substring(0, temp.indexOf(">"));
} else {
temp = packet.substring(3);
Sender = packet.substring(3, packet.indexOf(">"));
}
if (Sender != Config.callsign) { // Avoid listening to own packets
if (!thirdPartyPacket && !Utils::checkValidCallsign(Sender)) {
return;
if (packet.indexOf("NOGATE") >= 0) return;
bool thirdPartyPacket = false;
String temp, Sender;
int firstColonIndex = packet.indexOf(":");
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] == '}' && packet.indexOf("TCPIP") > 0) { // 3rd Party
thirdPartyPacket = true;
temp = packet.substring(packet.indexOf(":}") + 2);
Sender = temp.substring(0, temp.indexOf(">"));
} else {
temp = packet.substring(3);
Sender = packet.substring(3, packet.indexOf(">"));
}
String stationCallsign = Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign;
if (Sender == stationCallsign) return; // Avoid listening to self packets
if (!thirdPartyPacket && Config.tacticalCallsign == "" && !Utils::callsignIsValid(Sender)) return; // No thirdParty + no tactical y no valid callsign
if (STATION_Utils::check25SegBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) {
STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(temp, 2); // Digi
bool queryMessage = false;
int doubleColonIndex = temp.indexOf("::");
if (doubleColonIndex > 10) { // it's a message
String AddresseeAndMessage = temp.substring(doubleColonIndex + 2);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (Addressee == stationCallsign) { // it's a message for me!
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket);
}
if (STATION_Utils::check25SegBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) {
STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(temp, 2); // Digi
bool queryMessage = false;
if (temp.indexOf("::") > 10) { // it's a message
String AddresseeAndMessage = temp.substring(temp.indexOf("::") + 2);
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (Addressee == Config.callsign) { // it's a message for me!
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket);
}
}
if (!queryMessage) {
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
if (loraPacket != "") {
STATION_Utils::addToOutputPacketBuffer(loraPacket);
if (Config.digi.ecoMode != 1) displayToggle(true);
lastScreenOn = millis();
}
}
}
if (!queryMessage) {
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
if (loraPacket != "") {
STATION_Utils::addToOutputPacketBuffer(loraPacket);
if (Config.digi.ecoMode != 1) displayToggle(true);
lastScreenOn = millis();
}
}
}

View File

@@ -47,9 +47,13 @@
#ifdef HELTEC_WP_V1
EInkDisplay_WirelessPaperV1_1 display;
#endif
/*#ifdef HELTEC_WP_V1_2 // SOON!
#ifdef HELTEC_WP_V1_2
EInkDisplay_WirelessPaperV1_2 display;
#endif*/
#endif
#ifdef HELTEC_VM_E290
EInkDisplay_VisionMasterE290 display;
#endif
String lastEpaperText;
#else
#include <Adafruit_GFX.h>
@@ -88,14 +92,22 @@ void displaySetup() {
tft.setTextFont(0);
tft.fillScreen(TFT_BLACK);
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
sprite.createSprite(320,240);
sprite.createSprite(320, 240);
#else
sprite.createSprite(160,80);
sprite.createSprite(160, 80);
#endif
#else
#ifdef HAS_EPAPER
display.landscape();
display.printCenter("LoRa APRS iGate Initialising...");
if (Config.display.turn180) {
#if defined(HELTEC_VM_E290) || defined(HELTEC_WP_V1)
display.setRotation(3);
#endif
#if defined(HELTEC_WP_V1_2)
display.setRotation(1);
#endif
}
display.update();
#else
#ifdef OLED_DISPLAY_HAS_RST_PIN
@@ -106,7 +118,7 @@ void displaySetup() {
#endif
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
if (!display.begin(0x3c, false)) {
if (display.begin(0x3c, false)) {
displayFound = true;
if (Config.display.turn180) display.setRotation(2);
display.clearDisplay();
@@ -157,7 +169,6 @@ void displayToggle(bool toggle) {
digitalWrite(TFT_BL, LOW);
#else
#ifdef HAS_EPAPER
display.printCenter("Enabled EPAPER Display...");
display.update();
#else
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
@@ -195,7 +206,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2);
}
sprite.pushSprite(0,0);
sprite.pushSprite(0, 0);
#else
#ifdef HAS_EPAPER
display.clearMemory();
@@ -260,7 +271,7 @@ void displayShow(const String& header, const String& line1, const String& line2,
sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2);
}
sprite.pushSprite(0,0);
sprite.pushSprite(0, 0);
#else
#ifdef HAS_EPAPER
lastEpaperText = header + line1 + line2 + line3 + line4 + line5 + line6;

View File

@@ -1,21 +1,22 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#include <APRSPacketLib.h>
#include <TinyGPS++.h>
#include <WiFi.h>
#include "configuration.h"
@@ -33,6 +34,7 @@
extern Configuration Config;
extern HardwareSerial gpsSerial;
extern TinyGPSPlus gps;
extern bool stationCallsignIsValid;
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
@@ -42,83 +44,38 @@ namespace GPS_Utils {
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() {
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
Config.loramodule.txActive = false;
Config.aprs_is.messagesToRF = false;
Config.aprs_is.objectsToRF = false;
String beaconPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(Config.beacon.latitude, Config.beacon.longitude, 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
if (Config.callsign.indexOf("NOCALL-10") != 0) {
if (!stationCallsignIsValid) {
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
Config.loramodule.txActive = false;
Config.aprs_is.messagesToRF = false;
Config.aprs_is.objectsToRF = false;
Config.beacon.sendViaRF = false;
Config.digi.mode = 0;
Config.digi.backupDigiMode = false;
} else if (stationCallsignIsValid && Config.tacticalCallsign != "") {
beaconPacket = APRSPacketLib::generateBasePacket(Config.tacticalCallsign, "APLRG1", Config.beacon.path);
Config.aprs_is.active = false;
Config.beacon.sendViaAPRSIS = false;
Config.digi.backupDigiMode = false;
}
} else {
Config.beacon.sendViaAPRSIS = false;
Config.beacon.sendViaRF = false;
Config.digi.mode = 0;
Config.backupDigiMode = false;
}
generateBeaconFirstPart();
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
iGateBeaconPacket = beaconPacket;
iGateBeaconPacket += ",qAC:=";
iGateBeaconPacket += Config.beacon.overlay;
iGateBeaconPacket += encodedGPS;
iGateLoRaBeaconPacket = beaconPacket;
iGateLoRaBeaconPacket += ":=";
iGateLoRaBeaconPacket += Config.beacon.overlay;
iGateLoRaBeaconPacket += encodedGPS;
}
@@ -126,47 +83,40 @@ namespace GPS_Utils {
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) {
int indexOfExclamation = packet.indexOf(":!");
int indexOfEqual = packet.indexOf(":=");
const uint8_t OFFSET = 3; // Offset for encoded data in the packet
String GPSPacket;
String infoGPS;
if (indexOfExclamation > 10) {
GPSPacket = packet.substring(indexOfExclamation + OFFSET);
infoGPS = packet.substring(indexOfExclamation + OFFSET);
} else if (indexOfEqual > 10) {
GPSPacket = packet.substring(indexOfEqual + OFFSET);
infoGPS = packet.substring(indexOfEqual + OFFSET);
}
String encodedLatitude = GPSPacket.substring(0,4);
int Y1 = encodedLatitude[0] - 33;
int Y2 = encodedLatitude[1] - 33;
int Y3 = encodedLatitude[2] - 33;
int Y4 = encodedLatitude[3] - 33;
float decodedLatitude = 90.0 - (((Y1 * pow(91,3)) + (Y2 * pow(91,2)) + (Y3 * 91) + Y4) / 380926.0);
float decodedLatitude = APRSPacketLib::decodeBase91EncodedLatitude(infoGPS.substring(0,4));
float decodedLongitude = APRSPacketLib::decodeBase91EncodedLongitude(infoGPS.substring(4,8));
String encodedLongitude = GPSPacket.substring(4,8);
int X1 = encodedLongitude[0] - 33;
int X2 = encodedLongitude[1] - 33;
int X3 = encodedLongitude[2] - 33;
int X4 = encodedLongitude[3] - 33;
float decodedLongitude = -180.0 + (((X1 * pow(91,3)) + (X2 * pow(91,2)) + (X3 * 91) + X4) / 190463.0);
distance = String(calculateDistanceTo(decodedLatitude, decodedLongitude),1);
String decodedGPS = String(decodedLatitude,5);
decodedGPS += "N / ";
decodedGPS += String(decodedLongitude,5);
decodedGPS += "E / ";
decodedGPS += distance;
decodedGPS += "km";
String comment = GPSPacket.substring(12);
if (comment != "") {
decodedGPS += " / ";
decodedGPS += comment;
}
return decodedGPS;
return buildDistanceAndComment(decodedLatitude, decodedLongitude, infoGPS.substring(12));
}
String getReceivedGPS(const String& packet) {
@@ -184,60 +134,55 @@ namespace GPS_Utils {
}
String Latitude = infoGPS.substring(0,8); // First 8 characters are Latitude
int latitudeColonIndex = Latitude.indexOf(".");
float convertedLatitude = Latitude.substring(0,2).toFloat(); // First 2 digits (Degrees)
convertedLatitude += Latitude.substring(2,4).toFloat() / 60; // Next 2 digits (Minutes)
convertedLatitude += Latitude.substring(Latitude.indexOf(".") + 1, Latitude.indexOf(".") + 3).toFloat() / (60*100);
convertedLatitude += Latitude.substring(latitudeColonIndex + 1, latitudeColonIndex + 3).toFloat() / (60*100);
if (Latitude.endsWith("S")) convertedLatitude = -convertedLatitude; // Handle Southern Hemisphere
String Longitude = infoGPS.substring(9,18); // Next 9 characters are Longitude
int longitudeColonIndex = Longitude.indexOf(".");
float convertedLongitude = Longitude.substring(0,3).toFloat(); // First 3 digits (Degrees)
convertedLongitude += Longitude.substring(3,5).toFloat() / 60; // Next 2 digits (Minutes)
convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100);
convertedLongitude += Longitude.substring(longitudeColonIndex + 1, longitudeColonIndex + 3).toFloat() / (60*100);
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1);
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;
return buildDistanceAndComment(convertedLatitude, convertedLongitude, infoGPS.substring(19));
}
String getDistanceAndComment(const String& packet) {
int indexOfAt = packet.indexOf(":@");
if (indexOfAt > 10) {
return getReceivedGPS(packet);
} else {
const uint8_t ENCODED_BYTE_OFFSET = 14; // Offset for encoded data in the packet
int indexOfExclamation = packet.indexOf(":!");
int indexOfEqual = packet.indexOf(":=");
uint8_t encodedBytePosition = 0;
if (indexOfExclamation > 10) { // Determine the position where encoded data starts
encodedBytePosition = indexOfExclamation + ENCODED_BYTE_OFFSET;
} else if (indexOfEqual > 10) {
encodedBytePosition = indexOfEqual + ENCODED_BYTE_OFFSET;
}
if (indexOfAt > 10) return getReceivedGPS(packet);
if (encodedBytePosition != 0) {
char currentChar = packet[encodedBytePosition];
if (currentChar == 'G' || currentChar == 'Q' || currentChar == '[' || currentChar == 'H' || currentChar == 'X') {
return decodeEncodedGPS(packet); // If valid encoded data position is found, decode it
} else {
return getReceivedGPS(packet);
}
} else {
return " _ / _ / _ ";
}
const uint8_t nonEncondedLatitudeOffset = 9; // "N" / "S"
const uint8_t nonEncondedLongitudeOffset = 19; // "E" / "W"
const uint8_t encodedByteOffset = 14;
int indexOfExclamation = packet.indexOf(":!");
int indexOfEqual = packet.indexOf(":=");
int baseIndex = - 1;
if (indexOfExclamation > 10) {
baseIndex = indexOfExclamation;
} else if (indexOfEqual > 10) {
baseIndex = indexOfEqual;
}
if (baseIndex == -1) return " _ / _ / _ ";
int latitudeIndex = baseIndex + nonEncondedLatitudeOffset;
int longitudeIndex = baseIndex + nonEncondedLongitudeOffset;
int encodedByteIndex = baseIndex + encodedByteOffset;
int packetLength = packet.length();
if (latitudeIndex < packetLength && longitudeIndex < packetLength) {
char latChar = packet[latitudeIndex];
char lngChar = packet[longitudeIndex];
if ((latChar == 'N' || latChar == 'S') && (lngChar == 'E' || lngChar == 'W')) return getReceivedGPS(packet);
}
if (encodedByteIndex < packetLength) {
char byteChar = packet[encodedByteIndex];
if (byteChar == 'G' || byteChar == 'Q' || byteChar == '[' || byteChar == 'H' || byteChar == 'X' || byteChar == '3') return decodeEncodedGPS(packet);
}
return " _ / _ / _ ";
}
void setup() {

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -29,17 +29,16 @@ bool validateKISSFrame(const String& kissFormattedFrame) {
}
String encodeAddressAX25(String tnc2Address) {
bool hasBeenDigipited = tnc2Address.indexOf('*') != -1;
if (tnc2Address.indexOf('-') == -1) {
bool hasBeenDigipited = tnc2Address.indexOf('*') != -1;
int tnc2AddressIndex = tnc2Address.indexOf('-');
if (tnc2AddressIndex == -1) {
if (hasBeenDigipited) {
tnc2Address = tnc2Address.substring(0, tnc2Address.length() - 1);
}
tnc2Address += "-0";
}
int separatorIndex = tnc2Address.indexOf('-');
int separatorIndex = tnc2AddressIndex;
int ssid = tnc2Address.substring(separatorIndex + 1).toInt();
String kissAddress = "";
@@ -131,8 +130,9 @@ String encodeKISS(const String& frame) {
if (validateTNC2Frame(frame)) {
String address = "";
bool dstAddresWritten = false;
for (int p = 0; p <= frame.indexOf(':'); p++) {
bool dstAddresWritten = false;
int colonFrameIndex = frame.indexOf(':');
for (int p = 0; p <= colonFrameIndex; p++) {
char currentChar = frame.charAt(p);
if (currentChar == ':' || currentChar == '>' || currentChar == ',') {
if (!dstAddresWritten && (currentChar == ',' || currentChar == ':')) {
@@ -151,7 +151,7 @@ String encodeKISS(const String& frame) {
ax25Frame.setCharAt(ax25Frame.length() - 1, (char)(lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK));
ax25Frame += (char)APRS_CONTROL_FIELD;
ax25Frame += (char)APRS_INFORMATION_FIELD;
ax25Frame += frame.substring(frame.indexOf(':') + 1);
ax25Frame += frame.substring(colonFrameIndex + 1);
}
String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA);

View File

@@ -30,6 +30,7 @@
extern Configuration Config;
extern uint32_t lastRxTime;
extern bool packetIsBeacon;
extern std::vector<ReceivedPacket> receivedPackets;
@@ -80,9 +81,7 @@ namespace LoRa_Utils {
radio.XTAL = true;
#endif
int state = radio.begin(freq);
if (state == RADIOLIB_ERR_NONE) {
Utils::println("Initializing LoRa Module");
} else {
if (state != RADIOLIB_ERR_NONE) {
Utils::println("Starting LoRa failed! State: " + String(state));
while (true);
}
@@ -92,16 +91,31 @@ namespace LoRa_Utils {
#if defined(HAS_SX1278) || defined(HAS_SX1276)
radio.setDio0Action(setFlag, RISING);
#endif
radio.setSpreadingFactor(Config.loramodule.spreadingFactor);
float signalBandwidth = Config.loramodule.signalBandwidth/1000;
radio.setBandwidth(signalBandwidth);
radio.setCodingRate(Config.loramodule.codingRate4);
/*#ifdef SX126X_DIO3_TCXO_VOLTAGE
if (radio.setTCXO(float(SX126X_DIO3_TCXO_VOLTAGE)) == RADIOLIB_ERR_NONE) {
Utils::println("Set LoRa Module TCXO Voltage to:" + String(SX126X_DIO3_TCXO_VOLTAGE));
} else {
Utils::println("Set LoRa Module TCXO Voltage failed! State: " + String(state));
while (true);
}
#endif*/
radio.setSpreadingFactor(Config.loramodule.rxSpreadingFactor);
radio.setCodingRate(Config.loramodule.rxCodingRate4);
float signalBandwidth = Config.loramodule.rxSignalBandwidth / 1000;
radio.setBandwidth(signalBandwidth);
radio.setCRC(true);
#if (defined(RADIO_RXEN) && defined(RADIO_TXEN)) // QRP Labs LightGateway has 400M22S (SX1268)
radio.setRfSwitchPins(RADIO_RXEN, RADIO_TXEN);
#endif
/*#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setRfSwitchPins(RADIO_RXEN, RADIOLIB_NC);
radio.setDio2AsRfSwitch(true);
#endif*/
#ifdef HAS_1W_LORA // Ebyte E22 400M30S (SX1268) / 900M30S (SX1262) / Ebyte E220 400M30S (LLCC68)
state = radio.setOutputPower(Config.loramodule.power); // max value 20dB for 1W modules as they have Low Noise Amp
radio.setCurrentLimit(140); // to be validated (100 , 120, 140)?
@@ -119,6 +133,13 @@ namespace LoRa_Utils {
radio.setRxBoostedGainMode(true);
#endif
#if defined(HAS_TCXO) && !defined(HAS_1W_LORA)
radio.setDio2AsRfSwitch();
#endif
#ifdef HAS_TCXO
radio.setTCXO(1.8);
#endif
if (state == RADIOLIB_ERR_NONE) {
Utils::println("init : LoRa Module ... done!");
} else {
@@ -128,22 +149,30 @@ namespace LoRa_Utils {
}
void changeFreqTx() {
delay(500);
float freq = (float)Config.loramodule.txFreq / 1000000;
radio.setFrequency(freq);
radio.setSpreadingFactor(Config.loramodule.txSpreadingFactor);
radio.setCodingRate(Config.loramodule.txCodingRate4);
float signalBandwidth = Config.loramodule.txSignalBandwidth / 1000;
radio.setBandwidth(signalBandwidth);
}
void changeFreqRx() {
delay(500);
float freq = (float)Config.loramodule.rxFreq / 1000000;
radio.setFrequency(freq);
radio.setSpreadingFactor(Config.loramodule.rxSpreadingFactor);
radio.setCodingRate(Config.loramodule.rxCodingRate4);
float signalBandwidth = Config.loramodule.rxSignalBandwidth / 1000;
radio.setBandwidth(signalBandwidth);
}
void sendNewPacket(const String& newPacket) {
if (!Config.loramodule.txActive) return;
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
changeFreqTx();
if (!packetIsBeacon || (packetIsBeacon && Config.beacon.beaconFreq == 1)) {
changeFreqTx();
}
}
#ifdef INTERNAL_LED_PIN
@@ -165,7 +194,9 @@ namespace LoRa_Utils {
if (Config.digi.ecoMode != 1) digitalWrite(INTERNAL_LED_PIN, LOW); // disabled in Ultra Eco Mode
#endif
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
changeFreqRx();
if (!packetIsBeacon || (packetIsBeacon && Config.beacon.beaconFreq == 1)) {
changeFreqRx();
}
}
}
@@ -193,7 +224,7 @@ namespace LoRa_Utils {
if (packet != "") {
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();
snr = radio.getSNR();
freqError = radio.getFrequencyError();

View File

@@ -27,7 +27,7 @@
extern Configuration Config;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 15 * 60 * 1000); // Update interval 15 min
NTPClient* timeClient;
namespace NTP_Utils {
@@ -35,17 +35,17 @@ namespace NTP_Utils {
void setup() {
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") {
int gmt = Config.ntp.gmtCorrection * 3600;
timeClient.setTimeOffset(gmt);
timeClient.begin();
timeClient = new NTPClient(ntpUDP, Config.ntp.server.c_str(), gmt, 15 * 60 * 1000); // Update interval 15 min
timeClient->begin();
}
}
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() {
if (Config.digi.ecoMode == 0) return timeClient.getFormattedTime();
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0) return timeClient->getFormattedTime();
return "DigiEcoMode Active";
}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -20,6 +20,7 @@
#include "battery_utils.h"
#include "board_pinout.h"
#include "power_utils.h"
#include "utils.h"
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
#ifdef TTGO_T_Beam_S3_SUPREME_V3
@@ -43,48 +44,28 @@
#endif
extern Configuration Config;
extern bool stationCallsignIsValid;
namespace POWER_Utils {
#ifdef VEXT_CTRL
void vext_ctrl_ON() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
#endif
#if defined(HELTEC_WP_V1) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
#endif
}
void vext_ctrl_OFF() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
#endif
#if defined(HELTEC_WP_V1) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
#endif
}
#endif
#ifdef ADC_CTRL
#ifdef ADC_CTRL_PIN
void adc_ctrl_ON() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2)
digitalWrite(ADC_CTRL, HIGH);
#endif
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP_V1)
digitalWrite(ADC_CTRL, LOW);
#endif
digitalWrite(ADC_CTRL_PIN, ADC_CTRL_ON_STATE);
}
void adc_ctrl_OFF() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2)
digitalWrite(ADC_CTRL, LOW);
#endif
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP_V1)
digitalWrite(ADC_CTRL, HIGH);
#endif
digitalWrite(ADC_CTRL_PIN, !ADC_CTRL_ON_STATE);
}
#endif
#ifdef VEXT_CTRL_PIN
void vext_ctrl_ON() {
digitalWrite(VEXT_CTRL_PIN, Config.digi.ecoMode == 1 ? !VEXT_CTRL_ON_STATE : VEXT_CTRL_ON_STATE);
}
void vext_ctrl_OFF() {
digitalWrite(VEXT_CTRL_PIN, Config.digi.ecoMode == 1 ? VEXT_CTRL_ON_STATE : !VEXT_CTRL_ON_STATE);
}
#endif
@@ -112,7 +93,7 @@ namespace POWER_Utils {
#else
return false;
#endif
}
}
void activateGPS() {
#ifdef HAS_AXP192
@@ -315,17 +296,14 @@ namespace POWER_Utils {
Wire.begin(OLED_SDA, OLED_SCL);
#endif
#ifdef USE_WIRE_WITH_BOARD_I2C_PINS
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#ifdef SENSOR_I2C_BUS
SENSOR_I2C_BUS.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif
#ifdef USE_WIRE1_WITH_BOARD_I2C_PINS
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif
delay(1000);
BATTERY_Utils::setup();
BATTERY_Utils::startupBatteryHealth();
stationCallsignIsValid = Utils::callsignIsValid(Config.callsign);
setCpuFrequencyMhz(80);
}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -41,7 +41,7 @@ namespace QUERY_Utils {
String queryQuestion = query;
queryQuestion.toUpperCase();
if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") {
answer.concat("?APRSV ?APRSP ?APRSL ?APRSSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign
answer.concat("?APRSV ?APRSP ?APRSL ?APRSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign
} else if (queryQuestion == "?APRSV") {
answer.concat("CA2RXU_LoRa_iGate v");
answer.concat(versionNumber);
@@ -49,9 +49,9 @@ namespace QUERY_Utils {
answer.concat(versionDate);
} else if (queryQuestion == "?APRSP") {
answer.concat("iGate QTH: ");
answer.concat(String(Config.beacon.latitude,3));
answer.concat(String(Config.beacon.latitude,2));
answer.concat(" ");
answer.concat(String(Config.beacon.longitude,3));
answer.concat(String(Config.beacon.longitude,2));
} else if (queryQuestion == "?APRSL") {
if (lastHeardStations.size() == 0) {
char answerArray[50];
@@ -66,21 +66,25 @@ namespace QUERY_Utils {
} else if (queryQuestion == "?APRSSR") {
char signalData[35];
snprintf(signalData, sizeof(signalData), " %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
answer.concat(signalData);
answer.concat(signalData);
} /*else if (queryQuestion.indexOf("?APRSH") == 0) {
// sacar callsign despues de ?APRSH
Serial.println("escuchaste a X estacion? en las ultimas 24 o 8 horas?");
answer.concat("?APRSH on development 73!");
} *//*else if (queryQuestion.indexOf("?WHERE") == 0) {
} *//*else if (queryQuestion.indexOf("?WHERE") == 0) {
// agregar callsign para completar donde esta X callsign --> posicion
Serial.println("estaciones escuchadas directo (ultimos 30 min)");
answer.concat("?WHERE on development 73!");
} */
else if (STATION_Utils::isManager(station) && (!queryFromAPRSIS || !Config.remoteManagement.rfOnly)) {
if (queryQuestion.indexOf("?EM=OFF") == 0) {
if ((Config.digi.mode == 2 || Config.digi.mode == 3) && Config.loramodule.txActive && Config.loramodule.rxActive && !Config.aprs_is.active) {
if (Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) { // Exit Digipeater EcoMode or Digipeater without WiFiAP
answer = (Config.digi.ecoMode == 1) ? "DigiEcoMode:OFF" : "Digipeater + WiFiAP enabled";
int digiMode = Config.digi.mode;
int digiEcoMode = Config.digi.ecoMode;
int radioTxActive = Config.loramodule.txActive;
bool onlyRadioActive = radioTxActive && Config.loramodule.rxActive && !Config.aprs_is.active;
if (queryQuestion.startsWith("?EM=OFF")) {
if ((digiMode == 2 || digiMode == 3) && onlyRadioActive) {
if (digiEcoMode == 1 || digiEcoMode == 2) { // Exit Digipeater EcoMode or Digipeater without WiFiAP
answer = (digiEcoMode == 1) ? "DigiEcoMode:OFF" : "Digipeater + WiFiAP enabled";
Config.digi.ecoMode = 0;
Config.display.alwaysOn = true;
Config.display.timeout = 10;
@@ -92,9 +96,9 @@ namespace QUERY_Utils {
} else {
answer = "Digipeater Mode control not possible";
}
} else if (queryQuestion.indexOf("?EM=ON") == 0) {
if ((Config.digi.mode == 2 || Config.digi.mode == 3) && Config.loramodule.txActive && Config.loramodule.rxActive && !Config.aprs_is.active) {
if (Config.digi.ecoMode == 0) { // Start Digipeater EcoMode
} else if (queryQuestion.startsWith("?EM=ON")) {
if ((digiMode == 2 || digiMode == 3) && onlyRadioActive) {
if (digiEcoMode == 0) { // Start Digipeater EcoMode
answer = "DigiEcoMode:ON";
Config.digi.ecoMode = 1;
shouldSleepLowVoltage = true; // to make sure all packets in outputPacketBuffer are sent before restart.
@@ -105,31 +109,29 @@ namespace QUERY_Utils {
} else {
answer = "Digipeater Mode control not possible";
}
} else if (queryQuestion.indexOf("?EM=?") == 0) { // Digipeater EcoMode Status
if (Config.digi.ecoMode == 0) {
answer = "DigiEcoMode:OFF";
} else if (Config.digi.ecoMode == 1) {
answer = "DigiEcoMode:ON";
} else {
answer = "DigiEcoMode:OFF/Only Serial Output";
} else if (queryQuestion.startsWith("?EM=?")) { // Digipeater EcoMode Status
switch (digiEcoMode) {
case 0: answer = "DigiEcoMode:OFF"; break;
case 1: answer = "DigiEcoMode:ON"; break;
default: answer = "DigiEcoMode:OFF/Only Serial Output";
}
} else if (queryQuestion.indexOf("?TX=ON") == 0) {
if (Config.loramodule.txActive) {
} else if (queryQuestion.startsWith("?TX=ON")) {
if (radioTxActive) {
answer = "TX was ON";
} else {
Config.loramodule.txActive = true;
answer = "TX=ON";
}
} else if (queryQuestion.indexOf("?TX=OFF") == 0) {
if (!Config.loramodule.txActive) {
} else if (queryQuestion.startsWith("?TX=OFF")) {
if (!radioTxActive) {
answer = "TX was OFF";
} else {
Config.loramodule.txActive = false;
answer = "TX=OFF";
}
} else if (queryQuestion.indexOf("?TX=?") == 0) {
answer = (Config.loramodule.txActive) ? "TX=ON" : "TX=OFF";
} else if (queryQuestion.indexOf("?COMMIT") == 0) { // saving for next reboot
}
} else if (queryQuestion.startsWith("?TX=?")) {
answer = (radioTxActive) ? "TX=ON" : "TX=OFF";
} else if (queryQuestion.startsWith("?COMMIT")) { // saving for next reboot
answer = "New Config Saved";
Config.writeFile();
}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -51,12 +51,13 @@ namespace SLEEP_Utils {
if (Config.digi.ecoMode == 1) {
pinMode(RADIO_WAKEUP_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(RADIO_WAKEUP_PIN), wakeUpLoRaPacketReceived, RISING);
#if defined(TTGO_LORA32_V2_1) || defined(TTGO_LORA32_V2_1_915) || defined(TTGO_LORA32_T3S3_V1_2) || defined(TTGO_T_BEAM_V1_0) || defined(TTGO_T_BEAM_V1_0_915) || defined(TTGO_T_BEAM_V1_0_SX1268) || defined(TTGO_T_BEAM_V1_2) || defined(TTGO_T_BEAM_V1_2_915) || defined(TTGO_T_BEAM_V1_2_SX1262) || defined(TTGO_T_DECK_PLUS) || defined(TTGO_T_DECK_GPS) || defined(TTGO_T_Beam_S3_SUPREME_V3) || defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WP_V1) || defined(HELTEC_WS) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY) || defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V2) || defined(XIAO_ESP32S3_LORA) || defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) || defined(TROY_LoRa_APRS) || defined(OE5HWN_MeshCom) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915) || defined(ESP32_DIY_1W_LoRa_LLCC68) || defined(ESP32_DIY_1W_LoRa_Mesh_V1_2) || defined(WEMOS_S2_MINI_DIY_LoRa) || defined(WEMOS_D1_R32_RA02) || defined(WEMOS_LOLIN32_OLED_DIY_LoRa)
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
esp_sleep_enable_ext1_wakeup(GPIO_WAKEUP_PIN, ESP_EXT1_WAKEUP_ANY_HIGH);
#endif
#if defined(HELTEC_HTCT62) || defined(ESP32C3_DIY_1W_LoRa) || defined(ESP32C3_DIY_1W_LoRa_915) || defined(ESP32_C3_OctopusLab_LoRa)
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
esp_deep_sleep_enable_gpio_wakeup(1ULL << GPIO_WAKEUP_PIN, ESP_GPIO_WAKEUP_GPIO_HIGH);
#endif
//#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_NRF52)
}
#endif
}

View File

@@ -33,18 +33,31 @@ extern bool shouldSleepLowVoltage;
uint32_t lastTxTime = millis();
std::vector<LastHeardStation> lastHeardStations;
std::vector<String> outputPacketBuffer;
std::vector<Packet25SegBuffer> packet25SegBuffer;
std::vector<String> blacklist;
std::vector<String> managers;
std::vector<LastHeardStation> lastHeardObjects;
struct OutputPacketBuffer {
String packet;
bool isBeacon;
};
std::vector<OutputPacketBuffer> outputPacketBuffer;
struct Packet25SegBuffer {
uint32_t receivedTime;
String station;
String payload;
};
std::vector<Packet25SegBuffer> packet25SegBuffer;
bool saveNewDigiEcoModeConfig = false;
bool packetIsBeacon = false;
namespace STATION_Utils {
std::vector<String> loadCallSignList(const String& list) {
std::vector<String> loadCallsignList(const String& list) {
std::vector<String> loadedList;
String callsigns = list;
@@ -64,12 +77,12 @@ namespace STATION_Utils {
}
void loadBlacklistAndManagers() {
blacklist = loadCallSignList(Config.blacklist);
managers = loadCallSignList(Config.remoteManagement.managers);
blacklist = loadCallsignList(Config.blacklist);
managers = loadCallsignList(Config.remoteManagement.managers);
}
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("*");
if (wildcardIndex >= 0) {
String wildcard = list[i].substring(0, wildcardIndex);
@@ -138,7 +151,7 @@ namespace STATION_Utils {
}
}
if (!stationHeard) lastHeardStations.emplace_back(LastHeardStation{millis(), station});
Utils::activeStations();
Utils::showActiveStations();
}
bool wasHeard(const String& station) {
@@ -171,7 +184,9 @@ namespace STATION_Utils {
size_t currentIndex = 0;
while (currentIndex < outputPacketBuffer.size()) { // this sends all packets from output buffer
delay(3000); // and cleans buffer to avoid sending packets with time offset
LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex]); // next time it wakes up
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = true;
LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex].packet); // next time it wakes up
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = false;
currentIndex++;
}
outputPacketBuffer.clear();
@@ -190,13 +205,17 @@ namespace STATION_Utils {
uint32_t lastRx = millis() - lastRxTime;
uint32_t lastTx = millis() - lastTxTime;
if (outputPacketBuffer.size() > 0 && lastTx > timeToWait && lastRx > timeToWait) {
LoRa_Utils::sendNewPacket(outputPacketBuffer[0]);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = true;
LoRa_Utils::sendNewPacket(outputPacketBuffer[0].packet);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = false;
outputPacketBuffer.erase(outputPacketBuffer.begin());
lastTxTime = millis();
}
if (shouldSleepLowVoltage) {
while (outputPacketBuffer.size() > 0) {
LoRa_Utils::sendNewPacket(outputPacketBuffer[0]);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = true;
LoRa_Utils::sendNewPacket(outputPacketBuffer[0].packet);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = false;
outputPacketBuffer.erase(outputPacketBuffer.begin());
delay(4000);
}
@@ -209,8 +228,12 @@ namespace STATION_Utils {
}
}
void addToOutputPacketBuffer(const String& packet) {
outputPacketBuffer.push_back(packet);
void addToOutputPacketBuffer(const String& packet, bool flag) {
OutputPacketBuffer entry;
entry.packet = packet;
entry.isBeacon = flag;
outputPacketBuffer.push_back(entry);
}
}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -43,9 +43,11 @@ namespace SYSLOG_Utils {
char signalData[35];
snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
int colonIndex = packet.indexOf(":");
char nextChar = packet[colonIndex + 1];
String sender = packet.substring(3, packet.indexOf(">"));
int colonIndex = packet.indexOf(":");
char nextChar = packet[colonIndex + 1];
int greaterThanIndex = packet.indexOf(">");
int telemetryPacketIndex = packet.indexOf(":T#");
String sender = packet.substring(3, greaterThanIndex);
switch (type) {
case 0: // CRC
@@ -58,13 +60,12 @@ namespace SYSLOG_Utils {
if (nextChar == ':') {
syslogPacket.concat("MESSAGE / ");
syslogPacket.concat(sender);
syslogPacket.concat(" ---> ");
syslogPacket.concat(" ---> ");
syslogPacket.concat(packet.substring(colonIndex + 2));
} else if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
syslogPacket.concat("GPS / ");
syslogPacket.concat(sender);
syslogPacket.concat(" / ");
int greaterThanIndex = packet.indexOf(">");
if (packet.indexOf("WIDE1-1") > 10) {
syslogPacket.concat(packet.substring(greaterThanIndex + 1, packet.indexOf(",")));
syslogPacket.concat(" / WIDE1-1");
@@ -87,11 +88,11 @@ namespace SYSLOG_Utils {
syslogPacket.concat(sender);
syslogPacket.concat(" ---> ");
syslogPacket.concat(packet.substring(colonIndex + 2));
} else if (packet.indexOf(":T#") >= 10 && packet.indexOf(":=/") == -1) {
} else if (telemetryPacketIndex >= 10 && packet.indexOf(":=/") == -1) {
syslogPacket.concat("TELEMETRY / ");
syslogPacket.concat(sender);
syslogPacket.concat(" ---> ");
syslogPacket.concat(packet.substring(packet.indexOf(":T#") + 3));
syslogPacket.concat(packet.substring(telemetryPacketIndex + 3));
} else {
syslogPacket.concat(packet);
}
@@ -141,10 +142,6 @@ namespace SYSLOG_Utils {
void setup() {
if (WiFi.status() == WL_CONNECTED) {
udpClient.begin(0);
udpClient.beginPacket("syslog.trackiot.cc", 15243);
String hiddenLogPacket = Config.callsign + "," + versionDate;
udpClient.write((const uint8_t*)hiddenLogPacket.c_str(), hiddenLogPacket.length());
udpClient.endPacket();
if (Config.syslog.active) Serial.println("init : Syslog Server ... done! (at " + Config.syslog.server + ")");
}
}

View File

@@ -68,10 +68,10 @@ namespace TELEMETRY_Utils {
}
void sendBaseTelemetryPacket(const String& prefix, const std::vector<String>& values) {
String packet = prefix + joinWithCommas(values);
String packet = prefix + joinWithCommas(values);
String currentCallsign = (Config.tacticalCallsign != "") ? Config.tacticalCallsign : Config.callsign;
if (Config.beacon.sendViaAPRSIS) {
String baseAPRSISTelemetryPacket = APRSPacketLib::generateMessagePacket(Config.callsign, "APLRG1", "TCPIP,qAC", Config.callsign, packet);
String baseAPRSISTelemetryPacket = APRSPacketLib::generateMessagePacket(currentCallsign, "APLRG1", "TCPIP,qAC", currentCallsign, packet);
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket);
#else
@@ -79,7 +79,7 @@ namespace TELEMETRY_Utils {
#endif
delay(300);
} else if (Config.beacon.sendViaRF) {
String baseRFTelemetryPacket = APRSPacketLib::generateMessagePacket(Config.callsign, "APLRG1", Config.beacon.path, Config.callsign, packet);
String baseRFTelemetryPacket = APRSPacketLib::generateMessagePacket(currentCallsign, "APLRG1", Config.beacon.path, currentCallsign, packet);
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket);
delay(3000);
}
@@ -122,7 +122,7 @@ namespace TELEMETRY_Utils {
telemetryCounter++;
if (telemetryCounter == 1000) telemetryCounter = 0;
if (Config.battery.sendInternalVoltage) telemetry += generateEncodedTelemetryBytes(BATTERY_Utils::checkInternalVoltage(), false, 0);
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(BATTERY_Utils::checkExternalVoltage(), false, 1);
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(BATTERY_Utils::checkExternalVoltage(), false, Config.battery.useExternalI2CSensor ? 0 : 1);
telemetry += "|";
return telemetry;
}

View File

@@ -21,12 +21,15 @@
#include "configuration.h"
#include "station_utils.h"
#include "kiss_protocol.h"
#include "aprs_is_utils.h"
#include "kiss_utils.h"
#include "tnc_utils.h"
#include "utils.h"
extern Configuration Config;
extern Configuration Config;
extern WiFiClient aprsIsClient;
extern bool passcodeValid;
#define MAX_CLIENTS 4
#define INPUT_BUFFER_SIZE (2 + MAX_CLIENTS)
@@ -94,7 +97,8 @@ namespace TNC_Utils {
String sender = frame.substring(0,frame.indexOf(">"));
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 {
Utils::println("Ignored own frame from KISS");
}
@@ -131,8 +135,8 @@ namespace TNC_Utils {
}
}
void sendToClients(const String& packet) {
String cleanPacket = packet.substring(3);
void sendToClients(const String& packet, bool stripBytes) {
String cleanPacket = stripBytes ? packet.substring(3): packet;
const String kissEncoded = encodeKISS(cleanPacket);
@@ -152,8 +156,8 @@ namespace TNC_Utils {
Utils::println(cleanPacket);
}
void sendToSerial(const String& packet) {
String cleanPacket = packet.substring(3);
void sendToSerial(const String& packet, bool stripBytes) {
String cleanPacket = stripBytes ? packet.substring(3): packet;
Serial.print(encodeKISS(cleanPacket));
Serial.flush();
}

View File

@@ -1,21 +1,22 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#include <APRSPacketLib.h>
#include <TinyGPS++.h>
#include <WiFi.h>
#include "telemetry_utils.h"
@@ -33,6 +34,7 @@
#include "display.h"
#include "utils.h"
#define DAY_MS (24UL * 60UL * 60UL * 1000UL)
extern Configuration Config;
extern TinyGPSPlus gps;
@@ -52,18 +54,20 @@ extern int freqError;
extern String distance;
extern bool WiFiConnected;
extern int wxModuleType;
extern bool backUpDigiMode;
extern bool backupDigiMode;
extern bool shouldSleepLowVoltage;
extern bool transmitFlag;
extern bool passcodeValid;
extern std::vector<LastHeardStation> lastHeardStations;
bool statusAfterBoot = true;
bool sendStartTelemetry = true;
bool beaconUpdate = false;
uint32_t lastBeaconTx = 0;
uint32_t lastScreenOn = millis();
bool statusAfterBoot = true;
bool sendStartTelemetry = true;
bool beaconUpdate = false;
uint32_t lastBeaconTx = 0;
uint32_t lastScreenOn = millis();
uint32_t lastStatusTx = 0;
bool stationCallsignIsValid = false;
String beaconPacket;
String secondaryBeaconPacket;
@@ -71,26 +75,26 @@ String secondaryBeaconPacket;
namespace Utils {
void processStatus() {
String status = Config.callsign;
status.concat(">APLRG1");
if (Config.beacon.path.indexOf("WIDE") == 0) {
status.concat(",");
status.concat(Config.beacon.path);
}
String status = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
delay(1000);
status.concat(",qAC:>");
status.concat(Config.beacon.statusPacket);
APRS_IS_Utils::upload(status);
SYSLOG_Utils::log(2, status, 0, 0.0, 0); // APRSIS TX
statusAfterBoot = false;
}
if (statusAfterBoot && !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF) {
status.concat(":>");
status.concat(Config.beacon.statusPacket);
STATION_Utils::addToOutputPacketBuffer(status);
statusAfterBoot = false;
STATION_Utils::addToOutputPacketBuffer(status, true); // treated also as beacon on Tx Freq
}
statusAfterBoot = false;
lastStatusTx = millis();
}
void checkStatusInterval() {
if (lastStatusTx == 0 || millis() - lastStatusTx > DAY_MS) statusAfterBoot = true;
}
String getLocalIP() {
@@ -98,7 +102,7 @@ namespace Utils {
return "** WiFi AP Killed **";
} else if (!WiFiConnected) {
return "IP : 192.168.4.1";
} else if (backUpDigiMode) {
} else if (backupDigiMode) {
return "- BACKUP DIGI MODE -";
} else {
return "IP : " + String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3]);
@@ -123,11 +127,15 @@ namespace Utils {
#ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN,LOW);
#endif
firstLine = Config.callsign;
if (Config.tacticalCallsign != "") {
firstLine = Config.tacticalCallsign;
} else {
firstLine = Config.callsign;
}
seventhLine = " listening...";
}
void activeStations() {
void showActiveStations() {
char buffer[30]; // Adjust size as needed
sprintf(buffer, "Stations (%dmin) = %2d", Config.rememberStationTime, lastHeardStations.size());
fourthLine = buffer;
@@ -149,9 +157,9 @@ namespace Utils {
if (beaconUpdate) {
if (!Config.display.alwaysOn && Config.display.timeout != 0) displayToggle(true);
if (sendStartTelemetry &&
if (sendStartTelemetry &&
Config.battery.sendVoltageAsTelemetry &&
!Config.wxsensor.active &&
!Config.wxsensor.active &&
(Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage) &&
(lastBeaconTx > 0)) {
TELEMETRY_Utils::sendEquationsUnitsParameters();
@@ -159,7 +167,7 @@ namespace Utils {
STATION_Utils::deleteNotHeard();
activeStations();
showActiveStations();
beaconPacket = iGateBeaconPacket;
secondaryBeaconPacket = iGateLoRaBeaconPacket;
@@ -167,10 +175,18 @@ namespace Utils {
if (Config.beacon.gpsActive && Config.digi.ecoMode == 0) {
GPS_Utils::getData();
if (gps.location.isUpdated() && gps.location.lat() != 0.0 && gps.location.lng() != 0.0) {
GPS_Utils::generateBeaconFirstPart();
String encodedGPS = GPS_Utils::encodeGPS(gps.location.lat(), gps.location.lng(), Config.beacon.overlay, Config.beacon.symbol);
beaconPacket = iGateBeaconPacket + encodedGPS;
secondaryBeaconPacket = iGateLoRaBeaconPacket + encodedGPS;
String basePacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
beaconPacket = basePacket;
beaconPacket += ",qAC:!";
beaconPacket += Config.beacon.overlay;
beaconPacket += encodedGPS;
secondaryBeaconPacket = basePacket;
secondaryBeaconPacket += ":=";
secondaryBeaconPacket += Config.beacon.overlay;
secondaryBeaconPacket += encodedGPS;
}
}
#endif
@@ -182,6 +198,12 @@ namespace Utils {
}
beaconPacket += Config.beacon.comment;
secondaryBeaconPacket += Config.beacon.comment;
if (stationCallsignIsValid && Config.tacticalCallsign != "") {
beaconPacket += " de ";
beaconPacket += Config.callsign;
secondaryBeaconPacket += " de ";
secondaryBeaconPacket += Config.callsign;
}
#if defined(BATTERY_PIN) || defined(HAS_AXP192) || defined(HAS_AXP2101)
if (Config.battery.sendInternalVoltage || Config.battery.monitorInternalVoltage) {
@@ -222,7 +244,7 @@ namespace Utils {
if (Config.battery.sendExternalVoltage) {
char externalVoltageInfo[10]; // "xx.xxV\0" (max 7 chars)
snprintf(externalVoltageInfo, sizeof(externalVoltageInfo), "%.2fV", externalVoltage);
char sixthLineBuffer[25]; // Ensure enough space
snprintf(sixthLineBuffer, sizeof(sixthLineBuffer), " (Ext V=%s)", externalVoltageInfo);
sixthLine = sixthLineBuffer;
@@ -243,7 +265,7 @@ namespace Utils {
secondaryBeaconPacket += encodedTelemetry;
}
if (Config.beacon.sendViaAPRSIS && Config.aprs_is.active && passcodeValid && !backUpDigiMode) {
if (Config.beacon.sendViaAPRSIS && Config.aprs_is.active && passcodeValid && !backupDigiMode) {
Utils::println("-- Sending Beacon to APRSIS --");
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING IGATE BEACON", 0);
seventhLine = " listening...";
@@ -255,11 +277,11 @@ namespace Utils {
if (Config.syslog.logBeaconOverTCPIP) SYSLOG_Utils::log(1, "tcp" + beaconPacket, 0, 0.0, 0); // APRSIS TX
}
if (Config.beacon.sendViaRF || backUpDigiMode) {
if (Config.beacon.sendViaRF || backupDigiMode) {
Utils::println("-- Sending Beacon to RF --");
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING DIGI BEACON", 0);
seventhLine = " listening...";
STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket);
STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket, true);
}
lastBeaconTx = millis();
@@ -267,9 +289,8 @@ namespace Utils {
beaconUpdate = false;
}
if (statusAfterBoot && Config.beacon.statusActive && !Config.beacon.statusPacket.isEmpty()) {
processStatus();
}
checkStatusInterval();
if (statusAfterBoot && Config.beacon.statusActive && !Config.beacon.statusPacket.isEmpty()) processStatus();
}
void checkDisplayInterval() {
@@ -284,13 +305,13 @@ namespace Utils {
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);
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();
ESP.restart();
}
}
void typeOfPacket(const String& packet, const uint8_t packetType) {
String sender = packet.substring(0,packet.indexOf(">"));
switch (packetType) {
case 0: // LoRa-APRS
fifthLine = "LoRa Rx ----> APRS-IS";
@@ -306,6 +327,7 @@ namespace Utils {
int firstColonIndex = packet.indexOf(":");
char nextChar = packet[firstColonIndex + 1];
String sender = packet.substring(0,packet.indexOf(">"));
for (int i = sender.length(); i < 9; i++) {
sender += " ";
}
@@ -393,14 +415,16 @@ namespace Utils {
}
}
bool checkValidCallsign(const String& callsign) {
bool callsignIsValid(const String& callsign) {
if (callsign == "WLNK-1") return true;
String cleanCallsign;
if (callsign.indexOf("-") > 0) { // SSID Validation
cleanCallsign = callsign.substring(0, callsign.indexOf("-"));
String ssid = callsign.substring(callsign.indexOf("-") + 1);
int hypenCallsignIndex = callsign.indexOf("-");
if (hypenCallsignIndex > 0) { // SSID Validation
cleanCallsign = callsign.substring(0, hypenCallsignIndex);
String ssid = callsign.substring(hypenCallsignIndex + 1);
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++) {
if (!isAlphaNumeric(ssid[i])) return false;
}
@@ -436,4 +460,11 @@ namespace Utils {
return true;
}
void startupDelay() {
if (Config.startupDelay > 0) {
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(Config.startupDelay * 60 * 1000);
}
}
}

View File

@@ -157,7 +157,10 @@ namespace WEB_Utils {
Config.wifiAPs.push_back(wifiap);
}
Config.startupDelay = getParamIntSafe("startupDelay", Config.startupDelay);
Config.callsign = getParamStringSafe("callsign", Config.callsign);
Config.tacticalCallsign = getParamStringSafe("tacticalCallsign", Config.tacticalCallsign);
Config.wifiAutoAP.password = getParamStringSafe("wifi.autoAP.password", Config.wifiAutoAP.password);
Config.wifiAutoAP.timeout = getParamIntSafe("wifi.autoAP.timeout", Config.wifiAutoAP.timeout);
@@ -175,6 +178,7 @@ namespace WEB_Utils {
Config.beacon.interval = getParamIntSafe("beacon.interval", Config.beacon.interval);
Config.beacon.sendViaAPRSIS = request->hasParam("beacon.sendViaAPRSIS", 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.longitude = getParamDoubleSafe("beacon.longitude", Config.beacon.longitude);
Config.beacon.comment = getParamStringSafe("beacon.comment", Config.beacon.comment);
@@ -188,7 +192,7 @@ namespace WEB_Utils {
}
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);
@@ -196,16 +200,19 @@ namespace WEB_Utils {
Config.digi.mode = getParamIntSafe("digi.mode", Config.digi.mode);
Config.digi.ecoMode = getParamIntSafe("digi.ecoMode", Config.digi.ecoMode);
Config.digi.backupDigiMode = request->hasParam("digi.backupDigiMode", true);
Config.loramodule.txFreq = getParamIntSafe("lora.txFreq", Config.loramodule.txFreq);
Config.loramodule.rxFreq = getParamIntSafe("lora.rxFreq", Config.loramodule.rxFreq);
Config.loramodule.spreadingFactor = getParamIntSafe("lora.spreadingFactor", Config.loramodule.spreadingFactor);
Config.loramodule.signalBandwidth = getParamIntSafe("lora.signalBandwidth", Config.loramodule.signalBandwidth);
Config.loramodule.codingRate4 = getParamIntSafe("lora.codingRate4", Config.loramodule.codingRate4);
Config.loramodule.power = getParamIntSafe("lora.power", Config.loramodule.power);
Config.loramodule.txActive = request->hasParam("lora.txActive", true);
Config.loramodule.rxActive = request->hasParam("lora.rxActive", true);
Config.loramodule.rxFreq = getParamIntSafe("lora.rxFreq", Config.loramodule.rxFreq);
Config.loramodule.rxSpreadingFactor = getParamIntSafe("lora.rxSpreadingFactor", Config.loramodule.rxSpreadingFactor);
Config.loramodule.rxCodingRate4 = getParamIntSafe("lora.rxCodingRate4", Config.loramodule.rxCodingRate4);
Config.loramodule.rxSignalBandwidth = getParamIntSafe("lora.rxSignalBandwidth", Config.loramodule.rxSignalBandwidth);
Config.loramodule.txActive = request->hasParam("lora.txActive", true);
Config.loramodule.txFreq = getParamIntSafe("lora.txFreq", Config.loramodule.txFreq);
Config.loramodule.txSpreadingFactor = getParamIntSafe("lora.txSpreadingFactor", Config.loramodule.txSpreadingFactor);
Config.loramodule.txCodingRate4 = getParamIntSafe("lora.txCodingRate4", Config.loramodule.txCodingRate4);
Config.loramodule.txSignalBandwidth = getParamIntSafe("lora.txSignalBandwidth", Config.loramodule.txSignalBandwidth);
Config.loramodule.power = getParamIntSafe("lora.power", Config.loramodule.power);
Config.display.alwaysOn = request->hasParam("display.alwaysOn", true);
if (!Config.display.alwaysOn) {
@@ -213,7 +220,6 @@ namespace WEB_Utils {
}
Config.display.turn180 = request->hasParam("display.turn180", true);
Config.battery.sendInternalVoltage = request->hasParam("battery.sendInternalVoltage", true);
Config.battery.monitorInternalVoltage = request->hasParam("battery.monitorInternalVoltage", true);
if (Config.battery.monitorInternalVoltage) {
@@ -221,6 +227,9 @@ namespace WEB_Utils {
}
Config.battery.sendExternalVoltage = request->hasParam("battery.sendExternalVoltage", true);
if (Config.battery.sendExternalVoltage) {
Config.battery.useExternalI2CSensor = request->hasParam("battery.useExternalI2CSensor", true);
}
if (Config.battery.sendExternalVoltage) {
Config.battery.externalVoltagePin = getParamIntSafe("battery.externalVoltagePin", Config.battery.externalVoltagePin);
Config.battery.voltageDividerR1 = getParamFloatSafe("battery.voltageDividerR1", Config.battery.voltageDividerR1);
@@ -232,7 +241,6 @@ namespace WEB_Utils {
}
Config.battery.sendVoltageAsTelemetry = request->hasParam("battery.sendVoltageAsTelemetry", true);
Config.wxsensor.active = request->hasParam("wxsensor.active", true);
if (Config.wxsensor.active) {
Config.wxsensor.heightCorrection = getParamIntSafe("wxsensor.heightCorrection", Config.wxsensor.heightCorrection);
@@ -240,7 +248,6 @@ namespace WEB_Utils {
Config.beacon.symbol = "_";
}
Config.syslog.active = request->hasParam("syslog.active", true);
if (Config.syslog.active) {
Config.syslog.server = getParamStringSafe("syslog.server", Config.syslog.server);
@@ -248,11 +255,10 @@ namespace WEB_Utils {
Config.syslog.logBeaconOverTCPIP = request->hasParam("syslog.logBeaconOverTCPIP", true);
}
Config.tnc.enableServer = request->hasParam("tnc.enableServer", true);
Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true);
Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true);
Config.tnc.aprsBridgeActive = request->hasParam("tnc.aprsBridgeActive", true);
Config.mqtt.active = request->hasParam("mqtt.active", true);
if (Config.mqtt.active) {
@@ -261,9 +267,9 @@ namespace WEB_Utils {
Config.mqtt.username = getParamStringSafe("mqtt.username", Config.mqtt.username);
Config.mqtt.password = getParamStringSafe("mqtt.password", Config.mqtt.password);
Config.mqtt.port = getParamIntSafe("mqtt.port", Config.mqtt.port);
Config.mqtt.beaconOverMqtt = request->hasParam("mqtt.beaconOverMqtt", true);
}
Config.rebootMode = request->hasParam("other.rebootMode", true);
if (Config.rebootMode) {
Config.rebootModeTime = getParamIntSafe("other.rebootModeTime", Config.rebootModeTime);
@@ -281,12 +287,11 @@ namespace WEB_Utils {
Config.remoteManagement.managers = getParamStringSafe("remoteManagement.managers", Config.remoteManagement.managers);
Config.remoteManagement.rfOnly = request->hasParam("remoteManagement.rfOnly", true);
Config.ntp.server = getParamStringSafe("ntp.server", Config.ntp.server);
Config.ntp.gmtCorrection = getParamFloatSafe("ntp.gmtCorrection", Config.ntp.gmtCorrection);
Config.rememberStationTime = getParamIntSafe("other.rememberStationTime", Config.rememberStationTime);
Config.backupDigiMode = request->hasParam("other.backupDigiMode", true);
bool saveSuccess = Config.writeFile();
if (saveSuccess) {

View File

@@ -29,47 +29,54 @@ extern Configuration Config;
extern uint8_t myWiFiAPIndex;
extern int myWiFiAPSize;
extern WiFi_AP *currentWiFi;
extern bool backUpDigiMode;
extern bool backupDigiMode;
extern uint32_t lastServerCheck;
bool WiFiConnected = false;
uint32_t WiFiAutoAPTime = millis();
bool WiFiAutoAPStarted = false;
uint32_t previousWiFiMillis = 0;
uint8_t wifiCounter = 0;
uint32_t lastBackupDigiTime = millis();
uint32_t lastWiFiCheck = 0;
namespace WIFI_Utils {
void checkWiFi() {
if (Config.digi.ecoMode == 0) {
if (backUpDigiMode) {
uint32_t WiFiCheck = millis() - lastBackupDigiTime;
if (WiFi.status() != WL_CONNECTED && WiFiCheck >= 15 * 60 * 1000) {
Serial.println("*** Stopping BackUp Digi Mode ***");
backUpDigiMode = false;
wifiCounter = 0;
} else if (WiFi.status() == WL_CONNECTED) {
Serial.println("*** WiFi Reconnect Success (Stopping Backup Digi Mode) ***");
backUpDigiMode = false;
wifiCounter = 0;
}
}
if (Config.digi.ecoMode != 0) return;
uint32_t currentTime = millis();
if (!backUpDigiMode && (WiFi.status() != WL_CONNECTED) && ((millis() - previousWiFiMillis) >= 30 * 1000) && !WiFiAutoAPStarted) {
Serial.print(millis());
if (backupDigiMode) {
if (WiFi.status() != WL_CONNECTED && ((currentTime - lastBackupDigiTime) >= 15 * 60 * 1000)) {
Serial.println("*** Stopping BackUp Digi Mode ***");
backupDigiMode = false;
wifiCounter = 0;
} else if (WiFi.status() == WL_CONNECTED) {
Serial.println("*** WiFi Reconnect Success (Stopping Backup Digi Mode) ***");
backupDigiMode = false;
wifiCounter = 0;
}
}
if (!backupDigiMode && ((currentTime - lastWiFiCheck) >= 30 * 1000) && !WiFiAutoAPStarted) {
lastWiFiCheck = currentTime;
if (WiFi.status() == WL_CONNECTED) {
if (Config.digi.backupDigiMode && (currentTime - lastServerCheck > 30 * 1000)) {
Serial.println("*** Server Connection LOST → Backup Digi Mode ***");
backupDigiMode = true;
WiFi.disconnect();
lastBackupDigiTime = currentTime;
}
} else {
Serial.println("Reconnecting to WiFi...");
WiFi.disconnect();
WIFI_Utils::startWiFi();//WiFi.reconnect();
previousWiFiMillis = millis();
WIFI_Utils::startWiFi();
if (Config.backupDigiMode) {
wifiCounter++;
}
if (Config.digi.backupDigiMode) wifiCounter++;
if (wifiCounter >= 2) {
Serial.println("*** Starting BackUp Digi Mode ***");
backUpDigiMode = true;
lastBackupDigiTime = millis();
backupDigiMode = true;
lastBackupDigiTime = currentTime;
}
}
}
@@ -98,21 +105,21 @@ namespace WIFI_Utils {
delay(500);
unsigned long start = millis();
displayShow("", "Connecting to WiFi:", "", currentWiFi->ssid + " ...", 0);
Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.println("' ...");
Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.print("' ");
WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str());
while (WiFi.status() != WL_CONNECTED && wifiCounter<myWiFiAPSize) {
while (WiFi.status() != WL_CONNECTED && wifiCounter < myWiFiAPSize) {
delay(500);
#ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN,HIGH);
digitalWrite(INTERNAL_LED_PIN, HIGH);
#endif
Serial.print('.');
delay(500);
#ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN,LOW);
digitalWrite(INTERNAL_LED_PIN, LOW);
#endif
if ((millis() - start) > 10000){
delay(1000);
if(myWiFiAPIndex >= (myWiFiAPSize - 1)) {
if (myWiFiAPIndex >= (myWiFiAPSize - 1)) {
myWiFiAPIndex = 0;
wifiCounter++;
} else {
@@ -129,15 +136,15 @@ namespace WIFI_Utils {
}
}
#ifdef INTERNAL_LED_PIN
digitalWrite(INTERNAL_LED_PIN,LOW);
digitalWrite(INTERNAL_LED_PIN, LOW);
#endif
if (WiFi.status() == WL_CONNECTED) {
Serial.print("Connected as ");
Serial.print("\nConnected as ");
Serial.print(WiFi.localIP());
Serial.print(" / MAC Address: ");
Serial.println(WiFi.macAddress());
displayShow("", " Connected!!", "" , " loading ...", 1000);
} else if (WiFi.status() != WL_CONNECTED) {
} else {
startAP = true;
Serial.println("\nNot connected to WiFi! Starting Auto AP");

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -42,6 +42,7 @@ float newHum, newTemp, newPress, newGas;
Adafruit_BME280 bme280;
Adafruit_AHTX0 aht20;
#if defined(HELTEC_V3) || defined(HELTEC_V3_2)
Adafruit_BMP280 bmp280(&Wire1);
Adafruit_Si7021 si7021 = Adafruit_Si7021();
@@ -60,9 +61,9 @@ namespace WX_Utils {
void getWxModuleAddres() {
uint8_t err, addr;
for(addr = 1; addr < 0x7F; addr++) {
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
Wire1.beginTransmission(addr);
err = Wire1.endTransmission();
#ifdef SENSOR_I2C_BUS
SENSOR_I2C_BUS.beginTransmission(addr);
err = SENSOR_I2C_BUS.endTransmission();
#else
Wire.beginTransmission(addr);
#ifdef LIGHTGATEWAY_PLUS_1_0
@@ -119,15 +120,19 @@ namespace WX_Utils {
Serial.println("BMP280 sensor found");
wxModuleType = 2;
wxModuleFound = true;
if (aht20.begin()) {
Serial.println("AHT20 sensor found");
if (wxModuleType == 2) wxModuleType = 6;
}
}
}
} else if (wxModuleAddress == 0x40) {
} else if (wxModuleAddress == 0x40 && Config.battery.useExternalI2CSensor == false) {
if(si7021.begin()) {
Serial.println("Si7021 sensor found");
wxModuleType = 4;
wxModuleFound = true;
}
}
}
#ifdef LIGHTGATEWAY_PLUS_1_0
else if (wxModuleAddress == 0x70) {
if (shtc3.begin()) {
@@ -156,7 +161,7 @@ namespace WX_Utils {
Adafruit_BMP280::SAMPLING_X1,
Adafruit_BMP280::SAMPLING_X1,
Adafruit_BMP280::FILTER_OFF
);
);
Serial.println("BMP280 Module init done!");
break;
case 3:
@@ -265,15 +270,27 @@ namespace WX_Utils {
newPress = 0;
break;
case 5: // SHTC3
#ifdef LIGHTGATEWAY_PLUS_1_0
sensors_event_t humidity, temp;
shtc3.getEvent(&humidity, &temp);
newTemp = temp.temperature;
newHum = humidity.relative_humidity;
newPress = 0;
#endif
{
#ifdef LIGHTGATEWAY_PLUS_1_0
sensors_event_t humidity, temp;
shtc3.getEvent(&humidity, &temp);
newTemp = temp.temperature;
newHum = humidity.relative_humidity;
newPress = 0;
#endif
}
break;
}
case 6: // BMP280 + AHT20
{
bmp280.takeForcedMeasurement();
newTemp = bmp280.readTemperature();
newPress = (bmp280.readPressure() / 100.0F);
sensors_event_t humidity, temp;
aht20.getEvent(&humidity, &temp);
newHum = humidity.relative_humidity;
}
break;
}
if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) {
Serial.println("BME/BMP/Si7021 Module data failed");
@@ -281,16 +298,16 @@ namespace WX_Utils {
return ".../...g...t...";
} else {
String tempStr = generateTempString(((newTemp + Config.wxsensor.temperatureCorrection) * 1.8) + 32);
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);
} else if (wxModuleType == 2) {
humStr = "..";
}
String presStr = (wxModuleType == 4 || wxModuleType == 5)
? "....."
String presStr = (wxModuleType == 4 || wxModuleType == 5)
? "....."
: generatePresString(newPress + getAltitudeCorrection() / CORRECTION_FACTOR);
fifthLine = "BME-> ";

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -45,5 +45,5 @@
#define OLED_SDA 0
#define OLED_SCL 1
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#endif

View File

@@ -4,6 +4,9 @@ board_build.mcu = esp32c3
build_flags =
${common.build_flags}
${common.usb_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_C3_OctopusLab_LoRa
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -22,6 +22,7 @@
// LoRa Radio
#define HAS_SX1268
#define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -22,6 +22,7 @@
// LoRa Radio
#define HAS_SX1262
#define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa_915
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -39,7 +39,7 @@
// Display
#define HAS_DISPLAY
#undef OLED_SDA
#undef OLED_SCL
#undef OLED_RST

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa_LLCC68
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_1W_LoRa_Mesh_V1_2
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_LoRa
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_LoRa_915
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -44,8 +44,8 @@
// Aditional Config
#define INTERNAL_LED_PIN 13 // 13 for V1.1 and 12 for V1.0
#define BATTERY_PIN 35
#define BATTERY_PIN 35
#define HAS_A7670
#define A7670_PWR_PIN 4
#define A7670_ResetPin 5

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_LoRa_A7670
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -31,7 +31,7 @@
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display
// Display
#define HAS_DISPLAY
#undef OLED_SDA

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D ESP32_DIY_LoRa_A7670_915
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D LoRaHAM_V2
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -22,6 +22,7 @@
// LoRa Radio
#define HAS_SX1268
#define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D OE5HWN_MeshCom
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -4,6 +4,9 @@ board_build.mcu = esp32s3
build_flags =
${common.build_flags}
${common.usb_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D LIGHTGATEWAY_1_0
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -22,6 +22,7 @@
// LoRa Radio
#define HAS_SX1268
#define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_VCC_PIN 21
#define RADIO_SCLK_PIN 12
#define RADIO_MISO_PIN 13

View File

@@ -4,6 +4,9 @@ board_build.mcu = esp32s3
build_flags =
${common.build_flags}
${common.usb_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D LIGHTGATEWAY_PLUS_1_0
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -2,6 +2,9 @@
board = esp32dev
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D TROY_LoRa_APRS
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -2,6 +2,9 @@
board = wemos_d1_uno32
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D WEMOS_D1_R32_RA02
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@@ -2,6 +2,9 @@
board = lolin32
build_flags =
${common.build_flags}
-D RADIOLIB_EXCLUDE_LR11X0=1
-D RADIOLIB_EXCLUDE_SX126X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D WEMOS_LOLIN32_OLED_DIY_LoRa
lib_deps =
${common.lib_deps}

View File

@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
*
* This file is part of LoRa APRS iGate.
*
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -31,7 +31,7 @@
#define GPIO_WAKEUP_PIN GPIO_SEL_38
// I2C
#define USE_WIRE_WITH_BOARD_I2C_PINS
#define SENSOR_I2C_BUS Wire
#define BOARD_I2C_SDA 11
#define BOARD_I2C_SCL 12

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