Compare commits

...

131 Commits

Author SHA1 Message Date
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
richonguzman
a89181b5df update complete 2025-09-26 11:37:02 -03:00
richonguzman
9a94a193f9 update before release 2025-09-25 22:01:38 -03:00
richonguzman
9c7f6c7934 led update 2025-09-25 17:54:53 -03:00
richonguzman
53e8941262 blue led add 2025-09-25 17:33:59 -03:00
richonguzman
60a148d362 testing1 2025-09-25 15:55:18 -03:00
richonguzman
36cd1578cc adding basic stuff 2025-09-25 15:50:28 -03:00
richonguzman
c3036f290f TCPIP correction 2025-09-25 15:35:38 -03:00
richonguzman
8eb4ef1dc2 testeando ultimos valores 2025-09-17 17:00:49 -03:00
richonguzman
d5930380b4 testing new config init validation 2025-09-17 16:53:09 -03:00
richonguzman
e42f4b59ed prueba nueva web write desde formulario 2025-09-17 15:29:13 -03:00
richonguzman
2697f2a5f2 update a configuration cpp 2025-09-17 14:29:37 -03:00
richonguzman
36c970aed9 json file size fix 2025-09-14 20:00:56 -03:00
richonguzman
10e1581bc2 new board 2025-09-14 19:54:20 -03:00
Ricardo Guzman (Richonguzman)
bc57d1609a Merge pull request #321 from richonguzman/richonguzman-patch-6
Update README.md
2025-09-09 10:22:52 -03:00
Ricardo Guzman (Richonguzman)
37380c6b9d Update README.md 2025-09-09 10:22:27 -03:00
Ricardo Guzman (Richonguzman)
855c84c079 Merge pull request #320 from richonguzman/richonguzman-patch-5
Update build for Heltec wireless paper v1
2025-09-09 10:02:05 -03:00
Ricardo Guzman (Richonguzman)
bb4df08f06 Update build for Heltec wireless paper v1 2025-09-09 09:56:26 -03:00
richonguzman
6d0e98f4a2 version Date update 2025-09-09 09:42:25 -03:00
richonguzman
c5dcc4c7ab SensorRead with UEM fix 2025-09-08 21:28:54 -03:00
richonguzman
5c5c940d71 mDNS added 2025-09-08 19:00:23 -03:00
richonguzman
57decd2ac7 gpsAmbiguity fix 2025-09-03 18:00:15 -04:00
richonguzman
26a9d1fa99 not build 2025-09-02 10:17:34 -04:00
richonguzman
bf1ab1bfb4 v1_2 missing library update 2025-09-02 10:17:03 -04:00
richonguzman
d32f35ced1 epaper test1 2025-09-01 21:32:28 -04:00
richonguzman
db37567538 62.5K BW added 2025-08-31 13:00:44 -04:00
richonguzman
dc1bdbb703 mqtt without user and pass 2025-08-29 15:01:34 -04:00
richonguzman
74a8fa7969 updatedVersion 2025-08-29 14:51:07 -04:00
richonguzman
b65a6de018 ready for testing 2025-08-29 14:18:13 -04:00
richonguzman
1840ce2338 test with disabling boxes 2025-08-29 12:17:07 -04:00
richonguzman
b8ef262cc7 many switches fix 2025-08-29 10:33:35 -04:00
richonguzman
bb795816e1 corrected json 2025-08-27 18:10:03 -04:00
richonguzman
a046791722 more updates 2025-08-27 17:44:27 -04:00
richonguzman
88eda7b3fd test mqtt 2 2025-08-27 13:29:26 -04:00
richonguzman
03991e245d test MQTT 1 2025-08-27 13:25:11 -04:00
richonguzman
8a02e953c3 testNewTelemetry 2025-08-26 18:33:58 -04:00
richonguzman
4651938cb1 monitorMod1 2025-08-26 17:44:04 -04:00
richonguzman
83a39c2093 syslog decoding update 2025-08-20 13:01:36 -04:00
richonguzman
bc596f099d hidden Syslog enabled 2025-08-20 12:32:05 -04:00
richonguzman
438a6ee536 js update too 2025-08-19 12:52:18 -04:00
richonguzman
206c79f0a9 lora32 test ok 2025-08-19 12:30:40 -04:00
richonguzman
236cfa724e ready for testing 2025-08-17 22:04:22 -04:00
153 changed files with 2723 additions and 1317 deletions

View File

@@ -29,6 +29,8 @@ jobs:
chip: esp32s3 chip: esp32s3
- name: heltec_wireless_stick_lite_v3_display - name: heltec_wireless_stick_lite_v3_display
chip: esp32s3 chip: esp32s3
- name: heltec_wireless_bridge
chip: esp32
- name: ESP32_DIY_LoRa - name: ESP32_DIY_LoRa
chip: esp32 chip: esp32
- name: ESP32_DIY_LoRa_915 - name: ESP32_DIY_LoRa_915
@@ -67,7 +69,11 @@ jobs:
chip: esp32s3 chip: esp32s3
- name: heltec_ht-ct62 - name: heltec_ht-ct62
chip: esp32c3 chip: esp32c3
- name: heltec_wireless_paper - name: heltec_wireless_paper_v1
chip: esp32s3
- name: heltec_wireless_paper_v1_2
chip: esp32s3
- name: heltec_vision_master_e290
chip: esp32s3 chip: esp32s3
- name: OE5HWN_MeshCom - name: OE5HWN_MeshCom
chip: esp32 chip: esp32

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. This firmware is for using ESP32 based boards with LoRa Modules and GPS to live in the APRS world.
![Screenshot](https://github.com/richonguzman/LoRa_APRS_iGate/blob/main/images/iGateOledScreen.jpeg) ![Screenshot](https://github.com/richonguzman/LoRa_APRS_iGate/raw/main/images/iGateOledScreen.jpeg)
__(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__
<br /> <br />
____________________________________________________ ____________________________________________________
## 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 /> # <a href="https://drive.google.com/file/d/1Hff_Szd7ks8RC7_RiV6POxPJlclbO05M/view?usp=sharing" target="_blank">LoRa APRS iGate CA2RXU Firmware Manual</a>
# WEB FLASHER/INSTALLER is <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">here</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 ## SUPPORTED BOARDS (<a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/Supported-Boards-and-Buying-Links" target="_blank">Buying links</a>).
### Buying links --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/108.-Supported-Boards-and-Buying-Links" target="_blank">here</a>.
(NOTE: all boards with 433-868-915 MHz versions) (NOTE: all boards with 433-868-915 MHz versions)
- TTGO Lilygo LoRa32 T3S3 V1.2 and LoRa32 V2.1 (V1.6 is the same). - TTGO Lilygo LoRa32 T3S3 V1.2 and LoRa32 V2.1 (V1.6 is the same).
- TTGO T-Beam V1.0 , V1.1, V1.2 (also variations with SX1262 and SX1268 LoRa Modules). - 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). - 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. - QRP Labs LightGateway 1.0 and Plus 1.0.
- Faketec V3 (NRF52840 + Heltec HTRA62 SX1262) - ESP32 + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
- ESP32 Wroom + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
- ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version. - ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version.
@@ -58,8 +50,18 @@ ____________________________________________________
<br /> <br />
## Timeline (Versions): # Timeline (Versions):
- 2025-12-22 Heltec Wireless Paper V1.2 and VisionMaster E290 Added. Thanks HA5SZI.
- 2025-12-18 TCXO and packet decoding updates.
- 2025-12-01 APRSPacketLib updates, AHT20 sensor added, INA219 support added.
- 2025-10-15 APRS Bridge for TNC added.
- 2025-10-13 Rx and Tx Frequencies are now with fully configurable.
- 2025-10-13 Startup Delay to allow the Router/Modem to start WiFiAP before connecting.
- 2025-10-12 Choose to send Beacon on Rx or Tx frequency.
- 2025-10-11 User defined NTP server and send beacon over MQTT added.
- 2025-10-10 Converted the Wiki into a PDF manual.
- 2025-09-26 Heltec Wireless Bridge support added.
- 2025-09-09 MQTT added (pub+sub), Status defined by Op now and many fixes more.
- 2025-06-20 Digipeaters now with updated EcoMode (Board Sleeps until packet Rx reducing current consumption to almost 10% at idle). - 2025-06-20 Digipeaters now with updated EcoMode (Board Sleeps until packet Rx reducing current consumption to almost 10% at idle).
- 2025-06-20 New Boards Added: Heltec T114 MeshNode, Faketec V3 as Digipeaters and QRP Labs LightGateway Plus 1.0. - 2025-06-20 New Boards Added: Heltec T114 MeshNode, Faketec V3 as Digipeaters and QRP Labs LightGateway Plus 1.0.
- 2025-06-19 DateVersion format Change. Licence changed into GNU GPLv3. - 2025-06-19 DateVersion format Change. Licence changed into GNU GPLv3.

View File

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

View File

@@ -16,30 +16,40 @@
"symbol": "a", "symbol": "a",
"path": "WIDE1-1", "path": "WIDE1-1",
"sendViaAPRSIS": false, "sendViaAPRSIS": false,
"sendViaRF": false "sendViaRF": false,
"beaconFreq": 1,
"statusActive": false,
"statusPacket": "",
"gpsActive": false,
"ambiguityLevel": 0
}, },
"aprs_is": { "aprs_is": {
"active": false, "active": false,
"passcode": "XYZVW",
"server": "rotate.aprs2.net",
"port": 14580,
"filter": "m/10",
"messagesToRF": false, "messagesToRF": false,
"objectsToRF": false "objectsToRF": false,
"server": "rotate.aprs2.net",
"passcode": "XYZVW",
"port": 14580,
"filter": "m/10"
}, },
"personalNote": "",
"blacklist": "",
"digi": { "digi": {
"mode": 0, "mode": 0,
"ecoMode": 0 "ecoMode": 0
}, },
"lora": { "lora": {
"txFreq": 433775000, "rxActive": true,
"rxFreq": 433775000, "rxFreq": 433775000,
"spreadingFactor": 12, "rxSpreadingFactor": 12,
"signalBandwidth": 125000, "rxCodingRate4": 5,
"codingRate4": 5, "rxSignalBandwidth": 125000,
"power": 20,
"txActive": false, "txActive": false,
"rxActive": true "txFreq": 433775000,
"txSpreadingFactor": 12,
"txCodingRate4": 5,
"txSignalBandwidth": 125000,
"power": 20
}, },
"display": { "display": {
"alwaysOn": true, "alwaysOn": true,
@@ -51,11 +61,12 @@
"monitorInternalVoltage": false, "monitorInternalVoltage": false,
"internalSleepVoltage": 2.9, "internalSleepVoltage": 2.9,
"sendExternalVoltage": false, "sendExternalVoltage": false,
"externalVoltagePin": 34,
"monitorExternalVoltage": false, "monitorExternalVoltage": false,
"externalSleepVoltage": 10.9, "externalSleepVoltage": 10.9,
"useExternalI2CSensor": false,
"voltageDividerR1": 100.0, "voltageDividerR1": 100.0,
"voltageDividerR2": 27.0, "voltageDividerR2": 27.0,
"externalVoltagePin": 34,
"sendVoltageAsTelemetry": false "sendVoltageAsTelemetry": false
}, },
"wxsensor": { "wxsensor": {
@@ -66,12 +77,23 @@
"syslog": { "syslog": {
"active": false, "active": false,
"server": "lora.link9.net", "server": "lora.link9.net",
"port": 1514 "port": 1514,
"logBeaconOverTCPIP": false
}, },
"tnc": { "tnc": {
"enableServer": false, "enableServer": false,
"enableSerial": false, "enableSerial": false,
"acceptOwn": false "acceptOwn": false,
"aprsBrigdeActive": false
},
"mqtt": {
"active": false,
"server": "",
"topic": "",
"username": "",
"password": "",
"port": 1883,
"beaconOverMqtt": false
}, },
"ota": { "ota": {
"username": "", "username": "",
@@ -81,19 +103,19 @@
"username": "admin", "username": "admin",
"password": "" "password": ""
}, },
"ntp": {
"gmtCorrection": 0.0
},
"remoteManagement": { "remoteManagement": {
"managers": "", "managers": "",
"rfOnly": true "rfOnly": true
}, },
"ntp": {
"server": "pool.ntp.org",
"gmtCorrection": 0.0
},
"other": { "other": {
"rememberStationTime": 30, "rememberStationTime": 30,
"backupDigiMode": false, "backupDigiMode": false,
"rebootMode": false, "rebootMode": false,
"rebootModeTime": 6 "rebootModeTime": 6,
}, "startupDelay": 0
"personalNote": "", }
"blacklist": ""
} }

File diff suppressed because it is too large Load Diff

View File

@@ -49,27 +49,6 @@ function fetchSettings() {
}); });
} }
const alwaysOnCheckbox = document.querySelector(
'input[name="display.alwaysOn"]'
);
const timeoutInput = document.querySelector('input[name="display.timeout"]');
alwaysOnCheckbox.addEventListener("change", function () {
timeoutInput.disabled = this.checked;
});
// timeoutInput.addEventListener("change", function () {
// alwaysOnCheckbox.disabled = this.value !== "";
// });
const logCheckbox = document.querySelector('input[name="syslog.active"]');
const serverField = document.querySelector('input[name="syslog.server"]');
const portField = document.querySelector('input[name="syslog.port"]');
logCheckbox.addEventListener("change", function () {
serverField.disabled = !this.checked;
portField.disabled = !this.checked;
});
function loadSettings(settings) { function loadSettings(settings) {
currentSettings = settings; currentSettings = settings;
@@ -116,6 +95,7 @@ function loadSettings(settings) {
networksContainer.appendChild(networkElement); networksContainer.appendChild(networkElement);
networkCount++; networkCount++;
}); });
document.getElementById("startupDelay").value = settings.startupDelay;
// APRS-IS // APRS-IS
document.getElementById("aprs_is.active").checked = settings.aprs_is.active; document.getElementById("aprs_is.active").checked = settings.aprs_is.active;
@@ -125,6 +105,13 @@ function loadSettings(settings) {
document.getElementById("aprs_is.port").value = settings.aprs_is.port; document.getElementById("aprs_is.port").value = settings.aprs_is.port;
document.getElementById("aprs_is.filter").value = settings.aprs_is.filter; document.getElementById("aprs_is.filter").value = settings.aprs_is.filter;
document.getElementById("aprs_is.passcode").value = settings.aprs_is.passcode; document.getElementById("aprs_is.passcode").value = settings.aprs_is.passcode;
APRSISCheckbox.checked = settings.aprs_is.active;
APRSISGateMessages.disabled = !APRSISCheckbox.checked;
APRSISGateObjects.disabled = !APRSISCheckbox.checked;
APRSISServer.disabled = !APRSISCheckbox.checked;
APRSISPort.disabled = !APRSISCheckbox.checked;
APRSISPasscode.disabled = !APRSISCheckbox.checked;
APRSISFilter.disabled = !APRSISCheckbox.checked;
// Beacon // Beacon
document.getElementById("beacon.latitude").value = settings.beacon.latitude; document.getElementById("beacon.latitude").value = settings.beacon.latitude;
@@ -132,74 +119,120 @@ function loadSettings(settings) {
document.getElementById("beacon.interval").value = settings.beacon.interval; document.getElementById("beacon.interval").value = settings.beacon.interval;
document.getElementById("other.rememberStationTime").value = settings.other.rememberStationTime; document.getElementById("other.rememberStationTime").value = settings.other.rememberStationTime;
document.getElementById("beacon.sendViaAPRSIS").checked = settings.beacon.sendViaAPRSIS; document.getElementById("beacon.sendViaAPRSIS").checked = settings.beacon.sendViaAPRSIS;
document.getElementById("beacon.sendViaRF").checked = settings.beacon.sendViaRF; document.getElementById("beacon.sendViaRF").checked = settings.beacon.sendViaRF;
document.getElementById("beacon.beaconFreq").value = settings.beacon.beaconFreq;
BeaconingViaRFCheckbox.checked = settings.beacon.sendViaRF;
BeaconingFrequency.disabled = !BeaconingViaRFCheckbox.checked;
document.getElementById("beacon.statusActive").checked = settings.beacon.statusActive;
document.getElementById("beacon.statusPacket").value = settings.beacon.statusPacket;
StatusCheckbox.checked = settings.beacon.statusActive;
StatusPacket.disabled = !StatusCheckbox.checked;
document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive; document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive;
document.getElementById("beacon.gpsAmbiguity").checked = settings.beacon.gpsAmbiguity; document.getElementById("beacon.ambiguityLevel").value = settings.beacon.ambiguityLevel;
// Black List // Black List
document.getElementById("blacklist").value = settings.blacklist; document.getElementById("blacklist").value = settings.blacklist;
// Digi // Digi
document.getElementById("digi.mode").value = settings.digi.mode; document.getElementById("digi.mode").value = settings.digi.mode;
document.getElementById("digi.ecoMode").checked = settings.digi.ecoMode; document.getElementById("digi.ecoMode").value = settings.digi.ecoMode;
// LoRa // 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.rxActive").checked = settings.lora.rxActive;
document.getElementById("lora.spreadingFactor").value = settings.lora.spreadingFactor; document.getElementById("lora.rxFreq").value = settings.lora.rxFreq;
document.getElementById("lora.signalBandwidth").value = settings.lora.signalBandwidth; document.getElementById("lora.rxSpreadingFactor").value = settings.lora.rxSpreadingFactor;
document.getElementById("lora.codingRate4").value = settings.lora.codingRate4; 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; document.getElementById("lora.power").value = settings.lora.power;
// Display // Display
document.getElementById("display.alwaysOn").checked = settings.display.alwaysOn; document.getElementById("display.alwaysOn").checked = settings.display.alwaysOn;
document.getElementById("display.turn180").checked = settings.display.turn180; document.getElementById("display.turn180").checked = settings.display.turn180;
document.getElementById("display.timeout").value = settings.display.timeout; document.getElementById("display.timeout").value = settings.display.timeout;
DisplayAlwaysOnCheckbox.checked = settings.display.alwaysOn;
if (settings.display.alwaysOn) { DisplayTimeout.disabled = DisplayAlwaysOnCheckbox.checked;
timeoutInput.disabled = true;
}
// BATTERY // BATTERY
document.getElementById("battery.sendInternalVoltage").checked = settings.battery.sendInternalVoltage; document.getElementById("battery.sendInternalVoltage").checked = settings.battery.sendInternalVoltage;
document.getElementById("battery.monitorInternalVoltage").checked = settings.battery.monitorInternalVoltage; document.getElementById("battery.monitorInternalVoltage").checked = settings.battery.monitorInternalVoltage;
document.getElementById("battery.internalSleepVoltage").value = settings.battery.internalSleepVoltage.toFixed(1); document.getElementById("battery.internalSleepVoltage").value = settings.battery.internalSleepVoltage.toFixed(1);
MonitorInternalVoltageCheckbox.checked = settings.battery.monitorInternalVoltage;
MonitorInternalSleepVoltage.disabled = !MonitorInternalVoltageCheckbox.checked;
document.getElementById("battery.sendExternalVoltage").checked = settings.battery.sendExternalVoltage; document.getElementById("battery.sendExternalVoltage").checked = settings.battery.sendExternalVoltage;
document.getElementById("battery.useExternalI2CSensor").checked = settings.battery.useExternalI2CSensor;
document.getElementById("battery.externalVoltagePin").value = settings.battery.externalVoltagePin; document.getElementById("battery.externalVoltagePin").value = settings.battery.externalVoltagePin;
document.getElementById("battery.voltageDividerR1").value = settings.battery.voltageDividerR1.toFixed(1); document.getElementById("battery.voltageDividerR1").value = settings.battery.voltageDividerR1.toFixed(1);
document.getElementById("battery.voltageDividerR2").value = settings.battery.voltageDividerR2.toFixed(1); document.getElementById("battery.voltageDividerR2").value = settings.battery.voltageDividerR2.toFixed(1);
SendExternalVoltageCheckbox.checked = settings.battery.sendExternalVoltage;
UseExternalI2CSensorCheckbox.disabled = !SendExternalVoltageCheckbox.checked;
ExternalVoltagePin.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR1.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR2.disabled = !SendExternalVoltageCheckbox.checked || UseExternalI2CSensorCheckbox.checked;
document.getElementById("battery.monitorExternalVoltage").checked = settings.battery.monitorExternalVoltage; document.getElementById("battery.monitorExternalVoltage").checked = settings.battery.monitorExternalVoltage;
document.getElementById("battery.externalSleepVoltage").value = settings.battery.externalSleepVoltage.toFixed(1); document.getElementById("battery.externalSleepVoltage").value = settings.battery.externalSleepVoltage.toFixed(1);
MonitorExternalVoltageCheckbox.checked = settings.battery.monitorExternalVoltage;
MonitorExternalSleepVoltage.disabled = !MonitorExternalVoltageCheckbox.checked;
document.getElementById("battery.sendVoltageAsTelemetry").checked = settings.battery.sendVoltageAsTelemetry; document.getElementById("battery.sendVoltageAsTelemetry").checked = settings.battery.sendVoltageAsTelemetry;
// TELEMETRY WX SENSOR // TELEMETRY WX SENSOR
document.getElementById("wxsensor.active").checked = settings.wxsensor.active; document.getElementById("wxsensor.active").checked = settings.wxsensor.active;
document.getElementById("wxsensor.heightCorrection").value = settings.wxsensor.heightCorrection; document.getElementById("wxsensor.heightCorrection").value = settings.wxsensor.heightCorrection;
document.getElementById("wxsensor.temperatureCorrection").value = settings.wxsensor.temperatureCorrection.toFixed(1); document.getElementById("wxsensor.temperatureCorrection").value = settings.wxsensor.temperatureCorrection.toFixed(1);
TelemetryCheckbox.checked = settings.wxsensor.active;
TelemetryHeightCorrection.disabled = !TelemetryCheckbox.checked;
TelemetryTempCorrection.disabled = !TelemetryCheckbox.checked;
// SYSLOG // SYSLOG
document.getElementById("syslog.active").checked = settings.syslog.active; document.getElementById("syslog.active").checked = settings.syslog.active;
document.getElementById("syslog.server").value = settings.syslog.server; document.getElementById("syslog.server").value = settings.syslog.server;
document.getElementById("syslog.port").value = settings.syslog.port; document.getElementById("syslog.port").value = settings.syslog.port;
document.getElementById("syslog.logBeaconOverTCPIP").checked = settings.syslog.logBeaconOverTCPIP;
if (settings.syslog.active) { SyslogCheckbox.checked = settings.syslog.active;
serverField.disabled = false; SyslogServer.disabled = !SyslogCheckbox.checked;
portField.disabled = false; SyslogPort.disabled = !SyslogCheckbox.checked;
} SyslogBeaconOverTCPIP.disabled = !SyslogCheckbox.checked;
// TNC // TNC
if (settings.tnc) { if (settings.tnc) {
document.getElementById("tnc.enableServer").checked = settings.tnc.enableServer; document.getElementById("tnc.enableServer").checked = settings.tnc.enableServer;
document.getElementById("tnc.enableSerial").checked = settings.tnc.enableSerial; document.getElementById("tnc.enableSerial").checked = settings.tnc.enableSerial;
document.getElementById("tnc.acceptOwn").checked = settings.tnc.acceptOwn; document.getElementById("tnc.acceptOwn").checked = settings.tnc.acceptOwn;
document.getElementById("tnc.aprsBridgeActive").checked = settings.tnc.aprsBridgeActive;
} }
// MQTT
document.getElementById("mqtt.active").checked = settings.mqtt.active;
document.getElementById("mqtt.server").value = settings.mqtt.server;
document.getElementById("mqtt.topic").value = settings.mqtt.topic;
document.getElementById("mqtt.username").value = settings.mqtt.username;
document.getElementById("mqtt.password").value = settings.mqtt.password;
document.getElementById("mqtt.port").value = settings.mqtt.port;
document.getElementById("mqtt.beaconOverMqtt").checked = settings.mqtt.beaconOverMqtt;
MqttCheckbox.checked = settings.mqtt.active;
MqttServer.disabled = !MqttCheckbox.checked;
MqttTopic.disabled = !MqttCheckbox.checked;
MqttUsername.disabled = !MqttCheckbox.checked;
MqttPassword.disabled = !MqttCheckbox.checked;
MqttPort.disabled = !MqttCheckbox.checked;
MqttBeaconOverMqtt.disabled = !MqttCheckbox.checked;
// Reboot // Reboot
document.getElementById("other.rebootMode").checked = settings.other.rebootMode; document.getElementById("other.rebootMode").checked = settings.other.rebootMode;
document.getElementById("other.rebootModeTime").value = settings.other.rebootModeTime; document.getElementById("other.rebootModeTime").value = settings.other.rebootModeTime;
RebootModeCheckbox.checked = settings.other.rebootMode;
RebootModeTime.disabled = !RebootModeCheckbox.check;
// WiFi Auto AP // WiFi Auto AP
document.getElementById("wifi.autoAP.password").value = settings.wifi.autoAP.password; document.getElementById("wifi.autoAP.password").value = settings.wifi.autoAP.password;
@@ -213,20 +246,22 @@ function loadSettings(settings) {
document.getElementById("webadmin.active").checked = settings.webadmin.active; document.getElementById("webadmin.active").checked = settings.webadmin.active;
document.getElementById("webadmin.username").value = settings.webadmin.username; document.getElementById("webadmin.username").value = settings.webadmin.username;
document.getElementById("webadmin.password").value = settings.webadmin.password; document.getElementById("webadmin.password").value = settings.webadmin.password;
WebadminCheckbox.checked = settings.webadmin.active;
// NTP WebadminUsername.disabled = !WebadminCheckbox.check;
document.getElementById("ntp.gmtCorrection").value = settings.ntp.gmtCorrection; WebadminPassword.disabled = !WebadminCheckbox.check;
// Experimental
document.getElementById("other.backupDigiMode").checked = settings.other.backupDigiMode;
// Management over APRS // Management over APRS
document.getElementById("remoteManagement.managers").value = settings.remoteManagement.managers; document.getElementById("remoteManagement.managers").value = settings.remoteManagement.managers;
document.getElementById("remoteManagement.rfOnly").checked = settings.remoteManagement.rfOnly; document.getElementById("remoteManagement.rfOnly").checked = settings.remoteManagement.rfOnly;
// NTP
document.getElementById("ntp.server").value = settings.ntp.server;
document.getElementById("ntp.gmtCorrection").value = settings.ntp.gmtCorrection;
// Experimental
document.getElementById("other.backupDigiMode").checked = settings.other.backupDigiMode;
updateImage(); updateImage();
refreshSpeedStandard();
toggleFields();
} }
function showToast(message) { function showToast(message) {
@@ -253,8 +288,6 @@ document.getElementById('reboot').addEventListener('click', function (e) {
showToast("Your device will be rebooted in a while"); showToast("Your device will be rebooted in a while");
}); });
const wxsensorCheckbox = document.querySelector("input[name='wxsensor.active']");
function updateImage() { function updateImage() {
const value = document.getElementById("beacon.overlay").value + document.getElementById("beacon.symbol").value; const value = document.getElementById("beacon.overlay").value + document.getElementById("beacon.symbol").value;
@@ -280,70 +313,127 @@ function updateImage() {
} }
} }
function toggleFields() { // Beaconing Switches
const sendExternalVoltageCheckbox = document.querySelector( const BeaconingViaRFCheckbox = document.querySelector('input[name="beacon.sendViaRF"]');
'input[name="battery.sendExternalVoltage"]' const BeaconingFrequency = document.querySelector('select[name="beacon.beaconFreq"]');
); BeaconingViaRFCheckbox.addEventListener("change", function() {
const externalVoltagePinInput = document.querySelector( BeaconingFrequency.disabled = !this.checked;
'input[name="battery.externalVoltagePin"]'
);
externalVoltagePinInput.disabled = !sendExternalVoltageCheckbox.checked;
voltageDividerR1.disabled = !sendExternalVoltageCheckbox.checked;
voltageDividerR2.disabled = !sendExternalVoltageCheckbox.checked;
const WebadminCheckbox = document.querySelector(
'input[name="webadmin.active"]'
);
const WebadminUsername = document.querySelector(
'input[name="webadmin.username"]'
);
const WebadminPassword = document.querySelector(
'input[name="webadmin.password"]'
);
WebadminUsername.disabled = !WebadminCheckbox.checked;
WebadminPassword.disabled = !WebadminCheckbox.checked;
}
const sendExternalVoltageCheckbox = document.querySelector(
'input[name="battery.sendExternalVoltage"]'
);
const externalVoltagePinInput = document.querySelector(
'input[name="battery.externalVoltagePin"]'
);
const voltageDividerR1 = document.querySelector(
'input[name="battery.voltageDividerR1"]'
);
const voltageDividerR2 = document.querySelector(
'input[name="battery.voltageDividerR2"]'
);
sendExternalVoltageCheckbox.addEventListener("change", function () {
externalVoltagePinInput.disabled = !this.checked;
voltageDividerR1.disabled = !this.checked;
voltageDividerR2.disabled = !this.checked;
}); });
const WebadminCheckbox = document.querySelector( // Status Switch
'input[name="webadmin.active"]' const StatusCheckbox = document.querySelector('input[name="beacon.statusActive"]');
); const StatusPacket = document.querySelector('input[name="beacon.statusPacket"]');
StatusCheckbox.addEventListener("change", function() {
StatusPacket.disabled = !this.checked;
});
const WebadminUsername = document.querySelector( // APRS-IS Switches
'input[name="webadmin.username"]' const APRSISCheckbox = document.querySelector('input[name="aprs_is.active"]');
); const APRSISGateMessages = document.querySelector('input[name="aprs_is.messagesToRF"]');
const APRSISGateObjects = document.querySelector('input[name="aprs_is.objectsToRF"]');
const APRSISServer = document.querySelector('input[name="aprs_is.server"]');
const APRSISPort = document.querySelector('input[name="aprs_is.port"]');
const APRSISPasscode = document.querySelector('input[name="aprs_is.passcode"]');
const APRSISFilter = document.querySelector('input[name="aprs_is.filter"]');
APRSISCheckbox.addEventListener("change", function() {
APRSISGateMessages.disabled = !this.checked;
APRSISGateObjects.disabled = !this.checked;
APRSISServer.disabled = !this.checked;
APRSISPort.disabled = !this.checked;
APRSISPasscode.disabled = !this.checked;
APRSISFilter.disabled = !this.checked;
});
const WebadminPassword = document.querySelector( // Display Switches
'input[name="webadmin.password"]' const DisplayAlwaysOnCheckbox = document.querySelector('input[name="display.alwaysOn"]');
); const DisplayTimeout = document.querySelector('input[name="display.timeout"]');
DisplayAlwaysOnCheckbox.addEventListener("change", function () {
DisplayTimeout.disabled = this.checked;
});
// Battery Switches
const MonitorInternalVoltageCheckbox = document.querySelector('input[name="battery.monitorInternalVoltage"]');
const MonitorInternalSleepVoltage = document.querySelector('input[name="battery.internalSleepVoltage"]');
MonitorInternalVoltageCheckbox.addEventListener("change", function () {
MonitorInternalSleepVoltage.disabled = !this.checked;
});
const MonitorExternalVoltageCheckbox = document.querySelector('input[name="battery.monitorExternalVoltage"]');
const MonitorExternalSleepVoltage = document.querySelector('input[name="battery.externalSleepVoltage"]');
MonitorExternalVoltageCheckbox.addEventListener("change", function () {
MonitorExternalSleepVoltage.disabled = !this.checked;
});
const SendExternalVoltageCheckbox = document.querySelector('input[name="battery.sendExternalVoltage"]');
const UseExternalI2CSensorCheckbox = document.querySelector('input[name="battery.useExternalI2CSensor"]');
const ExternalVoltagePin = document.querySelector('input[name="battery.externalVoltagePin"]');
const ExternalVoltageDividerR1 = document.querySelector('input[name="battery.voltageDividerR1"]');
const ExternalVoltageDividerR2 = document.querySelector('input[name="battery.voltageDividerR2"]');
SendExternalVoltageCheckbox.addEventListener("change", function () {
UseExternalI2CSensorCheckbox.disabled = !this.checked;
ExternalVoltagePin.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR1.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
ExternalVoltageDividerR2.disabled = !this.checked || UseExternalI2CSensorCheckbox.checked;
});
UseExternalI2CSensorCheckbox.addEventListener("change", function () {
ExternalVoltagePin.disabled = this.checked;
ExternalVoltageDividerR1.disabled = this.checked;
ExternalVoltageDividerR2.disabled = this.checked;
});
// Telemetry Switches
const TelemetryCheckbox = document.querySelector('input[name="wxsensor.active"]');
const TelemetryHeightCorrection = document.querySelector('input[name="wxsensor.heightCorrection"]');
const TelemetryTempCorrection = document.querySelector('input[name="wxsensor.temperatureCorrection"]');
TelemetryCheckbox.addEventListener("change", function () {
TelemetryHeightCorrection.disabled = !this.checked;
TelemetryTempCorrection.disabled = !this.checked;
});
// Syslog Switches
const SyslogCheckbox = document.querySelector('input[name="syslog.active"]');
const SyslogServer = document.querySelector('input[name="syslog.server"]');
const SyslogPort = document.querySelector('input[name="syslog.port"]');
const SyslogBeaconOverTCPIP = document.querySelector('input[name="syslog.logBeaconOverTCPIP"]');
SyslogCheckbox.addEventListener("change", function () {
SyslogServer.disabled = !this.checked;
SyslogPort.disabled = !this.checked;
SyslogBeaconOverTCPIP.disabled = !this.checked
});
// MQTT Switches
const MqttCheckbox = document.querySelector('input[name="mqtt.active"]');
const MqttServer = document.querySelector('input[name="mqtt.server"]');
const MqttTopic = document.querySelector('input[name="mqtt.topic"]');
const MqttUsername = document.querySelector('input[name="mqtt.username"]');
const MqttPassword = document.querySelector('input[name="mqtt.password"]');
const MqttPort = document.querySelector('input[name="mqtt.port"]');
const MqttBeaconOverMqtt = document.querySelector('input[name="mqtt.beaconOverMqtt"]');
MqttCheckbox.addEventListener("change", function () {
MqttServer.disabled = !this.checked;
MqttTopic.disabled = !this.checked;
MqttUsername.disabled = !this.checked;
MqttPassword.disabled = !this.checked;
MqttPort.disabled = !this.checked;
MqttBeaconOverMqtt.disabled = !this.checked;
});
// Reboot Switches
const RebootModeCheckbox = document.querySelector('input[name="other.rebootMode"]');
const RebootModeTime = document.querySelector('input[name="other.rebootModeTime"]');
RebootModeCheckbox.addEventListener("change", function() {
RebootModeTime.disabled = !this.checked;
});
// Web Admin Switches
const WebadminCheckbox = document.querySelector('input[name="webadmin.active"]');
const WebadminUsername = document.querySelector('input[name="webadmin.username"]');
const WebadminPassword = document.querySelector('input[name="webadmin.password"]');
WebadminCheckbox.addEventListener("change", function () { WebadminCheckbox.addEventListener("change", function () {
WebadminUsername.disabled = !this.checked; WebadminUsername.disabled = !this.checked;
WebadminPassword.disabled = !this.checked; WebadminPassword.disabled = !this.checked;
}); });
document.querySelector(".new button").addEventListener("click", function () { document.querySelector(".new button").addEventListener("click", function () {
const networksContainer = document.querySelector(".list-networks"); const networksContainer = document.querySelector(".list-networks");
@@ -391,65 +481,6 @@ document
updateImage(); updateImage();
}); });
const speedStandards = {
300: [125, 5, 12],
244: [125, 6, 12],
209: [125, 7, 12],
183: [125, 8, 12],
610: [125, 8, 10],
1200: [125, 7, 9],
};
function refreshSpeedStandard() {
const bw = Number(document.getElementById("lora.signalBandwidth").value);
const cr4 = Number(document.getElementById("lora.codingRate4").value);
const sf = Number(document.getElementById("lora.spreadingFactor").value);
let found = false;
for (const speed in speedStandards) {
const standard = speedStandards[speed];
if (standard[0] !== bw / 1000) continue;
if (standard[1] !== cr4) continue;
if (standard[2] !== sf) continue;
document.getElementById("action.speed").value = speed;
found = true;
break;
}
if (!found) {
document.getElementById("action.speed").value = "";
}
}
document
.getElementById("lora.signalBandwidth")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.codingRate4")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.spreadingFactor")
.addEventListener("focusout", refreshSpeedStandard);
document.getElementById("action.speed").addEventListener("change", function () {
const speed = document.getElementById("action.speed").value;
if (speed !== "") {
const value = speedStandards[Number(speed)];
const bw = value[0];
const cr4 = value[1];
const sf = value[2];
document.getElementById("lora.signalBandwidth").value = bw * 1000;
document.getElementById("lora.codingRate4").value = cr4;
document.getElementById("lora.spreadingFactor").value = sf;
}
});
const form = document.querySelector("form"); const form = document.querySelector("form");

Binary file not shown.

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

@@ -32,9 +32,6 @@ namespace BATTERY_Utils {
float checkExternalVoltage(); float checkExternalVoltage();
void startupBatteryHealth(); void startupBatteryHealth();
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType);
String generateEncodedTelemetry();
} }
#endif #endif

View File

@@ -45,10 +45,13 @@ public:
String overlay; String overlay;
String symbol; String symbol;
String path; String path;
bool sendViaRF;
bool sendViaAPRSIS; bool sendViaAPRSIS;
bool sendViaRF;
int beaconFreq;
bool statusActive;
String statusPacket;
bool gpsActive; bool gpsActive;
bool gpsAmbiguity; int ambiguityLevel;
}; };
class APRS_IS { class APRS_IS {
@@ -65,18 +68,21 @@ public:
class DIGI { class DIGI {
public: public:
int mode; int mode;
int ecoMode; // 0 = Not Active | 1 = Ultra EcoMode | 2 = Not Active (WiFi OFF, Serial ON) int ecoMode; // 0 = Not Active | 1 = Ultra EcoMode | 2 = Not Active (WiFi OFF, Serial ON)
}; };
class LoraModule { class LoraModule {
public: public:
long txFreq;
long rxFreq;
bool txActive;
bool rxActive; bool rxActive;
int spreadingFactor; long rxFreq;
long signalBandwidth; int rxSpreadingFactor;
int codingRate4; int rxCodingRate4;
long rxSignalBandwidth;
bool txActive;
long txFreq;
int txSpreadingFactor;
int txCodingRate4;
long txSignalBandwidth;
int power; int power;
}; };
@@ -96,6 +102,7 @@ public:
int externalVoltagePin; int externalVoltagePin;
bool monitorExternalVoltage; bool monitorExternalVoltage;
float externalSleepVoltage; float externalSleepVoltage;
bool useExternalI2CSensor;
float voltageDividerR1; float voltageDividerR1;
float voltageDividerR2; float voltageDividerR2;
bool sendVoltageAsTelemetry; bool sendVoltageAsTelemetry;
@@ -113,6 +120,7 @@ public:
bool active; bool active;
String server; String server;
int port; int port;
bool logBeaconOverTCPIP;
}; };
class TNC { class TNC {
@@ -120,6 +128,7 @@ public:
bool enableServer; bool enableServer;
bool enableSerial; bool enableSerial;
bool acceptOwn; bool acceptOwn;
bool aprsBridgeActive;
}; };
class OTA { class OTA {
@@ -137,6 +146,7 @@ public:
class NTP { class NTP {
public: public:
String server;
float gmtCorrection; float gmtCorrection;
}; };
@@ -146,6 +156,17 @@ public:
bool rfOnly; bool rfOnly;
}; };
class MQTT {
public:
bool active;
String server;
String topic;
String username;
String password;
int port;
bool beaconOverMqtt;
};
class Configuration { class Configuration {
public: public:
String callsign; String callsign;
@@ -153,6 +174,7 @@ public:
bool backupDigiMode; bool backupDigiMode;
bool rebootMode; bool rebootMode;
int rebootModeTime; int rebootModeTime;
int startupDelay;
String personalNote; String personalNote;
String blacklist; String blacklist;
std::vector<WiFi_AP> wifiAPs; std::vector<WiFi_AP> wifiAPs;
@@ -170,9 +192,10 @@ public:
WEBADMIN webadmin; WEBADMIN webadmin;
NTP ntp; NTP ntp;
REMOTE_MANAGEMENT remoteManagement; REMOTE_MANAGEMENT remoteManagement;
MQTT mqtt;
void init(); void setDefaultValues();
void writeFile(); bool writeFile();
Configuration(); Configuration();
private: private:

34
include/mqtt_utils.h Normal file
View File

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

View File

@@ -41,7 +41,6 @@ namespace POWER_Utils {
double getBatteryVoltage(); double getBatteryVoltage();
bool isBatteryConnected(); bool isBatteryConnected();
void activateMeasurement();
void activateGPS(); void activateGPS();
void deactivateGPS(); void deactivateGPS();
void activateLoRa(); void activateLoRa();

View File

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

33
include/telemetry_utils.h Normal file
View File

@@ -0,0 +1,33 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
* This file is part of LoRa APRS iGate.
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef TELEMETRY_UTILS_H_
#define TELEMETRY_UTILS_H_
#include <Arduino.h>
namespace TELEMETRY_Utils {
void sendEquationsUnitsParameters();
String generateEncodedTelemetryBytes(float value, bool counterBytes, byte telemetryType);
String generateEncodedTelemetry();
}
#endif

View File

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

View File

@@ -35,7 +35,7 @@ namespace Utils {
void processStatus(); void processStatus();
String getLocalIP(); String getLocalIP();
void setupDisplay(); void setupDisplay();
void activeStations(); void showActiveStations();
void checkBeaconInterval(); void checkBeaconInterval();
void checkDisplayInterval(); void checkDisplayInterval();
void validateFreqs(); void validateFreqs();
@@ -46,6 +46,7 @@ namespace Utils {
void checkRebootTime(); void checkRebootTime();
void checkSleepByLowBatteryVoltage(uint8_t mode); void checkSleepByLowBatteryVoltage(uint8_t mode);
bool checkValidCallsign(const String& callsign); bool checkValidCallsign(const String& callsign);
void startupDelay();
} }

View File

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

View File

@@ -33,7 +33,7 @@
╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝
Ricardo Guzman - CA2RXU Ricardo Guzman - CA2RXU
https://github.com/richonguzman/LoRa_APRS_iGate https://github.com/richonguzman/LoRa_APRS_iGate
(donations : http://paypal.me/richonguzman) (donations : http://paypal.me/richonguzman)
___________________________________________________________________*/ ___________________________________________________________________*/
@@ -51,6 +51,7 @@ ___________________________________________________________________*/
#include "syslog_utils.h" #include "syslog_utils.h"
#include "power_utils.h" #include "power_utils.h"
#include "sleep_utils.h" #include "sleep_utils.h"
#include "mqtt_utils.h"
#include "lora_utils.h" #include "lora_utils.h"
#include "wifi_utils.h" #include "wifi_utils.h"
#include "digi_utils.h" #include "digi_utils.h"
@@ -66,9 +67,11 @@ ___________________________________________________________________*/
#endif #endif
String versionDate = "2025-08-04"; String versionDate = "2025-12-28";
String versionNumber = "3.1.6.3";
Configuration Config; Configuration Config;
WiFiClient espClient; WiFiClient aprsIsClient;
WiFiClient mqttClient;
#ifdef HAS_GPS #ifdef HAS_GPS
HardwareSerial gpsSerial(1); HardwareSerial gpsSerial(1);
TinyGPSPlus gps; TinyGPSPlus gps;
@@ -94,7 +97,6 @@ bool modemLoggedToAPRSIS = false;
std::vector<ReceivedPacket> receivedPackets; std::vector<ReceivedPacket> receivedPackets;
String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine; String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine;
//#define STARTUP_DELAY 5 //min
void setup() { void setup() {
@@ -105,12 +107,7 @@ void setup() {
Utils::validateFreqs(); Utils::validateFreqs();
GPS_Utils::setup(); GPS_Utils::setup();
STATION_Utils::loadBlacklistAndManagers(); STATION_Utils::loadBlacklistAndManagers();
Utils::startupDelay();
#ifdef STARTUP_DELAY // (TEST) just to wait for WiFi init of Routers
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(STARTUP_DELAY * 60 * 1000);
#endif
SLEEP_Utils::setup(); SLEEP_Utils::setup();
WIFI_Utils::setup(); WIFI_Utils::setup();
NTP_Utils::setup(); NTP_Utils::setup();
@@ -118,6 +115,7 @@ void setup() {
WX_Utils::setup(); WX_Utils::setup();
WEB_Utils::setup(); WEB_Utils::setup();
TNC_Utils::setup(); TNC_Utils::setup();
MQTT_Utils::setup();
#ifdef HAS_A7670 #ifdef HAS_A7670
A7670_Utils::setup(); A7670_Utils::setup();
#endif #endif
@@ -166,11 +164,13 @@ void loop() {
if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect(); if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect();
#else #else
WIFI_Utils::checkWiFi(); WIFI_Utils::checkWiFi();
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) APRS_IS_Utils::connect(); if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !aprsIsClient.connected()) APRS_IS_Utils::connect();
if (Config.mqtt.active && (WiFi.status() == WL_CONNECTED) && !mqttClient.connected()) MQTT_Utils::connect();
#endif #endif
NTP_Utils::update(); NTP_Utils::update();
TNC_Utils::loop(); TNC_Utils::loop();
MQTT_Utils::loop();
Utils::checkDisplayInterval(); Utils::checkDisplayInterval();
Utils::checkBeaconInterval(); Utils::checkBeaconInterval();
@@ -183,7 +183,7 @@ void loop() {
} }
if (packet != "") { if (packet != "") {
if (Config.aprs_is.active) { // If APRSIS enabled if (Config.aprs_is.active) { // If APRSIS enabled
APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS
} }
@@ -192,17 +192,12 @@ void loop() {
DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi
} }
if (Config.tnc.enableServer) { // If TNC server enabled if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet, true); // Send received packet to TNC KISS
TNC_Utils::sendToClients(packet); // 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.tnc.enableSerial) { // If Serial KISS enabled
TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
}
} }
if (Config.aprs_is.active) { if (Config.aprs_is.active) APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
}
STATION_Utils::processOutputPacketBuffer(); STATION_Utils::processOutputPacketBuffer();

View File

@@ -16,6 +16,7 @@
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <APRSPacketLib.h>
#include <WiFi.h> #include <WiFi.h>
#include "configuration.h" #include "configuration.h"
#include "aprs_is_utils.h" #include "aprs_is_utils.h"
@@ -25,12 +26,13 @@
#include "query_utils.h" #include "query_utils.h"
#include "A7670_utils.h" #include "A7670_utils.h"
#include "digi_utils.h" #include "digi_utils.h"
#include "tnc_utils.h"
#include "display.h" #include "display.h"
#include "utils.h" #include "utils.h"
extern Configuration Config; extern Configuration Config;
extern WiFiClient espClient; extern WiFiClient aprsIsClient;
extern uint32_t lastScreenOn; extern uint32_t lastScreenOn;
extern String firstLine; extern String firstLine;
extern String secondLine; extern String secondLine;
@@ -41,6 +43,7 @@ extern String sixthLine;
extern String seventhLine; extern String seventhLine;
extern bool modemLoggedToAPRSIS; extern bool modemLoggedToAPRSIS;
extern bool backUpDigiMode; extern bool backUpDigiMode;
extern String versionNumber;
uint32_t lastRxTime = millis(); uint32_t lastRxTime = millis();
bool passcodeValid = false; bool passcodeValid = false;
@@ -53,17 +56,17 @@ bool passcodeValid = false;
namespace APRS_IS_Utils { namespace APRS_IS_Utils {
void upload(const String& line) { void upload(const String& line) {
espClient.print(line + "\r\n"); aprsIsClient.print(line + "\r\n");
} }
void connect() { void connect() {
Serial.print("Connecting to APRS-IS ... "); Serial.print("Connecting to APRS-IS ... ");
uint8_t count = 0; uint8_t count = 0;
while (!espClient.connect(Config.aprs_is.server.c_str(), Config.aprs_is.port) && count < 20) { while (!aprsIsClient.connect(Config.aprs_is.server.c_str(), Config.aprs_is.port) && count < 20) {
Serial.println("Didn't connect with server..."); Serial.println("Didn't connect with server...");
delay(1000); delay(1000);
espClient.stop(); aprsIsClient.stop();
espClient.flush(); aprsIsClient.flush();
Serial.println("Run client.stop"); Serial.println("Run client.stop");
Serial.println("Trying to connect with Server: " + String(Config.aprs_is.server) + " AprsServerPort: " + String(Config.aprs_is.port)); Serial.println("Trying to connect with Server: " + String(Config.aprs_is.server) + " AprsServerPort: " + String(Config.aprs_is.port));
count++; count++;
@@ -78,7 +81,9 @@ namespace APRS_IS_Utils {
aprsAuth += Config.callsign; aprsAuth += Config.callsign;
aprsAuth += " pass "; aprsAuth += " pass ";
aprsAuth += Config.aprs_is.passcode; aprsAuth += Config.aprs_is.passcode;
aprsAuth += " vers CA2RXU_iGate 3.0 filter "; aprsAuth += " vers CA2RXUiGate ";
aprsAuth += versionNumber;
aprsAuth += " filter ";
aprsAuth += Config.aprs_is.filter; aprsAuth += Config.aprs_is.filter;
upload(aprsAuth); upload(aprsAuth);
} }
@@ -110,7 +115,7 @@ namespace APRS_IS_Utils {
aprsisState = "--"; aprsisState = "--";
} }
#else #else
if (espClient.connected()) { if (aprsIsClient.connected()) {
aprsisState = "OK"; aprsisState = "OK";
} else { } else {
aprsisState = "--"; aprsisState = "--";
@@ -192,7 +197,7 @@ namespace APRS_IS_Utils {
} }
void processLoRaPacket(const String& packet) { void processLoRaPacket(const String& packet) {
if (passcodeValid && (espClient.connected() || modemLoggedToAPRSIS)) { if (passcodeValid && (aprsIsClient.connected() || modemLoggedToAPRSIS)) {
if (packet.indexOf("NOGATE") == -1 && packet.indexOf("RFONLY") == -1) { if (packet.indexOf("NOGATE") == -1 && packet.indexOf("RFONLY") == -1) {
int firstColonIndex = packet.indexOf(":"); int firstColonIndex = packet.indexOf(":");
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) { if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) {
@@ -232,12 +237,7 @@ namespace APRS_IS_Utils {
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType) { String buildPacketToTx(const String& aprsisPacket, uint8_t packetType) {
String packet = aprsisPacket; String packet = aprsisPacket;
packet.trim(); packet.trim();
String outputPacket = Config.callsign; String outputPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
outputPacket += ">APLRG1";
if (Config.beacon.path != "") {
outputPacket += ",";
outputPacket += Config.beacon.path;
}
outputPacket += ":}"; outputPacket += ":}";
outputPacket += packet.substring(0, packet.indexOf(",")); // Callsign>Tocall outputPacket += packet.substring(0, packet.indexOf(",")); // Callsign>Tocall
outputPacket.concat(",TCPIP,"); outputPacket.concat(",TCPIP,");
@@ -278,8 +278,9 @@ namespace APRS_IS_Utils {
if (!passcodeValid && packet.indexOf(Config.callsign) != -1) { if (!passcodeValid && packet.indexOf(Config.callsign) != -1) {
if (packet.indexOf("unverified") != -1 ) { if (packet.indexOf("unverified") != -1 ) {
Serial.println("\n****APRS PASSCODE NOT VALID****\n"); Serial.println("\n****APRS PASSCODE NOT VALID****\n");
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 0); displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 3000);
while (1) {}; aprsIsClient.stop();
Config.aprs_is.active = false;
} else if (packet.indexOf("verified") != -1 ) { } else if (packet.indexOf("verified") != -1 ) {
passcodeValid = true; passcodeValid = true;
} }
@@ -364,6 +365,10 @@ namespace APRS_IS_Utils {
Serial.println(" ---> Rejected (Time): No Tx"); Serial.println(" ---> Rejected (Time): No Tx");
} }
} }
if (Config.tnc.aprsBridgeActive) {
if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS
if (Config.tnc.enableSerial) TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
}
} }
} }
@@ -371,9 +376,9 @@ namespace APRS_IS_Utils {
#ifdef HAS_A7670 #ifdef HAS_A7670
A7670_Utils::listenAPRSIS(); A7670_Utils::listenAPRSIS();
#else #else
if (espClient.connected()) { if (aprsIsClient.connected()) {
if (espClient.available()) { if (aprsIsClient.available()) {
String aprsisPacket = espClient.readStringUntil('\r'); String aprsisPacket = aprsIsClient.readStringUntil('\r');
aprsisPacket.trim(); // Serial.println(aprsisPacket); aprsisPacket.trim(); // Serial.println(aprsisPacket);
processAPRSISPacket(aprsisPacket); processAPRSISPacket(aprsisPacket);
lastRxTime = millis(); lastRxTime = millis();
@@ -383,7 +388,7 @@ namespace APRS_IS_Utils {
} }
void firstConnection() { void firstConnection() {
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) { if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !aprsIsClient.connected()) {
connect(); connect();
while (!passcodeValid) { while (!passcodeValid) {
listenAPRSIS(); listenAPRSIS();

View File

@@ -16,7 +16,7 @@
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <Arduino.h> #include <Adafruit_INA219.h>
#include "battery_utils.h" #include "battery_utils.h"
#include "configuration.h" #include "configuration.h"
#include "board_pinout.h" #include "board_pinout.h"
@@ -37,8 +37,10 @@ float multiplyCorrection = 0.035;
float voltageDividerTransformation = 0.0; float voltageDividerTransformation = 0.0;
int telemetryCounter = random(1,999); uint8_t externalI2CSensorAddress = 0x00;
int externalI2CSensorType = 0; // 0 = None | 1 = INA219
Adafruit_INA219 ina219;
#ifdef HAS_ADC_CALIBRATION #ifdef HAS_ADC_CALIBRATION
@@ -100,6 +102,30 @@ namespace BATTERY_Utils {
#endif #endif
} }
void getI2CVoltageSensorAddress() {
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();
#else
Wire.beginTransmission(addr);
err = Wire.endTransmission();
#endif
delay(5);
if (err == 0) {
if (addr == 0x40) { // INA219
externalI2CSensorAddress = addr;
}
}
}
}
bool detectINA219(uint8_t addr) {
ina219 = Adafruit_INA219(addr);
return ina219.begin();
}
void setup() { void setup() {
if ((Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) && Config.battery.voltageDividerR2 != 0) voltageDividerTransformation = (Config.battery.voltageDividerR1 + Config.battery.voltageDividerR2) / Config.battery.voltageDividerR2; if ((Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) && Config.battery.voltageDividerR2 != 0) voltageDividerTransformation = (Config.battery.voltageDividerR1 + Config.battery.voltageDividerR2) / Config.battery.voltageDividerR2;
@@ -109,6 +135,14 @@ namespace BATTERY_Utils {
adcCalibration(); adcCalibration();
} }
#endif #endif
getI2CVoltageSensorAddress();
if (externalI2CSensorAddress != 0x00) {
if (detectINA219(externalI2CSensorAddress)) {
Serial.println("INA219 sensor found");
externalI2CSensorType = 1; // INA219
}
}
} }
float checkInternalVoltage() { float checkInternalVoltage() {
@@ -149,7 +183,7 @@ namespace BATTERY_Utils {
#ifdef ADC_CTRL #ifdef ADC_CTRL
POWER_Utils::adc_ctrl_OFF(); POWER_Utils::adc_ctrl_OFF();
#ifdef HELTEC_WP #ifdef HELTEC_WP_V1
double inputDivider = (1.0 / (10.0 + 10.0)) * 10.0; // The voltage divider is a 10k + 10k resistor in series double inputDivider = (1.0 / (10.0 + 10.0)) * 10.0; // The voltage divider is a 10k + 10k resistor in series
#else #else
double inputDivider = (1.0 / (390.0 + 100.0)) * 100.0; // The voltage divider is a 390k + 100k resistor in series, 100k on the low side. double inputDivider = (1.0 / (390.0 + 100.0)) * 100.0; // The voltage divider is a 390k + 100k resistor in series, 100k on the low side.
@@ -179,37 +213,49 @@ namespace BATTERY_Utils {
} }
float checkExternalVoltage() { float checkExternalVoltage() {
int sample; if (externalI2CSensorType == 0) {
int sampleSum = 0; int sample;
for (int i = 0; i < 100; i++) { int sampleSum = 0;
#ifdef HAS_ADC_CALIBRATION for (int i = 0; i < 100; i++) {
if (calibrationEnable){ #ifdef HAS_ADC_CALIBRATION
sample = adc1_get_raw(ExternalVoltage_ADC_Channel); if (calibrationEnable){
} else { sample = adc1_get_raw(ExternalVoltage_ADC_Channel);
} else {
sample = analogRead(Config.battery.externalVoltagePin);
}
#else
sample = analogRead(Config.battery.externalVoltagePin); 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 #else
sample = analogRead(Config.battery.externalVoltagePin); extVoltage = ((((sampleSum/100.0)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
#endif #endif
sampleSum += sample;
delayMicroseconds(50); return extVoltage; // raw voltage without mapping
}
float extVoltage; // return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
#ifdef HAS_ADC_CALIBRATION } else if (externalI2CSensorType == 1) { // INA219
if (calibrationEnable){ int sampleSum = 0;
extVoltage = esp_adc_cal_raw_to_voltage(sampleSum / 100, &adc_chars) * voltageDividerTransformation; // in mV for (int i = 0; i < 100; i++) {
extVoltage /= 1000; sampleSum += ina219.getBusVoltage_V() * 1000.0;
} else { delayMicroseconds(50);
extVoltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
} }
#else float extVoltage = sampleSum/100.0;
extVoltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection; return extVoltage/1000.0;
#endif } else {
return 0.0;
return extVoltage; // raw voltage without mapping }
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
} }
void startupBatteryHealth() { void startupBatteryHealth() {
@@ -218,7 +264,7 @@ namespace BATTERY_Utils {
shouldSleepLowVoltage = true; shouldSleepLowVoltage = true;
} }
#endif #endif
#ifndef HELTEC_WP #ifndef HELTEC_WP_V1
if (Config.battery.monitorExternalVoltage && checkExternalVoltage() < Config.battery.externalSleepVoltage + 0.1) { if (Config.battery.monitorExternalVoltage && checkExternalVoltage() < Config.battery.externalSleepVoltage + 0.1) {
shouldSleepLowVoltage = true; shouldSleepLowVoltage = true;
} }
@@ -228,43 +274,4 @@ namespace BATTERY_Utils {
} }
} }
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType) { // 0 = internal battery(0-4,2V) , 1 = external battery(0-15V)
String encodedBytes;
int tempValue;
if (firstBytes) {
tempValue = value;
} else {
switch (voltageType) {
case 0:
tempValue = value * 100; // Internal voltage calculation
break;
case 1:
tempValue = (value * 100) / 2; // External voltage calculation
break;
default:
tempValue = value;
break;
}
}
int firstByte = tempValue / 91;
tempValue -= firstByte * 91;
encodedBytes = char(firstByte + 33);
encodedBytes += char(tempValue + 33);
return encodedBytes;
}
String generateEncodedTelemetry() {
String telemetry = "|";
telemetry += generateEncodedTelemetryBytes(telemetryCounter, true, 0);
telemetryCounter++;
if (telemetryCounter == 1000) telemetryCounter = 0;
if (Config.battery.sendInternalVoltage) telemetry += generateEncodedTelemetryBytes(checkInternalVoltage(), false, 0);
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(checkExternalVoltage(), false, 1);
telemetry += "|";
return telemetry;
}
} }

View File

@@ -26,127 +26,159 @@
bool shouldSleepStop = true; bool shouldSleepStop = true;
void Configuration::writeFile() { bool Configuration::writeFile() {
Serial.println("Saving config..."); Serial.println("Saving configuration...");
StaticJsonDocument<2560> data; StaticJsonDocument<3584> data;
File configFile = SPIFFS.open("/igate_conf.json", "w"); File configFile = SPIFFS.open("/igate_conf.json", "w");
if (wifiAPs[0].ssid != "") { // We don't want to save Auto AP empty SSID if (!configFile) {
for (int i = 0; i < wifiAPs.size(); i++) { Serial.println("Error: Could not open config file for writing");
data["wifi"]["AP"][i]["ssid"] = wifiAPs[i].ssid; return false;
data["wifi"]["AP"][i]["password"] = wifiAPs[i].password;
}
} }
try {
data["wifi"]["autoAP"]["password"] = wifiAutoAP.password; if (wifiAPs[0].ssid != "") { // We don't want to save Auto AP empty SSID
data["wifi"]["autoAP"]["timeout"] = wifiAutoAP.timeout; for (int i = 0; i < wifiAPs.size(); i++) {
data["wifi"]["AP"][i]["ssid"] = wifiAPs[i].ssid;
data["wifi"]["AP"][i]["password"] = wifiAPs[i].password;
}
}
data["callsign"] = callsign; data["other"]["startupDelay"] = startupDelay;
data["aprs_is"]["active"] = aprs_is.active; data["wifi"]["autoAP"]["password"] = wifiAutoAP.password;
data["aprs_is"]["passcode"] = aprs_is.passcode; data["wifi"]["autoAP"]["timeout"] = wifiAutoAP.timeout;
data["aprs_is"]["server"] = aprs_is.server;
data["aprs_is"]["port"] = aprs_is.port;
data["aprs_is"]["filter"] = aprs_is.filter;
data["aprs_is"]["messagesToRF"] = aprs_is.messagesToRF;
data["aprs_is"]["objectsToRF"] = aprs_is.objectsToRF;
data["beacon"]["comment"] = beacon.comment; callsign.trim();
data["beacon"]["interval"] = beacon.interval; callsign.toUpperCase();
data["beacon"]["latitude"] = beacon.latitude; data["callsign"] = callsign;
data["beacon"]["longitude"] = beacon.longitude;
data["beacon"]["overlay"] = beacon.overlay;
data["beacon"]["symbol"] = beacon.symbol;
data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
data["beacon"]["path"] = beacon.path;
data["beacon"]["gpsActive"] = beacon.gpsActive; data["aprs_is"]["active"] = aprs_is.active;
data["beacon"]["gpsAmbiguity"] = beacon.gpsAmbiguity; data["aprs_is"]["passcode"] = aprs_is.passcode;
data["aprs_is"]["server"] = aprs_is.server;
data["aprs_is"]["port"] = aprs_is.port;
data["aprs_is"]["filter"] = aprs_is.filter;
data["aprs_is"]["messagesToRF"] = aprs_is.messagesToRF;
data["aprs_is"]["objectsToRF"] = aprs_is.objectsToRF;
data["digi"]["mode"] = digi.mode; data["beacon"]["comment"] = beacon.comment;
data["digi"]["ecoMode"] = digi.ecoMode; data["beacon"]["interval"] = beacon.interval;
#if defined(HAS_A7670) data["beacon"]["latitude"] = beacon.latitude;
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2; data["beacon"]["longitude"] = beacon.longitude;
#endif data["beacon"]["overlay"] = beacon.overlay;
data["beacon"]["symbol"] = beacon.symbol;
data["beacon"]["path"] = beacon.path;
data["lora"]["rxFreq"] = loramodule.rxFreq; data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
data["lora"]["txFreq"] = loramodule.txFreq; data["beacon"]["sendViaRF"] = beacon.sendViaRF;
data["lora"]["spreadingFactor"] = loramodule.spreadingFactor; data["beacon"]["beaconFreq"] = beacon.beaconFreq;
data["lora"]["signalBandwidth"] = loramodule.signalBandwidth;
data["lora"]["codingRate4"] = loramodule.codingRate4;
data["lora"]["power"] = loramodule.power;
data["lora"]["txActive"] = loramodule.txActive;
data["lora"]["rxActive"] = loramodule.rxActive;
data["display"]["alwaysOn"] = display.alwaysOn; data["beacon"]["statusActive"] = beacon.statusActive;
data["display"]["timeout"] = display.timeout; data["beacon"]["statusPacket"] = beacon.statusPacket;
data["display"]["turn180"] = display.turn180;
data["battery"]["sendInternalVoltage"] = battery.sendInternalVoltage; data["beacon"]["gpsActive"] = beacon.gpsActive;
data["battery"]["monitorInternalVoltage"] = battery.monitorInternalVoltage; data["beacon"]["ambiguityLevel"] = beacon.ambiguityLevel;
data["battery"]["internalSleepVoltage"] = battery.internalSleepVoltage;
data["battery"]["sendExternalVoltage"] = battery.sendExternalVoltage; data["personalNote"] = personalNote;
data["battery"]["externalVoltagePin"] = battery.externalVoltagePin;
data["battery"]["monitorExternalVoltage"] = battery.monitorExternalVoltage;
data["battery"]["externalSleepVoltage"] = battery.externalSleepVoltage;
data["battery"]["voltageDividerR1"] = battery.voltageDividerR1;
data["battery"]["voltageDividerR2"] = battery.voltageDividerR2;
data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry; data["blacklist"] = blacklist;
data["wxsensor"]["active"] = wxsensor.active; data["digi"]["mode"] = digi.mode;
data["wxsensor"]["heightCorrection"] = wxsensor.heightCorrection; data["digi"]["ecoMode"] = digi.ecoMode;
data["wxsensor"]["temperatureCorrection"] = wxsensor.temperatureCorrection; #if defined(HAS_A7670)
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
#endif
data["syslog"]["active"] = syslog.active; data["lora"]["rxActive"] = loramodule.rxActive;
data["syslog"]["server"] = syslog.server; data["lora"]["rxFreq"] = loramodule.rxFreq;
data["syslog"]["port"] = syslog.port; data["lora"]["rxSpreadingFactor"] = loramodule.rxSpreadingFactor;
data["lora"]["rxCodingRate4"] = loramodule.rxCodingRate4;
data["lora"]["rxSignalBandwidth"] = loramodule.rxSignalBandwidth;
data["lora"]["txActive"] = loramodule.txActive;
data["lora"]["txFreq"] = loramodule.txFreq;
data["lora"]["txSpreadingFactor"] = loramodule.txSpreadingFactor;
data["lora"]["txCodingRate4"] = loramodule.txCodingRate4;
data["lora"]["txSignalBandwidth"] = loramodule.txSignalBandwidth;
data["lora"]["power"] = loramodule.power;
data["tnc"]["enableServer"] = tnc.enableServer; data["display"]["alwaysOn"] = display.alwaysOn;
data["tnc"]["enableSerial"] = tnc.enableSerial; data["display"]["timeout"] = display.timeout;
data["tnc"]["acceptOwn"] = tnc.acceptOwn; data["display"]["turn180"] = display.turn180;
data["other"]["rebootMode"] = rebootMode; data["battery"]["sendInternalVoltage"] = battery.sendInternalVoltage;
data["other"]["rebootModeTime"] = rebootModeTime; data["battery"]["monitorInternalVoltage"] = battery.monitorInternalVoltage;
data["battery"]["internalSleepVoltage"] = battery.internalSleepVoltage;
data["ota"]["username"] = ota.username; data["battery"]["sendExternalVoltage"] = battery.sendExternalVoltage;
data["ota"]["password"] = ota.password; 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["other"]["rememberStationTime"] = rememberStationTime; data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry;
data["other"]["backupDigiMode"] = backupDigiMode; data["wxsensor"]["active"] = wxsensor.active;
data["wxsensor"]["heightCorrection"] = wxsensor.heightCorrection;
data["wxsensor"]["temperatureCorrection"] = wxsensor.temperatureCorrection;
data["personalNote"] = personalNote; data["syslog"]["active"] = syslog.active;
data["syslog"]["server"] = syslog.server;
data["syslog"]["port"] = syslog.port;
data["syslog"]["logBeaconOverTCPIP"] = syslog.logBeaconOverTCPIP;
data["blacklist"] = blacklist; data["tnc"]["enableServer"] = tnc.enableServer;
data["tnc"]["enableSerial"] = tnc.enableSerial;
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
data["tnc"]["aprsBridgeActive"] = tnc.aprsBridgeActive;
data["webadmin"]["active"] = webadmin.active; data["mqtt"]["active"] = mqtt.active;
data["webadmin"]["username"] = webadmin.username; data["mqtt"]["server"] = mqtt.server;
data["webadmin"]["password"] = webadmin.password; data["mqtt"]["topic"] = mqtt.topic;
data["mqtt"]["username"] = mqtt.username;
data["mqtt"]["password"] = mqtt.password;
data["mqtt"]["port"] = mqtt.port;
data["mqtt"]["beaconOverMqtt"] = mqtt.beaconOverMqtt;
data["ntp"]["gmtCorrection"] = ntp.gmtCorrection; data["ota"]["username"] = ota.username;
data["ota"]["password"] = ota.password;
data["remoteManagement"]["managers"] = remoteManagement.managers; data["webadmin"]["active"] = webadmin.active;
data["remoteManagement"]["rfOnly"] = remoteManagement.rfOnly; data["webadmin"]["username"] = webadmin.username;
data["webadmin"]["password"] = webadmin.password;
serializeJson(data, configFile); data["remoteManagement"]["managers"] = remoteManagement.managers;
data["remoteManagement"]["rfOnly"] = remoteManagement.rfOnly;
configFile.close(); data["ntp"]["server"] = ntp.server;
data["ntp"]["gmtCorrection"] = ntp.gmtCorrection;
Serial.println("Config saved"); data["other"]["rebootMode"] = rebootMode;
delay(200); data["other"]["rebootModeTime"] = rebootModeTime;
data["other"]["rememberStationTime"] = rememberStationTime;
data["other"]["backupDigiMode"] = backupDigiMode;
serializeJson(data, configFile);
configFile.close();
return true;
} catch (...) {
Serial.println("Error: Exception occurred while saving config");
configFile.close();
return false;
}
} }
bool Configuration::readFile() { bool Configuration::readFile() {
Serial.println("Reading config.."); Serial.println("Reading config..");
File configFile = SPIFFS.open("/igate_conf.json", "r"); File configFile = SPIFFS.open("/igate_conf.json", "r");
if (configFile) { if (configFile) {
StaticJsonDocument<2560> data; bool needsRewrite = false;
StaticJsonDocument<3584> data;
DeserializationError error = deserializeJson(data, configFile); DeserializationError error = deserializeJson(data, configFile);
if (error) { if (error) {
@@ -162,12 +194,46 @@ bool Configuration::readFile() {
wifiAPs.push_back(wifiap); wifiAPs.push_back(wifiap);
} }
if (!data["other"].containsKey("startupDelay")) needsRewrite = true;
startupDelay = data["other"]["startupDelay"] | 0;
if (!data["wifi"]["autoAP"].containsKey("password") ||
!data["wifi"]["autoAP"].containsKey("timeout")) needsRewrite = true;
wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890"; wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890";
wifiAutoAP.timeout = data["wifi"]["autoAP"]["timeout"] | 10; wifiAutoAP.timeout = data["wifi"]["autoAP"]["timeout"] | 10;
if (!data.containsKey("callsign")) needsRewrite = true;
callsign = data["callsign"] | "NOCALL-10"; callsign = data["callsign"] | "NOCALL-10";
rememberStationTime = data["other"]["rememberStationTime"] | 30;
if (!data["aprs_is"].containsKey("active") ||
!data["aprs_is"].containsKey("passcode") ||
!data["aprs_is"].containsKey("server") ||
!data["aprs_is"].containsKey("port") ||
!data["aprs_is"].containsKey("filter") ||
!data["aprs_is"].containsKey("messagesToRF") ||
!data["aprs_is"].containsKey("objectsToRF")) needsRewrite = true;
aprs_is.active = data["aprs_is"]["active"] | false;
aprs_is.passcode = data["aprs_is"]["passcode"] | "XYZWV";
aprs_is.server = data["aprs_is"]["server"] | "rotate.aprs2.net";
aprs_is.port = data["aprs_is"]["port"] | 14580;
aprs_is.filter = data["aprs_is"]["filter"] | "m/10";
aprs_is.messagesToRF = data["aprs_is"]["messagesToRF"] | false;
aprs_is.objectsToRF = data["aprs_is"]["objectsToRF"] | false;
if (!data["beacon"].containsKey("latitude") ||
!data["beacon"].containsKey("longitude") ||
!data["beacon"].containsKey("comment") ||
!data["beacon"].containsKey("interval") ||
!data["beacon"].containsKey("overlay") ||
!data["beacon"].containsKey("symbol") ||
!data["beacon"].containsKey("path") ||
!data["beacon"].containsKey("sendViaAPRSIS") ||
!data["beacon"].containsKey("sendViaRF") ||
!data["beacon"].containsKey("beaconFreq") ||
!data["beacon"].containsKey("statusActive") ||
!data["beacon"].containsKey("statusPacket") ||
!data["beacon"].containsKey("gpsActive") ||
!data["beacon"].containsKey("ambiguityLevel")) needsRewrite = true;
beacon.latitude = data["beacon"]["latitude"] | 0.0; beacon.latitude = data["beacon"]["latitude"] | 0.0;
beacon.longitude = data["beacon"]["longitude"] | 0.0; beacon.longitude = data["beacon"]["longitude"] | 0.0;
beacon.comment = data["beacon"]["comment"] | "LoRa APRS"; beacon.comment = data["beacon"]["comment"] | "LoRa APRS";
@@ -177,84 +243,156 @@ bool Configuration::readFile() {
beacon.path = data["beacon"]["path"] | "WIDE1-1"; beacon.path = data["beacon"]["path"] | "WIDE1-1";
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false; beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false;
beacon.sendViaRF = data["beacon"]["sendViaRF"] | false; beacon.sendViaRF = data["beacon"]["sendViaRF"] | false;
beacon.beaconFreq = data["beacon"]["beaconFreq"] | 1;
beacon.statusActive = data["beacon"]["statusActive"] | false;
beacon.statusPacket = data["beacon"]["statusPacket"] | "";
beacon.gpsActive = data["beacon"]["gpsActive"] | false; beacon.gpsActive = data["beacon"]["gpsActive"] | false;
beacon.gpsAmbiguity = data["beacon"]["gpsAmbiguity"] | false; beacon.ambiguityLevel = data["beacon"]["ambiguityLevel"] | 0;
aprs_is.active = data["aprs_is"]["active"] | false; if (!data.containsKey("personalNote")) needsRewrite = true;
aprs_is.passcode = data["aprs_is"]["passcode"] | "XYZWV"; personalNote = data["personalNote"] | "personal note here";
aprs_is.server = data["aprs_is"]["server"] | "rotate.aprs2.net";
aprs_is.port = data["aprs_is"]["port"] | 14580;
aprs_is.filter = data["aprs_is"]["filter"] | "m/10";
aprs_is.messagesToRF = data["aprs_is"]["messagesToRF"] | false;
aprs_is.objectsToRF = data["aprs_is"]["objectsToRF"] | false;
if (!data.containsKey("blacklist")) needsRewrite = true;
blacklist = data["blacklist"] | "station callsign";
if (!data["digi"].containsKey("mode") ||
!data["digi"].containsKey("ecoMode")) needsRewrite = true;
digi.mode = data["digi"]["mode"] | 0; digi.mode = data["digi"]["mode"] | 0;
digi.ecoMode = data["digi"]["ecoMode"] | 0; digi.ecoMode = data["digi"]["ecoMode"] | 0;
if (digi.ecoMode == 1) shouldSleepStop = false; if (digi.ecoMode == 1) shouldSleepStop = false;
#if defined(HAS_A7670) #if defined(HAS_A7670)
if (digi.ecoMode == 1) digi.ecoMode = 2; if (digi.ecoMode == 1) digi.ecoMode = 2;
#endif #endif
loramodule.txFreq = data["lora"]["txFreq"] | 433775000; if (!data["lora"].containsKey("rxActive") ||
!data["lora"].containsKey("rxFreq") ||
!data["lora"].containsKey("rxSpreadingFactor") ||
!data["lora"].containsKey("rxCodingRate4") ||
!data["lora"].containsKey("rxSignalBandwidth") ||
!data["lora"].containsKey("txActive") ||
!data["lora"].containsKey("txFreq") ||
!data["lora"].containsKey("txSpreadingFactor") ||
!data["lora"].containsKey("txCodingRate4") ||
!data["lora"].containsKey("txSignalBandwidth") ||
!data["lora"].containsKey("power")) needsRewrite = true;
loramodule.rxActive = data["lora"]["rxActive"] | true;
loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000; loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000;
loramodule.spreadingFactor = data["lora"]["spreadingFactor"] | 12; loramodule.rxSpreadingFactor = data["lora"]["rxSpreadingFactor"] | 12;
loramodule.signalBandwidth = data["lora"]["signalBandwidth"] | 125000; loramodule.rxCodingRate4 = data["lora"]["rxCodingRate4"] | 5;
loramodule.codingRate4 = data["lora"]["codingRate4"] | 5; loramodule.rxSignalBandwidth = data["lora"]["rxSignalBandwidth"] | 125000;
loramodule.power = data["lora"]["power"] | 20;
loramodule.txActive = data["lora"]["txActive"] | false; loramodule.txActive = data["lora"]["txActive"] | false;
loramodule.rxActive = data["lora"]["rxActive"] | false; loramodule.txFreq = data["lora"]["txFreq"] | 433775000;
loramodule.txSpreadingFactor = data["lora"]["txSpreadingFactor"] | 12;
display.alwaysOn = data["display"]["alwaysOn"] | true; 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;
#ifdef HAS_EPAPER
display.alwaysOn = true;
#else
display.alwaysOn = data["display"]["alwaysOn"] | true;
#endif
display.timeout = data["display"]["timeout"] | 4; display.timeout = data["display"]["timeout"] | 4;
display.turn180 = data["display"]["turn180"] | false; display.turn180 = data["display"]["turn180"] | false;
if (!data["battery"].containsKey("sendInternalVoltage") ||
!data["battery"].containsKey("monitorInternalVoltage") ||
!data["battery"].containsKey("internalSleepVoltage") ||
!data["battery"].containsKey("sendExternalVoltage") ||
!data["battery"].containsKey("monitorExternalVoltage") ||
!data["battery"].containsKey("externalSleepVoltage") ||
!data["battery"].containsKey("useExternalI2CSensor") ||
!data["battery"].containsKey("voltageDividerR1") ||
!data["battery"].containsKey("voltageDividerR2") ||
!data["battery"].containsKey("externalVoltagePin") ||
!data["battery"].containsKey("sendVoltageAsTelemetry")) needsRewrite = true;
battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"] | false; battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"] | false;
battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false; battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false;
battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 2.9; battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 2.9;
battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false; battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false;
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
battery.monitorExternalVoltage = data["battery"]["monitorExternalVoltage"] | false; battery.monitorExternalVoltage = data["battery"]["monitorExternalVoltage"] | false;
battery.externalSleepVoltage = data["battery"]["externalSleepVoltage"] | 10.9; battery.externalSleepVoltage = data["battery"]["externalSleepVoltage"] | 10.9;
battery.useExternalI2CSensor = data["battery"]["useExternalI2CSensor"] | false;
battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0; battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0;
battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0; battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0;
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | false; battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | false;
if (!data["wxsensor"].containsKey("active") ||
!data["wxsensor"].containsKey("heightCorrection") ||
!data["wxsensor"].containsKey("temperatureCorrection")) needsRewrite = true;
wxsensor.active = data["wxsensor"]["active"] | false; wxsensor.active = data["wxsensor"]["active"] | false;
wxsensor.heightCorrection = data["wxsensor"]["heightCorrection"] | 0; wxsensor.heightCorrection = data["wxsensor"]["heightCorrection"] | 0;
wxsensor.temperatureCorrection = data["wxsensor"]["temperatureCorrection"] | 0.0; wxsensor.temperatureCorrection = data["wxsensor"]["temperatureCorrection"] | 0.0;
if (!data["syslog"].containsKey("active") ||
!data["syslog"].containsKey("server") ||
!data["syslog"].containsKey("port") ||
!data["syslog"].containsKey("logBeaconOverTCPIP")) needsRewrite = true;
syslog.active = data["syslog"]["active"] | false; syslog.active = data["syslog"]["active"] | false;
syslog.server = data["syslog"]["server"] | "lora.link9.net"; syslog.server = data["syslog"]["server"] | "lora.link9.net";
syslog.port = data["syslog"]["port"] | 1514; syslog.port = data["syslog"]["port"] | 1514;
syslog.logBeaconOverTCPIP = data["syslog"]["logBeaconOverTCPIP"] | false;
if (!data["tnc"].containsKey("enableServer") ||
!data["tnc"].containsKey("enableSerial") ||
!data["tnc"].containsKey("acceptOwn") ||
!data["tnc"].containsKey("aprsBridgeActive")) needsRewrite = true;
tnc.enableServer = data["tnc"]["enableServer"] | false; tnc.enableServer = data["tnc"]["enableServer"] | false;
tnc.enableSerial = data["tnc"]["enableSerial"] | false; tnc.enableSerial = data["tnc"]["enableSerial"] | false;
tnc.acceptOwn = data["tnc"]["acceptOwn"] | false; tnc.acceptOwn = data["tnc"]["acceptOwn"] | false;
tnc.aprsBridgeActive = data["tnc"]["aprsBridgeActive"] | false;
if (!data["mqtt"].containsKey("active") ||
!data["mqtt"].containsKey("server") ||
!data["mqtt"].containsKey("topic") ||
!data["mqtt"].containsKey("username") ||
!data["mqtt"].containsKey("password") ||
!data["mqtt"].containsKey("port") ||
!data["mqtt"].containsKey("beaconOverMqtt")) needsRewrite = true;
mqtt.active = data["mqtt"]["active"] | false;
mqtt.server = data["mqtt"]["server"] | "";
mqtt.topic = data["mqtt"]["topic"] | "aprs-igate";
mqtt.username = data["mqtt"]["username"] | "";
mqtt.password = data["mqtt"]["password"] | "";
mqtt.port = data["mqtt"]["port"] | 1883;
mqtt.beaconOverMqtt = data["mqtt"]["beaconOverMqtt"] | false;
if (!data["ota"].containsKey("username") ||
!data["ota"].containsKey("password")) needsRewrite = true;
ota.username = data["ota"]["username"] | ""; ota.username = data["ota"]["username"] | "";
ota.password = data["ota"]["password"] | ""; ota.password = data["ota"]["password"] | "";
if (!data["webadmin"].containsKey("active") ||
!data["webadmin"].containsKey("username") ||
!data["webadmin"].containsKey("password")) needsRewrite = true;
webadmin.active = data["webadmin"]["active"] | false; webadmin.active = data["webadmin"]["active"] | false;
webadmin.username = data["webadmin"]["username"] | "admin"; webadmin.username = data["webadmin"]["username"] | "admin";
webadmin.password = data["webadmin"]["password"] | ""; webadmin.password = data["webadmin"]["password"] | "";
if (!data["remoteManagement"].containsKey("managers") ||
!data["remoteManagement"].containsKey("rfOnly")) needsRewrite = true;
remoteManagement.managers = data["remoteManagement"]["managers"] | "";
remoteManagement.rfOnly = data["remoteManagement"]["rfOnly"] | true;
if (!data["ntp"].containsKey("server") ||
!data["ntp"].containsKey("gmtCorrection")) needsRewrite = true;
ntp.server = data["ntp"]["server"] | "pool.ntp.org";
ntp.gmtCorrection = data["ntp"]["gmtCorrection"] | 0.0; ntp.gmtCorrection = data["ntp"]["gmtCorrection"] | 0.0;
backupDigiMode = data["other"]["backupDigiMode"] | false; if (!data["other"].containsKey("rebootMode") ||
!data["other"].containsKey("rebootModeTime")) needsRewrite = true;
rebootMode = data["other"]["rebootMode"] | false; rebootMode = data["other"]["rebootMode"] | false;
rebootModeTime = data["other"]["rebootModeTime"] | 6; rebootModeTime = data["other"]["rebootModeTime"] | 6;
personalNote = data["personalNote"] | "personal note here"; if (!data["other"].containsKey("rememberStationTime")) needsRewrite = true;
rememberStationTime = data["other"]["rememberStationTime"] | 30;
blacklist = data["blacklist"] | "station callsign"; if (!data["other"].containsKey("backupDigiMode")) needsRewrite = true;
backupDigiMode = data["other"]["backupDigiMode"] | false;
remoteManagement.managers = data["remoteManagement"]["managers"] | "";
remoteManagement.rfOnly = data["remoteManagement"]["rfOnly"] | true;
if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP
WiFi_AP wifiap; WiFi_AP wifiap;
@@ -264,6 +402,13 @@ bool Configuration::readFile() {
wifiAPs.push_back(wifiap); wifiAPs.push_back(wifiap);
} }
configFile.close(); configFile.close();
if (needsRewrite) {
Serial.println("Config JSON incomplete, rewriting...");
writeFile();
delay(1000);
ESP.restart();
}
Serial.println("Config read successfuly"); Serial.println("Config read successfuly");
return true; return true;
} else { } else {
@@ -272,7 +417,7 @@ bool Configuration::readFile() {
} }
} }
void Configuration::init() { void Configuration::setDefaultValues() {
WiFi_AP wifiap; WiFi_AP wifiap;
wifiap.ssid = ""; wifiap.ssid = "";
@@ -280,31 +425,13 @@ void Configuration::init() {
wifiAPs.push_back(wifiap); wifiAPs.push_back(wifiap);
startupDelay = 0;
wifiAutoAP.password = "1234567890"; wifiAutoAP.password = "1234567890";
wifiAutoAP.timeout = 10; wifiAutoAP.timeout = 10;
callsign = "N0CALL-10"; callsign = "N0CALL-10";
beacon.comment = "LoRa APRS";
beacon.latitude = 0.0;
beacon.longitude = 0.0;
beacon.interval = 15;
beacon.overlay = "L";
beacon.symbol = "a";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.path = "WIDE1-1";
beacon.gpsActive = false;
beacon.gpsAmbiguity = false;
digi.mode = 0;
digi.ecoMode = 0;
tnc.enableServer = false;
tnc.enableSerial = false;
tnc.acceptOwn = false;
aprs_is.active = false; aprs_is.active = false;
aprs_is.passcode = "XYZVW"; aprs_is.passcode = "XYZVW";
aprs_is.server = "rotate.aprs2.net"; aprs_is.server = "rotate.aprs2.net";
@@ -313,64 +440,104 @@ void Configuration::init() {
aprs_is.messagesToRF = false; aprs_is.messagesToRF = false;
aprs_is.objectsToRF = false; aprs_is.objectsToRF = false;
loramodule.txFreq = 433775000; beacon.comment = "LoRa APRS";
loramodule.rxFreq = 433775000; beacon.latitude = 0.0;
loramodule.spreadingFactor = 12; beacon.longitude = 0.0;
loramodule.signalBandwidth = 125000; beacon.interval = 15;
loramodule.codingRate4 = 5; beacon.overlay = "L";
loramodule.power = 20; beacon.symbol = "a";
loramodule.txActive = false; beacon.path = "WIDE1-1";
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
beacon.beaconFreq = 1;
beacon.statusActive = false;
beacon.statusPacket = "";
beacon.gpsActive = false;
beacon.ambiguityLevel = 0;
personalNote = "";
blacklist = "";
digi.mode = 0;
digi.ecoMode = 0;
loramodule.rxActive = true; 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.alwaysOn = true;
display.timeout = 4; display.timeout = 4;
display.turn180 = false; display.turn180 = false;
syslog.active = false;
syslog.server = "lora.link9.net";
syslog.port = 1514;
wxsensor.active = false;
wxsensor.heightCorrection = 0;
wxsensor.temperatureCorrection = 0.0;
ota.username = "";
ota.password = "";
rememberStationTime = 30;
battery.sendInternalVoltage = false; battery.sendInternalVoltage = false;
battery.monitorInternalVoltage = false; battery.monitorInternalVoltage = false;
battery.internalSleepVoltage = 2.9; battery.internalSleepVoltage = 2.9;
battery.sendExternalVoltage = false; battery.sendExternalVoltage = false;
battery.externalVoltagePin = 34;
battery.monitorExternalVoltage = false; battery.monitorExternalVoltage = false;
battery.externalSleepVoltage = 10.9; battery.externalSleepVoltage = 10.9;
battery.useExternalI2CSensor = false;
battery.voltageDividerR1 = 100.0; battery.voltageDividerR1 = 100.0;
battery.voltageDividerR2 = 27.0; battery.voltageDividerR2 = 27.0;
battery.externalVoltagePin = 34;
battery.sendVoltageAsTelemetry = false; battery.sendVoltageAsTelemetry = false;
backupDigiMode = false; wxsensor.active = false;
wxsensor.heightCorrection = 0;
wxsensor.temperatureCorrection = 0.0;
rebootMode = false; syslog.active = false;
rebootModeTime = 0; syslog.server = "lora.link9.net";
syslog.port = 1514;
syslog.logBeaconOverTCPIP = false;
personalNote = ""; tnc.enableServer = false;
tnc.enableSerial = false;
tnc.acceptOwn = false;
tnc.aprsBridgeActive = false;
blacklist = ""; mqtt.active = false;
mqtt.server = "";
mqtt.topic = "aprs-igate";
mqtt.username = "";
mqtt.password = "";
mqtt.port = 1883;
mqtt.beaconOverMqtt = false;
ota.username = "";
ota.password = "";
webadmin.active = false; webadmin.active = false;
webadmin.username = "admin"; webadmin.username = "admin";
webadmin.password = ""; webadmin.password = "";
ntp.gmtCorrection = 0.0;
remoteManagement.managers = ""; remoteManagement.managers = "";
remoteManagement.rfOnly = true; remoteManagement.rfOnly = true;
Serial.println("All is Written!"); ntp.server = "pool.ntp.org";
ntp.gmtCorrection = 0.0;
rebootMode = false;
rebootModeTime = 0;
rememberStationTime = 30;
backupDigiMode = false;
Serial.println("New Data Created... All is Written!");
} }
Configuration::Configuration() { Configuration::Configuration() {
@@ -383,8 +550,9 @@ Configuration::Configuration() {
bool exists = SPIFFS.exists("/igate_conf.json"); bool exists = SPIFFS.exists("/igate_conf.json");
if (!exists) { if (!exists) {
init(); setDefaultValues();
writeFile(); writeFile();
delay(1000);
ESP.restart(); ESP.restart();
} }

View File

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

View File

@@ -16,6 +16,7 @@
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <APRSPacketLib.h>
#include <TinyGPS++.h> #include <TinyGPS++.h>
#include <WiFi.h> #include <WiFi.h>
#include "configuration.h" #include "configuration.h"
@@ -31,7 +32,6 @@
#endif #endif
extern Configuration Config; extern Configuration Config;
extern WiFiClient espClient;
extern HardwareSerial gpsSerial; extern HardwareSerial gpsSerial;
extern TinyGPSPlus gps; extern TinyGPSPlus gps;
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket; String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
@@ -43,70 +43,6 @@ namespace GPS_Utils {
return iGateLoRaBeaconPacket; return iGateLoRaBeaconPacket;
} }
char *ax25_base91enc(char *s, uint8_t n, uint32_t v) {
for(s += n, *s = '\0'; n; n--) {
*(--s) = v % 91 + 33;
v /= 91;
}
return(s);
}
float roundToTwoDecimals(float degrees) {
return round(degrees * 100) / 100;
}
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
String encodedData = overlay;
uint32_t aprs_lat, aprs_lon;
float processedLatitude = latitude;
float processedLongitude = longitude;
if (Config.beacon.gpsActive && Config.beacon.gpsAmbiguity) {
processedLatitude = roundToTwoDecimals(latitude);
processedLongitude = roundToTwoDecimals(longitude);
}
aprs_lat = 900000000 - processedLatitude * 10000000;
aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
aprs_lon = 900000000 + processedLongitude * 10000000 / 2;
aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
String Ns, Ew, helper;
if(processedLatitude < 0) { Ns = "S"; } else { Ns = "N"; }
if(processedLatitude < 0) { processedLatitude = -processedLatitude; }
if(processedLongitude < 0) { Ew = "W"; } else { Ew = "E"; }
if(processedLongitude < 0) { processedLongitude = -processedLongitude; }
char helper_base91[] = {"0000\0"};
int i;
ax25_base91enc(helper_base91, 4, aprs_lat);
for (i = 0; i < 4; i++) {
encodedData += helper_base91[i];
}
ax25_base91enc(helper_base91, 4, aprs_lon);
for (i = 0; i < 4; i++) {
encodedData += helper_base91[i];
}
encodedData += symbol;
encodedData += " ";
encodedData += "\x47";
return encodedData;
}
void generateBeaconFirstPart() {
String beaconPacket = Config.callsign;
beaconPacket += ">APLRG1";
if (Config.beacon.path.indexOf("WIDE") == 0) {
beaconPacket += ",";
beaconPacket += Config.beacon.path;
}
iGateBeaconPacket = beaconPacket;
iGateBeaconPacket += ",qAC:!";
iGateLoRaBeaconPacket = beaconPacket;
iGateLoRaBeaconPacket += ":!";
}
void generateBeacons() { void generateBeacons() {
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) { if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000); displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
@@ -117,9 +53,17 @@ namespace GPS_Utils {
Config.digi.mode = 0; Config.digi.mode = 0;
Config.backupDigiMode = false; Config.backupDigiMode = false;
} }
generateBeaconFirstPart(); String beaconPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol); String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(Config.beacon.latitude, Config.beacon.longitude, 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
iGateBeaconPacket = beaconPacket;
iGateBeaconPacket += ",qAC:!";
iGateBeaconPacket += Config.beacon.overlay;
iGateBeaconPacket += encodedGPS; iGateBeaconPacket += encodedGPS;
iGateLoRaBeaconPacket = beaconPacket;
iGateLoRaBeaconPacket += ":=";
iGateLoRaBeaconPacket += Config.beacon.overlay;
iGateLoRaBeaconPacket += encodedGPS; iGateLoRaBeaconPacket += encodedGPS;
} }
@@ -127,47 +71,40 @@ namespace GPS_Utils {
return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0; return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0;
} }
String buildDistanceAndComment(float latitude, float longitude, const String& comment) {
distance = String(calculateDistanceTo(latitude, longitude),1);
String distanceAndComment = String(latitude,5);
distanceAndComment += "N / ";
distanceAndComment += String(longitude,5);
distanceAndComment += "E / ";
distanceAndComment += distance;
distanceAndComment += "km";
if (comment != "") {
distanceAndComment += " / ";
distanceAndComment += comment;
}
return distanceAndComment;
}
String decodeEncodedGPS(const String& packet) { String decodeEncodedGPS(const String& packet) {
int indexOfExclamation = packet.indexOf(":!"); int indexOfExclamation = packet.indexOf(":!");
int indexOfEqual = packet.indexOf(":="); int indexOfEqual = packet.indexOf(":=");
const uint8_t OFFSET = 3; // Offset for encoded data in the packet const uint8_t OFFSET = 3; // Offset for encoded data in the packet
String GPSPacket; String infoGPS;
if (indexOfExclamation > 10) { if (indexOfExclamation > 10) {
GPSPacket = packet.substring(indexOfExclamation + OFFSET); infoGPS = packet.substring(indexOfExclamation + OFFSET);
} else if (indexOfEqual > 10) { } else if (indexOfEqual > 10) {
GPSPacket = packet.substring(indexOfEqual + OFFSET); infoGPS = packet.substring(indexOfEqual + OFFSET);
} }
String encodedLatitude = GPSPacket.substring(0,4); float decodedLatitude = APRSPacketLib::decodeBase91EncodedLatitude(infoGPS.substring(0,4));
int Y1 = encodedLatitude[0] - 33; float decodedLongitude = APRSPacketLib::decodeBase91EncodedLongitude(infoGPS.substring(4,8));
int Y2 = encodedLatitude[1] - 33;
int Y3 = encodedLatitude[2] - 33;
int Y4 = encodedLatitude[3] - 33;
float decodedLatitude = 90.0 - (((Y1 * pow(91,3)) + (Y2 * pow(91,2)) + (Y3 * 91) + Y4) / 380926.0);
String encodedLongitude = GPSPacket.substring(4,8); return buildDistanceAndComment(decodedLatitude, decodedLongitude, infoGPS.substring(12));
int X1 = encodedLongitude[0] - 33;
int X2 = encodedLongitude[1] - 33;
int X3 = encodedLongitude[2] - 33;
int X4 = encodedLongitude[3] - 33;
float decodedLongitude = -180.0 + (((X1 * pow(91,3)) + (X2 * pow(91,2)) + (X3 * 91) + X4) / 190463.0);
distance = String(calculateDistanceTo(decodedLatitude, decodedLongitude),1);
String decodedGPS = String(decodedLatitude,5);
decodedGPS += "N / ";
decodedGPS += String(decodedLongitude,5);
decodedGPS += "E / ";
decodedGPS += distance;
decodedGPS += "km";
String comment = GPSPacket.substring(12);
if (comment != "") {
decodedGPS += " / ";
decodedGPS += comment;
}
return decodedGPS;
} }
String getReceivedGPS(const String& packet) { String getReceivedGPS(const String& packet) {
@@ -196,49 +133,42 @@ namespace GPS_Utils {
convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100); convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100);
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1); return buildDistanceAndComment(convertedLatitude, convertedLongitude, infoGPS.substring(19));
String decodedGPS = String(convertedLatitude,5);
decodedGPS += "N / ";
decodedGPS += String(convertedLongitude,5);
decodedGPS += "E / ";
decodedGPS += distance;
decodedGPS += "km";
String comment = infoGPS.substring(19);
if (comment != "") {
decodedGPS += " / ";
decodedGPS += comment;
}
return decodedGPS;
} }
String getDistanceAndComment(const String& packet) { String getDistanceAndComment(const String& packet) {
int indexOfAt = packet.indexOf(":@"); int indexOfAt = packet.indexOf(":@");
if (indexOfAt > 10) { if (indexOfAt > 10) return getReceivedGPS(packet);
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 (encodedBytePosition != 0) { const uint8_t nonEncondedLatitudeOffset = 9; // "N" / "S"
char currentChar = packet[encodedBytePosition]; const uint8_t nonEncondedLongitudeOffset = 19; // "E" / "W"
if (currentChar == 'G' || currentChar == 'Q' || currentChar == '[' || currentChar == 'H' || currentChar == 'X') { const uint8_t encondedByteOffset = 14;
return decodeEncodedGPS(packet); // If valid encoded data position is found, decode it
} else { int indexOfExclamation = packet.indexOf(":!");
return getReceivedGPS(packet); int indexOfEqual = packet.indexOf(":=");
} int baseIndex = - 1;
} else { if (indexOfExclamation > 10) {
return " _ / _ / _ "; baseIndex = indexOfExclamation;
} } else if (indexOfEqual > 10) {
baseIndex = indexOfEqual;
} }
if (baseIndex == -1) return " _ / _ / _ ";
int latitudeIndex = baseIndex + nonEncondedLatitudeOffset;
int longitudeIndex = baseIndex + nonEncondedLongitudeOffset;
int encondedByteIndex = baseIndex + encondedByteOffset;
int packetLength = packet.length();
if (latitudeIndex >= packetLength || longitudeIndex >= packetLength || encondedByteIndex >= packetLength) return " _ / _ / _ ";
char latChar = packet[latitudeIndex];
char lngChar = packet[longitudeIndex];
if ((latChar == 'N' || latChar == 'S') && (lngChar == 'E' || lngChar == 'W')) return getReceivedGPS(packet);
char byteChar = packet[encondedByteIndex];
if (byteChar == 'G' || byteChar == 'Q' || byteChar == '[' || byteChar == 'H' || byteChar == 'X' || byteChar == '3') return decodeEncodedGPS(packet);
return " _ / _ / _ ";
} }
void setup() { void setup() {

View File

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

101
src/mqtt_utils.cpp Normal file
View File

@@ -0,0 +1,101 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
* This file is part of LoRa APRS iGate.
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include "configuration.h"
#include "station_utils.h"
#include "mqtt_utils.h"
extern Configuration Config;
extern WiFiClient mqttClient;
PubSubClient pubSub;
namespace MQTT_Utils {
void sendToMqtt(const String& packet) {
if (!pubSub.connected()) {
Serial.println("Can not send to MQTT because it is not connected");
return;
}
const String cleanPacket = packet.substring(3);
const String sender = cleanPacket.substring(0, cleanPacket.indexOf(">"));
const String topic = String(Config.mqtt.topic + "/" + sender);
const bool result = pubSub.publish(topic.c_str(), cleanPacket.c_str());
if (result) {
Serial.print("Packet sent to MQTT topic "); Serial.println(topic);
} else {
Serial.println("Packet not sent to MQTT (check connection)");
}
}
void receivedFromMqtt(char* topic, byte* payload, unsigned int length) {
Serial.print("Received from MQTT topic "); Serial.print(topic); Serial.print(": ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
STATION_Utils::addToOutputPacketBuffer(String(payload, length));
}
void connect() {
if (pubSub.connected()) return;
if (Config.mqtt.server.isEmpty() || Config.mqtt.port <= 0) {
Serial.println("Connect to MQTT server KO because no host or port given");
return;
}
pubSub.setServer(Config.mqtt.server.c_str(), Config.mqtt.port);
Serial.print("Trying to connect with MQTT Server: " + String(Config.mqtt.server) + " MqttServerPort: " + String(Config.mqtt.port));
bool connected = false;
if (!Config.mqtt.username.isEmpty()) {
connected = pubSub.connect(Config.callsign.c_str(), Config.mqtt.username.c_str(), Config.mqtt.password.c_str());
} else {
connected = pubSub.connect(Config.callsign.c_str());
}
if (connected) {
Serial.println(" -> Connected !");
const String subscribedTopic = Config.mqtt.topic + "/" + Config.callsign + "/#";
if (!pubSub.subscribe(subscribedTopic.c_str())) {
Serial.println("Subscribed to MQTT Failed");
}
Serial.print("Subscribed to MQTT topic : ");
Serial.println(subscribedTopic);
} else {
Serial.println(" -> Not Connected (Retry in a few secs)");
}
}
void loop() {
if (!Config.mqtt.active) return;
if (!pubSub.connected()) return;
pubSub.loop();
}
void setup() {
if (!Config.mqtt.active) return;
pubSub.setClient(mqttClient);
pubSub.setCallback(receivedFromMqtt);
}
}

View File

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

View File

@@ -49,19 +49,19 @@ namespace POWER_Utils {
#ifdef VEXT_CTRL #ifdef VEXT_CTRL
void vext_ctrl_ON() { void vext_ctrl_ON() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3) #if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3) || defined(HELTEC_VM_E290)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH); digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
#endif #endif
#if defined(HELTEC_WP) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3) #if defined(HELTEC_WP_V1) || defined(HELTEC_WP_V1_2) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW); digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
#endif #endif
} }
void vext_ctrl_OFF() { void vext_ctrl_OFF() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3) #if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3) || defined(HELTEC_VM_E290)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW); digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
#endif #endif
#if defined(HELTEC_WP) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3) #if defined(HELTEC_WP_V1) || defined(HELTEC_WP_V1_2) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3)
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH); digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
#endif #endif
} }
@@ -70,24 +70,34 @@ namespace POWER_Utils {
#ifdef ADC_CTRL #ifdef ADC_CTRL
void adc_ctrl_ON() { void adc_ctrl_ON() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2) #if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2) || defined(HELTEC_VM_E290)
digitalWrite(ADC_CTRL, HIGH); digitalWrite(ADC_CTRL, HIGH);
#endif #endif
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP) #if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP_V1) || defined(HELTEC_WP_V1_2)
digitalWrite(ADC_CTRL, LOW); digitalWrite(ADC_CTRL, LOW);
#endif #endif
} }
void adc_ctrl_OFF() { void adc_ctrl_OFF() {
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2) #if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2) || defined(HELTEC_VM_E290)
digitalWrite(ADC_CTRL, LOW); digitalWrite(ADC_CTRL, LOW);
#endif #endif
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP) #if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP_V1) || defined(HELTEC_WP_V1_2)
digitalWrite(ADC_CTRL, HIGH); digitalWrite(ADC_CTRL, HIGH);
#endif #endif
} }
#endif #endif
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
void activateMeasurement() {
PMU.disableTSPinMeasure();
PMU.enableBattDetection();
PMU.enableVbusVoltageMeasure();
PMU.enableBattVoltageMeasure();
PMU.enableSystemVoltageMeasure();
}
#endif
double getBatteryVoltage() { double getBatteryVoltage() {
#if defined(HAS_AXP192) || defined(HAS_AXP2101) #if defined(HAS_AXP192) || defined(HAS_AXP2101)
return (PMU.getBattVoltage() / 1000.0); return (PMU.getBattVoltage() / 1000.0);
@@ -102,17 +112,7 @@ namespace POWER_Utils {
#else #else
return false; return false;
#endif #endif
} }
void activateMeasurement() {
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
PMU.disableTSPinMeasure();
PMU.enableBattDetection();
PMU.enableVbusVoltageMeasure();
PMU.enableBattVoltageMeasure();
PMU.enableSystemVoltageMeasure();
#endif
}
void activateGPS() { void activateGPS() {
#ifdef HAS_AXP192 #ifdef HAS_AXP192
@@ -296,18 +296,6 @@ namespace POWER_Utils {
adc_ctrl_OFF(); adc_ctrl_OFF();
#endif #endif
#if defined(HELTEC_WIRELESS_TRACKER)
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WS) || defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) || defined(TTGO_LORA32_T3S3_V1_2) || defined(HELTEC_V2)
Wire.begin(OLED_SDA, OLED_SCL);
#endif
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WP) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS) #if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
pinMode(BOARD_POWERON, OUTPUT); pinMode(BOARD_POWERON, OUTPUT);
digitalWrite(BOARD_POWERON, HIGH); digitalWrite(BOARD_POWERON, HIGH);
@@ -321,9 +309,20 @@ namespace POWER_Utils {
digitalWrite(TFT_CS, HIGH); digitalWrite(TFT_CS, HIGH);
delay(500); delay(500);
#endif
#ifdef USE_WIRE_WITH_OLED_PINS
Wire.begin(OLED_SDA, OLED_SCL);
#endif
#ifdef USE_WIRE_WITH_BOARD_I2C_PINS
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL); Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif #endif
#ifdef USE_WIRE1_WITH_BOARD_I2C_PINS
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
#endif
delay(1000); delay(1000);
BATTERY_Utils::setup(); BATTERY_Utils::setup();
BATTERY_Utils::startupBatteryHealth(); BATTERY_Utils::startupBatteryHealth();

View File

@@ -31,6 +31,7 @@ extern float snr;
extern int freqError; extern int freqError;
extern bool shouldSleepLowVoltage; extern bool shouldSleepLowVoltage;
extern bool saveNewDigiEcoModeConfig; extern bool saveNewDigiEcoModeConfig;
extern String versionNumber;
namespace QUERY_Utils { namespace QUERY_Utils {
@@ -42,7 +43,9 @@ namespace QUERY_Utils {
if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") { if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") {
answer.concat("?APRSV ?APRSP ?APRSL ?APRSSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign answer.concat("?APRSV ?APRSP ?APRSL ?APRSSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign
} else if (queryQuestion == "?APRSV") { } else if (queryQuestion == "?APRSV") {
answer.concat("CA2RXU_LoRa_iGate 3.0 v"); answer.concat("CA2RXU_LoRa_iGate v");
answer.concat(versionNumber);
answer.concat(" ");
answer.concat(versionDate); answer.concat(versionDate);
} else if (queryQuestion == "?APRSP") { } else if (queryQuestion == "?APRSP") {
answer.concat("iGate QTH: "); answer.concat("iGate QTH: ");

View File

@@ -51,7 +51,7 @@ namespace SLEEP_Utils {
if (Config.digi.ecoMode == 1) { if (Config.digi.ecoMode == 1) {
pinMode(RADIO_WAKEUP_PIN, INPUT); pinMode(RADIO_WAKEUP_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(RADIO_WAKEUP_PIN), wakeUpLoRaPacketReceived, RISING); attachInterrupt(digitalPinToInterrupt(RADIO_WAKEUP_PIN), wakeUpLoRaPacketReceived, RISING);
#if defined(TTGO_LORA32_V2_1) || defined(TTGO_LORA32_V2_1_915) || defined(TTGO_LORA32_T3S3_V1_2) || defined(TTGO_T_BEAM_V1_0) || defined(TTGO_T_BEAM_V1_0_915) || defined(TTGO_T_BEAM_V1_0_SX1268) || defined(TTGO_T_BEAM_V1_2) || defined(TTGO_T_BEAM_V1_2_915) || defined(TTGO_T_BEAM_V1_2_SX1262) || defined(TTGO_T_DECK_PLUS) || defined(TTGO_T_DECK_GPS) || defined(TTGO_T_Beam_S3_SUPREME_V3) || defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WP) || defined(HELTEC_WS) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY) || defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V2) || defined(XIAO_ESP32S3_LORA) || defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) || defined(TROY_LoRa_APRS) || defined(OE5HWN_MeshCom) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915) || defined(ESP32_DIY_1W_LoRa_LLCC68) || defined(ESP32_DIY_1W_LoRa_Mesh_V1_2) || defined(WEMOS_S2_MINI_DIY_LoRa) || defined(WEMOS_D1_R32_RA02) || defined(WEMOS_LOLIN32_OLED_DIY_LoRa) #if defined(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)
esp_sleep_enable_ext1_wakeup(GPIO_WAKEUP_PIN, ESP_EXT1_WAKEUP_ANY_HIGH); esp_sleep_enable_ext1_wakeup(GPIO_WAKEUP_PIN, ESP_EXT1_WAKEUP_ANY_HIGH);
#endif #endif
#if defined(HELTEC_HTCT62) || defined(ESP32C3_DIY_1W_LoRa) || defined(ESP32C3_DIY_1W_LoRa_915) || defined(ESP32_C3_OctopusLab_LoRa) #if defined(HELTEC_HTCT62) || defined(ESP32C3_DIY_1W_LoRa) || defined(ESP32C3_DIY_1W_LoRa_915) || defined(ESP32_C3_OctopusLab_LoRa)

View File

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

View File

@@ -24,6 +24,8 @@
extern Configuration Config; extern Configuration Config;
extern String versionDate;
extern String versionNumber;
WiFiUDP udpClient; WiFiUDP udpClient;
@@ -34,7 +36,9 @@ namespace SYSLOG_Utils {
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) { if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
String syslogPacket = "<165>1 - "; String syslogPacket = "<165>1 - ";
syslogPacket.concat(Config.callsign); syslogPacket.concat(Config.callsign);
syslogPacket.concat(" CA2RXU_LoRa_iGate_3.0 - - - "); //RFC5424 The Syslog Protocol syslogPacket.concat(" CA2RXU_LoRa_iGate_");
syslogPacket.concat(versionNumber);
syslogPacket.concat(" - - - "); //RFC5424 The Syslog Protocol
char signalData[35]; char signalData[35];
snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError); snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
@@ -102,9 +106,12 @@ namespace SYSLOG_Utils {
if (nextChar == '>') { if (nextChar == '>') {
syslogPacket.concat("StartUp_Status / "); syslogPacket.concat("StartUp_Status / ");
syslogPacket.concat(packet.substring(colonIndex + 2)); syslogPacket.concat(packet.substring(colonIndex + 2));
} else { } else if (nextChar == ':') {
syslogPacket.concat("QUERY / "); syslogPacket.concat("QUERY / ");
syslogPacket.concat(packet); syslogPacket.concat(packet);
} else {
syslogPacket.concat("BEACON / ");
syslogPacket.concat(packet);
} }
break; break;
case 3: // TX case 3: // TX
@@ -132,9 +139,13 @@ namespace SYSLOG_Utils {
} }
void setup() { void setup() {
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
udpClient.begin(WiFi.localIP(), 0); udpClient.begin(0);
Serial.println("init : Syslog Server ... done! (at " + Config.syslog.server + ")"); 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 + ")");
} }
} }

130
src/telemetry_utils.cpp Normal file
View File

@@ -0,0 +1,130 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
* This file is part of LoRa APRS iGate.
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#include <APRSPacketLib.h>
#include <Arduino.h>
#include <vector>
#include "telemetry_utils.h"
#include "aprs_is_utils.h"
#include "configuration.h"
#include "station_utils.h"
#include "battery_utils.h"
#include "lora_utils.h"
#include "wx_utils.h"
#include "display.h"
extern Configuration Config;
extern bool sendStartTelemetry;
int telemetryCounter = random(1,999);
namespace TELEMETRY_Utils {
String joinWithCommas(const std::vector<String>& items) {
String result;
for (size_t i = 0; i < items.size(); ++i) {
result += items[i];
if (i < items.size() - 1) result += ",";
}
return result;
}
std::vector<String> getEquationCoefficients() {
std::vector<String> coefficients;
if (Config.battery.sendInternalVoltage) coefficients.push_back("0,0.01,0");
if (Config.battery.sendExternalVoltage) coefficients.push_back("0,0.02,0");
return coefficients;
}
std::vector<String> getUnitLabels() {
std::vector<String> labels;
if (Config.battery.sendInternalVoltage) labels.push_back("VDC");
if (Config.battery.sendExternalVoltage) labels.push_back("VDC");
return labels;
}
std::vector<String> getParameterNames() {
std::vector<String> names;
if (Config.battery.sendInternalVoltage) names.push_back("V_Batt");
if (Config.battery.sendExternalVoltage) names.push_back("V_Ext");
return names;
}
void sendBaseTelemetryPacket(const String& prefix, const std::vector<String>& values) {
String packet = prefix + joinWithCommas(values);
if (Config.beacon.sendViaAPRSIS) {
String baseAPRSISTelemetryPacket = APRSPacketLib::generateMessagePacket(Config.callsign, "APLRG1", "TCPIP,qAC", Config.callsign, packet);
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket);
#else
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket);
#endif
delay(300);
} else if (Config.beacon.sendViaRF) {
String baseRFTelemetryPacket = APRSPacketLib::generateMessagePacket(Config.callsign, "APLRG1", Config.beacon.path, Config.callsign, packet);
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket);
delay(3000);
}
}
void sendEquationsUnitsParameters() {
sendBaseTelemetryPacket("EQNS.", getEquationCoefficients());
sendBaseTelemetryPacket("UNIT.", getUnitLabels());
sendBaseTelemetryPacket("PARM.", getParameterNames());
sendStartTelemetry = false;
}
String generateEncodedTelemetryBytes(float value, bool counterBytes, byte telemetryType) {
String encodedBytes;
int tempValue;
if (counterBytes) {
tempValue = value;
} else {
switch (telemetryType) {
case 0: tempValue = value * 100; break; // Internal voltage (0-4,2V), Humidity, Gas calculation
case 1: tempValue = (value * 100) / 2; break; // External voltage calculation (0-15V)
case 2: tempValue = (value * 10) + 500; break; // Temperature
case 3: tempValue = (value * 8); break; // Pressure
default: tempValue = value; break;
}
}
int firstByte = tempValue / 91;
tempValue -= firstByte * 91;
encodedBytes = char(firstByte + 33);
encodedBytes += char(tempValue + 33);
return encodedBytes;
}
String generateEncodedTelemetry() {
String telemetry = "|";
telemetry += generateEncodedTelemetryBytes(telemetryCounter, true, 0);
telemetryCounter++;
if (telemetryCounter == 1000) telemetryCounter = 0;
if (Config.battery.sendInternalVoltage) telemetry += generateEncodedTelemetryBytes(BATTERY_Utils::checkInternalVoltage(), false, 0);
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(BATTERY_Utils::checkExternalVoltage(), false, Config.battery.useExternalI2CSensor ? 0 : 1);
telemetry += "|";
return telemetry;
}
}

View File

@@ -17,14 +17,19 @@
*/ */
#include <WiFi.h> #include <WiFi.h>
#include "ESPmDNS.h"
#include "configuration.h" #include "configuration.h"
#include "station_utils.h" #include "station_utils.h"
#include "kiss_protocol.h" #include "kiss_protocol.h"
#include "aprs_is_utils.h"
#include "kiss_utils.h" #include "kiss_utils.h"
#include "tnc_utils.h"
#include "utils.h" #include "utils.h"
extern Configuration Config; extern Configuration Config;
extern WiFiClient aprsIsClient;
extern bool passcodeValid;
#define MAX_CLIENTS 4 #define MAX_CLIENTS 4
#define INPUT_BUFFER_SIZE (2 + MAX_CLIENTS) #define INPUT_BUFFER_SIZE (2 + MAX_CLIENTS)
@@ -45,6 +50,17 @@ namespace TNC_Utils {
if (Config.tnc.enableServer && Config.digi.ecoMode == 0) { if (Config.tnc.enableServer && Config.digi.ecoMode == 0) {
tncServer.stop(); tncServer.stop();
tncServer.begin(); tncServer.begin();
String host = "igate-" + Config.callsign;
if (!MDNS.begin(host.c_str())) {
Serial.println("Error Starting mDNS");
tncServer.stop();
return;
}
if (!MDNS.addService("tnc", "tcp", TNC_PORT)) {
Serial.println("Error: Could not add mDNS service");
}
Serial.println("TNC server started successfully");
Serial.println("mDNS Host: " + host + ".local");
} }
} }
@@ -81,7 +97,8 @@ namespace TNC_Utils {
String sender = frame.substring(0,frame.indexOf(">")); String sender = frame.substring(0,frame.indexOf(">"));
if (Config.tnc.acceptOwn || sender != Config.callsign) { if (Config.tnc.acceptOwn || sender != Config.callsign) {
STATION_Utils::addToOutputPacketBuffer(frame); if (Config.loramodule.txActive) STATION_Utils::addToOutputPacketBuffer(frame);
if (Config.tnc.aprsBridgeActive && Config.aprs_is.active && passcodeValid && aprsIsClient.connected()) APRS_IS_Utils::upload(frame);
} else { } else {
Utils::println("Ignored own frame from KISS"); Utils::println("Ignored own frame from KISS");
} }
@@ -118,8 +135,8 @@ namespace TNC_Utils {
} }
} }
void sendToClients(const String& packet) { void sendToClients(const String& packet, bool stripBytes) {
String cleanPacket = packet.substring(3); String cleanPacket = stripBytes ? packet.substring(3): packet;
const String kissEncoded = encodeKISS(cleanPacket); const String kissEncoded = encodeKISS(cleanPacket);
@@ -139,8 +156,8 @@ namespace TNC_Utils {
Utils::println(cleanPacket); Utils::println(cleanPacket);
} }
void sendToSerial(const String& packet) { void sendToSerial(const String& packet, bool stripBytes) {
String cleanPacket = packet.substring(3); String cleanPacket = stripBytes ? packet.substring(3): packet;
Serial.print(encodeKISS(cleanPacket)); Serial.print(encodeKISS(cleanPacket));
Serial.flush(); Serial.flush();
} }

View File

@@ -16,8 +16,10 @@
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <APRSPacketLib.h>
#include <TinyGPS++.h> #include <TinyGPS++.h>
#include <WiFi.h> #include <WiFi.h>
#include "telemetry_utils.h"
#include "configuration.h" #include "configuration.h"
#include "station_utils.h" #include "station_utils.h"
#include "battery_utils.h" #include "battery_utils.h"
@@ -34,7 +36,6 @@
extern Configuration Config; extern Configuration Config;
extern WiFiClient espClient;
extern TinyGPSPlus gps; extern TinyGPSPlus gps;
extern String versionDate; extern String versionDate;
extern String firstLine; extern String firstLine;
@@ -71,24 +72,20 @@ String secondaryBeaconPacket;
namespace Utils { namespace Utils {
void processStatus() { void processStatus() {
String status = Config.callsign; String status = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
status.concat(">APLRG1");
if (Config.beacon.path.indexOf("WIDE") == 0) {
status.concat(",");
status.concat(Config.beacon.path);
}
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) { if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
delay(1000); delay(1000);
status.concat(",qAC:>https://github.com/richonguzman/LoRa_APRS_iGate "); status.concat(",qAC:>");
status.concat(versionDate); status.concat(Config.beacon.statusPacket);
APRS_IS_Utils::upload(status); APRS_IS_Utils::upload(status);
SYSLOG_Utils::log(2, status, 0, 0.0, 0); // APRSIS TX SYSLOG_Utils::log(2, status, 0, 0.0, 0); // APRSIS TX
statusAfterBoot = false; statusAfterBoot = false;
} }
if (statusAfterBoot && !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF) { if (statusAfterBoot && !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF) {
status.concat(":>https://github.com/richonguzman/LoRa_APRS_iGate "); status.concat(":>");
status.concat(versionDate); status.concat(Config.beacon.statusPacket);
STATION_Utils::addToOutputPacketBuffer(status); STATION_Utils::addToOutputPacketBuffer(status, true); // treated also as beacon on Tx Freq
statusAfterBoot = false; statusAfterBoot = false;
} }
} }
@@ -127,83 +124,12 @@ namespace Utils {
seventhLine = " listening..."; seventhLine = " listening...";
} }
void activeStations() { void showActiveStations() {
char buffer[30]; // Adjust size as needed char buffer[30]; // Adjust size as needed
sprintf(buffer, "Stations (%dmin) = %2d", Config.rememberStationTime, lastHeardStations.size()); sprintf(buffer, "Stations (%dmin) = %2d", Config.rememberStationTime, lastHeardStations.size());
fourthLine = buffer; fourthLine = buffer;
} }
void sendInitialTelemetryPackets() {
char sender[10]; // 9 characters + null terminator
snprintf(sender, sizeof(sender), "%-9s", Config.callsign.c_str()); // Left-align with spaces
String baseAPRSISTelemetryPacket = Config.callsign;
baseAPRSISTelemetryPacket += ">APLRG1,TCPIP,qAC::";
baseAPRSISTelemetryPacket += sender;
baseAPRSISTelemetryPacket += ":";
String baseRFTelemetryPacket = Config.callsign;
baseRFTelemetryPacket += ">APLRG1";
if (Config.beacon.path.indexOf("WIDE") != -1) {
baseRFTelemetryPacket += ",";
baseRFTelemetryPacket += Config.beacon.path;
}
baseRFTelemetryPacket += "::";
baseRFTelemetryPacket += sender;
baseRFTelemetryPacket += ":";
String telemetryPacket1 = "EQNS.";
if (Config.battery.sendInternalVoltage) {
telemetryPacket1 += "0,0.01,0";
}
if (Config.battery.sendExternalVoltage) {
telemetryPacket1 += String(Config.battery.sendInternalVoltage ? ",0,0.02,0" : "0,0.02,0");
}
String telemetryPacket2 = "UNIT.";
if (Config.battery.sendInternalVoltage) {
telemetryPacket2 += "VDC";
}
if (Config.battery.sendExternalVoltage) {
telemetryPacket2 += String(Config.battery.sendInternalVoltage ? ",VDC" : "VDC");
}
String telemetryPacket3 = "PARM.";
if (Config.battery.sendInternalVoltage) {
telemetryPacket3 += "V_Batt";
}
if (Config.battery.sendExternalVoltage) {
telemetryPacket3 += String(Config.battery.sendInternalVoltage ? ",V_Ext" : "V_Ext");
}
if (Config.beacon.sendViaAPRSIS) {
#ifdef HAS_A7670
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket + telemetryPacket1);
delay(300);
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket + telemetryPacket2);
delay(300);
A7670_Utils::uploadToAPRSIS(baseAPRSISTelemetryPacket + telemetryPacket3);
delay(300);
#else
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket + telemetryPacket1);
delay(300);
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket + telemetryPacket2);
delay(300);
APRS_IS_Utils::upload(baseAPRSISTelemetryPacket + telemetryPacket3);
delay(300);
#endif
delay(300);
} else if (Config.beacon.sendViaRF) {
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket + telemetryPacket1);
delay(3000);
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket + telemetryPacket2);
delay(3000);
LoRa_Utils::sendNewPacket(baseRFTelemetryPacket + telemetryPacket3);
delay(3000);
}
sendStartTelemetry = false;
}
void checkBeaconInterval() { void checkBeaconInterval() {
uint32_t lastTx = millis() - lastBeaconTx; uint32_t lastTx = millis() - lastBeaconTx;
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) { if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
@@ -225,12 +151,12 @@ namespace Utils {
!Config.wxsensor.active && !Config.wxsensor.active &&
(Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage) && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage) &&
(lastBeaconTx > 0)) { (lastBeaconTx > 0)) {
sendInitialTelemetryPackets(); TELEMETRY_Utils::sendEquationsUnitsParameters();
} }
STATION_Utils::deleteNotHeard(); STATION_Utils::deleteNotHeard();
activeStations(); showActiveStations();
beaconPacket = iGateBeaconPacket; beaconPacket = iGateBeaconPacket;
secondaryBeaconPacket = iGateLoRaBeaconPacket; secondaryBeaconPacket = iGateLoRaBeaconPacket;
@@ -238,10 +164,18 @@ namespace Utils {
if (Config.beacon.gpsActive && Config.digi.ecoMode == 0) { if (Config.beacon.gpsActive && Config.digi.ecoMode == 0) {
GPS_Utils::getData(); GPS_Utils::getData();
if (gps.location.isUpdated() && gps.location.lat() != 0.0 && gps.location.lng() != 0.0) { if (gps.location.isUpdated() && gps.location.lat() != 0.0 && gps.location.lng() != 0.0) {
GPS_Utils::generateBeaconFirstPart(); String basePacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
String encodedGPS = GPS_Utils::encodeGPS(gps.location.lat(), gps.location.lng(), Config.beacon.overlay, Config.beacon.symbol); String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
beaconPacket = iGateBeaconPacket + encodedGPS;
secondaryBeaconPacket = iGateLoRaBeaconPacket + encodedGPS; beaconPacket = basePacket;
beaconPacket += ",qAC:!";
beaconPacket += Config.beacon.overlay;
beaconPacket += encodedGPS;
secondaryBeaconPacket = basePacket;
secondaryBeaconPacket += ":=";
secondaryBeaconPacket += Config.beacon.overlay;
secondaryBeaconPacket += encodedGPS;
} }
} }
#endif #endif
@@ -281,7 +215,7 @@ namespace Utils {
} }
#endif #endif
#ifndef HELTEC_WP #ifndef HELTEC_WP_V1
if (Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) { if (Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) {
float externalVoltage = BATTERY_Utils::checkExternalVoltage(); float externalVoltage = BATTERY_Utils::checkExternalVoltage();
if (Config.battery.monitorExternalVoltage && externalVoltage < Config.battery.externalSleepVoltage) { if (Config.battery.monitorExternalVoltage && externalVoltage < Config.battery.externalSleepVoltage) {
@@ -309,7 +243,7 @@ namespace Utils {
#endif #endif
if (Config.battery.sendVoltageAsTelemetry && !Config.wxsensor.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)){ if (Config.battery.sendVoltageAsTelemetry && !Config.wxsensor.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)){
String encodedTelemetry = BATTERY_Utils::generateEncodedTelemetry(); String encodedTelemetry = TELEMETRY_Utils::generateEncodedTelemetry();
beaconPacket += encodedTelemetry; beaconPacket += encodedTelemetry;
secondaryBeaconPacket += encodedTelemetry; secondaryBeaconPacket += encodedTelemetry;
} }
@@ -323,13 +257,14 @@ namespace Utils {
#else #else
APRS_IS_Utils::upload(beaconPacket); APRS_IS_Utils::upload(beaconPacket);
#endif #endif
if (Config.syslog.logBeaconOverTCPIP) SYSLOG_Utils::log(1, "tcp" + beaconPacket, 0, 0.0, 0); // APRSIS TX
} }
if (Config.beacon.sendViaRF || backUpDigiMode) { if (Config.beacon.sendViaRF || backUpDigiMode) {
Utils::println("-- Sending Beacon to RF --"); Utils::println("-- Sending Beacon to RF --");
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING DIGI BEACON", 0); displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING DIGI BEACON", 0);
seventhLine = " listening..."; seventhLine = " listening...";
STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket); STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket, true);
} }
lastBeaconTx = millis(); lastBeaconTx = millis();
@@ -337,7 +272,7 @@ namespace Utils {
beaconUpdate = false; beaconUpdate = false;
} }
if (statusAfterBoot) { if (statusAfterBoot && Config.beacon.statusActive && !Config.beacon.statusPacket.isEmpty()) {
processStatus(); processStatus();
} }
} }
@@ -354,6 +289,7 @@ namespace Utils {
Serial.println("Tx Freq less than 125kHz from Rx Freq ---> NOT VALID"); Serial.println("Tx Freq less than 125kHz from Rx Freq ---> NOT VALID");
displayShow("Tx Freq is less than ", "125kHz from Rx Freq", "device will autofix", "and then reboot", 1000); displayShow("Tx Freq is less than ", "125kHz from Rx Freq", "device will autofix", "and then reboot", 1000);
Config.loramodule.txFreq = Config.loramodule.rxFreq; // Inform about that but then change the TX QRG to RX QRG and reset the device Config.loramodule.txFreq = Config.loramodule.rxFreq; // Inform about that but then change the TX QRG to RX QRG and reset the device
Config.beacon.beaconFreq = 1; // return to LoRa Tx Beacon Freq
Config.writeFile(); Config.writeFile();
ESP.restart(); ESP.restart();
} }
@@ -471,6 +407,7 @@ namespace Utils {
cleanCallsign = callsign.substring(0, callsign.indexOf("-")); cleanCallsign = callsign.substring(0, callsign.indexOf("-"));
String ssid = callsign.substring(callsign.indexOf("-") + 1); String ssid = callsign.substring(callsign.indexOf("-") + 1);
if (ssid.indexOf("-") != -1 || ssid.length() > 2) return false; if (ssid.indexOf("-") != -1 || ssid.length() > 2) return false;
if (ssid.length() == 2 && ssid[0] == '0') return false;
for (int i = 0; i < ssid.length(); i++) { for (int i = 0; i < ssid.length(); i++) {
if (!isAlphaNumeric(ssid[i])) return false; if (!isAlphaNumeric(ssid[i])) return false;
} }
@@ -506,4 +443,11 @@ namespace Utils {
return true; return true;
} }
void startupDelay() {
if (Config.startupDelay > 0) {
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(Config.startupDelay * 60 * 1000);
}
}
} }

View File

@@ -115,135 +115,212 @@ namespace WEB_Utils {
} }
void handleWriteConfiguration(AsyncWebServerRequest *request) { void handleWriteConfiguration(AsyncWebServerRequest *request) {
Serial.println("Got new config from www"); Serial.println("Got new Configuration Data from www");
int networks = request->getParam("wifi.APs", true)->value().toInt(); auto getParamStringSafe = [&](const String& name, const String& defaultValue = "") -> String {
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value();
}
return defaultValue;
};
auto getParamIntSafe = [&](const String& name, int defaultValue = 0) -> int {
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value().toInt();
}
return defaultValue;
};
auto getParamFloatSafe = [&](const String& name, float defaultValue = 0.0) -> float {
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value().toFloat();
}
return defaultValue;
};
auto getParamDoubleSafe = [&](const String& name, double defaultValue = 0.0) -> double {
if (request->hasParam(name, true)) {
return request->getParam(name, true)->value().toDouble();
}
return defaultValue;
};
int networks = getParamIntSafe("wifi.APs");
Config.wifiAPs = {}; Config.wifiAPs = {};
for (int i=0; i<networks; i++) { for (int i = 0; i < networks; i++) {
WiFi_AP wifiap; WiFi_AP wifiap;
wifiap.ssid = request->getParam("wifi.AP." + String(i) + ".ssid", true)->value(); wifiap.ssid = getParamStringSafe("wifi.AP." + String(i) + ".ssid");
wifiap.password = request->getParam("wifi.AP." + String(i) + ".password", true)->value(); wifiap.password = getParamStringSafe("wifi.AP." + String(i) + ".password");
Config.wifiAPs.push_back(wifiap); Config.wifiAPs.push_back(wifiap);
} }
Config.callsign = request->getParam("callsign", true)->value(); Config.startupDelay = getParamIntSafe("startupDelay", Config.startupDelay);
Config.wifiAutoAP.password = request->getParam("wifi.autoAP.password", true)->value(); Config.callsign = getParamStringSafe("callsign", Config.callsign);
Config.wifiAutoAP.timeout = request->getParam("wifi.autoAP.timeout", true)->value().toInt();
Config.wifiAutoAP.password = getParamStringSafe("wifi.autoAP.password", Config.wifiAutoAP.password);
Config.wifiAutoAP.timeout = getParamIntSafe("wifi.autoAP.timeout", Config.wifiAutoAP.timeout);
Config.aprs_is.active = request->hasParam("aprs_is.active", true);
if (Config.aprs_is.active) {
Config.aprs_is.messagesToRF = request->hasParam("aprs_is.messagesToRF", true);
Config.aprs_is.objectsToRF = request->hasParam("aprs_is.objectsToRF", true);
Config.aprs_is.server = getParamStringSafe("aprs_is.server", Config.aprs_is.server);
Config.aprs_is.passcode = getParamStringSafe("aprs_is.passcode", Config.aprs_is.passcode);
Config.aprs_is.port = getParamIntSafe("aprs_is.port", Config.aprs_is.port);
Config.aprs_is.filter = getParamStringSafe("aprs_is.filter", Config.aprs_is.filter);
}
Config.aprs_is.active = request->hasParam("aprs_is.active", true); Config.beacon.interval = getParamIntSafe("beacon.interval", Config.beacon.interval);
Config.aprs_is.passcode = request->getParam("aprs_is.passcode", true)->value();
Config.aprs_is.server = request->getParam("aprs_is.server", true)->value();
Config.aprs_is.port = request->getParam("aprs_is.port", true)->value().toInt();
Config.aprs_is.filter = request->getParam("aprs_is.filter", true)->value();
Config.aprs_is.messagesToRF = request->hasParam("aprs_is.messagesToRF", true);
Config.aprs_is.objectsToRF = request->hasParam("aprs_is.objectsToRF", true);
Config.beacon.interval = request->getParam("beacon.interval", true)->value().toInt();
Config.beacon.sendViaAPRSIS = request->hasParam("beacon.sendViaAPRSIS", true); Config.beacon.sendViaAPRSIS = request->hasParam("beacon.sendViaAPRSIS", true);
Config.beacon.sendViaRF = request->hasParam("beacon.sendViaRF", true); Config.beacon.sendViaRF = request->hasParam("beacon.sendViaRF", true);
Config.beacon.latitude = request->getParam("beacon.latitude", true)->value().toDouble(); Config.beacon.beaconFreq = getParamIntSafe("beacon.beaconFreq", Config.beacon.beaconFreq);
Config.beacon.longitude = request->getParam("beacon.longitude", true)->value().toDouble(); Config.beacon.latitude = getParamDoubleSafe("beacon.latitude", Config.beacon.latitude);
Config.beacon.comment = request->getParam("beacon.comment", true)->value(); Config.beacon.longitude = getParamDoubleSafe("beacon.longitude", Config.beacon.longitude);
Config.beacon.overlay = request->getParam("beacon.overlay", true)->value(); Config.beacon.comment = getParamStringSafe("beacon.comment", Config.beacon.comment);
Config.beacon.symbol = request->getParam("beacon.symbol", true)->value(); Config.beacon.overlay = getParamStringSafe("beacon.overlay", Config.beacon.overlay);
Config.beacon.path = request->getParam("beacon.path", true)->value(); Config.beacon.symbol = getParamStringSafe("beacon.symbol", Config.beacon.symbol);
Config.beacon.path = getParamStringSafe("beacon.path", Config.beacon.path);
Config.beacon.statusActive = request->hasParam("beacon.statusActive", true);
if (Config.beacon.statusActive) {
Config.beacon.statusPacket = getParamStringSafe("beacon.statusPacket", Config.beacon.statusPacket);
}
Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true); Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true);
Config.beacon.gpsAmbiguity = request->hasParam("beacon.gpsAmbiguity", true); Config.beacon.ambiguityLevel = getParamIntSafe("beacon.ambiguityLevel", Config.beacon.ambiguityLevel);
Config.personalNote = getParamStringSafe("personalNote", Config.personalNote);
Config.digi.mode = request->getParam("digi.mode", true)->value().toInt(); Config.blacklist = getParamStringSafe("blacklist", Config.blacklist);
Config.digi.ecoMode = request->getParam("digi.ecoMode", true)->value().toInt();;
Config.digi.mode = getParamIntSafe("digi.mode", Config.digi.mode);
Config.digi.ecoMode = getParamIntSafe("digi.ecoMode", Config.digi.ecoMode);
Config.loramodule.txFreq = request->getParam("lora.txFreq", true)->value().toInt();
Config.loramodule.rxFreq = request->getParam("lora.rxFreq", true)->value().toInt();
Config.loramodule.spreadingFactor = request->getParam("lora.spreadingFactor", true)->value().toInt();
Config.loramodule.signalBandwidth = request->getParam("lora.signalBandwidth", true)->value().toInt();
Config.loramodule.codingRate4 = request->getParam("lora.codingRate4", true)->value().toInt();
Config.loramodule.power = request->getParam("lora.power", true)->value().toInt();
Config.loramodule.txActive = request->hasParam("lora.txActive", true);
Config.loramodule.rxActive = request->hasParam("lora.rxActive", true); Config.loramodule.rxActive = request->hasParam("lora.rxActive", true);
Config.loramodule.rxFreq = getParamIntSafe("lora.rxFreq", Config.loramodule.rxFreq);
Config.loramodule.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); Config.display.alwaysOn = request->hasParam("display.alwaysOn", true);
if (!Config.display.alwaysOn) { if (!Config.display.alwaysOn) {
Config.display.timeout = request->getParam("display.timeout", true)->value().toInt(); Config.display.timeout = getParamIntSafe("display.timeout", Config.display.timeout);
} }
Config.display.turn180 = request->hasParam("display.turn180", true); Config.display.turn180 = request->hasParam("display.turn180", true);
Config.battery.sendInternalVoltage = request->hasParam("battery.sendInternalVoltage", true); Config.battery.sendInternalVoltage = request->hasParam("battery.sendInternalVoltage", true);
Config.battery.monitorInternalVoltage = request->hasParam("battery.monitorInternalVoltage", true); Config.battery.monitorInternalVoltage = request->hasParam("battery.monitorInternalVoltage", true);
Config.battery.internalSleepVoltage = request->getParam("battery.internalSleepVoltage", true)->value().toFloat(); if (Config.battery.monitorInternalVoltage) {
Config.battery.internalSleepVoltage = getParamFloatSafe("battery.internalSleepVoltage", Config.battery.internalSleepVoltage);
}
Config.battery.sendExternalVoltage = request->hasParam("battery.sendExternalVoltage", true); Config.battery.sendExternalVoltage = request->hasParam("battery.sendExternalVoltage", true);
if (Config.battery.sendExternalVoltage) { if (Config.battery.sendExternalVoltage) {
Config.battery.externalVoltagePin = request->getParam("battery.externalVoltagePin", true)->value().toInt(); Config.battery.useExternalI2CSensor = request->hasParam("battery.useExternalI2CSensor", true);
Config.battery.voltageDividerR1 = request->getParam("battery.voltageDividerR1", true)->value().toFloat();
Config.battery.voltageDividerR2 = request->getParam("battery.voltageDividerR2", true)->value().toFloat();
} }
Config.battery.monitorExternalVoltage = request->hasParam("battery.monitorExternalVoltage", true); if (Config.battery.sendExternalVoltage) {
Config.battery.externalSleepVoltage = request->getParam("battery.externalSleepVoltage", true)->value().toFloat(); Config.battery.externalVoltagePin = getParamIntSafe("battery.externalVoltagePin", Config.battery.externalVoltagePin);
Config.battery.voltageDividerR1 = getParamFloatSafe("battery.voltageDividerR1", Config.battery.voltageDividerR1);
Config.battery.voltageDividerR2 = getParamFloatSafe("battery.voltageDividerR2", Config.battery.voltageDividerR2);
}
Config.battery.monitorExternalVoltage = request->hasParam("battery.monitorExternalVoltage", true);
if (Config.battery.monitorExternalVoltage) {
Config.battery.externalSleepVoltage = getParamFloatSafe("battery.externalSleepVoltage", Config.battery.externalSleepVoltage);
}
Config.battery.sendVoltageAsTelemetry = request->hasParam("battery.sendVoltageAsTelemetry", true);
Config.battery.sendVoltageAsTelemetry = request->hasParam("battery.sendVoltageAsTelemetry", true);
Config.wxsensor.active = request->hasParam("wxsensor.active", true); Config.wxsensor.active = request->hasParam("wxsensor.active", true);
Config.wxsensor.heightCorrection = request->getParam("wxsensor.heightCorrection", true)->value().toInt();
Config.wxsensor.temperatureCorrection = request->getParam("wxsensor.temperatureCorrection", true)->value().toFloat();
if (Config.wxsensor.active) { if (Config.wxsensor.active) {
Config.wxsensor.heightCorrection = getParamIntSafe("wxsensor.heightCorrection", Config.wxsensor.heightCorrection);
Config.wxsensor.temperatureCorrection = getParamFloatSafe("wxsensor.temperatureCorrection", Config.wxsensor.temperatureCorrection);
Config.beacon.symbol = "_"; Config.beacon.symbol = "_";
} }
Config.syslog.active = request->hasParam("syslog.active", true);
Config.syslog.active = request->hasParam("syslog.active", true);
if (Config.syslog.active) { if (Config.syslog.active) {
Config.syslog.server = request->getParam("syslog.server", true)->value(); Config.syslog.server = getParamStringSafe("syslog.server", Config.syslog.server);
Config.syslog.port = request->getParam("syslog.port", true)->value().toInt(); Config.syslog.port = getParamIntSafe("syslog.port", Config.syslog.port);
Config.syslog.logBeaconOverTCPIP = request->hasParam("syslog.logBeaconOverTCPIP", true);
} }
Config.tnc.enableServer = request->hasParam("tnc.enableServer", true); Config.tnc.enableServer = request->hasParam("tnc.enableServer", true);
Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true); Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true);
Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true); Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true);
Config.tnc.aprsBridgeActive = request->hasParam("tnc.aprsBridgeActive", true);
Config.mqtt.active = request->hasParam("mqtt.active", true);
if (Config.mqtt.active) {
Config.mqtt.server = getParamStringSafe("mqtt.server", Config.mqtt.server);
Config.mqtt.topic = getParamStringSafe("mqtt.topic", Config.mqtt.topic);
Config.mqtt.username = getParamStringSafe("mqtt.username", Config.mqtt.username);
Config.mqtt.password = getParamStringSafe("mqtt.password", Config.mqtt.password);
Config.mqtt.port = getParamIntSafe("mqtt.port", Config.mqtt.port);
Config.mqtt.beaconOverMqtt = request->hasParam("mqtt.beaconOverMqtt", true);
}
Config.rebootMode = request->hasParam("other.rebootMode", true); Config.rebootMode = request->hasParam("other.rebootMode", true);
Config.rebootModeTime = request->getParam("other.rebootModeTime", true)->value().toInt(); if (Config.rebootMode) {
Config.rebootModeTime = getParamIntSafe("other.rebootModeTime", Config.rebootModeTime);
}
Config.ota.username = request->getParam("ota.username", true)->value(); Config.ota.username = getParamStringSafe("ota.username", Config.ota.username);
Config.ota.password = request->getParam("ota.password", true)->value(); Config.ota.password = getParamStringSafe("ota.password", Config.ota.password);
Config.rememberStationTime = request->getParam("other.rememberStationTime", true)->value().toInt();
Config.backupDigiMode = request->hasParam("other.backupDigiMode", true);
Config.personalNote = request->getParam("personalNote", true)->value();
Config.blacklist = request->getParam("blacklist", true)->value();
Config.webadmin.active = request->hasParam("webadmin.active", true); Config.webadmin.active = request->hasParam("webadmin.active", true);
if (Config.webadmin.active) { if (Config.webadmin.active) {
Config.webadmin.username = request->getParam("webadmin.username", true)->value(); Config.webadmin.username = getParamStringSafe("webadmin.username", Config.webadmin.username);
Config.webadmin.password = request->getParam("webadmin.password", true)->value(); Config.webadmin.password = getParamStringSafe("webadmin.password", Config.webadmin.password);
} }
Config.ntp.gmtCorrection = request->getParam("ntp.gmtCorrection", true)->value().toFloat(); Config.remoteManagement.managers = getParamStringSafe("remoteManagement.managers", Config.remoteManagement.managers);
Config.remoteManagement.rfOnly = request->hasParam("remoteManagement.rfOnly", true);
Config.remoteManagement.managers = request->getParam("remoteManagement.managers", true)->value(); Config.ntp.server = getParamStringSafe("ntp.server", Config.ntp.server);
Config.remoteManagement.rfOnly = request->getParam("remoteManagement.rfOnly", true); Config.ntp.gmtCorrection = getParamFloatSafe("ntp.gmtCorrection", Config.ntp.gmtCorrection);
Config.writeFile(); Config.rememberStationTime = getParamIntSafe("other.rememberStationTime", Config.rememberStationTime);
AsyncWebServerResponse *response = request->beginResponse(302, "text/html", ""); Config.backupDigiMode = request->hasParam("other.backupDigiMode", true);
response->addHeader("Location", "/");
request->send(response); bool saveSuccess = Config.writeFile();
displayToggle(false);
delay(200); if (saveSuccess) {
ESP.restart(); Serial.println("Configuration saved successfully");
AsyncWebServerResponse *response = request->beginResponse(302, "text/html", "");
response->addHeader("Location", "/?success=1");
request->send(response);
displayToggle(false);
delay(500);
ESP.restart();
} else {
Serial.println("Error saving configuration!");
String errorPage = "<!DOCTYPE html><html><head><title>Error</title></head><body>";
errorPage += "<h1>Configuration Error:</h1>";
errorPage += "<p>Couldn't save new configuration. Please try again.</p>";
errorPage += "<a href='/'>Back</a></body></html>";
AsyncWebServerResponse *response = request->beginResponse(500, "text/html", errorPage);
request->send(response);
}
} }
void handleAction(AsyncWebServerRequest *request) { void handleAction(AsyncWebServerRequest *request) {

View File

@@ -60,7 +60,7 @@ namespace WIFI_Utils {
Serial.print(millis()); Serial.print(millis());
Serial.println("Reconnecting to WiFi..."); Serial.println("Reconnecting to WiFi...");
WiFi.disconnect(); WiFi.disconnect();
WIFI_Utils::startWiFi();//WiFi.reconnect(); WIFI_Utils::startWiFi();
previousWiFiMillis = millis(); previousWiFiMillis = millis();
if (Config.backupDigiMode) { if (Config.backupDigiMode) {
@@ -132,7 +132,7 @@ namespace WIFI_Utils {
digitalWrite(INTERNAL_LED_PIN,LOW); digitalWrite(INTERNAL_LED_PIN,LOW);
#endif #endif
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
Serial.print("Connected as "); Serial.print("\nConnected as ");
Serial.print(WiFi.localIP()); Serial.print(WiFi.localIP());
Serial.print(" / MAC Address: "); Serial.print(" / MAC Address: ");
Serial.println(WiFi.macAddress()); Serial.println(WiFi.macAddress());

View File

@@ -42,6 +42,7 @@ float newHum, newTemp, newPress, newGas;
Adafruit_BME280 bme280; Adafruit_BME280 bme280;
Adafruit_AHTX0 aht20;
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) #if defined(HELTEC_V3) || defined(HELTEC_V3_2)
Adafruit_BMP280 bmp280(&Wire1); Adafruit_BMP280 bmp280(&Wire1);
Adafruit_Si7021 si7021 = Adafruit_Si7021(); Adafruit_Si7021 si7021 = Adafruit_Si7021();
@@ -71,6 +72,7 @@ namespace WX_Utils {
#endif #endif
err = Wire.endTransmission(); err = Wire.endTransmission();
#endif #endif
delay(5);
if (err == 0) { if (err == 0) {
//Serial.println(addr); //this shows any connected board to I2C //Serial.println(addr); //this shows any connected board to I2C
if (addr == 0x76 || addr == 0x77) { // BME or BMP if (addr == 0x76 || addr == 0x77) { // BME or BMP
@@ -118,15 +120,19 @@ namespace WX_Utils {
Serial.println("BMP280 sensor found"); Serial.println("BMP280 sensor found");
wxModuleType = 2; wxModuleType = 2;
wxModuleFound = true; wxModuleFound = true;
if (aht20.begin()) {
Serial.println("AHT20 sensor found");
if (wxModuleType == 2) wxModuleType = 6;
}
} }
} }
} else if (wxModuleAddress == 0x40) { } else if (wxModuleAddress == 0x40 && Config.battery.useExternalI2CSensor == false) {
if(si7021.begin()) { if(si7021.begin()) {
Serial.println("Si7021 sensor found"); Serial.println("Si7021 sensor found");
wxModuleType = 4; wxModuleType = 4;
wxModuleFound = true; wxModuleFound = true;
} }
} }
#ifdef LIGHTGATEWAY_PLUS_1_0 #ifdef LIGHTGATEWAY_PLUS_1_0
else if (wxModuleAddress == 0x70) { else if (wxModuleAddress == 0x70) {
if (shtc3.begin()) { if (shtc3.begin()) {
@@ -264,15 +270,27 @@ namespace WX_Utils {
newPress = 0; newPress = 0;
break; break;
case 5: // SHTC3 case 5: // SHTC3
#ifdef LIGHTGATEWAY_PLUS_1_0 {
sensors_event_t humidity, temp; #ifdef LIGHTGATEWAY_PLUS_1_0
shtc3.getEvent(&humidity, &temp); sensors_event_t humidity, temp;
newTemp = temp.temperature; shtc3.getEvent(&humidity, &temp);
newHum = humidity.relative_humidity; newTemp = temp.temperature;
newPress = 0; newHum = humidity.relative_humidity;
#endif newPress = 0;
#endif
}
break; break;
} case 6: // BMP280 + AHT20
{
bmp280.takeForcedMeasurement();
newTemp = bmp280.readTemperature();
newPress = (bmp280.readPressure() / 100.0F);
sensors_event_t humidity, temp;
aht20.getEvent(&humidity, &temp);
newHum = humidity.relative_humidity;
}
break;
}
if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) { if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) {
Serial.println("BME/BMP/Si7021 Module data failed"); Serial.println("BME/BMP/Si7021 Module data failed");
@@ -280,16 +298,16 @@ namespace WX_Utils {
return ".../...g...t..."; return ".../...g...t...";
} else { } else {
String tempStr = generateTempString(((newTemp + Config.wxsensor.temperatureCorrection) * 1.8) + 32); String tempStr = generateTempString(((newTemp + Config.wxsensor.temperatureCorrection) * 1.8) + 32);
String humStr; String humStr;
if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4 || wxModuleType == 5) { if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4 || wxModuleType == 5 || wxModuleType == 6) {
humStr = generateHumString(newHum); humStr = generateHumString(newHum);
} else if (wxModuleType == 2) { } else if (wxModuleType == 2) {
humStr = ".."; humStr = "..";
} }
String presStr = (wxModuleType == 4 || wxModuleType == 5) String presStr = (wxModuleType == 4 || wxModuleType == 5)
? "....." ? "....."
: generatePresString(newPress + getAltitudeCorrection() / CORRECTION_FACTOR); : generatePresString(newPress + getAltitudeCorrection() / CORRECTION_FACTOR);
fifthLine = "BME-> "; fifthLine = "BME-> ";

View File

@@ -32,6 +32,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_NUM_3 #define GPIO_WAKEUP_PIN GPIO_NUM_3
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -22,6 +22,7 @@
// LoRa Radio // LoRa Radio
#define HAS_SX1268 #define HAS_SX1268
#define HAS_1W_LORA #define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18 #define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19 #define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23 #define RADIO_MOSI_PIN 23
@@ -34,6 +35,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12 #define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -22,6 +22,7 @@
// LoRa Radio // LoRa Radio
#define HAS_SX1262 #define HAS_SX1262
#define HAS_1W_LORA #define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18 #define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19 #define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23 #define RADIO_MOSI_PIN 23
@@ -34,8 +35,11 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12 #define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY
#undef OLED_SDA #undef OLED_SDA
#undef OLED_SCL #undef OLED_SCL

View File

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

View File

@@ -34,6 +34,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12 #define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -34,6 +34,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_33 #define GPIO_WAKEUP_PIN GPIO_SEL_33
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -30,6 +30,9 @@
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN #define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_26 #define GPIO_WAKEUP_PIN GPIO_SEL_26
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -30,6 +30,9 @@
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN #define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_26 #define GPIO_WAKEUP_PIN GPIO_SEL_26
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

@@ -2,6 +2,9 @@
board = esp32dev board = esp32dev
build_flags = build_flags =
${common.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 -D ESP32_DIY_LoRa_915
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}

View File

@@ -28,6 +28,9 @@
#define RADIO_RST_PIN 0 #define RADIO_RST_PIN 0
#define RADIO_BUSY_PIN 32 #define RADIO_BUSY_PIN 32
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

@@ -2,6 +2,9 @@
board = esp32dev board = esp32dev
build_flags = build_flags =
${common.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 -D ESP32_DIY_LoRa_A7670
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}

View File

@@ -28,6 +28,9 @@
#define RADIO_RST_PIN 0 #define RADIO_RST_PIN 0
#define RADIO_BUSY_PIN 32 #define RADIO_BUSY_PIN 32
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

@@ -2,6 +2,9 @@
board = esp32dev board = esp32dev
build_flags = build_flags =
${common.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 -D ESP32_DIY_LoRa_A7670_915
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}

View File

@@ -0,0 +1,50 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
*
* This file is part of LoRa APRS iGate.
*
* LoRa APRS iGate is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BOARD_PINOUT_H_
#define BOARD_PINOUT_H_
// LoRa Radio
#define HAS_SX1278
#define RADIO_SCLK_PIN 36
#define RADIO_MISO_PIN 37
#define RADIO_MOSI_PIN 35
#define RADIO_CS_PIN 34
#define RADIO_RST_PIN 2
#define RADIO_BUSY_PIN 3
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_3
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display
//#define HAS_DISPLAY
#undef OLED_SDA
#undef OLED_SCL
#undef OLED_RST
#define OLED_SDA 21
#define OLED_SCL 22
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
// Aditional Config
#define INTERNAL_LED_PIN 39
#endif

View File

@@ -0,0 +1,11 @@
[env:LoRaHAM_V2]
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}
${common.display_libs}

View File

@@ -22,6 +22,7 @@
// LoRa Radio // LoRa Radio
#define HAS_SX1268 #define HAS_SX1268
#define HAS_1W_LORA #define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_SCLK_PIN 18 #define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19 #define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23 #define RADIO_MOSI_PIN 23
@@ -34,6 +35,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_33 #define GPIO_WAKEUP_PIN GPIO_SEL_33
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -34,6 +34,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_5 #define GPIO_WAKEUP_PIN GPIO_SEL_5
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -22,6 +22,7 @@
// LoRa Radio // LoRa Radio
#define HAS_SX1268 #define HAS_SX1268
#define HAS_1W_LORA #define HAS_1W_LORA
#define HAS_TCXO
#define RADIO_VCC_PIN 21 #define RADIO_VCC_PIN 21
#define RADIO_SCLK_PIN 12 #define RADIO_SCLK_PIN 12
#define RADIO_MISO_PIN 13 #define RADIO_MISO_PIN 13
@@ -35,6 +36,9 @@
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN #define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_5 #define GPIO_WAKEUP_PIN GPIO_SEL_5
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -30,6 +30,9 @@
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN #define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_26 #define GPIO_WAKEUP_PIN GPIO_SEL_26
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

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

View File

@@ -31,6 +31,9 @@
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN #define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_12 #define GPIO_WAKEUP_PIN GPIO_SEL_12
// I2C
#define USE_WIRE_WITH_OLED_PINS
// Display // Display
#define HAS_DISPLAY #define HAS_DISPLAY

View File

@@ -2,6 +2,9 @@
board = wemos_d1_uno32 board = wemos_d1_uno32
build_flags = build_flags =
${common.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 -D WEMOS_D1_R32_RA02
lib_deps = lib_deps =
${common.lib_deps} ${common.lib_deps}

View File

@@ -30,7 +30,10 @@
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN #define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
#define GPIO_WAKEUP_PIN GPIO_SEL_25 #define GPIO_WAKEUP_PIN GPIO_SEL_25
// Display // I2C
#define USE_WIRE_WITH_OLED_PINS
// Display
#define HAS_DISPLAY #define HAS_DISPLAY
#undef OLED_SDA #undef OLED_SDA

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