Compare commits
426 Commits
V1.0.4.1
...
decodingFi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff8c7581fa | ||
|
|
449a8557d2 | ||
|
|
4419c98920 | ||
|
|
45bf90817b | ||
|
|
ef30a1bf58 | ||
|
|
9a705d3dfa | ||
|
|
63f74396d2 | ||
|
|
400b77c2d3 | ||
|
|
31daddf917 | ||
|
|
529a44018f | ||
|
|
5ba9c5b382 | ||
|
|
c7a0e3773b | ||
|
|
a5f9e5b844 | ||
|
|
24f407d51c | ||
|
|
2c6665b557 | ||
|
|
38f52564f1 | ||
|
|
49e92c622f | ||
|
|
5370850ae1 | ||
|
|
984e9f9561 | ||
|
|
f60252ef94 | ||
|
|
63257d6329 | ||
|
|
18929ad379 | ||
|
|
cda901142d | ||
|
|
8008143267 | ||
|
|
3416e21a8c | ||
|
|
5882b54c62 | ||
|
|
6a4c0ef01a | ||
|
|
0bd3201ef7 | ||
|
|
b5d9103ea5 | ||
|
|
20a5029da8 | ||
|
|
b3cc8af180 | ||
|
|
ca16761f68 | ||
|
|
e593b2c670 | ||
|
|
08a7e0aac1 | ||
|
|
9b258c42ab | ||
|
|
8712122d33 | ||
|
|
9e34684627 | ||
|
|
e4aa52241d | ||
|
|
c840ef01fa | ||
|
|
98f85725f8 | ||
|
|
10bde884b1 | ||
|
|
546e4f63a4 | ||
|
|
87f2ec2b7b | ||
|
|
c9c7e24aae | ||
|
|
b86133223d | ||
|
|
880a41bfd8 | ||
|
|
d610b3a90f | ||
|
|
23a33ed27c | ||
|
|
72ef827900 | ||
|
|
b756f97f55 | ||
|
|
2625e7b91d | ||
|
|
75337d6c29 | ||
|
|
a618383617 | ||
|
|
8add599838 | ||
|
|
83ec2265c6 | ||
|
|
bec4f4f473 | ||
|
|
87a67cd2c9 | ||
|
|
4e48b2d02d | ||
|
|
607277bfe9 | ||
|
|
c2f8596667 | ||
|
|
ba7ff2a2d9 | ||
|
|
b44eb1028d | ||
|
|
d7602268f2 | ||
|
|
2f6f9be28e | ||
|
|
92572245cd | ||
|
|
152217e71c | ||
|
|
d334164b6f | ||
|
|
61409ce683 | ||
|
|
742a6b6477 | ||
|
|
dd2fce3908 | ||
|
|
90b29b66d5 | ||
|
|
2a86cdb0d8 | ||
|
|
1abaa299e0 | ||
|
|
9cbd7c22fd | ||
|
|
e15e6def91 | ||
|
|
33c8be4f48 | ||
|
|
c2a7c26d78 | ||
|
|
387e8a988a | ||
|
|
48b075a554 | ||
|
|
7e76e43817 | ||
|
|
9b7b4e1838 | ||
|
|
5b9d56843b | ||
|
|
30eb1023dc | ||
|
|
1ba86af61b | ||
|
|
1d475bc8a2 | ||
|
|
70f9a6fa04 | ||
|
|
6747511850 | ||
|
|
96a4394e89 | ||
|
|
4e009bc14c | ||
|
|
4c11c66a1c | ||
|
|
a89181b5df | ||
|
|
9a94a193f9 | ||
|
|
9c7f6c7934 | ||
|
|
53e8941262 | ||
|
|
60a148d362 | ||
|
|
36cd1578cc | ||
|
|
c3036f290f | ||
|
|
8eb4ef1dc2 | ||
|
|
d5930380b4 | ||
|
|
e42f4b59ed | ||
|
|
2697f2a5f2 | ||
|
|
36c970aed9 | ||
|
|
10e1581bc2 | ||
|
|
bc57d1609a | ||
|
|
37380c6b9d | ||
|
|
855c84c079 | ||
|
|
bb4df08f06 | ||
|
|
6d0e98f4a2 | ||
|
|
c5dcc4c7ab | ||
|
|
5c5c940d71 | ||
|
|
57decd2ac7 | ||
|
|
26a9d1fa99 | ||
|
|
bf1ab1bfb4 | ||
|
|
d32f35ced1 | ||
|
|
db37567538 | ||
|
|
dc1bdbb703 | ||
|
|
74a8fa7969 | ||
|
|
b65a6de018 | ||
|
|
1840ce2338 | ||
|
|
b8ef262cc7 | ||
|
|
bb795816e1 | ||
|
|
a046791722 | ||
|
|
88eda7b3fd | ||
|
|
03991e245d | ||
|
|
8a02e953c3 | ||
|
|
4651938cb1 | ||
|
|
83a39c2093 | ||
|
|
bc596f099d | ||
|
|
438a6ee536 | ||
|
|
206c79f0a9 | ||
|
|
236cfa724e | ||
|
|
028a82a40e | ||
|
|
bb9a061266 | ||
|
|
fd24ce8598 | ||
|
|
6cbe8173e8 | ||
|
|
b4261f37cb | ||
|
|
b5a68a6758 | ||
|
|
ce3d136954 | ||
|
|
996a8976ba | ||
|
|
ce4ace5f21 | ||
|
|
93fe8aedc9 | ||
|
|
892709e07d | ||
|
|
2b90af1cf8 | ||
|
|
a5f420d591 | ||
|
|
368dd06e05 | ||
|
|
357483c44a | ||
|
|
6f9d71c1d8 | ||
|
|
6bff66c67b | ||
|
|
d443218d50 | ||
|
|
10c7f9036b | ||
|
|
02d9448762 | ||
|
|
61fc6b5f2a | ||
|
|
b45f378817 | ||
|
|
e11b2f5e4a | ||
|
|
c805c06e53 | ||
|
|
2a54830d08 | ||
|
|
6a262599f5 | ||
|
|
84e70afda2 | ||
|
|
46a3dc0d39 | ||
|
|
50b3094de9 | ||
|
|
48c919f3f2 | ||
|
|
5cda9a2285 | ||
|
|
194ae94a43 | ||
|
|
9baf066386 | ||
|
|
7595da21cd | ||
|
|
6be28b47ce | ||
|
|
3ea1a19a06 | ||
|
|
35cafac601 | ||
|
|
b59153d8df | ||
|
|
79b967034f | ||
|
|
a40502344f | ||
|
|
af1b623f7f | ||
|
|
0c7d13cf7d | ||
|
|
10798d2c9a | ||
|
|
3cc8fed15f | ||
|
|
725be9fed5 | ||
|
|
16d9ef1c06 | ||
|
|
d2b7c063f7 | ||
|
|
eaced15265 | ||
|
|
af4bc20ac2 | ||
|
|
14473cb7c7 | ||
|
|
aba82ef3e0 | ||
|
|
79405abe06 | ||
|
|
c623c0502e | ||
|
|
a6ae8523fa | ||
|
|
d454ab4ba2 | ||
|
|
5146db796e | ||
|
|
93e536e739 | ||
|
|
0af4670dee | ||
|
|
d70a35de1c | ||
|
|
eb5b3aaa25 | ||
|
|
ae3947b7ea | ||
|
|
4f9eac9e5c | ||
|
|
e689bb592c | ||
|
|
786f5bdc0e | ||
|
|
ea2a83bc26 | ||
|
|
c406b67484 | ||
|
|
7cc397ea33 | ||
|
|
503aba71d0 | ||
|
|
8b7e0fdcef | ||
|
|
ad996c8a49 | ||
|
|
dedd7152d9 | ||
|
|
5f5d7d7868 | ||
|
|
2e3cafd0f0 | ||
|
|
1c07c2fb2b | ||
|
|
4c63dd8bb7 | ||
|
|
b00fba9693 | ||
|
|
37162b9708 | ||
|
|
44d9732aa2 | ||
|
|
312bdc9d9f | ||
|
|
ff0b96bfe4 | ||
|
|
36a8ef0ffb | ||
|
|
5ce8059040 | ||
|
|
60aef00b24 | ||
|
|
72c2c144ae | ||
|
|
a1c552f197 | ||
|
|
8ede848199 | ||
|
|
a4a82b75c5 | ||
|
|
64ac924f1f | ||
|
|
bde4a7f042 | ||
|
|
d54b63df22 | ||
|
|
c3d94d673a | ||
|
|
45edf2ffa3 | ||
|
|
d628de9c9c | ||
|
|
f879182f62 | ||
|
|
c9ed618a8b | ||
|
|
6c7b8f9918 | ||
|
|
c2b4b3b92f | ||
|
|
f7319ce591 | ||
|
|
858162eb9c | ||
|
|
100b002309 | ||
|
|
3acf73bf5f | ||
|
|
53675e8084 | ||
|
|
4349019f7b | ||
|
|
411753c0aa | ||
|
|
ad6aed7f0d | ||
|
|
f325c54fc3 | ||
|
|
1a3966eadc | ||
|
|
ffd3eaeb49 | ||
|
|
df03a49123 | ||
|
|
58d647bad1 | ||
|
|
59988fbaf1 | ||
|
|
1ad13e3c09 | ||
|
|
ffe1a2f830 | ||
|
|
a0fdd78cb1 | ||
|
|
95e437cb50 | ||
|
|
9c488991e2 | ||
|
|
ddcd33b94a | ||
|
|
b688391a0e | ||
|
|
56d63d0389 | ||
|
|
ad5a5ccf18 | ||
|
|
92bc0a7667 | ||
|
|
ffcfc42b8a | ||
|
|
f3e0830473 | ||
|
|
62107a5b4a | ||
|
|
9801545965 | ||
|
|
dd89f56436 | ||
|
|
27593d8718 | ||
|
|
008575b422 | ||
|
|
b8eedabe9a | ||
|
|
7371b8e37a | ||
|
|
44605e82c2 | ||
|
|
0360085131 | ||
|
|
57f720b0d1 | ||
|
|
3d01ea03c0 | ||
|
|
afb6a60bfe | ||
|
|
06ef37e4dc | ||
|
|
9159362796 | ||
|
|
47c136fdd5 | ||
|
|
970d743b80 | ||
|
|
7c5e58f3b2 | ||
|
|
210c7acb70 | ||
|
|
8d102a73c1 | ||
|
|
5030d2d645 | ||
|
|
a95f55273f | ||
|
|
4014db03c8 | ||
|
|
d96d5f1963 | ||
|
|
d1cb732ae1 | ||
|
|
1504bf5a7f | ||
|
|
3c27e52df7 | ||
|
|
3d5f3a4914 | ||
|
|
db6292ce59 | ||
|
|
7995d23779 | ||
|
|
7f150bc4a0 | ||
|
|
ff5650f0c5 | ||
|
|
a91f0f3f3c | ||
|
|
c9577b6c21 | ||
|
|
10cb7c6e45 | ||
|
|
99052e594a | ||
|
|
a05917c494 | ||
|
|
e2e935797f | ||
|
|
5ceff0064f | ||
|
|
8beb7c0465 | ||
|
|
112e38312d | ||
|
|
5a1c6e7ed9 | ||
|
|
0defd5b289 | ||
|
|
cdac54c34e | ||
|
|
71e98487f6 | ||
|
|
510b5bdc59 | ||
|
|
00db358cc3 | ||
|
|
c0641986aa | ||
|
|
55378bb9f3 | ||
|
|
79cf50a630 | ||
|
|
03ccd2b12e | ||
|
|
d5ffd26c3c | ||
|
|
2f0f991785 | ||
|
|
975b140c73 | ||
|
|
f2abe34af1 | ||
|
|
a786d83b9e | ||
|
|
bf0a0d6e4c | ||
|
|
d08e5b1b27 | ||
|
|
f387e03b53 | ||
|
|
16215f9e56 | ||
|
|
ce01d0aa4d | ||
|
|
b0a05f89f6 | ||
|
|
58d8736969 | ||
|
|
df9060d906 | ||
|
|
2bd6844d89 | ||
|
|
0a79b6459b | ||
|
|
d5eb32a38b | ||
|
|
d6c5741fde | ||
|
|
1063a82294 | ||
|
|
6913b6b89f | ||
|
|
42c990f5cf | ||
|
|
a17900e4d0 | ||
|
|
3fd819bd4e | ||
|
|
640166a50b | ||
|
|
a9bcae62e2 | ||
|
|
3e0368383a | ||
|
|
8b790ef656 | ||
|
|
c110b41b99 | ||
|
|
be7fe23cd5 | ||
|
|
202d03c61f | ||
|
|
ddb45544db | ||
|
|
c9ea51c6da | ||
|
|
72fd2b45ec | ||
|
|
0e46c64880 | ||
|
|
dd12330e85 | ||
|
|
2edb183cad | ||
|
|
775677d5e8 | ||
|
|
a60a6d6e4b | ||
|
|
14abe14703 | ||
|
|
345fb1e937 | ||
|
|
916eb038e5 | ||
|
|
40fc177828 | ||
|
|
5dc6e99972 | ||
|
|
db5710ec6a | ||
|
|
65acb54dbe | ||
|
|
6088c61e73 | ||
|
|
a8c972c5e1 | ||
|
|
bd3a9f91d6 | ||
|
|
643a18f40c | ||
|
|
46e5bdd21e | ||
|
|
ea426f4126 | ||
|
|
5cbdd318a2 | ||
|
|
735988ee1c | ||
|
|
d5b666f458 | ||
|
|
702f4abcbb | ||
|
|
d0070e4381 | ||
|
|
f4744eab6a | ||
|
|
bd672857f4 | ||
|
|
9cc26ccdae | ||
|
|
8863838210 | ||
|
|
f11b6de57a | ||
|
|
55d6bc325f | ||
|
|
ee687e6959 | ||
|
|
d1732a6bba | ||
|
|
4fb4712a33 | ||
|
|
39276b0d32 | ||
|
|
22203a7c24 | ||
|
|
d8d832fde9 | ||
|
|
61db6c3132 | ||
|
|
9521a357b1 | ||
|
|
ee95931f00 | ||
|
|
60aa2b05ac | ||
|
|
bc5183a192 | ||
|
|
1019306c7c | ||
|
|
fbe00903ad | ||
|
|
291a6bc80e | ||
|
|
3b550c7f81 | ||
|
|
4659f8b2ff | ||
|
|
62d157d7f0 | ||
|
|
b0e116ee1d | ||
|
|
00b12c2bf5 | ||
|
|
85b21565db | ||
|
|
b9e2c0e8f0 | ||
|
|
b763b4af78 | ||
|
|
5553e7ae5c | ||
|
|
1afd039c3f | ||
|
|
1888084f31 | ||
|
|
dc92b822c8 | ||
|
|
3c305259e0 | ||
|
|
497d648c17 | ||
|
|
d267f6699c | ||
|
|
16ce6bc6d2 | ||
|
|
008b6250ea | ||
|
|
ddb83d1368 | ||
|
|
26e2dec2b2 | ||
|
|
174507377b | ||
|
|
864ed44a5d | ||
|
|
5d69cfbe1c | ||
|
|
e57d356fd1 | ||
|
|
8381f0916c | ||
|
|
beca09293a | ||
|
|
dcca18eeac | ||
|
|
2494acea55 | ||
|
|
0c50ce8de4 | ||
|
|
2f07d35861 | ||
|
|
91240c5be6 | ||
|
|
b4308a95d5 | ||
|
|
6c301795d6 | ||
|
|
5f0c6f4986 | ||
|
|
7c989e6e9e | ||
|
|
2bcda6d3a4 | ||
|
|
848492dc36 | ||
|
|
413bef67fb | ||
|
|
3fa4c9371a | ||
|
|
e846c38f97 | ||
|
|
b8446e3f1d | ||
|
|
c3984bc8da | ||
|
|
601b72da9f | ||
|
|
5d9493314d | ||
|
|
86cf12d6c1 | ||
|
|
e5e2c6a980 | ||
|
|
3b9d49c5f9 | ||
|
|
ab9443140b |
47
.github/workflows/build.yml
vendored
@@ -15,14 +15,22 @@ jobs:
|
|||||||
chip: esp32
|
chip: esp32
|
||||||
- name: ttgo-lora32-v21_915
|
- name: ttgo-lora32-v21_915
|
||||||
chip: esp32
|
chip: esp32
|
||||||
|
- name: ttgo_lora32_t3s3_v1_2
|
||||||
|
chip: esp32s3
|
||||||
- name: heltec-lora32-v2
|
- name: heltec-lora32-v2
|
||||||
chip: esp32
|
chip: esp32
|
||||||
- name: heltec_wifi_lora_32_V3
|
- name: heltec_wifi_lora_32_V3
|
||||||
chip: esp32s3
|
chip: esp32s3
|
||||||
|
- name: heltec_wifi_lora_32_V3_2
|
||||||
|
chip: esp32s3
|
||||||
- name: heltec_wireless_stick
|
- name: heltec_wireless_stick
|
||||||
chip: esp32s3
|
chip: esp32s3
|
||||||
- name: heltec_wireless_stick_lite_v3
|
- name: heltec_wireless_stick_lite_v3
|
||||||
chip: esp32s3
|
chip: esp32s3
|
||||||
|
- name: heltec_wireless_stick_lite_v3_display
|
||||||
|
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
|
||||||
@@ -31,6 +39,10 @@ jobs:
|
|||||||
chip: esp32
|
chip: esp32
|
||||||
- name: ESP32_DIY_1W_LoRa_915
|
- name: ESP32_DIY_1W_LoRa_915
|
||||||
chip: esp32
|
chip: esp32
|
||||||
|
- name: ESP32_DIY_1W_LoRa_LLCC68
|
||||||
|
chip: esp32
|
||||||
|
- name: ESP32_DIY_1W_LoRa_Mesh_V1_2
|
||||||
|
chip: esp32
|
||||||
- name: ttgo-t-beam-v1_2
|
- name: ttgo-t-beam-v1_2
|
||||||
chip: esp32
|
chip: esp32
|
||||||
- name: ttgo-t-beam-v1_2_915
|
- name: ttgo-t-beam-v1_2_915
|
||||||
@@ -43,6 +55,12 @@ jobs:
|
|||||||
chip: esp32
|
chip: esp32
|
||||||
- name: ttgo-t-beam-v1_2_SX1262
|
- name: ttgo-t-beam-v1_2_SX1262
|
||||||
chip: esp32
|
chip: esp32
|
||||||
|
- name: ttgo_t_deck_plus
|
||||||
|
chip: esp32s3
|
||||||
|
- name: ttgo_t_deck_GPS
|
||||||
|
chip: esp32s3
|
||||||
|
- name: ttgo_t_beam_s3_SUPREME_v3
|
||||||
|
chip: esp32s3
|
||||||
- name: ESP32_DIY_LoRa_A7670
|
- name: ESP32_DIY_LoRa_A7670
|
||||||
chip: esp32
|
chip: esp32
|
||||||
- name: ESP32_DIY_LoRa_A7670_915
|
- name: ESP32_DIY_LoRa_A7670_915
|
||||||
@@ -51,16 +69,34 @@ jobs:
|
|||||||
chip: esp32s3
|
chip: esp32s3
|
||||||
- name: heltec_ht-ct62
|
- name: heltec_ht-ct62
|
||||||
chip: esp32c3
|
chip: esp32c3
|
||||||
|
- name: heltec_wireless_paper_v1
|
||||||
|
chip: esp32s3
|
||||||
|
- name: heltec_wireless_paper_v1_2
|
||||||
|
chip: esp32s3
|
||||||
|
- name: heltec_vision_master_e290
|
||||||
|
chip: esp32s3
|
||||||
- name: OE5HWN_MeshCom
|
- name: OE5HWN_MeshCom
|
||||||
chip: esp32
|
chip: esp32
|
||||||
- name: WEMOS-LOLIN32-OLED-DIY
|
- name: WEMOS-LOLIN32-OLED-DIY
|
||||||
chip: esp32
|
chip: esp32
|
||||||
- name: WEMOS-D1-R32-RA02
|
- name: WEMOS-D1-R32-RA02
|
||||||
chip: esp32
|
chip: esp32
|
||||||
|
- name: WEMOS_S2_MINI_DIY_LoRa
|
||||||
|
chip: esp32s2
|
||||||
- name: esp32c3_DIY_1W_LoRa
|
- name: esp32c3_DIY_1W_LoRa
|
||||||
chip: esp32c3
|
chip: esp32c3
|
||||||
- name: esp32c3_DIY_1W_LoRa_915
|
- name: esp32c3_DIY_1W_LoRa_915
|
||||||
chip: esp32c3
|
chip: esp32c3
|
||||||
|
- name: ESP32_C3_OctopusLab_LoRa
|
||||||
|
chip: esp32c3
|
||||||
|
- name: QRPLabs_LightGateway_1_0
|
||||||
|
chip: esp32s3
|
||||||
|
- name: QRPLabs_LightGateway_Plus_1_0
|
||||||
|
chip: esp32s3
|
||||||
|
- name: XIAO_ESP32S3_WIO_SX1262
|
||||||
|
chip: esp32s3
|
||||||
|
- name: TROY_LoRa_APRS
|
||||||
|
chip: esp32
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
@@ -102,6 +138,17 @@ jobs:
|
|||||||
0xe000 installer/firmware/boot_app0.bin \
|
0xe000 installer/firmware/boot_app0.bin \
|
||||||
0x10000 installer/firmware/firmware.bin \
|
0x10000 installer/firmware/firmware.bin \
|
||||||
0x3D0000 installer/firmware/spiffs.bin
|
0x3D0000 installer/firmware/spiffs.bin
|
||||||
|
elif [ "${{ matrix.target.chip }}" == "esp32s2" ]; then
|
||||||
|
python installer/bin/esptool/esptool.py --chip esp32s2 merge_bin \
|
||||||
|
-o installer/web_factory.bin \
|
||||||
|
--flash_mode dio \
|
||||||
|
--flash_freq 40m \
|
||||||
|
--flash_size 4MB \
|
||||||
|
0x1000 installer/firmware/bootloader.bin \
|
||||||
|
0x8000 installer/firmware/partitions.bin \
|
||||||
|
0xe000 installer/firmware/boot_app0.bin \
|
||||||
|
0x10000 installer/firmware/firmware.bin \
|
||||||
|
0x3D0000 installer/firmware/spiffs.bin
|
||||||
elif [ "${{ matrix.target.chip }}" == "esp32s3" ]; then
|
elif [ "${{ matrix.target.chip }}" == "esp32s3" ]; then
|
||||||
python installer/bin/esptool/esptool.py --chip esp32s3 merge_bin \
|
python installer/bin/esptool/esptool.py --chip esp32s3 merge_bin \
|
||||||
-o installer/web_factory.bin \
|
-o installer/web_factory.bin \
|
||||||
|
|||||||
687
LICENSE
@@ -1,21 +1,674 @@
|
|||||||
MIT License
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (c) 2023 Ricardo Guzman (Richonguzman)
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Preamble
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The GNU General Public License is a free, copyleft license for
|
||||||
copies or substantial portions of the Software.
|
software and other kinds of works.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
The licenses for most software and other practical works are designed
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
to take away your freedom to share and change the works. By contrast,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
share and change all versions of a program--to make sure it remains free
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
GNU General Public License for most of our software; it applies also to
|
||||||
SOFTWARE.
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program 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.
|
||||||
|
|
||||||
|
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
|
|||||||
109
README.md
@@ -1,45 +1,46 @@
|
|||||||
# Richonguzman / CA2RXU LoRa APRS iGate/Digirepeater
|
# CA2RXU LoRa APRS iGate/Digipeater
|
||||||
|
|
||||||
This firmware is for using ESP32 based boards with LoRa Modules and GPS to live in the APRS world.
|
This firmware is for using ESP32 based boards with LoRa Modules and GPS to live in the APRS world.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
__(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__
|
__(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__
|
||||||
|
<br />
|
||||||
|
|
||||||
___________________________________________________
|
____________________________________________________
|
||||||
|
|
||||||
|
# <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">WEB FLASHER/INSTALLER</a>
|
||||||
|
|
||||||
|
|
||||||
|
# <a href="https://drive.google.com/file/d/1Hff_Szd7ks8RC7_RiV6POxPJlclbO05M/view?usp=sharing" target="_blank">LoRa APRS iGate CA2RXU Firmware Manual</a>
|
||||||
|
|
||||||
|
____________________________________________________
|
||||||
|
|
||||||
## You can support this project to continue to grow:
|
## You can support this project to continue to grow:
|
||||||
|
|
||||||
[<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/blob/main/images/github-sponsors.png">](https://github.com/sponsors/richonguzman) [<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/blob/main/images/paypalme.png">](http://paypal.me/richonguzman)
|
[<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/raw/main/images/github-sponsors.png">](https://github.com/sponsors/richonguzman) [<img src="https://github.com/richonguzman/LoRa_APRS_Tracker/raw/main/images/paypalme.png">](http://paypal.me/richonguzman)
|
||||||
|
|
||||||
____________________________________________________
|
____________________________________________________
|
||||||
|
|
||||||
# WEB FLASHER/INSTALLER is <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">here</a>
|
## SUPPORTED BOARDS (<a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/Supported-Boards-and-Buying-Links" target="_blank">Buying links</a>).
|
||||||
|
|
||||||
____________________________________________________
|
|
||||||
|
|
||||||
# WIKI
|
|
||||||
|
|
||||||
### FAQ, BME280, TNC and more --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/00.-FAQ-(frequently-asked-questions)" target="_blank">here</a>.
|
|
||||||
|
|
||||||
### Installation Guide --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/01.-Installation-Guide" target="_blank">here</a>.
|
|
||||||
|
|
||||||
|
|
||||||
____________________________________________________
|
|
||||||
|
|
||||||
# SUPPORTED BOARDS
|
|
||||||
|
|
||||||
### Buying links --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/108.-Supported-Boards-and-Buying-Links" target="_blank">here</a>.
|
|
||||||
|
|
||||||
(NOTE: all boards with 433-868-915 MHz versions)
|
(NOTE: all boards with 433-868-915 MHz versions)
|
||||||
|
|
||||||
- TTGO Lilygo LoRa32 v2.1 / v1.6 (they work 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.
|
||||||
|
|
||||||
- HELTEC V2, V3 , Wireless Stick, Wireless Stick Lite, HT-CT62, Wireless Tracker.
|
- T-Deck Plus (and also regular T-Deck with/without GPS).
|
||||||
|
|
||||||
- ESP32 Wroom + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
|
- HELTEC V2, V3, V3.2, T114, Wireless Stick, Wireless Stick Lite V3/V3.2, HT-CT62, Wireless Tracker, Wireless Paper.
|
||||||
|
|
||||||
|
- RAK Wireless 4631 + 19007(or 19003)
|
||||||
|
|
||||||
|
- Faketec V3 (NRF52840 + Heltec HTRA62(SX1262))
|
||||||
|
|
||||||
|
- QRP Labs LightGateway 1.0 and Plus 1.0.
|
||||||
|
|
||||||
|
- ESP32 + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
|
||||||
|
|
||||||
- ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version.
|
- ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version.
|
||||||
|
|
||||||
@@ -47,17 +48,57 @@ ____________________________________________________
|
|||||||
|
|
||||||
- Wemos Lolin32 Oled + SX1278 DIY Version.
|
- Wemos Lolin32 Oled + SX1278 DIY Version.
|
||||||
|
|
||||||
____________________________________________________
|
<br />
|
||||||
## Timeline (Versions):
|
|
||||||
|
|
||||||
- 2024.06.21 Tx packets coming from APRS-IS are (now) formatted into 3rd Party (as they should have been since the beginning).
|
# 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 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.03.20 Manager List added to enable/disable DigiEcoMode and Tx Control. Thanks LB5JJ.
|
||||||
|
- 2025.03.03 T-Beam Supreme board added and more BlackList rules added.
|
||||||
|
- 2025.02.28 Heltec Wireless Paper with Epaper working. Thanks SzymonPriv for pointing to the right library.
|
||||||
|
- 2025.02.25 Objects Rules update, GPS Boards: Satellites on Screen, Wx Height Correction from GPS Data.
|
||||||
|
- 2025.01.22 Added LILYGO T-DECK PLUS (and DIY+GPS version) board support.
|
||||||
|
- 2025.01.11 Added HELTEC V3.2 board support.
|
||||||
|
- 2025.01.07 TROY_LoRa_APRS board added. GMT in quarter hour fix and Beacon fix for TNC.
|
||||||
|
- 2025.01.02 Callsign Black List added.
|
||||||
|
- 2024.12.30 Fixed missing validation for correct Digipeater mode when not connected to APRS-IS.
|
||||||
|
- 2024.12.06 APRS-IS connnection and passcode validation added.
|
||||||
|
- 2024.11.06 (Silent Update) Working now with Board "VARIANTS".
|
||||||
|
- 2024.10.29 Added LILYGO Lora32 T3S3 support.
|
||||||
|
- 2024.10.25 Added QRP Labs LightGateway 1.0 support.
|
||||||
|
- 2024.10.21 Boards with GPS can now send Real-GPS Beacon (also posible: GPS ambiguity of ~ 1 km).
|
||||||
|
- 2024.10.14 Received Packets in WebUI show real Local Time (NTP with GMT offset).
|
||||||
|
- 2024.10.08 New EcoMode for Remote Digipeaters without WiFi/WiFiAP, Screen, Leds (Example: LILYGO LoRa32 uses only 24mA, with WifiAP 150mA). APRS Message/Queries can start/stop this mode too.
|
||||||
|
- 2024.10.06 Cross Frequency Digipeater Rules added.
|
||||||
|
- 2024.09.23 Libraries Update for SDK3
|
||||||
|
- 2024.09.23 Added Enconded Telemetry for Battery (+ External Voltage) in Station GPS Beacon Packet.
|
||||||
|
- 2024.08.23 Wemos S2 Mini DIY LoRa added.
|
||||||
|
- 2024.08.19 HELTEC Wireless Paper working (still missing Epaper code).
|
||||||
|
- 2024.08.13 Web Authentication for WebUI. Thanks Mitja S57PNX.
|
||||||
|
- 2024.08.05 WIDE2-n added to WIDE1-n in Digipeater Modes.
|
||||||
|
- 2024.06.27 External Voltage Divider Resistor configuration on WebUI. Thanks Tilen S54B.
|
||||||
|
- 2024.06.26 Personal Note information on WebUI for the Station. Thanks Tilen S54B.
|
||||||
|
- 2024.06.24 Callsign Validation fix. Thanks Helge SA7SKY.
|
||||||
|
- 2024.06.21 Tx packets coming from APRS-IS are (now) formatted into 3rd Party (as they should have been since the beginning). Thanks Lynn KJ4ERJ and Geoffrey F4FXL.
|
||||||
- 2024.06.18 All boards with 433 / 868 / 915 MHz versions.
|
- 2024.06.18 All boards with 433 / 868 / 915 MHz versions.
|
||||||
- 2024.06.10 ESP32C3 + 1W LoRa Module (E22 400M30S) support added.
|
- 2024.06.10 ESP32C3 + 1W LoRa Module (E22 400M30S) support added.
|
||||||
- 2024.06.09 Si7021 module added (with autodetected I2C Address)
|
- 2024.06.09 Si7021 module added (with autodetected I2C Address)
|
||||||
- 2024.06.08 Callsign Validation for all Station that iGate/Digi hears.
|
- 2024.06.08 Callsign Validation for all Station that iGate/Digi hears.
|
||||||
- 2024.05.27 Battery Monitor for internal and External Voltages (to make board sleep and avoid low discharge of batterys) T-Beam boards now with Battery readings as well.
|
- 2024.05.27 Battery Monitor for internal and External Voltages (to make board sleep and avoid low discharge of batterys) T-Beam boards now with Battery readings as well.
|
||||||
- 2024.05.23 Forced Reboot Mode added.
|
- 2024.05.23 Forced Reboot Mode added.
|
||||||
- 2024.05.22 Experimental backup-Digirepeater-Mode when "only" iGate mode loses WiFi connection added.
|
- 2024.05.22 Experimental backup-Digipeater-Mode when "only" iGate mode loses WiFi connection added.
|
||||||
- 2024.05.20 WebConfig update to control whether Messages and Objects should be Tx to RF.
|
- 2024.05.20 WebConfig update to control whether Messages and Objects should be Tx to RF.
|
||||||
- 2024.05.17 HELTEC Wireless Stick Lite v3 support added.
|
- 2024.05.17 HELTEC Wireless Stick Lite v3 support added.
|
||||||
- 2024.05.14 BME modules will be autodetected (I2C Address and if it is BME280/BMP280/BME680).
|
- 2024.05.14 BME modules will be autodetected (I2C Address and if it is BME280/BMP280/BME680).
|
||||||
@@ -69,12 +110,12 @@ ____________________________________________________
|
|||||||
- 2024.04.21 WEB INSTALLER (thanks Damian SQ2CPA).
|
- 2024.04.21 WEB INSTALLER (thanks Damian SQ2CPA).
|
||||||
- 2024.04.20 New Output Buffer process: no more packets lost.
|
- 2024.04.20 New Output Buffer process: no more packets lost.
|
||||||
- 2024.04.13 Received Packets added on WebUI.
|
- 2024.04.13 Received Packets added on WebUI.
|
||||||
- 2024.04.09 iGate/Digirepeater own GPS beacon is encoded (Base91) now.
|
- 2024.04.09 iGate/Digipeater own GPS beacon is encoded (Base91) now.
|
||||||
- 2024.03.18 OE5HWN MeshCom board support added.
|
- 2024.03.18 OE5HWN MeshCom board support added.
|
||||||
- 2024.02.25 New Web Configuration UI with WiFi AP (thanks Damian SQ2CPA).
|
- 2024.02.25 New Web Configuration UI with WiFi AP (thanks Damian SQ2CPA).
|
||||||
- 2023.01.28 Updated to ElegantOTA v.3 (AsyncElegantOTA was deprecated).
|
- 2023.01.28 Updated to ElegantOTA v.3 (AsyncElegantOTA was deprecated).
|
||||||
- 2024.01.19 TextSerialOutputForApp added to get text from Serial-Output over USB into PC for PinPoint App (https://www.pinpointaprs.com) and APRSIS32 App (http://aprsisce.wikidot.com)
|
- 2024.01.19 TextSerialOutputForApp added to get text from Serial-Output over USB into PC for PinPoint App (https://www.pinpointaprs.com) and APRSIS32 App (http://aprsisce.wikidot.com)
|
||||||
- 2024.01.12 Added iGate Mode to also repeat packets (like a iGate+DigiRepeater) in stationMode 2 and 5.
|
- 2024.01.12 Added iGate Mode to also repeat packets (like a iGate+Digipeater) in stationMode 2 and 5.
|
||||||
- 2024.01.11 Added iGate Mode to enable APRS-IS and LoRa beacon report at the same time.
|
- 2024.01.11 Added iGate Mode to enable APRS-IS and LoRa beacon report at the same time.
|
||||||
- 2024.01.05 Lilygo TTGO T-Beam V1, V1.2, V1 + SX1268, V1.2 + SX1262 support added.
|
- 2024.01.05 Lilygo TTGO T-Beam V1, V1.2, V1 + SX1268, V1.2 + SX1262 support added.
|
||||||
- 2024.01.02 EByte 400M30S 1 Watt LoRa module for DIY ESP32 iGate support added.
|
- 2024.01.02 EByte 400M30S 1 Watt LoRa module for DIY ESP32 iGate support added.
|
||||||
@@ -83,18 +124,18 @@ ____________________________________________________
|
|||||||
- 2023.12.07 MIC-E process and Syslog added.
|
- 2023.12.07 MIC-E process and Syslog added.
|
||||||
- 2023.12.06 HELTEC V2 board support added.
|
- 2023.12.06 HELTEC V2 board support added.
|
||||||
- 2023.11.26 Small correction to enable Syslog in stationMode5.
|
- 2023.11.26 Small correction to enable Syslog in stationMode5.
|
||||||
- 2023.10.09 Added "WIDE1-1" to Tx packets from iGate to be *repeated* by Digirepeaters.
|
- 2023.10.09 Added "WIDE1-1" to Tx packets from iGate to be *repeated* by Digipeaters.
|
||||||
- 2023.10.09 BMP280 module support added.
|
- 2023.10.09 BMP280 module support added.
|
||||||
- 2023.08.20 Added External Voltage Measurement (Max 15V!)
|
- 2023.08.20 Added External Voltage Measurement (Max 15V!)
|
||||||
- 2023.08.05 Ground Height Correction for Pressure readings added.
|
- 2023.08.05 Ground Height Correction for Pressure readings added.
|
||||||
- 2023.07.31 StationMode5 added: iGate when WiFi and APRS available, DigiRepeater when not.
|
- 2023.07.31 StationMode5 added: iGate when WiFi and APRS available, Digipeater when not.
|
||||||
- 2023.07.16 Small OTA, BME module update.
|
- 2023.07.16 Small OTA, BME module update.
|
||||||
- 2023.07.05 Adding monitor info of Battery connected.
|
- 2023.07.05 Adding monitor info of Battery connected.
|
||||||
- 2023.06.18 Info on Oled Screen mayor update, added RSSI and Distance to Listened Station.
|
- 2023.06.18 Info on Oled Screen mayor update, added RSSI and Distance to Listened Station.
|
||||||
- 2023.06.17 BME280 Module (Temperature, Humidity, Pressure) support added.
|
- 2023.06.17 BME280 Module (Temperature, Humidity, Pressure) support added.
|
||||||
- 2023.06.12 Syslog added.
|
- 2023.06.12 Syslog added.
|
||||||
- 2023.06.10 OTA update support for Firmware and Filesystem.
|
- 2023.06.10 OTA update support for Firmware and Filesystem.
|
||||||
- 2023.06.08 Adding Digirepeater Functions.
|
- 2023.06.08 Adding Digipeater Functions.
|
||||||
- 2023.06.06 Full repack of Code and adding _enableTx_ only for Ham Ops.
|
- 2023.06.06 Full repack of Code and adding _enableTx_ only for Ham Ops.
|
||||||
- 2023.05.23 Processing Query's from RF/LoRa or APRS-IS (Send "Help" Message to test).
|
- 2023.05.23 Processing Query's from RF/LoRa or APRS-IS (Send "Help" Message to test).
|
||||||
- 2023.05.19 Saving Last-Heard Stations for validating Tx Responses.
|
- 2023.05.19 Saving Last-Heard Stations for validating Tx Responses.
|
||||||
@@ -105,4 +146,4 @@ ____________________________________________________
|
|||||||
|
|
||||||
__________________________________________
|
__________________________________________
|
||||||
|
|
||||||
# Hope You Enjoy this, 73 !! CA2RXU , Valparaiso, Chile
|
# Hope You Enjoy this, 73! CA2RXU, Valparaiso, Chile
|
||||||
|
|||||||
47
common_settings.ini
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
[common]
|
||||||
|
build_flags =
|
||||||
|
-Werror -Wall
|
||||||
|
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
||||||
|
-D RADIOLIB_EXCLUDE_CC1101=1
|
||||||
|
-D RADIOLIB_EXCLUDE_RF69=1
|
||||||
|
-D RADIOLIB_EXCLUDE_RFM2X=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SX1231=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SX1233=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SI443X=1
|
||||||
|
-D RADIOLIB_EXCLUDE_NRF24=1
|
||||||
|
-D RADIOLIB_EXCLUDE_AFSK=1
|
||||||
|
-D RADIOLIB_EXCLUDE_APRS=1
|
||||||
|
-D RADIOLIB_EXCLUDE_AX25=1
|
||||||
|
-D RADIOLIB_EXCLUDE_BELL=1
|
||||||
|
-D RADIOLIB_EXCLUDE_FSK4=1
|
||||||
|
-D RADIOLIB_EXCLUDE_HELLSCHREIBER=1
|
||||||
|
-D RADIOLIB_EXCLUDE_LORAWAN=1
|
||||||
|
-D RADIOLIB_EXCLUDE_MORSE=1
|
||||||
|
-D RADIOLIB_EXCLUDE_PAGER=1
|
||||||
|
-D RADIOLIB_EXCLUDE_DIRECT_RECEIVE=1
|
||||||
|
-D RADIOLIB_EXCLUDE_RTTY=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SSTV=1
|
||||||
|
-I variants/${PIOENV}
|
||||||
|
lib_deps =
|
||||||
|
adafruit/Adafruit Unified Sensor @ 1.1.14
|
||||||
|
adafruit/Adafruit AHTX0 @ 2.0.5
|
||||||
|
adafruit/Adafruit BME280 Library @ 2.2.4
|
||||||
|
adafruit/Adafruit BMP280 Library @ 2.6.8
|
||||||
|
adafruit/Adafruit BME680 Library @ 2.0.4
|
||||||
|
adafruit/Adafruit INA219 @ 1.2.3
|
||||||
|
adafruit/Adafruit Si7021 Library @ 1.5.3
|
||||||
|
arduino-libraries/NTPClient @ 3.2.1
|
||||||
|
ayushsharma82/ElegantOTA @ 3.1.5
|
||||||
|
bblanchon/ArduinoJson @ 6.21.3
|
||||||
|
jgromes/RadioLib @ 7.1.0
|
||||||
|
knolleary/PubSubClient @ 2.8
|
||||||
|
mathieucarbou/AsyncTCP @ 3.2.5
|
||||||
|
mathieucarbou/ESPAsyncWebServer @ 3.2.3
|
||||||
|
mikalhart/TinyGPSPlus @ 1.0.3
|
||||||
|
richonguzman/APRSPacketLib @ 1.0.4
|
||||||
|
display_libs =
|
||||||
|
adafruit/Adafruit GFX Library @ 1.11.9
|
||||||
|
adafruit/Adafruit SSD1306 @ 2.5.10
|
||||||
|
usb_flags=
|
||||||
|
-DARDUINO_USB_MODE=1
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"wifi": {
|
"wifi": {
|
||||||
"autoAP": {
|
"autoAP": {
|
||||||
"password": "1234567890",
|
"password": "1234567890",
|
||||||
"powerOff": 10
|
"timeout": 10
|
||||||
},
|
},
|
||||||
"AP": []
|
"AP": []
|
||||||
},
|
},
|
||||||
@@ -16,29 +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
|
||||||
},
|
},
|
||||||
"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,
|
||||||
@@ -48,37 +59,63 @@
|
|||||||
"battery": {
|
"battery": {
|
||||||
"sendInternalVoltage": false,
|
"sendInternalVoltage": false,
|
||||||
"monitorInternalVoltage": false,
|
"monitorInternalVoltage": false,
|
||||||
"internalSleepVoltage": 3.0,
|
"internalSleepVoltage": 2.9,
|
||||||
"sendExternalVoltage": false,
|
"sendExternalVoltage": false,
|
||||||
"externalVoltagePin": 34,
|
|
||||||
"monitorExternalVoltage": false,
|
"monitorExternalVoltage": false,
|
||||||
"externalSleepVoltage": 10.9
|
"externalSleepVoltage": 10.9,
|
||||||
|
"useExternalI2CSensor": false,
|
||||||
|
"voltageDividerR1": 100.0,
|
||||||
|
"voltageDividerR2": 27.0,
|
||||||
|
"externalVoltagePin": 34,
|
||||||
|
"sendVoltageAsTelemetry": false
|
||||||
},
|
},
|
||||||
"bme": {
|
"wxsensor": {
|
||||||
"active": false,
|
"active": false,
|
||||||
"heightCorrection": 0,
|
"heightCorrection": 0,
|
||||||
"temperatureCorrection": 0.0
|
"temperatureCorrection": 0.0
|
||||||
},
|
},
|
||||||
"syslog": {
|
"syslog": {
|
||||||
"active": false,
|
"active": false,
|
||||||
"server": "192.168.0.100",
|
"server": "lora.link9.net",
|
||||||
"port": 514
|
"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": "",
|
||||||
"password": ""
|
"password": ""
|
||||||
},
|
},
|
||||||
|
"webadmin": {
|
||||||
|
"username": "admin",
|
||||||
|
"password": ""
|
||||||
|
},
|
||||||
|
"remoteManagement": {
|
||||||
|
"managers": "",
|
||||||
|
"rfOnly": true
|
||||||
|
},
|
||||||
|
"ntp": {
|
||||||
|
"server": "pool.ntp.org",
|
||||||
|
"gmtCorrection": 0.0
|
||||||
|
},
|
||||||
"other": {
|
"other": {
|
||||||
"rememberStationTime": 30,
|
"rememberStationTime": 30,
|
||||||
"lowPowerMode": false,
|
|
||||||
"lowVoltageCutOff": 0,
|
|
||||||
"backupDigiMode": false,
|
"backupDigiMode": false,
|
||||||
"rebootMode": false,
|
"rebootMode": false,
|
||||||
"rebootModeTime": 6
|
"rebootModeTime": 6,
|
||||||
|
"startupDelay": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
data_embed/favicon.png
Normal file
|
After Width: | Height: | Size: 813 B |
@@ -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;
|
||||||
@@ -79,7 +58,7 @@ function loadSettings(settings) {
|
|||||||
document.getElementById("beacon.path").value = settings.beacon.path;
|
document.getElementById("beacon.path").value = settings.beacon.path;
|
||||||
document.getElementById("beacon.symbol").value = settings.beacon.symbol;
|
document.getElementById("beacon.symbol").value = settings.beacon.symbol;
|
||||||
document.getElementById("beacon.overlay").value = settings.beacon.overlay;
|
document.getElementById("beacon.overlay").value = settings.beacon.overlay;
|
||||||
|
document.getElementById("personalNote").value = settings.personalNote;
|
||||||
document.getElementById("action.symbol").value = settings.beacon.overlay + settings.beacon.symbol;
|
document.getElementById("action.symbol").value = settings.beacon.overlay + settings.beacon.symbol;
|
||||||
|
|
||||||
document.querySelector(".list-networks").innerHTML = "";
|
document.querySelector(".list-networks").innerHTML = "";
|
||||||
@@ -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,82 +119,149 @@ 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.ambiguityLevel").value = settings.beacon.ambiguityLevel;
|
||||||
|
|
||||||
|
// Black List
|
||||||
|
document.getElementById("blacklist").value = settings.blacklist;
|
||||||
|
|
||||||
// Digi
|
// Digi
|
||||||
document.getElementById("digi.mode").value = settings.digi.mode;
|
document.getElementById("digi.mode").value = settings.digi.mode;
|
||||||
|
document.getElementById("digi.ecoMode").value = settings.digi.ecoMode;
|
||||||
|
|
||||||
// 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.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;
|
||||||
// TELEMETRY BME/WX
|
MonitorExternalSleepVoltage.disabled = !MonitorExternalVoltageCheckbox.checked;
|
||||||
document.getElementById("bme.active").checked = settings.bme.active;
|
|
||||||
document.getElementById("bme.heightCorrection").value = settings.bme.heightCorrection;
|
document.getElementById("battery.sendVoltageAsTelemetry").checked = settings.battery.sendVoltageAsTelemetry;
|
||||||
document.getElementById("bme.temperatureCorrection").value = settings.bme.temperatureCorrection.toFixed(1);
|
|
||||||
|
// TELEMETRY WX SENSOR
|
||||||
|
document.getElementById("wxsensor.active").checked = settings.wxsensor.active;
|
||||||
|
document.getElementById("wxsensor.heightCorrection").value = settings.wxsensor.heightCorrection;
|
||||||
|
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;
|
||||||
document.getElementById("wifi.autoAP.powerOff").value = settings.wifi.autoAP.powerOff;
|
document.getElementById("wifi.autoAP.timeout").value = settings.wifi.autoAP.timeout;
|
||||||
|
|
||||||
// OTA
|
// OTA
|
||||||
document.getElementById("ota.username").value = settings.ota.username;
|
document.getElementById("ota.username").value = settings.ota.username;
|
||||||
document.getElementById("ota.password").value = settings.ota.password;
|
document.getElementById("ota.password").value = settings.ota.password;
|
||||||
|
|
||||||
|
// Webadmin
|
||||||
|
document.getElementById("webadmin.active").checked = settings.webadmin.active;
|
||||||
|
document.getElementById("webadmin.username").value = settings.webadmin.username;
|
||||||
|
document.getElementById("webadmin.password").value = settings.webadmin.password;
|
||||||
|
WebadminCheckbox.checked = settings.webadmin.active;
|
||||||
|
WebadminUsername.disabled = !WebadminCheckbox.check;
|
||||||
|
WebadminPassword.disabled = !WebadminCheckbox.check;
|
||||||
|
|
||||||
|
// Management over APRS
|
||||||
|
document.getElementById("remoteManagement.managers").value = settings.remoteManagement.managers;
|
||||||
|
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
|
// Experimental
|
||||||
document.getElementById("other.backupDigiMode").checked = settings.other.backupDigiMode;
|
document.getElementById("other.backupDigiMode").checked = settings.other.backupDigiMode;
|
||||||
|
|
||||||
document.getElementById("other.lowPowerMode").checked = settings.other.lowPowerMode;
|
|
||||||
document.getElementById("other.lowVoltageCutOff").value = settings.other.lowVoltageCutOff || 0
|
|
||||||
|
|
||||||
updateImage();
|
updateImage();
|
||||||
refreshSpeedStandard();
|
|
||||||
toggleFields();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showToast(message) {
|
function showToast(message) {
|
||||||
@@ -234,10 +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 bmeCheckbox = document.querySelector("input[name='bme.active']");
|
|
||||||
|
|
||||||
const stationModeSelect = document.querySelector("select[name='stationMode']");
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -263,29 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendExternalVoltageCheckbox = document.querySelector(
|
|
||||||
'input[name="battery.sendExternalVoltage"]'
|
|
||||||
);
|
|
||||||
const externalVoltagePinInput = document.querySelector(
|
|
||||||
'input[name="battery.externalVoltagePin"]'
|
|
||||||
);
|
|
||||||
|
|
||||||
sendExternalVoltageCheckbox.addEventListener("change", function () {
|
|
||||||
externalVoltagePinInput.disabled = !this.checked;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Status Switch
|
||||||
|
const StatusCheckbox = document.querySelector('input[name="beacon.statusActive"]');
|
||||||
|
const StatusPacket = document.querySelector('input[name="beacon.statusPacket"]');
|
||||||
|
StatusCheckbox.addEventListener("change", function() {
|
||||||
|
StatusPacket.disabled = !this.checked;
|
||||||
|
});
|
||||||
|
|
||||||
|
// APRS-IS Switches
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display Switches
|
||||||
|
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 () {
|
||||||
|
WebadminUsername.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");
|
||||||
|
|
||||||
@@ -333,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");
|
||||||
|
|
||||||
@@ -457,13 +546,9 @@ function loadReceivedPackets(packets) {
|
|||||||
|
|
||||||
packets.forEach((packet) => {
|
packets.forEach((packet) => {
|
||||||
const element = document.createElement("tr");
|
const element = document.createElement("tr");
|
||||||
|
|
||||||
date.setTime(packet.millis);
|
|
||||||
|
|
||||||
const p = date.toUTCString().split(' ')
|
|
||||||
|
|
||||||
element.innerHTML = `
|
element.innerHTML = `
|
||||||
<td>${p[p.length-2]}</td>
|
<td>${packet.rxTime}</td>
|
||||||
<td>${packet.packet}</td>
|
<td>${packet.packet}</td>
|
||||||
<td>${packet.RSSI}</td>
|
<td>${packet.RSSI}</td>
|
||||||
<td>${packet.SNR}</td>
|
<td>${packet.SNR}</td>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 203 KiB |
|
Before Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 711 KiB |
36
include/A7670_utils.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/* 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 A7670_UTILS_H_
|
||||||
|
#define A7670_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace A7670_Utils {
|
||||||
|
|
||||||
|
bool checkModemOn();
|
||||||
|
void setup();
|
||||||
|
bool checkATResponse(const String& ATMessage);
|
||||||
|
void APRS_IS_connect();
|
||||||
|
void uploadToAPRSIS(const String& packet);
|
||||||
|
void listenAPRSIS();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for project header files.
|
|
||||||
|
|
||||||
A header file is a file containing C declarations and macro definitions
|
|
||||||
to be shared between several project source files. You request the use of a
|
|
||||||
header file in your project source file (C, C++, etc) located in `src` folder
|
|
||||||
by including it, with the C preprocessing directive `#include'.
|
|
||||||
|
|
||||||
```src/main.c
|
|
||||||
|
|
||||||
#include "header.h"
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Including a header file produces the same results as copying the header file
|
|
||||||
into each source file that needs it. Such copying would be time-consuming
|
|
||||||
and error-prone. With a header file, the related declarations appear
|
|
||||||
in only one place. If they need to be changed, they can be changed in one
|
|
||||||
place, and programs that include the header file will automatically use the
|
|
||||||
new version when next recompiled. The header file eliminates the labor of
|
|
||||||
finding and changing all the copies as well as the risk that a failure to
|
|
||||||
find one copy will result in inconsistencies within a program.
|
|
||||||
|
|
||||||
In C, the usual convention is to give header files names that end with `.h'.
|
|
||||||
It is most portable to use only letters, digits, dashes, and underscores in
|
|
||||||
header file names, and at most one dot.
|
|
||||||
|
|
||||||
Read more about using header files in official GCC documentation:
|
|
||||||
|
|
||||||
* Include Syntax
|
|
||||||
* Include Operation
|
|
||||||
* Once-Only Headers
|
|
||||||
* Computed Includes
|
|
||||||
|
|
||||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
||||||
45
include/aprs_is_utils.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/* 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 APRS_IS_UTILS_H_
|
||||||
|
#define APRS_IS_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace APRS_IS_Utils {
|
||||||
|
|
||||||
|
void upload(const String& line);
|
||||||
|
void connect();
|
||||||
|
|
||||||
|
void checkStatus();
|
||||||
|
String checkForStartingBytes(const String& packet);
|
||||||
|
|
||||||
|
String buildPacketToUpload(const String& packet);
|
||||||
|
bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty);
|
||||||
|
void processLoRaPacket(const String& packet);
|
||||||
|
|
||||||
|
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType);
|
||||||
|
void processAPRSISPacket(const String& packet);
|
||||||
|
void listenAPRSIS();
|
||||||
|
|
||||||
|
void firstConnection();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
37
include/battery_utils.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/* 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 BATTERY_UTILS_H_
|
||||||
|
#define BATTERY_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace BATTERY_Utils {
|
||||||
|
|
||||||
|
void adcCalibration();
|
||||||
|
void adcCalibrationCheck();
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
float checkInternalVoltage();
|
||||||
|
float checkExternalVoltage();
|
||||||
|
void startupBatteryHealth();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/* 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 CONFIGURATION_H_
|
#ifndef CONFIGURATION_H_
|
||||||
#define CONFIGURATION_H_
|
#define CONFIGURATION_H_
|
||||||
|
|
||||||
@@ -5,6 +23,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
|
|
||||||
|
|
||||||
class WiFi_AP {
|
class WiFi_AP {
|
||||||
public:
|
public:
|
||||||
String ssid;
|
String ssid;
|
||||||
@@ -14,7 +33,7 @@ public:
|
|||||||
class WiFi_Auto_AP {
|
class WiFi_Auto_AP {
|
||||||
public:
|
public:
|
||||||
String password;
|
String password;
|
||||||
int powerOff;
|
int timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BEACON {
|
class BEACON {
|
||||||
@@ -26,8 +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;
|
||||||
|
int ambiguityLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
class APRS_IS {
|
class APRS_IS {
|
||||||
@@ -44,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)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,9 +102,13 @@ public:
|
|||||||
int externalVoltagePin;
|
int externalVoltagePin;
|
||||||
bool monitorExternalVoltage;
|
bool monitorExternalVoltage;
|
||||||
float externalSleepVoltage;
|
float externalSleepVoltage;
|
||||||
|
bool useExternalI2CSensor;
|
||||||
|
float voltageDividerR1;
|
||||||
|
float voltageDividerR2;
|
||||||
|
bool sendVoltageAsTelemetry;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BME {
|
class WXSENSOR {
|
||||||
public:
|
public:
|
||||||
bool active;
|
bool active;
|
||||||
int heightCorrection;
|
int heightCorrection;
|
||||||
@@ -89,6 +120,7 @@ public:
|
|||||||
bool active;
|
bool active;
|
||||||
String server;
|
String server;
|
||||||
int port;
|
int port;
|
||||||
|
bool logBeaconOverTCPIP;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TNC {
|
class TNC {
|
||||||
@@ -96,6 +128,7 @@ public:
|
|||||||
bool enableServer;
|
bool enableServer;
|
||||||
bool enableSerial;
|
bool enableSerial;
|
||||||
bool acceptOwn;
|
bool acceptOwn;
|
||||||
|
bool aprsBridgeActive;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OTA {
|
class OTA {
|
||||||
@@ -104,16 +137,46 @@ public:
|
|||||||
String password;
|
String password;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class WEBADMIN {
|
||||||
|
public:
|
||||||
|
bool active;
|
||||||
|
String username;
|
||||||
|
String password;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NTP {
|
||||||
|
public:
|
||||||
|
String server;
|
||||||
|
float gmtCorrection;
|
||||||
|
};
|
||||||
|
|
||||||
|
class REMOTE_MANAGEMENT {
|
||||||
|
public:
|
||||||
|
String managers;
|
||||||
|
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:
|
||||||
bool reload; // ?
|
|
||||||
String callsign;
|
String callsign;
|
||||||
int rememberStationTime;
|
int rememberStationTime;
|
||||||
bool lowPowerMode;
|
|
||||||
double lowVoltageCutOff;
|
|
||||||
bool backupDigiMode;
|
bool backupDigiMode;
|
||||||
bool rebootMode;
|
bool rebootMode;
|
||||||
int rebootModeTime;
|
int rebootModeTime;
|
||||||
|
int startupDelay;
|
||||||
|
String personalNote;
|
||||||
|
String blacklist;
|
||||||
std::vector<WiFi_AP> wifiAPs;
|
std::vector<WiFi_AP> wifiAPs;
|
||||||
WiFi_Auto_AP wifiAutoAP;
|
WiFi_Auto_AP wifiAutoAP;
|
||||||
BEACON beacon;
|
BEACON beacon;
|
||||||
@@ -122,14 +185,17 @@ public:
|
|||||||
LoraModule loramodule;
|
LoraModule loramodule;
|
||||||
Display display;
|
Display display;
|
||||||
BATTERY battery;
|
BATTERY battery;
|
||||||
BME bme;
|
WXSENSOR wxsensor;
|
||||||
SYSLOG syslog;
|
SYSLOG syslog;
|
||||||
TNC tnc;
|
TNC tnc;
|
||||||
OTA ota;
|
OTA ota;
|
||||||
|
WEBADMIN webadmin;
|
||||||
void init();
|
NTP ntp;
|
||||||
void writeFile();
|
REMOTE_MANAGEMENT remoteManagement;
|
||||||
void check();
|
MQTT mqtt;
|
||||||
|
|
||||||
|
void setDefaultValues();
|
||||||
|
bool writeFile();
|
||||||
Configuration();
|
Configuration();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
34
include/digi_utils.h
Normal 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 DIGI_UTILS_H_
|
||||||
|
#define DIGI_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DIGI_Utils {
|
||||||
|
|
||||||
|
String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq);
|
||||||
|
String generateDigipeatedPacket(const String& packet, bool thirdParty);
|
||||||
|
void processLoRaPacket(const String& packet);
|
||||||
|
void checkEcoMode();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
32
include/display.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* 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 DISPLAY_H_
|
||||||
|
#define DISPLAY_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
|
||||||
|
|
||||||
|
void displaySetup();
|
||||||
|
void displayToggle(bool toggle);
|
||||||
|
|
||||||
|
void displayShow(const String& header, const String& line1, const String& line2, const String& line3, int wait = 0);
|
||||||
|
void displayShow(const String& header, const String& line1, const String& line2, const String& line3, const String& line4, const String& line5, const String& line6, int wait = 0);
|
||||||
|
|
||||||
|
#endif
|
||||||
41
include/gps_utils.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* 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 GPS_UTILS_H_
|
||||||
|
#define GPS_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace GPS_Utils {
|
||||||
|
|
||||||
|
String getiGateLoRaBeaconPacket();
|
||||||
|
char *ax25_base91enc(char *s, uint8_t n, uint32_t v);
|
||||||
|
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol);
|
||||||
|
void generateBeaconFirstPart();
|
||||||
|
void generateBeacons();
|
||||||
|
String decodeEncodedGPS(const String& packet);
|
||||||
|
String getReceivedGPS(const String& packet);
|
||||||
|
String getDistanceAndComment(const String& packet);
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
void getData();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
41
include/kiss_protocol.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DCD_ON 0x03
|
||||||
|
|
||||||
|
#define FEND 0xC0
|
||||||
|
#define FESC 0xDB
|
||||||
|
#define TFEND 0xDC
|
||||||
|
#define TFESC 0xDD
|
||||||
|
|
||||||
|
#define CMD_UNKNOWN 0xFE
|
||||||
|
#define CMD_DATA 0x00
|
||||||
|
#define CMD_HARDWARE 0x06
|
||||||
|
|
||||||
|
#define HW_RSSI 0x21
|
||||||
|
|
||||||
|
#define CMD_ERROR 0x90
|
||||||
|
#define ERROR_INITRADIO 0x01
|
||||||
|
#define ERROR_TXFAILED 0x02
|
||||||
|
#define ERROR_QUEUE_FULL 0x04
|
||||||
|
|
||||||
|
#define APRS_CONTROL_FIELD 0x03
|
||||||
|
#define APRS_INFORMATION_FIELD 0xf0
|
||||||
|
|
||||||
|
#define HAS_BEEN_DIGIPITED_MASK 0b10000000
|
||||||
|
#define IS_LAST_ADDRESS_POSITION_MASK 0b1
|
||||||
34
include/kiss_utils.h
Normal 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 KISS_UTILS_H_
|
||||||
|
#define KISS_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
String encodeAddressAX25(String tnc2Address);
|
||||||
|
String decodeAddressAX25(const String& ax25Address, bool& isLast, bool isRelay);
|
||||||
|
|
||||||
|
String encapsulateKISS(const String& ax25Frame, uint8_t cmd);
|
||||||
|
String decapsulateKISS(const String& frame);
|
||||||
|
|
||||||
|
String encodeKISS(const String& frame);
|
||||||
|
String decodeKISS(const String& inputFrame, bool& dataFrame);
|
||||||
|
|
||||||
|
#endif
|
||||||
38
include/lora_utils.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* 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 LORA_UTILS_H_
|
||||||
|
#define LORA_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace LoRa_Utils {
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
void sendNewPacket(const String& newPacket);
|
||||||
|
String receivePacketFromSleep();
|
||||||
|
String receivePacket();
|
||||||
|
void changeFreqTx();
|
||||||
|
void changeFreqRx();
|
||||||
|
void wakeRadio();
|
||||||
|
void sleepRadio();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
34
include/mqtt_utils.h
Normal 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
|
||||||
33
include/ntp_utils.h
Normal 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 NTP_UTILS_H_
|
||||||
|
#define NTP_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace NTP_Utils {
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
void update();
|
||||||
|
String getFormatedTime();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
35
include/ota_utils.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/* 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 OTA_UTILS_H_
|
||||||
|
#define OTA_UTILS_H_
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace OTA_Utils {
|
||||||
|
|
||||||
|
void setup(AsyncWebServer *server);
|
||||||
|
void onOTAStart();
|
||||||
|
void onOTAProgress(size_t current, size_t final);
|
||||||
|
void onOTAEnd(bool success);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
53
include/power_utils.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/* 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 POWER_UTILS_H_
|
||||||
|
#define POWER_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
||||||
|
#include "XPowersLib.h"
|
||||||
|
#else
|
||||||
|
#include <Wire.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace POWER_Utils {
|
||||||
|
|
||||||
|
#ifdef VEXT_CTRL
|
||||||
|
void vext_ctrl_ON();
|
||||||
|
void vext_ctrl_OFF();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ADC_CTRL
|
||||||
|
void adc_ctrl_ON();
|
||||||
|
void adc_ctrl_OFF();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
double getBatteryVoltage();
|
||||||
|
bool isBatteryConnected();
|
||||||
|
void activateGPS();
|
||||||
|
void deactivateGPS();
|
||||||
|
void activateLoRa();
|
||||||
|
void deactivateLoRa();
|
||||||
|
bool begin(TwoWire &port);
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
31
include/query_utils.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/* 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 QUERY_UTILS_H_
|
||||||
|
#define QUERY_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace QUERY_Utils {
|
||||||
|
|
||||||
|
String process(const String& query, const String& station, bool queryFromAPRSIS, bool thirdParty);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
33
include/sleep_utils.h
Normal 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 SLEEP_UTILS_H_
|
||||||
|
#define SLEEP_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
namespace SLEEP_Utils {
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
void checkWakeUpFlag();
|
||||||
|
void startSleeping();
|
||||||
|
void checkSerial();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
48
include/station_utils.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/* 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 STATION_UTILS_H_
|
||||||
|
#define STATION_UTILS_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct LastHeardStation {
|
||||||
|
uint32_t lastHeardTime;
|
||||||
|
String station;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace STATION_Utils {
|
||||||
|
|
||||||
|
void loadBlacklistAndManagers();
|
||||||
|
bool isBlacklisted(const String& callsign);
|
||||||
|
bool isManager(const String& callsign);
|
||||||
|
bool checkObjectTime(const String& packet);
|
||||||
|
void deleteNotHeard();
|
||||||
|
void updateLastHeard(const String& station);
|
||||||
|
bool wasHeard(const String& station);
|
||||||
|
void clean25SegBuffer();
|
||||||
|
bool check25SegBuffer(const String& station, const String& textMessage);
|
||||||
|
void processOutputPacketBufferUltraEcoMode();
|
||||||
|
void processOutputPacketBuffer();
|
||||||
|
void addToOutputPacketBuffer(const String& packet, bool flag = false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
32
include/syslog_utils.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* 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 SYSLOG_H_
|
||||||
|
#define SYSLOG_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace SYSLOG_Utils {
|
||||||
|
|
||||||
|
void log(const uint8_t type ,const String& packet, const int rssi, const float snr, const int freqError);
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
33
include/telemetry_utils.h
Normal 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
|
||||||
35
include/tnc_utils.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/* 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 TNC_UTILS_H_
|
||||||
|
#define TNC_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace TNC_Utils {
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
void sendToClients(const String& packet, bool stripBytes = false);
|
||||||
|
void sendToSerial(const String& packet, bool stripBytes = false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
53
include/utils.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/* 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 UTILS_H_
|
||||||
|
#define UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
class ReceivedPacket {
|
||||||
|
public:
|
||||||
|
String rxTime;
|
||||||
|
String packet;
|
||||||
|
int RSSI;
|
||||||
|
float SNR;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
void processStatus();
|
||||||
|
String getLocalIP();
|
||||||
|
void setupDisplay();
|
||||||
|
void showActiveStations();
|
||||||
|
void checkBeaconInterval();
|
||||||
|
void checkDisplayInterval();
|
||||||
|
void validateFreqs();
|
||||||
|
void typeOfPacket(const String& packet, const uint8_t packetType);
|
||||||
|
void print(const String& text);
|
||||||
|
void println(const String& text);
|
||||||
|
void checkRebootMode();
|
||||||
|
void checkRebootTime();
|
||||||
|
void checkSleepByLowBatteryVoltage(uint8_t mode);
|
||||||
|
bool checkValidCallsign(const String& callsign);
|
||||||
|
void startupDelay();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
43
include/web_utils.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/* 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 WEB_UTILS_H_
|
||||||
|
#define WEB_UTILS_H_
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <ESPmDNS.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <SPIFFS.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace WEB_Utils {
|
||||||
|
|
||||||
|
void handleNotFound(AsyncWebServerRequest *request);
|
||||||
|
void handleStatus(AsyncWebServerRequest *request);
|
||||||
|
void handleHome(AsyncWebServerRequest *request);
|
||||||
|
void handleStyle(AsyncWebServerRequest *request);
|
||||||
|
void handleScript(AsyncWebServerRequest *request);
|
||||||
|
void handleBootstrapStyle(AsyncWebServerRequest *request);
|
||||||
|
void handleBootstrapScript(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
35
include/wifi_utils.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/* 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 WIFI_UTILS_H_
|
||||||
|
#define WIFI_UTILS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace WIFI_Utils {
|
||||||
|
|
||||||
|
void checkWiFi();
|
||||||
|
void startAutoAP();
|
||||||
|
void startWiFi();
|
||||||
|
void checkAutoAPTimeout();
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
42
include/wx_utils.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/* 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 WX_UTILS_H_
|
||||||
|
#define WX_UTILS_H_
|
||||||
|
|
||||||
|
#include <Adafruit_Sensor.h>
|
||||||
|
#include <Adafruit_AHTX0.h>
|
||||||
|
#include <Adafruit_BME280.h>
|
||||||
|
#include <Adafruit_BMP280.h>
|
||||||
|
#include <Adafruit_BME680.h>
|
||||||
|
#include "Adafruit_Si7021.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace WX_Utils {
|
||||||
|
|
||||||
|
void getWxModuleAddres();
|
||||||
|
void setup();
|
||||||
|
String generateTempString(const float sensorTemp);
|
||||||
|
String generateHumString(const float sensorHum);
|
||||||
|
String generatePresString(const float sensorPres);
|
||||||
|
String readDataSensor();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo Loading...
|
|
||||||
|
|
||||||
where /q python.exe
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo "Python is not installed. Please install it and try again."
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
where /q pip
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo "pip is not installed. Please install it and try again."
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
pip show pyserial >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo "Installing pyserial..."
|
|
||||||
pip install pyserial
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Your firmware will be upgraded without factory reset.
|
|
||||||
|
|
||||||
echo:
|
|
||||||
|
|
||||||
echo IF THIS IS YOUR FIRST FLASH ON THIS BOARD PLEASE USE install_with_factory_reset.bat INSTEAD!
|
|
||||||
|
|
||||||
echo:
|
|
||||||
|
|
||||||
echo Available COM ports:
|
|
||||||
python.exe -c "import serial.tools.list_ports; print('\n'.join([str(c) for c in serial.tools.list_ports.comports()]))"
|
|
||||||
|
|
||||||
echo:
|
|
||||||
|
|
||||||
set /p port="Enter COM port (for example COM5): "
|
|
||||||
|
|
||||||
python.exe ./bin/esptool/esptool.py --chip esp32 --port "%port%" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin
|
|
||||||
|
|
||||||
pause
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "Your firmware will be upgraded without factory reset."
|
|
||||||
echo "IF THIS IS YOUR FIRST FLASH ON THIS BOARD PLEASE USE install_with_factory_reset.sh INSTEAD!"
|
|
||||||
|
|
||||||
read -p "Enter COM port (for example /dev/ttyS5): " port
|
|
||||||
|
|
||||||
PYTHON_CMD=python
|
|
||||||
if command -v python3 &> /dev/null
|
|
||||||
then
|
|
||||||
PYTHON_CMD=python3
|
|
||||||
fi
|
|
||||||
|
|
||||||
PYTHON_CMD ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin
|
|
||||||
|
|
||||||
echo "Firmware flashed"
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo Loading...
|
|
||||||
|
|
||||||
where /q python.exe
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo "Python is not installed. Please install it and try again."
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
where /q pip
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo "pip is not installed. Please install it and try again."
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
pip show pyserial >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo "Installing pyserial..."
|
|
||||||
pip install pyserial
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
echo Your current configuration will be LOST!!!
|
|
||||||
echo Your current configuration will be LOST!!!
|
|
||||||
echo Your current configuration will be LOST!!!
|
|
||||||
|
|
||||||
echo:
|
|
||||||
|
|
||||||
echo If you already have this board flashed with our firmware please use install_upgrade.bat instead!
|
|
||||||
|
|
||||||
echo:
|
|
||||||
|
|
||||||
echo Available COM ports:
|
|
||||||
python.exe -c "import serial.tools.list_ports; print('\n'.join([str(c) for c in serial.tools.list_ports.comports()]))"
|
|
||||||
|
|
||||||
echo:
|
|
||||||
|
|
||||||
set /p port="Enter COM port (for example COM5): "
|
|
||||||
|
|
||||||
python.exe bin/esptool/esptool.py --chip esp32 --port "%port%" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 2686976 firmware/spiffs.bin
|
|
||||||
|
|
||||||
python.exe bin/esptool/esptool.py --chip esp32 --port "%port%" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin
|
|
||||||
|
|
||||||
pause
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "Your firmware will be upgraded without factory reset."
|
|
||||||
echo "IF THIS IS YOUR FIRST FLASH ON THIS BOARD PLEASE USE install_with_factory_reset.sh INSTEAD!"
|
|
||||||
|
|
||||||
read -p "Enter COM port (for example /dev/ttyS5): " port
|
|
||||||
|
|
||||||
PYTHON_CMD=python
|
|
||||||
if command -v python3 &> /dev/null
|
|
||||||
then
|
|
||||||
PYTHON_CMD=python3
|
|
||||||
fi
|
|
||||||
|
|
||||||
$PYTHON_CMD ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 2686976 firmware/spiffs.bin
|
|
||||||
|
|
||||||
$PYTHON_CMD ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin
|
|
||||||
|
|
||||||
echo "Firmware flashed"
|
|
||||||
377
platformio.ini
@@ -11,6 +11,10 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
default_envs = ttgo-lora32-v21
|
default_envs = ttgo-lora32-v21
|
||||||
|
|
||||||
|
extra_configs =
|
||||||
|
common_settings.ini
|
||||||
|
variants/*/platformio.ini
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
platform = espressif32 @ 6.7.0
|
platform = espressif32 @ 6.7.0
|
||||||
board_build.partitions = min_spiffs.csv
|
board_build.partitions = min_spiffs.csv
|
||||||
@@ -22,376 +26,7 @@ board_build.embed_files =
|
|||||||
data_embed/script.js.gz
|
data_embed/script.js.gz
|
||||||
data_embed/bootstrap.css.gz
|
data_embed/bootstrap.css.gz
|
||||||
data_embed/bootstrap.js.gz
|
data_embed/bootstrap.js.gz
|
||||||
|
data_embed/favicon.png.gz
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:tools/compress.py
|
pre:tools/compress.py
|
||||||
debug_tool = esp-prog
|
debug_tool = esp-prog
|
||||||
|
|
||||||
[common]
|
|
||||||
lib_deps =
|
|
||||||
jgromes/RadioLib @ 6.6.0
|
|
||||||
mikalhart/TinyGPSPlus @ 1.0.3
|
|
||||||
bblanchon/ArduinoJson @ 6.21.3
|
|
||||||
adafruit/Adafruit Unified Sensor @ 1.1.14
|
|
||||||
adafruit/Adafruit BME280 Library @ 2.2.4
|
|
||||||
adafruit/Adafruit BMP280 Library @ 2.6.8
|
|
||||||
adafruit/Adafruit BME680 Library @ 2.0.4
|
|
||||||
adafruit/Adafruit Si7021 Library @ 1.5.3
|
|
||||||
ayushsharma82/ElegantOTA @ 3.1.1
|
|
||||||
ottowinter/ESPAsyncWebServer-esphome @ 3.2.2
|
|
||||||
esphome/AsyncTCP-esphome @ 2.1.1
|
|
||||||
|
|
||||||
[env:ttgo-lora32-v21]
|
|
||||||
board = ttgo-lora32-v21
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_LORA32_V2_1
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:ttgo-lora32-v21_915]
|
|
||||||
board = ttgo-lora32-v21
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_LORA32_V2_1_915
|
|
||||||
-DHAS_SX1276
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:heltec-lora32-v2]
|
|
||||||
board = ttgo-lora32-v21
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_V2
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:heltec_wifi_lora_32_V3]
|
|
||||||
board = heltec_wifi_lora_32_V3
|
|
||||||
board_build.mcu = esp32s3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_V3
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:heltec_wireless_stick]
|
|
||||||
board = heltec_wifi_lora_32_V3
|
|
||||||
board_build.mcu = esp32s3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_WS
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:heltec_wireless_stick_lite_v3]
|
|
||||||
board = esp32-s3-devkitc-1
|
|
||||||
board_build.mcu = esp32s3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_WSL_V3
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
|
|
||||||
[env:ESP32_DIY_LoRa]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_LoRa
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:ESP32_DIY_LoRa_915]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_LoRa_915
|
|
||||||
-DHAS_SX1276
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:ESP32_DIY_1W_LoRa]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_1W_LoRa
|
|
||||||
-DHAS_SX1268
|
|
||||||
-DHAS_1W_LORA
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:ESP32_DIY_1W_LoRa_915]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_1W_LoRa_915
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DHAS_1W_LORA
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:ttgo-t-beam-v1_2]
|
|
||||||
board = ttgo-t-beam
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_Beam_V1_2
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DHAS_AXP2101
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
lewisxhe/XPowersLib @ 0.2.4
|
|
||||||
|
|
||||||
[env:ttgo-t-beam-v1_2_915]
|
|
||||||
board = ttgo-t-beam
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_Beam_V1_2_915
|
|
||||||
-DHAS_SX1276
|
|
||||||
-DHAS_AXP2101
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
lewisxhe/XPowersLib @ 0.2.4
|
|
||||||
|
|
||||||
[env:ttgo-t-beam-v1]
|
|
||||||
board = ttgo-t-beam
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_Beam_V1_0
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DHAS_AXP192
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
lewisxhe/XPowersLib @ 0.2.4
|
|
||||||
|
|
||||||
[env:ttgo-t-beam-v1_915]
|
|
||||||
board = ttgo-t-beam
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_Beam_V1_0_915
|
|
||||||
-DHAS_SX1276
|
|
||||||
-DHAS_AXP192
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
lewisxhe/XPowersLib @ 0.2.4
|
|
||||||
|
|
||||||
[env:ttgo-t-beam-v1_SX1268]
|
|
||||||
board = ttgo-t-beam
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_Beam_V1_0_SX1268
|
|
||||||
-DHAS_SX1268
|
|
||||||
-DHAS_AXP192
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
lewisxhe/XPowersLib @ 0.2.4
|
|
||||||
|
|
||||||
[env:ttgo-t-beam-v1_2_SX1262]
|
|
||||||
board = ttgo-t-beam
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_Beam_V1_2_SX1262
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DHAS_AXP2101
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
lewisxhe/XPowersLib @ 0.2.4
|
|
||||||
|
|
||||||
[env:ESP32_DIY_LoRa_A7670]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_LoRa_A7670
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DHAS_A7670
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
vshymanskyy/TinyGSM @ 0.12.0
|
|
||||||
vshymanskyy/StreamDebugger @ 1.0.1
|
|
||||||
|
|
||||||
[env:ESP32_DIY_LoRa_A7670_915]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_LoRa_A7670_915
|
|
||||||
-DHAS_SX1276
|
|
||||||
-DHAS_A7670
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
vshymanskyy/TinyGSM @ 0.12.0
|
|
||||||
vshymanskyy/StreamDebugger @ 1.0.1
|
|
||||||
|
|
||||||
[env:heltec_wireless_tracker]
|
|
||||||
board = esp32-s3-devkitc-1
|
|
||||||
board_build.mcu = esp32s3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_WIRELESS_TRACKER
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DHAS_TFT
|
|
||||||
-D USER_SETUP_LOADED
|
|
||||||
-D TFT_WIDTH=80
|
|
||||||
-D TFT_HEIGHT=160
|
|
||||||
-D ST7735_DRIVER
|
|
||||||
-D ST7735_GREENTAB160x80
|
|
||||||
-D TFT_RGB_ORDER=TFT_BGR
|
|
||||||
-D TFT_MOSI=42
|
|
||||||
-D TFT_SCLK=41
|
|
||||||
-D TFT_CS=38
|
|
||||||
-D TFT_DC=40
|
|
||||||
-D TFT_RST=39
|
|
||||||
-D TFT_BL=21
|
|
||||||
-D TFT_BACKLIGHT_ON=1
|
|
||||||
-D TOUCH_CS=-1
|
|
||||||
-D LOAD_GLCD
|
|
||||||
-D LOAD_FONT2
|
|
||||||
-D LOAD_FONT4
|
|
||||||
-D LOAD_FONT6
|
|
||||||
-D LOAD_FONT7
|
|
||||||
-D LOAD_FONT8
|
|
||||||
-D SPI_FREQUENCY=27000000
|
|
||||||
-D USE_HSPI_PORT
|
|
||||||
-DARDUINO_USB_MODE=1
|
|
||||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
bodmer/TFT_eSPI @ 2.5.43
|
|
||||||
|
|
||||||
[env:heltec_ht-ct62]
|
|
||||||
board = heltec_wireless_stick_lite
|
|
||||||
board_build.mcu = esp32c3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_HTCT62
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:OE5HWN_MeshCom]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DOE5HWN_MeshCom
|
|
||||||
-DHAS_SX1268
|
|
||||||
-DHAS_1W_LORA
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:WEMOS-LOLIN32-OLED-DIY]
|
|
||||||
board = lolin32
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DWEMOS_LOLIN32_OLED_DIY_LoRa
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:WEMOS-D1-R32-RA02]
|
|
||||||
board = wemos_d1_uno32
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DWEMOS_D1_R32_RA02
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:esp32c3_DIY_1W_LoRa]
|
|
||||||
board = esp32-c3-devkitm-1
|
|
||||||
board_build.mcu = esp32c3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32C3_DIY_1W_LoRa
|
|
||||||
-DHAS_SX1268
|
|
||||||
-DHAS_1W_LORA
|
|
||||||
-DARDUINO_USB_MODE=1
|
|
||||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
|
|
||||||
[env:esp32c3_DIY_1W_LoRa_915]
|
|
||||||
board = esp32-c3-devkitm-1
|
|
||||||
board_build.mcu = esp32c3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32C3_DIY_1W_LoRa_915
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DHAS_1W_LORA
|
|
||||||
-DARDUINO_USB_MODE=1
|
|
||||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
adafruit/Adafruit GFX Library @ 1.11.9
|
|
||||||
adafruit/Adafruit SSD1306 @ 2.5.10
|
|
||||||
@@ -1,11 +1,30 @@
|
|||||||
|
/* 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 "configuration.h"
|
#include "configuration.h"
|
||||||
#include "aprs_is_utils.h"
|
#include "aprs_is_utils.h"
|
||||||
#include "boards_pinout.h"
|
#include "board_pinout.h"
|
||||||
#include "A7670_utils.h"
|
#include "A7670_utils.h"
|
||||||
#include "lora_utils.h"
|
#include "lora_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAS_A7670
|
#ifdef HAS_A7670
|
||||||
#define TINY_GSM_MODEM_SIM7600 //The AT instruction of A7670 is compatible with SIM7600
|
#define TINY_GSM_MODEM_SIM7600 //The AT instruction of A7670 is compatible with SIM7600
|
||||||
#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
|
#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
|
||||||
@@ -20,9 +39,9 @@
|
|||||||
|
|
||||||
bool modemStartUp = false;
|
bool modemStartUp = false;
|
||||||
bool serverStartUp = false;
|
bool serverStartUp = false;
|
||||||
bool userBytesSended = false;
|
bool userBytesSent = false;
|
||||||
bool beaconBytesSended = false;
|
bool beaconBytesSent = false;
|
||||||
bool beaconSended = false;
|
bool beaconSent = false;
|
||||||
bool stationBeacon = false;
|
bool stationBeacon = false;
|
||||||
|
|
||||||
extern bool modemLoggedToAPRSIS;
|
extern bool modemLoggedToAPRSIS;
|
||||||
@@ -33,7 +52,7 @@
|
|||||||
bool checkModemOn() {
|
bool checkModemOn() {
|
||||||
bool modemReady = false;
|
bool modemReady = false;
|
||||||
Serial.print("Starting Modem ... ");
|
Serial.print("Starting Modem ... ");
|
||||||
show_display(firstLine, "Starting Modem...", " ", " ", 0);
|
displayShow(firstLine, "Starting Modem...", " ", " ", 0);
|
||||||
|
|
||||||
pinMode(A7670_ResetPin, OUTPUT); //A7670 Reset
|
pinMode(A7670_ResetPin, OUTPUT); //A7670 Reset
|
||||||
digitalWrite(A7670_ResetPin, LOW);
|
digitalWrite(A7670_ResetPin, LOW);
|
||||||
@@ -60,7 +79,7 @@
|
|||||||
modemReady = true;
|
modemReady = true;
|
||||||
i = 1;
|
i = 1;
|
||||||
Serial.println("Modem Ready!\n");
|
Serial.println("Modem Ready!\n");
|
||||||
show_display(firstLine, "Starting Modem...", "---> Modem Ready", " ", 0);
|
displayShow(firstLine, "Starting Modem...", "---> Modem Ready", " ", 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +97,7 @@
|
|||||||
delay(1000);
|
delay(1000);
|
||||||
//setup_gps(); // if gps active / won't be need for now
|
//setup_gps(); // if gps active / won't be need for now
|
||||||
} else {
|
} else {
|
||||||
show_display(firstLine, "Starting Modem...", "---> Failed !!!", " ", 0);
|
displayShow(firstLine, "Starting Modem...", "---> Failed !!!", " ", 0);
|
||||||
Serial.println(F("*********** Failed to connect to the modem! ***********"));
|
Serial.println(F("*********** Failed to connect to the modem! ***********"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,26 +117,26 @@
|
|||||||
//Serial.println(response); // DEBUG of Modem AT message
|
//Serial.println(response); // DEBUG of Modem AT message
|
||||||
if(response.indexOf("verified") >= 0) {
|
if(response.indexOf("verified") >= 0) {
|
||||||
Serial.println("Logged! (User Validated)\n");
|
Serial.println("Logged! (User Validated)\n");
|
||||||
show_display(firstLine, "Connecting APRS-IS...", "---> Logged!", " ", 1000);
|
displayShow(firstLine, "Connecting APRS-IS...", "---> Logged!", " ", 1000);
|
||||||
Serial.println("#################### APRS-IS FEED ####################");
|
Serial.println("#################### APRS-IS FEED ####################");
|
||||||
validAT = true;
|
validAT = true;
|
||||||
i = 1;
|
i = 1;
|
||||||
delayATMessage = 0;
|
delayATMessage = 0;
|
||||||
} else if (ATMessage == "AT+NETOPEN" && response.indexOf("OK") >= 0) {
|
} else if (ATMessage == "AT+NETOPEN" && response.indexOf("OK") >= 0) {
|
||||||
Serial.println("Port Open!");
|
Serial.println("Port Open!");
|
||||||
show_display(firstLine, "Opening Port...", "---> Port Open", " ", 0);
|
displayShow(firstLine, "Opening Port...", "---> Port Open", " ", 0);
|
||||||
validAT = true;
|
validAT = true;
|
||||||
i = 1;
|
i = 1;
|
||||||
delayATMessage = 0;
|
delayATMessage = 0;
|
||||||
} else if (ATMessage == "AT+NETOPEN" && response.indexOf("Network is already opened") >= 0) {
|
} else if (ATMessage == "AT+NETOPEN" && response.indexOf("Network is already opened") >= 0) {
|
||||||
Serial.println("Port Open! (was already opened)");
|
Serial.println("Port Open! (was already opened)");
|
||||||
show_display(firstLine, "Opening Port...", "---> Port Open", " ", 0);
|
displayShow(firstLine, "Opening Port...", "---> Port Open", " ", 0);
|
||||||
validAT = true;
|
validAT = true;
|
||||||
i = 1;
|
i = 1;
|
||||||
delayATMessage = 0;
|
delayATMessage = 0;
|
||||||
} else if (ATMessage.indexOf("AT+CIPOPEN") == 0 && response.indexOf("PB DONE") >= 0) {
|
} else if (ATMessage.indexOf("AT+CIPOPEN") == 0 && response.indexOf("PB DONE") >= 0) {
|
||||||
Serial.println("Contacted!");
|
Serial.println("Contacted!");
|
||||||
show_display(firstLine, "Connecting APRS-IS...", "---> Contacted", " ", 0);
|
displayShow(firstLine, "Connecting APRS-IS...", "---> Contacted", " ", 0);
|
||||||
validAT = true;
|
validAT = true;
|
||||||
i = 1;
|
i = 1;
|
||||||
delayATMessage = 0;
|
delayATMessage = 0;
|
||||||
@@ -128,8 +147,8 @@
|
|||||||
delayATMessage = 0;
|
delayATMessage = 0;
|
||||||
} else if (ATMessage.indexOf(Config.callsign) >= 3 && !modemLoggedToAPRSIS && response.indexOf("OK") >= 0 && !stationBeacon) { // login info
|
} else if (ATMessage.indexOf(Config.callsign) >= 3 && !modemLoggedToAPRSIS && response.indexOf("OK") >= 0 && !stationBeacon) { // login info
|
||||||
validAT = true;
|
validAT = true;
|
||||||
delayATMessage = 0;
|
delayATMessage = 0;
|
||||||
} else if (ATMessage.indexOf(Config.callsign) == 0 && !beaconSended && response.indexOf("OK") >= 0 && !stationBeacon) { // self beacon or querys
|
} else if (ATMessage.indexOf(Config.callsign) == 0 && !beaconSent && response.indexOf("OK") >= 0 && !stationBeacon) { // self beacon or querys
|
||||||
validAT = true;
|
validAT = true;
|
||||||
i = 1;
|
i = 1;
|
||||||
delayATMessage = 0;
|
delayATMessage = 0;
|
||||||
@@ -155,18 +174,18 @@
|
|||||||
Serial.println("-----> Connecting to APRS IS");
|
Serial.println("-----> Connecting to APRS IS");
|
||||||
while (!modemStartUp) {
|
while (!modemStartUp) {
|
||||||
Serial.print("Opening Port... ");
|
Serial.print("Opening Port... ");
|
||||||
show_display(firstLine, "Opening Port...", " ", " ", 0);
|
displayShow(firstLine, "Opening Port...", " ", " ", 0);
|
||||||
modemStartUp = checkATResponse("AT+NETOPEN");
|
modemStartUp = checkATResponse("AT+NETOPEN");
|
||||||
delay(2000);
|
delay(2000);
|
||||||
} while (!serverStartUp) {
|
} while (!serverStartUp) {
|
||||||
Serial.print("Connecting APRS-IS Server... ");
|
Serial.print("Connecting APRS-IS Server... ");
|
||||||
show_display(firstLine, "Connecting APRS-IS...", " ", " ", 0);
|
displayShow(firstLine, "Connecting APRS-IS...", " ", " ", 0);
|
||||||
serverStartUp = checkATResponse("AT+CIPOPEN=0,\"TCP\",\"" + String(Config.aprs_is.server) + "\"," + String(Config.aprs_is.port));
|
serverStartUp = checkATResponse("AT+CIPOPEN=0,\"TCP\",\"" + String(Config.aprs_is.server) + "\"," + String(Config.aprs_is.port));
|
||||||
delay(2000);
|
delay(2000);
|
||||||
} while (!userBytesSended) {
|
} while (!userBytesSent) {
|
||||||
Serial.print("Writing User Login Data ");
|
Serial.print("Writing User Login Data ");
|
||||||
show_display(firstLine, "Connecting APRS-IS...", "---> User Login Data", " ", 0);
|
displayShow(firstLine, "Connecting APRS-IS...", "---> User Login Data", " ", 0);
|
||||||
userBytesSended = checkATResponse("AT+CIPSEND=0," + String(loginInfo.length()+1));
|
userBytesSent = checkATResponse("AT+CIPSEND=0," + String(loginInfo.length()+1));
|
||||||
delay(2000);
|
delay(2000);
|
||||||
} while (!modemLoggedToAPRSIS) {
|
} while (!modemLoggedToAPRSIS) {
|
||||||
Serial.print(".");
|
Serial.print(".");
|
||||||
@@ -176,19 +195,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
void uploadToAPRSIS(const String& packet) {
|
void uploadToAPRSIS(const String& packet) {
|
||||||
beaconBytesSended = checkATResponse("AT+CIPSEND=0," + String(packet.length()+1));
|
beaconBytesSent = checkATResponse("AT+CIPSEND=0," + String(packet.length()+1));
|
||||||
delay(2000);
|
delay(2000);
|
||||||
if (beaconBytesSended) {
|
if (beaconBytesSent) {
|
||||||
Serial.print(".");
|
Serial.print(".");
|
||||||
beaconSended = checkATResponse(packet);
|
beaconSent = checkATResponse(packet);
|
||||||
}
|
}
|
||||||
if (!beaconSended) {
|
if (!beaconSent) {
|
||||||
Serial.println("------------------------------------> UPLOAD FAILED!!!");
|
Serial.println("------------------------------------> UPLOAD FAILED!!!");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Packet Uploaded to APRS-IS!");
|
Serial.println("Packet Uploaded to APRS-IS!");
|
||||||
}
|
}
|
||||||
beaconBytesSended = false;
|
beaconBytesSent = false;
|
||||||
beaconSended = false;
|
beaconSent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void listenAPRSIS() {
|
void listenAPRSIS() {
|
||||||
@@ -200,6 +219,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#ifndef A7670_UTILS_H_
|
|
||||||
#define A7670_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
namespace A7670_Utils {
|
|
||||||
|
|
||||||
bool checkModemOn();
|
|
||||||
void setup();
|
|
||||||
bool checkATResponse(const String& ATMessage);
|
|
||||||
void APRS_IS_connect();
|
|
||||||
void uploadToAPRSIS(const String& packet);
|
|
||||||
void listenAPRSIS();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,186 +1,220 @@
|
|||||||
/*______________________________________________________________________________________________________________
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
██╗ ██████╗ ██████╗ █████╗ █████╗ ██████╗ ██████╗ ███████╗ ██╗ ██████╗ █████╗ ████████╗███████╗
|
/*___________________________________________________________________
|
||||||
██║ ██╔═══██╗██╔══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔══██╗██╔════╝ ██║██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝
|
|
||||||
██║ ██║ ██║██████╔╝███████║ ███████║██████╔╝██████╔╝███████╗ ██║██║ ███╗███████║ ██║ █████╗
|
|
||||||
██║ ██║ ██║██╔══██╗██╔══██║ ██╔══██║██╔═══╝ ██╔══██╗╚════██║ ██║██║ ██║██╔══██║ ██║ ██╔══╝
|
|
||||||
███████╗╚██████╔╝██║ ██║██║ ██║ ██║ ██║██║ ██║ ██║███████║ ██║╚██████╔╝██║ ██║ ██║ ███████╗
|
|
||||||
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝
|
|
||||||
|
|
||||||
Ricardo Guzman - CA2RXU
|
██╗ ██████╗ ██████╗ █████╗ █████╗ ██████╗ ██████╗ ███████╗
|
||||||
https://github.com/richonguzman/LoRa_APRS_Tracker
|
██║ ██╔═══██╗██╔══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔══██╗██╔════╝
|
||||||
(donations : http://paypal.me/richonguzman)
|
██║ ██║ ██║██████╔╝███████║ ███████║██████╔╝██████╔╝███████╗
|
||||||
______________________________________________________________________________________________________________*/
|
██║ ██║ ██║██╔══██╗██╔══██║ ██╔══██║██╔═══╝ ██╔══██╗╚════██║
|
||||||
|
███████╗╚██████╔╝██║ ██║██║ ██║ ██║ ██║██║ ██║ ██║███████║
|
||||||
|
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝
|
||||||
|
|
||||||
|
██╗ ██████╗ █████╗ ████████╗███████╗
|
||||||
|
██║██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝
|
||||||
|
██║██║ ███╗███████║ ██║ █████╗
|
||||||
|
██║██║ ██║██╔══██║ ██║ ██╔══╝
|
||||||
|
██║╚██████╔╝██║ ██║ ██║ ███████╗
|
||||||
|
╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝
|
||||||
|
|
||||||
|
|
||||||
|
Ricardo Guzman - CA2RXU
|
||||||
|
https://github.com/richonguzman/LoRa_APRS_iGate
|
||||||
|
(donations : http://paypal.me/richonguzman)
|
||||||
|
___________________________________________________________________*/
|
||||||
|
|
||||||
#include <ElegantOTA.h>
|
#include <ElegantOTA.h>
|
||||||
|
#include <TinyGPS++.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "battery_utils.h"
|
|
||||||
#include "aprs_is_utils.h"
|
#include "aprs_is_utils.h"
|
||||||
#include "station_utils.h"
|
#include "station_utils.h"
|
||||||
#include "boards_pinout.h"
|
#include "battery_utils.h"
|
||||||
|
#include "board_pinout.h"
|
||||||
#include "syslog_utils.h"
|
#include "syslog_utils.h"
|
||||||
#include "query_utils.h"
|
|
||||||
#include "power_utils.h"
|
#include "power_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"
|
||||||
#include "gps_utils.h"
|
#include "gps_utils.h"
|
||||||
#include "bme_utils.h"
|
|
||||||
#include "web_utils.h"
|
#include "web_utils.h"
|
||||||
#include "tnc_utils.h"
|
#include "tnc_utils.h"
|
||||||
|
#include "ntp_utils.h"
|
||||||
|
#include "wx_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#ifdef HAS_A7670
|
#ifdef HAS_A7670
|
||||||
#include "A7670_utils.h"
|
#include "A7670_utils.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
String versionDate = "2024.06.25";
|
|
||||||
Configuration Config;
|
|
||||||
WiFiClient espClient;
|
|
||||||
|
|
||||||
uint8_t myWiFiAPIndex = 0;
|
String versionDate = "2025-12-28";
|
||||||
int myWiFiAPSize = Config.wifiAPs.size();
|
String versionNumber = "3.1.6.3";
|
||||||
WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
|
Configuration Config;
|
||||||
|
WiFiClient aprsIsClient;
|
||||||
|
WiFiClient mqttClient;
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
HardwareSerial gpsSerial(1);
|
||||||
|
TinyGPSPlus gps;
|
||||||
|
uint32_t gpsSatelliteTime = 0;
|
||||||
|
bool gpsInfoToggle = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool isUpdatingOTA = false;
|
uint8_t myWiFiAPIndex = 0;
|
||||||
uint32_t lastBatteryCheck = 0;
|
int myWiFiAPSize = Config.wifiAPs.size();
|
||||||
|
WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
|
||||||
|
|
||||||
bool backUpDigiMode = false;
|
bool isUpdatingOTA = false;
|
||||||
bool modemLoggedToAPRSIS = false;
|
uint32_t lastBatteryCheck = 0;
|
||||||
|
|
||||||
|
bool backUpDigiMode = false;
|
||||||
|
bool modemLoggedToAPRSIS = false;
|
||||||
|
|
||||||
|
#ifdef HAS_EPAPER
|
||||||
|
uint32_t lastEpaperTime = 0;
|
||||||
|
extern String lastEpaperText;
|
||||||
|
#endif
|
||||||
|
|
||||||
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() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
POWER_Utils::setup();
|
POWER_Utils::setup();
|
||||||
Utils::setupDisplay();
|
Utils::setupDisplay();
|
||||||
Config.check();
|
|
||||||
LoRa_Utils::setup();
|
LoRa_Utils::setup();
|
||||||
Utils::validateFreqs();
|
Utils::validateFreqs();
|
||||||
GPS_Utils::generateBeacons();
|
GPS_Utils::setup();
|
||||||
|
STATION_Utils::loadBlacklistAndManagers();
|
||||||
#ifdef STARTUP_DELAY // (TEST) just to wait for WiFi init of Routers
|
Utils::startupDelay();
|
||||||
show_display("", " STARTUP DELAY ...", "", "", 0);
|
SLEEP_Utils::setup();
|
||||||
delay(STARTUP_DELAY * 60 * 1000);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HELTEC_HTCT62
|
|
||||||
if (Config.lowPowerMode) {
|
|
||||||
gpio_wakeup_enable(GPIO_NUM_3, GPIO_INTR_HIGH_LEVEL);
|
|
||||||
esp_deep_sleep_enable_gpio_wakeup(GPIO_NUM_3, ESP_GPIO_WAKEUP_GPIO_HIGH);
|
|
||||||
long lastBeacon = 0;
|
|
||||||
LoRa_Utils::startReceive();
|
|
||||||
while (true) {
|
|
||||||
auto wakeup_reason = esp_sleep_get_wakeup_cause();
|
|
||||||
if (wakeup_reason == 7) { // packet received
|
|
||||||
Serial.println("Received packet");
|
|
||||||
String packet = LoRa_Utils::receivePacket();
|
|
||||||
Serial.println(packet);
|
|
||||||
if (Config.digi.mode == 2) DIGI_Utils::processLoRaPacket(packet);
|
|
||||||
|
|
||||||
if (packet.indexOf(Config.callsign + ":?APRSELP{") != -1) { // Send `?APRSELP` to exit low power
|
|
||||||
Serial.println("Got ?APRSELP message, exiting from low power mode");
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
long time = esp_timer_get_time() / 1000000;
|
|
||||||
if (lastBeacon == 0 || time - lastBeacon >= Config.beacon.interval * 60) {
|
|
||||||
Serial.println("Sending beacon");
|
|
||||||
String comment = Config.beacon.comment;
|
|
||||||
if (Config.battery.sendInternalVoltage) comment += " Batt=" + String(BATTERY_Utils::checkInternalVoltage(),2) + "V";
|
|
||||||
if (Config.battery.sendExternalVoltage) comment += " Ext=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V";
|
|
||||||
STATION_Utils::addToOutputPacketBuffer(GPS_Utils::getiGateLoRaBeaconPacket() + comment);
|
|
||||||
lastBeacon = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoRa_Utils::startReceive();
|
|
||||||
Serial.println("Sleeping");
|
|
||||||
long sleep = (Config.beacon.interval * 60) - (time - lastBeacon);
|
|
||||||
Serial.flush();
|
|
||||||
esp_sleep_enable_timer_wakeup(sleep * 1000000);
|
|
||||||
esp_light_sleep_start();
|
|
||||||
Serial.println("Waked up");
|
|
||||||
}
|
|
||||||
Config.loramodule.rxActive = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
WIFI_Utils::setup();
|
WIFI_Utils::setup();
|
||||||
|
NTP_Utils::setup();
|
||||||
SYSLOG_Utils::setup();
|
SYSLOG_Utils::setup();
|
||||||
BME_Utils::setup();
|
WX_Utils::setup();
|
||||||
WEB_Utils::setup();
|
WEB_Utils::setup();
|
||||||
TNC_Utils::setup();
|
TNC_Utils::setup();
|
||||||
|
MQTT_Utils::setup();
|
||||||
#ifdef HAS_A7670
|
#ifdef HAS_A7670
|
||||||
A7670_Utils::setup();
|
A7670_Utils::setup();
|
||||||
#endif
|
#endif
|
||||||
Utils::checkRebootMode();
|
Utils::checkRebootMode();
|
||||||
|
APRS_IS_Utils::firstConnection();
|
||||||
|
SLEEP_Utils::checkSerial();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
WIFI_Utils::checkIfAutoAPShouldPowerOff();
|
if (Config.digi.ecoMode == 1) {
|
||||||
|
SLEEP_Utils::checkWakeUpFlag();
|
||||||
|
Utils::checkBeaconInterval();
|
||||||
|
STATION_Utils::processOutputPacketBufferUltraEcoMode();
|
||||||
|
Utils::checkSleepByLowBatteryVoltage(1);
|
||||||
|
SLEEP_Utils::startSleeping();
|
||||||
|
} else {
|
||||||
|
WIFI_Utils::checkAutoAPTimeout();
|
||||||
|
|
||||||
if (isUpdatingOTA) {
|
if (isUpdatingOTA) {
|
||||||
ElegantOTA.loop();
|
ElegantOTA.loop();
|
||||||
return; // Don't process IGate and Digi during OTA update
|
return; // Don't process IGate and Digi during OTA update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
if (Config.beacon.gpsActive) {
|
||||||
|
if (millis() - gpsSatelliteTime > 5000) {
|
||||||
|
gpsInfoToggle = !gpsInfoToggle;
|
||||||
|
gpsSatelliteTime = millis();
|
||||||
|
}
|
||||||
|
if (gpsInfoToggle) {
|
||||||
|
thirdLine = "Satellite(s): ";
|
||||||
|
String gpsData = String(gps.satellites.value());
|
||||||
|
if (gpsData.length() < 2) gpsData = "0" + gpsData; // Ensure two-digit formatting
|
||||||
|
thirdLine += gpsData;
|
||||||
|
} else {
|
||||||
|
thirdLine = Utils::getLocalIP();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thirdLine = Utils::getLocalIP();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
thirdLine = Utils::getLocalIP();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (Config.lowVoltageCutOff > 0) {
|
#ifdef HAS_A7670
|
||||||
BATTERY_Utils::checkIfShouldSleep();
|
if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect();
|
||||||
}
|
#else
|
||||||
|
WIFI_Utils::checkWiFi();
|
||||||
|
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
|
||||||
|
|
||||||
thirdLine = Utils::getLocalIP();
|
NTP_Utils::update();
|
||||||
|
TNC_Utils::loop();
|
||||||
|
MQTT_Utils::loop();
|
||||||
|
|
||||||
WIFI_Utils::checkWiFi(); // Always use WiFi, not related to IGate/Digi mode
|
Utils::checkDisplayInterval();
|
||||||
|
Utils::checkBeaconInterval();
|
||||||
|
|
||||||
|
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
|
||||||
|
|
||||||
#ifdef HAS_A7670
|
String packet = "";
|
||||||
if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect();
|
if (Config.loramodule.rxActive) {
|
||||||
#else
|
packet = LoRa_Utils::receivePacket(); // We need to fetch LoRa packet above APRSIS and Digi
|
||||||
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) APRS_IS_Utils::connect();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TNC_Utils::loop();
|
|
||||||
|
|
||||||
Utils::checkDisplayInterval();
|
|
||||||
Utils::checkBeaconInterval();
|
|
||||||
|
|
||||||
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
|
|
||||||
|
|
||||||
String packet = "";
|
|
||||||
if (Config.loramodule.rxActive) {
|
|
||||||
packet = LoRa_Utils::receivePacket(); // We need to fetch LoRa packet above APRSIS and Digi
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet != "") {
|
|
||||||
if (Config.aprs_is.active) { // If APRSIS enabled
|
|
||||||
APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.digi.mode == 2 || backUpDigiMode) { // If Digi enabled
|
if (packet != "") {
|
||||||
STATION_Utils::clean25SegBuffer();
|
if (Config.aprs_is.active) { // If APRSIS enabled
|
||||||
DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi
|
APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backUpDigiMode)) { // If Digi enabled
|
||||||
|
STATION_Utils::clean25SegBuffer();
|
||||||
|
DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet, true); // Send received packet to TNC KISS
|
||||||
|
if (Config.tnc.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.enableServer) { // If TNC server enabled
|
if (Config.aprs_is.active) APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
|
||||||
TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS
|
|
||||||
}
|
STATION_Utils::processOutputPacketBuffer();
|
||||||
if (Config.tnc.enableSerial) { // If Serial KISS enabled
|
|
||||||
TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
|
#ifdef HAS_EPAPER // Only consider updating every 10 seconds (when data to show is different from before)
|
||||||
}
|
if(lastEpaperTime == 0 || millis() - lastEpaperTime > 10000) {
|
||||||
|
String posibleEpaperText = firstLine + secondLine + thirdLine + fourthLine + fifthLine + sixthLine + seventhLine;
|
||||||
|
if (lastEpaperText != posibleEpaperText) {
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
|
lastEpaperText = posibleEpaperText;
|
||||||
|
lastEpaperTime = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Utils::checkRebootTime();
|
||||||
|
Utils::checkSleepByLowBatteryVoltage(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.aprs_is.active) { // If APRSIS enabled
|
|
||||||
APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
|
|
||||||
}
|
|
||||||
|
|
||||||
STATION_Utils::processOutputPacketBuffer();
|
|
||||||
|
|
||||||
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
|
||||||
Utils::checkRebootTime();
|
|
||||||
Utils::checkSleepByLowBatteryVoltage(1);
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,38 @@
|
|||||||
|
/* 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 <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "aprs_is_utils.h"
|
#include "aprs_is_utils.h"
|
||||||
#include "station_utils.h"
|
#include "station_utils.h"
|
||||||
|
#include "board_pinout.h"
|
||||||
#include "syslog_utils.h"
|
#include "syslog_utils.h"
|
||||||
#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;
|
||||||
@@ -21,28 +43,30 @@ 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;
|
||||||
|
|
||||||
#ifdef HAS_A7670
|
#ifdef HAS_A7670
|
||||||
extern bool stationBeacon;
|
extern bool stationBeacon;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
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++;
|
||||||
@@ -50,19 +74,18 @@ namespace APRS_IS_Utils {
|
|||||||
}
|
}
|
||||||
if (count == 20) {
|
if (count == 20) {
|
||||||
Serial.println("Tried: " + String(count) + " FAILED!");
|
Serial.println("Tried: " + String(count) + " FAILED!");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Serial.println("Connected!\n(Server: " + String(Config.aprs_is.server) + " / Port: " + String(Config.aprs_is.port) + ")");
|
Serial.println("Connected!\n(Server: " + String(Config.aprs_is.server) + " / Port: " + String(Config.aprs_is.port) + ")");
|
||||||
// String filter = "t/m/" + Config.callsign + "/" + (String)Config.aprs_is.reportingDistance;
|
// String filter = "t/m/" + Config.callsign + "/" + (String)Config.aprs_is.reportingDistance;
|
||||||
|
String aprsAuth = "user ";
|
||||||
String aprsauth = "user ";
|
aprsAuth += Config.callsign;
|
||||||
aprsauth += Config.callsign;
|
aprsAuth += " pass ";
|
||||||
aprsauth += " pass ";
|
aprsAuth += Config.aprs_is.passcode;
|
||||||
aprsauth += Config.aprs_is.passcode;
|
aprsAuth += " vers CA2RXUiGate ";
|
||||||
aprsauth += " vers CA2RXU_LoRa_iGate 1.3 filter ";
|
aprsAuth += versionNumber;
|
||||||
aprsauth += Config.aprs_is.filter;
|
aprsAuth += " filter ";
|
||||||
upload(aprsauth);
|
aprsAuth += Config.aprs_is.filter;
|
||||||
delay(200);
|
upload(aprsAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,13 +94,13 @@ namespace APRS_IS_Utils {
|
|||||||
if (WiFi.status() == WL_CONNECTED) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
wifiState = "OK";
|
wifiState = "OK";
|
||||||
} else {
|
} else {
|
||||||
if (backUpDigiMode) {
|
if (backUpDigiMode || Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) {
|
||||||
wifiState = "--";
|
wifiState = "--";
|
||||||
} else {
|
} else {
|
||||||
wifiState = "AP";
|
wifiState = "AP";
|
||||||
}
|
}
|
||||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
}
|
}
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
}
|
}
|
||||||
@@ -92,16 +115,16 @@ namespace APRS_IS_Utils {
|
|||||||
aprsisState = "--";
|
aprsisState = "--";
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (espClient.connected()) {
|
if (aprsIsClient.connected()) {
|
||||||
aprsisState = "OK";
|
aprsisState = "OK";
|
||||||
} else {
|
} else {
|
||||||
aprsisState = "--";
|
aprsisState = "--";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(aprsisState == "--" && !Config.display.alwaysOn && Config.display.timeout != 0) {
|
if(aprsisState == "--" && !Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
secondLine = "WiFi: ";
|
secondLine = "WiFi: ";
|
||||||
secondLine += wifiState;
|
secondLine += wifiState;
|
||||||
@@ -118,27 +141,28 @@ namespace APRS_IS_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String buildPacketToUpload(const String& packet) {
|
String buildPacketToUpload(const String& packet) {
|
||||||
String buildedPacket = packet.substring(3, packet.indexOf(":"));
|
String packetToUpload = packet.substring(3, packet.indexOf(":"));
|
||||||
if (!(Config.aprs_is.active && Config.digi.mode == 0)) { // Check if NOT only IGate
|
if (Config.aprs_is.active && passcodeValid && Config.aprs_is.messagesToRF) {
|
||||||
buildedPacket += ",qAR,";
|
packetToUpload += ",qAR,";
|
||||||
} else {
|
} else {
|
||||||
buildedPacket += ",qAO,";
|
packetToUpload += ",qAO,";
|
||||||
}
|
}
|
||||||
buildedPacket += Config.callsign;
|
packetToUpload += Config.callsign;
|
||||||
buildedPacket += checkForStartingBytes(packet.substring(packet.indexOf(":")));
|
packetToUpload += checkForStartingBytes(packet.substring(packet.indexOf(":")));
|
||||||
return buildedPacket;
|
return packetToUpload;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool processReceivedLoRaMessage(const String& sender, const String& packet) {
|
bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty) {
|
||||||
String receivedMessage;
|
String receivedMessage;
|
||||||
if (packet.indexOf("{") > 0) { // ack?
|
if (packet.indexOf("{") > 0) { // ack?
|
||||||
String ackMessage = "ack";
|
String ackMessage = "ack";
|
||||||
ackMessage.concat(packet.substring(packet.indexOf("{") + 1));
|
ackMessage.concat(packet.substring(packet.indexOf("{") + 1));
|
||||||
ackMessage.trim();
|
ackMessage.trim();
|
||||||
//Serial.println(ackMessage);
|
//Serial.println(ackMessage);
|
||||||
|
|
||||||
String addToBuffer = Config.callsign;
|
String addToBuffer = Config.callsign;
|
||||||
addToBuffer += ">APLRG1,RFONLY";
|
addToBuffer += ">APLRG1";
|
||||||
|
if (!thirdParty) addToBuffer += ",RFONLY";
|
||||||
if (Config.beacon.path != "") {
|
if (Config.beacon.path != "") {
|
||||||
addToBuffer += ",";
|
addToBuffer += ",";
|
||||||
addToBuffer += Config.beacon.path;
|
addToBuffer += Config.beacon.path;
|
||||||
@@ -160,11 +184,11 @@ namespace APRS_IS_Utils {
|
|||||||
}
|
}
|
||||||
if (receivedMessage.indexOf("?") == 0) {
|
if (receivedMessage.indexOf("?") == 0) {
|
||||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
}
|
}
|
||||||
STATION_Utils::addToOutputPacketBuffer(QUERY_Utils::process(receivedMessage, sender, 0)); // LoRa
|
STATION_Utils::addToOutputPacketBuffer(QUERY_Utils::process(receivedMessage, sender, false, thirdParty));
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, "Callsign = " + sender, "TYPE --> QUERY", 0);
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, "Callsign = " + sender, "TYPE --> QUERY", 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -173,40 +197,38 @@ namespace APRS_IS_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void processLoRaPacket(const String& packet) {
|
void processLoRaPacket(const String& packet) {
|
||||||
if (espClient.connected() || modemLoggedToAPRSIS) {
|
if (passcodeValid && (aprsIsClient.connected() || modemLoggedToAPRSIS)) {
|
||||||
if (packet != "") {
|
if (packet.indexOf("NOGATE") == -1 && packet.indexOf("RFONLY") == -1) {
|
||||||
if ((packet.substring(0, 3) == "\x3c\xff\x01") && (packet.indexOf("NOGATE") == -1) && (packet.indexOf("RFONLY") == -1)) {
|
int firstColonIndex = packet.indexOf(":");
|
||||||
int firstColonIndex = packet.indexOf(":");
|
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) {
|
||||||
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) {
|
const String& Sender = packet.substring(3, packet.indexOf(">"));
|
||||||
const String& Sender = packet.substring(3, packet.indexOf(">"));
|
if (Sender != Config.callsign && Utils::checkValidCallsign(Sender)) {
|
||||||
if (Sender != Config.callsign && Utils::checkValidCallsign(Sender)) {
|
STATION_Utils::updateLastHeard(Sender);
|
||||||
STATION_Utils::updateLastHeard(Sender);
|
Utils::typeOfPacket(packet.substring(3), 0); // LoRa-APRS
|
||||||
Utils::typeOfPacket(packet.substring(3), 0); // LoRa-APRS
|
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
|
||||||
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
|
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
||||||
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
Addressee.trim();
|
||||||
Addressee.trim();
|
bool queryMessage = false;
|
||||||
bool queryMessage = false;
|
if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
|
||||||
if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
|
queryMessage = processReceivedLoRaMessage(Sender, checkForStartingBytes(AddresseeAndMessage), false);
|
||||||
queryMessage = processReceivedLoRaMessage(Sender, checkForStartingBytes(AddresseeAndMessage));
|
|
||||||
}
|
|
||||||
if (!queryMessage) {
|
|
||||||
const String& aprsPacket = buildPacketToUpload(packet);
|
|
||||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
|
||||||
display_toggle(true);
|
|
||||||
}
|
|
||||||
lastScreenOn = millis();
|
|
||||||
#ifdef HAS_A7670
|
|
||||||
stationBeacon = true;
|
|
||||||
A7670_Utils::uploadToAPRSIS(aprsPacket);
|
|
||||||
stationBeacon = false;
|
|
||||||
#else
|
|
||||||
upload(aprsPacket);
|
|
||||||
#endif
|
|
||||||
Utils::println("---> Uploaded to APRS-IS");
|
|
||||||
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if (!queryMessage) {
|
||||||
|
const String& aprsPacket = buildPacketToUpload(packet);
|
||||||
|
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||||
|
displayToggle(true);
|
||||||
|
}
|
||||||
|
lastScreenOn = millis();
|
||||||
|
#ifdef HAS_A7670
|
||||||
|
stationBeacon = true;
|
||||||
|
A7670_Utils::uploadToAPRSIS(aprsPacket);
|
||||||
|
stationBeacon = false;
|
||||||
|
#else
|
||||||
|
upload(aprsPacket);
|
||||||
|
#endif
|
||||||
|
Utils::println("---> Uploaded to APRS-IS");
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,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,");
|
||||||
@@ -258,13 +275,23 @@ namespace APRS_IS_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void processAPRSISPacket(const String& packet) {
|
void processAPRSISPacket(const String& packet) {
|
||||||
if (!packet.startsWith("#")) {
|
if (!passcodeValid && packet.indexOf(Config.callsign) != -1) {
|
||||||
|
if (packet.indexOf("unverified") != -1 ) {
|
||||||
|
Serial.println("\n****APRS PASSCODE NOT VALID****\n");
|
||||||
|
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 3000);
|
||||||
|
aprsIsClient.stop();
|
||||||
|
Config.aprs_is.active = false;
|
||||||
|
} else if (packet.indexOf("verified") != -1 ) {
|
||||||
|
passcodeValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (passcodeValid && !packet.startsWith("#")) {
|
||||||
if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
|
if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
|
||||||
String Sender = packet.substring(0, packet.indexOf(">"));
|
String Sender = packet.substring(0, packet.indexOf(">"));
|
||||||
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
|
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
|
||||||
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
||||||
Addressee.trim();
|
Addressee.trim();
|
||||||
if (Addressee == Config.callsign) { // its for me!
|
if (Addressee == Config.callsign) { // its for me!
|
||||||
String receivedMessage;
|
String receivedMessage;
|
||||||
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
|
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
|
||||||
String ackMessage = "ack";
|
String ackMessage = "ack";
|
||||||
@@ -284,17 +311,18 @@ namespace APRS_IS_Utils {
|
|||||||
A7670_Utils::uploadToAPRSIS(ackPacket);
|
A7670_Utils::uploadToAPRSIS(ackPacket);
|
||||||
#else
|
#else
|
||||||
upload(ackPacket);
|
upload(ackPacket);
|
||||||
#endif
|
#endif
|
||||||
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
|
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
|
||||||
} else {
|
} else {
|
||||||
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
|
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
|
||||||
}
|
}
|
||||||
if (receivedMessage.indexOf("?") == 0) {
|
if (receivedMessage.indexOf("?") == 0) {
|
||||||
Utils::println("Received Query APRS-IS : " + packet);
|
Utils::println("Rx Query (APRS-IS) : " + packet);
|
||||||
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, 1); // APRSIS
|
Sender.trim();
|
||||||
|
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false);
|
||||||
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
|
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
|
||||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
}
|
}
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
delay(500);
|
delay(500);
|
||||||
@@ -302,7 +330,7 @@ namespace APRS_IS_Utils {
|
|||||||
A7670_Utils::uploadToAPRSIS(queryAnswer);
|
A7670_Utils::uploadToAPRSIS(queryAnswer);
|
||||||
#else
|
#else
|
||||||
upload(queryAnswer);
|
upload(queryAnswer);
|
||||||
#endif
|
#endif
|
||||||
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
|
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
|
||||||
fifthLine = "APRS-IS ----> APRS-IS";
|
fifthLine = "APRS-IS ----> APRS-IS";
|
||||||
sixthLine = Config.callsign;
|
sixthLine = Config.callsign;
|
||||||
@@ -314,22 +342,32 @@ namespace APRS_IS_Utils {
|
|||||||
seventhLine = "QUERY = ";
|
seventhLine = "QUERY = ";
|
||||||
seventhLine += receivedMessage;
|
seventhLine += receivedMessage;
|
||||||
}
|
}
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
} else {
|
} else {
|
||||||
Utils::print("Received Message from APRS-IS : " + packet);
|
Utils::print("Rx Message (APRS-IS): " + packet);
|
||||||
if (STATION_Utils::wasHeard(Addressee)) {
|
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
|
||||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
|
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
|
||||||
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
|
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
|
||||||
Utils::println("Received Object from APRS-IS : " + packet);
|
Utils::print("Rx Object (APRS-IS) : " + packet);
|
||||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
|
if (STATION_Utils::checkObjectTime(packet)) {
|
||||||
display_toggle(true);
|
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
|
||||||
lastScreenOn = millis();
|
displayToggle(true);
|
||||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
lastScreenOn = millis();
|
||||||
|
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||||
|
Serial.println();
|
||||||
|
} else {
|
||||||
|
Serial.println(" ---> Rejected (Time): No Tx");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Config.tnc.aprsBridgeActive) {
|
||||||
|
if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS
|
||||||
|
if (Config.tnc.enableSerial) TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,10 +376,10 @@ 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');
|
||||||
// Serial.println(aprsisPacket);
|
aprsisPacket.trim(); // Serial.println(aprsisPacket);
|
||||||
processAPRSISPacket(aprsisPacket);
|
processAPRSISPacket(aprsisPacket);
|
||||||
lastRxTime = millis();
|
lastRxTime = millis();
|
||||||
}
|
}
|
||||||
@@ -349,4 +387,13 @@ namespace APRS_IS_Utils {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void firstConnection() {
|
||||||
|
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !aprsIsClient.connected()) {
|
||||||
|
connect();
|
||||||
|
while (!passcodeValid) {
|
||||||
|
listenAPRSIS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#ifndef APRS_IS_UTILS_H_
|
|
||||||
#define APRS_IS_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace APRS_IS_Utils {
|
|
||||||
|
|
||||||
void upload(const String& line);
|
|
||||||
void connect();
|
|
||||||
void checkStatus();
|
|
||||||
String checkForStartingBytes(const String& packet);
|
|
||||||
|
|
||||||
String buildPacketToUpload(const String& packet);
|
|
||||||
bool processReceivedLoRaMessage(const String& sender, const String& packet);
|
|
||||||
void processLoRaPacket(const String& packet);
|
|
||||||
|
|
||||||
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType);
|
|
||||||
void processAPRSISPacket(const String& packet);
|
|
||||||
void listenAPRSIS();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,23 +1,70 @@
|
|||||||
#include <Arduino.h>
|
/* 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 <Adafruit_INA219.h>
|
||||||
#include "battery_utils.h"
|
#include "battery_utils.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "boards_pinout.h"
|
#include "board_pinout.h"
|
||||||
#include "power_utils.h"
|
#include "power_utils.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
extern Configuration Config;
|
|
||||||
extern uint32_t lastBatteryCheck;
|
|
||||||
|
|
||||||
bool shouldSleepLowVoltage = false;
|
extern Configuration Config;
|
||||||
|
extern uint32_t lastBatteryCheck;
|
||||||
|
|
||||||
float adcReadingTransformation = (3.3/4095);
|
bool shouldSleepLowVoltage = false;
|
||||||
float voltageDividerCorrection = 0.288;
|
|
||||||
|
|
||||||
// for External Voltage Measurment (MAX = 15Volts !!!)
|
float adcReadingTransformation = (3.3/4095);
|
||||||
float R1 = 100.000; //in Kilo-Ohms
|
int adcReadings = 20;
|
||||||
float R2 = 27.000; //in Kilo-Ohms
|
float voltageDividerCorrection = 0.288;
|
||||||
float readingCorrection = 0.125;
|
float readingCorrection = 0.125;
|
||||||
float multiplyCorrection = 0.035;
|
float multiplyCorrection = 0.035;
|
||||||
|
|
||||||
|
float voltageDividerTransformation = 0.0;
|
||||||
|
|
||||||
|
uint8_t externalI2CSensorAddress = 0x00;
|
||||||
|
int externalI2CSensorType = 0; // 0 = None | 1 = INA219
|
||||||
|
|
||||||
|
Adafruit_INA219 ina219;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
|
#include <esp_adc_cal.h>
|
||||||
|
|
||||||
|
#if defined(TTGO_LORA32_V2_1) || defined(TTGO_LORA32_V2_1_915)
|
||||||
|
#define InternalBattery_ADC_Channel ADC1_CHANNEL_7 // t_lora32 pin35
|
||||||
|
#define ExternalVoltage_ADC_Channel ADC1_CHANNEL_6 // t_lora32 pin34
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_VREF
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP_FIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_adc_cal_characteristics_t adc_chars;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool calibrationEnable = false;
|
||||||
|
|
||||||
|
|
||||||
namespace BATTERY_Utils {
|
namespace BATTERY_Utils {
|
||||||
@@ -26,6 +73,78 @@ namespace BATTERY_Utils {
|
|||||||
return (voltage - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
return (voltage - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void adcCalibration() {
|
||||||
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
|
if (calibrationEnable) {
|
||||||
|
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||||
|
adc1_config_channel_atten(InternalBattery_ADC_Channel, ADC_ATTEN_DB_12);
|
||||||
|
adc1_config_channel_atten(ExternalVoltage_ADC_Channel, ADC_ATTEN_DB_12);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void adcCalibrationCheck() {
|
||||||
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
|
esp_err_t ret;
|
||||||
|
ret = esp_adc_cal_check_efuse(ADC_EXAMPLE_CALI_SCHEME);
|
||||||
|
/*if (ret == ESP_ERR_NOT_SUPPORTED) {
|
||||||
|
Serial.println("Calibration scheme not supported, skip software calibration");
|
||||||
|
} else if (ret == ESP_ERR_INVALID_VERSION) {
|
||||||
|
Serial.println("eFuse not burnt, skip software calibration");
|
||||||
|
} else */
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_12, ADC_WIDTH_BIT_12, 1100, &adc_chars);
|
||||||
|
//Serial.printf("eFuse Vref:%u mV\n", adc_chars.vref);
|
||||||
|
calibrationEnable = true;
|
||||||
|
} /*else {
|
||||||
|
Serial.println("Invalid Calibration Arg");
|
||||||
|
}*/
|
||||||
|
#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() {
|
||||||
|
if ((Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) && Config.battery.voltageDividerR2 != 0) voltageDividerTransformation = (Config.battery.voltageDividerR1 + Config.battery.voltageDividerR2) / Config.battery.voltageDividerR2;
|
||||||
|
|
||||||
|
#if defined(HAS_ADC_CALIBRATION)
|
||||||
|
if (Config.battery.sendInternalVoltage || Config.battery.monitorInternalVoltage || Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) {
|
||||||
|
adcCalibrationCheck();
|
||||||
|
adcCalibration();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
getI2CVoltageSensorAddress();
|
||||||
|
if (externalI2CSensorAddress != 0x00) {
|
||||||
|
if (detectINA219(externalI2CSensorAddress)) {
|
||||||
|
Serial.println("INA219 sensor found");
|
||||||
|
externalI2CSensorType = 1; // INA219
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float checkInternalVoltage() {
|
float checkInternalVoltage() {
|
||||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
||||||
if(POWER_Utils::isBatteryConnected()) {
|
if(POWER_Utils::isBatteryConnected()) {
|
||||||
@@ -34,67 +153,108 @@ namespace BATTERY_Utils {
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int sample;
|
|
||||||
int sampleSum = 0;
|
|
||||||
#ifdef ADC_CTRL
|
#ifdef ADC_CTRL
|
||||||
#if defined(HELTEC_WIRELESS_TRACKER)
|
POWER_Utils::adc_ctrl_ON();
|
||||||
digitalWrite(ADC_CTRL, HIGH);
|
|
||||||
#endif
|
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3)
|
|
||||||
digitalWrite(ADC_CTRL, LOW);
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++) {
|
int sampleSum = 0;
|
||||||
#ifdef BATTERY_PIN
|
for (int i = 0; i < adcReadings; i++) {
|
||||||
sample = analogRead(BATTERY_PIN);
|
#if defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915)
|
||||||
|
sampleSum = 0;
|
||||||
|
#else
|
||||||
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
|
if (calibrationEnable){
|
||||||
|
sampleSum += adc1_get_raw(InternalBattery_ADC_Channel);
|
||||||
|
} else {
|
||||||
|
sampleSum += analogRead(BATTERY_PIN);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#ifdef BATTERY_PIN
|
||||||
|
sampleSum += analogRead(BATTERY_PIN);
|
||||||
|
#else
|
||||||
|
sampleSum += 0;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
|
delay(3);
|
||||||
sample = 0;
|
|
||||||
#endif
|
|
||||||
sampleSum += sample;
|
|
||||||
delayMicroseconds(50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ADC_CTRL
|
#ifdef ADC_CTRL
|
||||||
#if defined(HELTEC_WIRELESS_TRACKER)
|
POWER_Utils::adc_ctrl_OFF();
|
||||||
digitalWrite(ADC_CTRL, LOW);
|
|
||||||
#endif
|
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3)
|
|
||||||
digitalWrite(ADC_CTRL, HIGH);
|
|
||||||
#endif
|
|
||||||
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.
|
|
||||||
return (((sampleSum/100) * adcReadingTransformation) / inputDivider) + 0.285; // Yes, this offset is excessive, but the ADC on the ESP32s3 is quite inaccurate and noisy. Adjust to own measurements.
|
|
||||||
#else
|
|
||||||
return (2 * (sampleSum/100) * adcReadingTransformation) + voltageDividerCorrection; // raw voltage without mapping
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#ifdef HELTEC_WP_V1
|
||||||
|
double inputDivider = (1.0 / (10.0 + 10.0)) * 10.0; // The voltage divider is a 10k + 10k resistor in series
|
||||||
|
#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.
|
||||||
|
#endif
|
||||||
|
return (((sampleSum/adcReadings) * adcReadingTransformation) / inputDivider) + 0.285; // Yes, this offset is excessive, but the ADC on the ESP32s3 is quite inaccurate and noisy. Adjust to own measurements.
|
||||||
|
#else
|
||||||
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
|
if (calibrationEnable){
|
||||||
|
float voltage = esp_adc_cal_raw_to_voltage(sampleSum / adcReadings, &adc_chars);
|
||||||
|
voltage *= 2; // for 100K/100K voltage divider
|
||||||
|
voltage /= 1000;
|
||||||
|
return voltage;
|
||||||
|
} else {
|
||||||
|
return (2 * (sampleSum/adcReadings) * adcReadingTransformation) + voltageDividerCorrection; // raw voltage without mapping
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||||
|
double inputDivider = (1.0 / (560.0 + 100.0)) * 100.0; // The voltage divider is a 560k + 100k resistor in series, 100k on the low side.
|
||||||
|
return (((sampleSum/adcReadings) * adcReadingTransformation) / inputDivider) + 0.41;
|
||||||
|
#else
|
||||||
|
return (2 * (sampleSum/adcReadings) * adcReadingTransformation) + voltageDividerCorrection; // raw voltage without mapping
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
// return mapVoltage(voltage, 3.34, 4.71, 3.0, 4.2); // mapped voltage
|
// return mapVoltage(voltage, 3.34, 4.71, 3.0, 4.2); // mapped voltage
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
float checkExternalVoltage() {
|
float checkExternalVoltage() {
|
||||||
int sample;
|
if (externalI2CSensorType == 0) {
|
||||||
int sampleSum = 0;
|
int sample;
|
||||||
for (int i = 0; i < 100; i++) {
|
int sampleSum = 0;
|
||||||
sample = analogRead(Config.battery.externalVoltagePin);
|
for (int i = 0; i < 100; i++) {
|
||||||
sampleSum += sample;
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
delayMicroseconds(50);
|
if (calibrationEnable){
|
||||||
}
|
sample = adc1_get_raw(ExternalVoltage_ADC_Channel);
|
||||||
|
} else {
|
||||||
float voltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * ((R1+R2)/R2)) - multiplyCorrection;
|
sample = analogRead(Config.battery.externalVoltagePin);
|
||||||
|
}
|
||||||
return voltage; // raw voltage without mapping
|
#else
|
||||||
|
sample = analogRead(Config.battery.externalVoltagePin);
|
||||||
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
|
#endif
|
||||||
}
|
sampleSum += sample;
|
||||||
|
delayMicroseconds(50);
|
||||||
void checkIfShouldSleep() {
|
|
||||||
if (lastBatteryCheck == 0 || millis() - lastBatteryCheck >= 15 * 60 * 1000) {
|
|
||||||
lastBatteryCheck = millis();
|
|
||||||
if (checkInternalVoltage() < Config.lowVoltageCutOff) {
|
|
||||||
ESP.deepSleep(1800000000); // 30 min sleep (60s = 60e6)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
extVoltage = ((((sampleSum/100.0)* adcReadingTransformation) + readingCorrection) * voltageDividerTransformation) - multiplyCorrection;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return extVoltage; // raw voltage without mapping
|
||||||
|
|
||||||
|
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
|
||||||
|
} else if (externalI2CSensorType == 1) { // INA219
|
||||||
|
int sampleSum = 0;
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
sampleSum += ina219.getBusVoltage_V() * 1000.0;
|
||||||
|
delayMicroseconds(50);
|
||||||
|
}
|
||||||
|
float extVoltage = sampleSum/100.0;
|
||||||
|
return extVoltage/1000.0;
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +264,11 @@ namespace BATTERY_Utils {
|
|||||||
shouldSleepLowVoltage = true;
|
shouldSleepLowVoltage = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (Config.battery.monitorExternalVoltage && checkExternalVoltage() < Config.battery.externalSleepVoltage + 0.1) {
|
#ifndef HELTEC_WP_V1
|
||||||
shouldSleepLowVoltage = true;
|
if (Config.battery.monitorExternalVoltage && checkExternalVoltage() < Config.battery.externalSleepVoltage + 0.1) {
|
||||||
}
|
shouldSleepLowVoltage = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (shouldSleepLowVoltage) {
|
if (shouldSleepLowVoltage) {
|
||||||
Utils::checkSleepByLowBatteryVoltage(0);
|
Utils::checkSleepByLowBatteryVoltage(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#ifndef BATTERY_UTILS_H_
|
|
||||||
#define BATTERY_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace BATTERY_Utils {
|
|
||||||
|
|
||||||
float checkInternalVoltage();
|
|
||||||
float checkExternalVoltage();
|
|
||||||
void checkIfShouldSleep(); // ????
|
|
||||||
void startupBatteryHealth();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
#include "bme_utils.h"
|
|
||||||
#include "configuration.h"
|
|
||||||
#include "gps_utils.h"
|
|
||||||
#include "display.h"
|
|
||||||
|
|
||||||
#define SEALEVELPRESSURE_HPA (1013.25)
|
|
||||||
#define CORRECTION_FACTOR (8.2296) // for meters
|
|
||||||
|
|
||||||
extern Configuration Config;
|
|
||||||
extern String fifthLine;
|
|
||||||
|
|
||||||
int wxModuleType = 0;
|
|
||||||
uint8_t wxModuleAddress = 0x00;
|
|
||||||
|
|
||||||
float newHum, newTemp, newPress, newGas;
|
|
||||||
|
|
||||||
|
|
||||||
Adafruit_BME280 bme280;
|
|
||||||
#ifdef HELTEC_V3
|
|
||||||
Adafruit_BMP280 bmp280(&Wire1);
|
|
||||||
Adafruit_Si7021 sensor = Adafruit_Si7021();
|
|
||||||
#else
|
|
||||||
Adafruit_BMP280 bmp280;
|
|
||||||
Adafruit_BME680 bme680;
|
|
||||||
Adafruit_Si7021 sensor = Adafruit_Si7021();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace BME_Utils {
|
|
||||||
|
|
||||||
void getWxModuleAddres() {
|
|
||||||
uint8_t err, addr;
|
|
||||||
for(addr = 1; addr < 0x7F; addr++) {
|
|
||||||
#ifdef HELTEC_V3
|
|
||||||
Wire1.beginTransmission(addr);
|
|
||||||
err = Wire1.endTransmission();
|
|
||||||
#else
|
|
||||||
Wire.beginTransmission(addr);
|
|
||||||
err = Wire.endTransmission();
|
|
||||||
#endif
|
|
||||||
if (err == 0) {
|
|
||||||
//Serial.println(addr); this shows any connected board to I2C
|
|
||||||
if (addr == 0x76 || addr == 0x77) { // BME/BMP
|
|
||||||
wxModuleAddress = addr;
|
|
||||||
return;
|
|
||||||
} else if (addr == 0x40) { // Si7011
|
|
||||||
wxModuleAddress = addr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
if (Config.bme.active) {
|
|
||||||
getWxModuleAddres();
|
|
||||||
if (wxModuleAddress != 0x00) {
|
|
||||||
bool wxModuleFound = false;
|
|
||||||
if (wxModuleAddress == 0x76 || wxModuleAddress == 0x77) {
|
|
||||||
#ifdef HELTEC_V3
|
|
||||||
if (bme280.begin(wxModuleAddress, &Wire1)) {
|
|
||||||
Serial.println("BME280 sensor found");
|
|
||||||
wxModuleType = 1;
|
|
||||||
wxModuleFound = true;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (bme280.begin(wxModuleAddress)) {
|
|
||||||
Serial.println("BME280 sensor found");
|
|
||||||
wxModuleType = 1;
|
|
||||||
wxModuleFound = true;
|
|
||||||
}
|
|
||||||
if (!wxModuleFound) {
|
|
||||||
if (bme680.begin(wxModuleAddress)) {
|
|
||||||
Serial.println("BME680 sensor found");
|
|
||||||
wxModuleType = 3;
|
|
||||||
wxModuleFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!wxModuleFound) {
|
|
||||||
if (bmp280.begin(wxModuleAddress)) {
|
|
||||||
Serial.println("BMP280 sensor found");
|
|
||||||
wxModuleType = 2;
|
|
||||||
wxModuleFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (wxModuleAddress == 0x40) {
|
|
||||||
if(sensor.begin()) {
|
|
||||||
Serial.println("Si7021 sensor found");
|
|
||||||
wxModuleType = 4;
|
|
||||||
wxModuleFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!wxModuleFound) {
|
|
||||||
show_display("ERROR", "", "BME/BMP/Si7021 sensor active", "but no sensor found...", 2000);
|
|
||||||
Serial.println("BME/BMP/Si7021 sensor Active in config but not found! Check Wiring");
|
|
||||||
} else {
|
|
||||||
switch (wxModuleType) {
|
|
||||||
case 1:
|
|
||||||
bme280.setSampling(Adafruit_BME280::MODE_FORCED,
|
|
||||||
Adafruit_BME280::SAMPLING_X1,
|
|
||||||
Adafruit_BME280::SAMPLING_X1,
|
|
||||||
Adafruit_BME280::SAMPLING_X1,
|
|
||||||
Adafruit_BME280::FILTER_OFF
|
|
||||||
);
|
|
||||||
Serial.println("BME280 Module init done!");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
bmp280.setSampling(Adafruit_BMP280::MODE_FORCED,
|
|
||||||
Adafruit_BMP280::SAMPLING_X1,
|
|
||||||
Adafruit_BMP280::SAMPLING_X1,
|
|
||||||
Adafruit_BMP280::FILTER_OFF
|
|
||||||
);
|
|
||||||
Serial.println("BMP280 Module init done!");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
#ifndef HELTEC_V3
|
|
||||||
bme680.setTemperatureOversampling(BME680_OS_1X);
|
|
||||||
bme680.setHumidityOversampling(BME680_OS_1X);
|
|
||||||
bme680.setPressureOversampling(BME680_OS_1X);
|
|
||||||
bme680.setIIRFilterSize(BME680_FILTER_SIZE_0);
|
|
||||||
Serial.println("BMP680 Module init done!");
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateTempString(const float bmeTemp) {
|
|
||||||
String strTemp = String((int)bmeTemp);
|
|
||||||
switch (strTemp.length()) {
|
|
||||||
case 1:
|
|
||||||
return "00" + strTemp;
|
|
||||||
case 2:
|
|
||||||
return "0" + strTemp;
|
|
||||||
case 3:
|
|
||||||
return strTemp;
|
|
||||||
default:
|
|
||||||
return "-999";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateHumString(const float bmeHum) {
|
|
||||||
String strHum = String((int)bmeHum);
|
|
||||||
switch (strHum.length()) {
|
|
||||||
case 1:
|
|
||||||
return "0" + strHum;
|
|
||||||
case 2:
|
|
||||||
return strHum;
|
|
||||||
case 3:
|
|
||||||
if ((int)bmeHum == 100) {
|
|
||||||
return "00";
|
|
||||||
} else {
|
|
||||||
return "-99";
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return "-99";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String generatePresString(const float bmePress) {
|
|
||||||
String strPress = String((int)bmePress);
|
|
||||||
String decPress = String(int((bmePress - int(bmePress)) * 10));
|
|
||||||
switch (strPress.length()) {
|
|
||||||
case 1:
|
|
||||||
return "000" + strPress + decPress;
|
|
||||||
case 2:
|
|
||||||
return "00" + strPress + decPress;
|
|
||||||
case 3:
|
|
||||||
return "0" + strPress + decPress;
|
|
||||||
case 4:
|
|
||||||
return strPress + decPress;
|
|
||||||
case 5:
|
|
||||||
return strPress;
|
|
||||||
default:
|
|
||||||
return "-99999";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String readDataSensor() {
|
|
||||||
switch (wxModuleType) {
|
|
||||||
case 1: // BME280
|
|
||||||
bme280.takeForcedMeasurement();
|
|
||||||
newTemp = bme280.readTemperature();
|
|
||||||
newPress = (bme280.readPressure() / 100.0F);
|
|
||||||
newHum = bme280.readHumidity();
|
|
||||||
break;
|
|
||||||
case 2: // BMP280
|
|
||||||
bmp280.takeForcedMeasurement();
|
|
||||||
newTemp = bmp280.readTemperature();
|
|
||||||
newPress = (bmp280.readPressure() / 100.0F);
|
|
||||||
newHum = 0;
|
|
||||||
break;
|
|
||||||
case 3: // BME680
|
|
||||||
#ifndef HELTEC_V3
|
|
||||||
bme680.performReading();
|
|
||||||
delay(50);
|
|
||||||
if (bme680.endReading()) {
|
|
||||||
newTemp = bme680.temperature;
|
|
||||||
newPress = (bme680.pressure / 100.0F);
|
|
||||||
newHum = bme680.humidity;
|
|
||||||
newGas = bme680.gas_resistance / 1000.0; // in Kilo ohms
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case 4: // Si7021
|
|
||||||
newTemp = sensor.readTemperature();
|
|
||||||
newPress = 0;
|
|
||||||
newHum = sensor.readHumidity();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String wx;
|
|
||||||
if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) {
|
|
||||||
Serial.println("BME/BMP/Si7021 Module data failed");
|
|
||||||
wx = ".../...g...t...r...p...P...h..b.....";
|
|
||||||
fifthLine = "";
|
|
||||||
return wx;
|
|
||||||
} else {
|
|
||||||
String tempStr = generateTempString(((newTemp + Config.bme.temperatureCorrection) * 1.8) + 32);
|
|
||||||
|
|
||||||
String humStr;
|
|
||||||
if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4) {
|
|
||||||
humStr = generateHumString(newHum);
|
|
||||||
} else if (wxModuleType == 2) {
|
|
||||||
humStr = "..";
|
|
||||||
}
|
|
||||||
|
|
||||||
String presStr;
|
|
||||||
if (wxModuleAddress == 4) {
|
|
||||||
presStr = ".....";
|
|
||||||
} else {
|
|
||||||
presStr = generatePresString(newPress + (Config.bme.heightCorrection/CORRECTION_FACTOR));
|
|
||||||
}
|
|
||||||
|
|
||||||
fifthLine = "BME-> ";
|
|
||||||
fifthLine += String(int(newTemp + Config.bme.temperatureCorrection));
|
|
||||||
fifthLine += "C ";
|
|
||||||
fifthLine += humStr;
|
|
||||||
fifthLine += "% ";
|
|
||||||
fifthLine += presStr.substring(0,4);
|
|
||||||
fifthLine += "hPa";
|
|
||||||
|
|
||||||
wx = ".../...g...t";
|
|
||||||
wx += tempStr;
|
|
||||||
wx += "r...p...P...h";
|
|
||||||
wx += humStr;
|
|
||||||
wx += "b";
|
|
||||||
wx += presStr;
|
|
||||||
|
|
||||||
if (wxModuleType == 3) {
|
|
||||||
wx += "Gas: ";
|
|
||||||
wx += String(newGas);
|
|
||||||
wx += "Kohms";
|
|
||||||
}
|
|
||||||
return wx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#ifndef BME_UTILS_H_
|
|
||||||
#define BME_UTILS_H_
|
|
||||||
|
|
||||||
#include <Adafruit_Sensor.h>
|
|
||||||
#include <Adafruit_BME280.h>
|
|
||||||
#include <Adafruit_BMP280.h>
|
|
||||||
#include <Adafruit_BME680.h>
|
|
||||||
#include "Adafruit_Si7021.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace BME_Utils {
|
|
||||||
|
|
||||||
void getWxModuleAddres();
|
|
||||||
void setup();
|
|
||||||
String generateTempString(const float bmeTemp);
|
|
||||||
String generateHumString(const float bmeHum);
|
|
||||||
String generatePresString(const float bmePress);
|
|
||||||
String readDataSensor();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
#ifndef PINS_CONFIG_H_
|
|
||||||
#define PINS_CONFIG_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#undef OLED_SDA
|
|
||||||
#undef OLED_SCL
|
|
||||||
#undef OLED_RST
|
|
||||||
|
|
||||||
|
|
||||||
// LORA MODULES
|
|
||||||
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(ESP32_DIY_LoRa) || defined(TTGO_T_Beam_V1_2) || defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA32_V2_1_915) || defined(ESP32_DIY_LoRa_915) || defined(TTGO_T_Beam_V1_2_915) || defined(TTGO_T_Beam_V1_0_915)
|
|
||||||
#define RADIO_SCLK_PIN 5 // GPIO5 - SX1278 SCK
|
|
||||||
#define RADIO_MISO_PIN 19 // GPIO19 - SX1278 MISO
|
|
||||||
#define RADIO_MOSI_PIN 27 // GPIO27 - SX1278 MOSI
|
|
||||||
#define RADIO_CS_PIN 18 // GPIO18 - SX1278 CS ---> NSS
|
|
||||||
#define RADIO_RST_PIN 14 // GPIO14 - SX1278 RST
|
|
||||||
#define RADIO_BUSY_PIN 26 // GPIO26 - SX1278 IRQ ---->DIO0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WSL_V3) || defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_WS)
|
|
||||||
#define RADIO_SCLK_PIN 9 // SX1262 SCK
|
|
||||||
#define RADIO_MISO_PIN 11 // SX1262 MISO
|
|
||||||
#define RADIO_MOSI_PIN 10 // SX1262 MOSI
|
|
||||||
#define RADIO_CS_PIN 8 // SX1262 NSS
|
|
||||||
#define RADIO_RST_PIN 12 // SX1262 RST
|
|
||||||
#define RADIO_DIO1_PIN 14 // SX1262 DIO1
|
|
||||||
#define RADIO_BUSY_PIN 13 // SX1262 BUSY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915) // Ebyte E22 400M30S (SX1268) or E22 900M30S (SX1262)
|
|
||||||
#define RADIO_SCLK_PIN 18
|
|
||||||
#define RADIO_MISO_PIN 19
|
|
||||||
#define RADIO_MOSI_PIN 23
|
|
||||||
#define RADIO_CS_PIN 5
|
|
||||||
#define RADIO_RST_PIN 27
|
|
||||||
#define RADIO_DIO1_PIN 12
|
|
||||||
#define RADIO_BUSY_PIN 14
|
|
||||||
#define RADIO_RXEN 32
|
|
||||||
#define RADIO_TXEN 25
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WEMOS_LOLIN32_OLED_DIY_LoRa
|
|
||||||
#define RADIO_SCLK_PIN 15
|
|
||||||
#define RADIO_MISO_PIN 13
|
|
||||||
#define RADIO_MOSI_PIN 12
|
|
||||||
#define RADIO_CS_PIN 14
|
|
||||||
#define RADIO_RST_PIN 2
|
|
||||||
#define RADIO_BUSY_PIN 25
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TTGO_T_Beam_V1_0_SX1268) || defined(TTGO_T_Beam_V1_2_SX1262)
|
|
||||||
#define RADIO_SCLK_PIN 5
|
|
||||||
#define RADIO_MISO_PIN 19
|
|
||||||
#define RADIO_MOSI_PIN 27
|
|
||||||
#define RADIO_CS_PIN 18
|
|
||||||
#define RADIO_DIO0_PIN 26
|
|
||||||
#define RADIO_RST_PIN 23
|
|
||||||
#define RADIO_DIO1_PIN 33
|
|
||||||
#define RADIO_BUSY_PIN 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(OE5HWN_MeshCom)
|
|
||||||
#define RADIO_SCLK_PIN 18
|
|
||||||
#define RADIO_MISO_PIN 19
|
|
||||||
#define RADIO_MOSI_PIN 23
|
|
||||||
#define RADIO_CS_PIN 5
|
|
||||||
#define RADIO_RST_PIN 27
|
|
||||||
#define RADIO_DIO1_PIN 33
|
|
||||||
#define RADIO_BUSY_PIN 26
|
|
||||||
#define RADIO_RXEN 14
|
|
||||||
#define RADIO_TXEN 13
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HELTEC_HTCT62
|
|
||||||
#define RADIO_SCLK_PIN 10 // SX1262 SCK
|
|
||||||
#define RADIO_MISO_PIN 6 // SX1262 MISO
|
|
||||||
#define RADIO_MOSI_PIN 7 // SX1262 MOSI
|
|
||||||
#define RADIO_CS_PIN 8 // SX1262 NSS
|
|
||||||
#define RADIO_RST_PIN 5 // SX1262 RST
|
|
||||||
#define RADIO_DIO1_PIN 3 // SX1262 DIO1
|
|
||||||
#define RADIO_BUSY_PIN 4 // SX1262 BUSY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ESP32_DIY_LoRa_A7670) || defined(ESP32_DIY_LoRa_A7670_915)
|
|
||||||
#define RADIO_SCLK_PIN 18
|
|
||||||
#define RADIO_MISO_PIN 19
|
|
||||||
#define RADIO_MOSI_PIN 23
|
|
||||||
#define RADIO_CS_PIN 2
|
|
||||||
#define RADIO_RST_PIN 0
|
|
||||||
#define RADIO_BUSY_PIN 32
|
|
||||||
#define A7670_PWR_PIN 4
|
|
||||||
#define A7670_ResetPin 5
|
|
||||||
#define A7670_TX_PIN 26
|
|
||||||
#define A7670_RX_PIN 27
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WEMOS_D1_R32_RA02
|
|
||||||
#define RADIO_SCLK_PIN 18
|
|
||||||
#define RADIO_MISO_PIN 19
|
|
||||||
#define RADIO_MOSI_PIN 23
|
|
||||||
#define RADIO_CS_PIN 5
|
|
||||||
#define RADIO_BUSY_PIN 12
|
|
||||||
#define RADIO_RST_PIN 13
|
|
||||||
#define RADIO_DIO1_PIN 14
|
|
||||||
#define OLED_SDA 21
|
|
||||||
#define OLED_SCL 22
|
|
||||||
#define OLED_RST 36
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ESP32C3_DIY_1W_LoRa) || defined(ESP32C3_DIY_1W_LoRa_915)
|
|
||||||
#define RADIO_SCLK_PIN 8
|
|
||||||
#define RADIO_MISO_PIN 9
|
|
||||||
#define RADIO_MOSI_PIN 10
|
|
||||||
#define RADIO_CS_PIN 5
|
|
||||||
#define RADIO_RST_PIN 4
|
|
||||||
#define RADIO_DIO1_PIN 2
|
|
||||||
#define RADIO_BUSY_PIN 3
|
|
||||||
#define RADIO_RXEN 6
|
|
||||||
#define RADIO_TXEN 7
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// OLED
|
|
||||||
#if defined(TTGO_T_LORA32_V2_1) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa) || defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_Beam_V1_2) || defined(TTGO_T_Beam_V1_0_SX1268) || defined(TTGO_T_Beam_V1_2_SX1262) || defined(OE5HWN_MeshCom) || defined(ESP32_DIY_LoRa_A7670) || defined(TTGO_T_LORA32_V2_1_915) || defined(ESP32_DIY_LoRa_915) || defined(TTGO_T_Beam_V1_0_915) || defined(TTGO_T_Beam_V1_2_915) || defined(ESP32_DIY_LoRa_A7670_915) || defined(ESP32_DIY_1W_LoRa_915)
|
|
||||||
#define OLED_SDA 21
|
|
||||||
#define OLED_SCL 22
|
|
||||||
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HELTEC_V2
|
|
||||||
#define OLED_SDA 4
|
|
||||||
#define OLED_SCL 15
|
|
||||||
#define OLED_RST 16
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WS)
|
|
||||||
#define OLED_SDA 17
|
|
||||||
#define OLED_SCL 18
|
|
||||||
#define OLED_RST 21
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WEMOS_LOLIN32_OLED_DIY_LoRa
|
|
||||||
#define OLED_SDA 5
|
|
||||||
#define OLED_SCL 4
|
|
||||||
#define OLED_RST -1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(HELTEC_HTCT62) && !defined(HELTEC_WSL_V3) && !defined(ESP32C3_DIY_1W_LoRa) && !defined(ESP32C3_DIY_1W_LoRa_915)
|
|
||||||
#define HAS_DISPLAY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Leds and other stuff
|
|
||||||
#ifdef HELTEC_HTCT62
|
|
||||||
#define BATTERY_PIN 1
|
|
||||||
#endif
|
|
||||||
#if defined(TTGO_T_LORA32_V2_1) || defined(TTGO_T_LORA32_V2_1_915)
|
|
||||||
#define INTERNAL_LED_PIN 25 // Green Led
|
|
||||||
#define BATTERY_PIN 35
|
|
||||||
#endif
|
|
||||||
#if defined(HELTEC_V2)
|
|
||||||
#define INTERNAL_LED_PIN 25
|
|
||||||
#define BATTERY_PIN 37
|
|
||||||
#define ADC_CTRL 21
|
|
||||||
#endif
|
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WSL_V3) || defined(HELTEC_WS)
|
|
||||||
#define INTERNAL_LED_PIN 35
|
|
||||||
#define BATTERY_PIN 1
|
|
||||||
#define VEXT_CTRL 36
|
|
||||||
#define ADC_CTRL 37 // Heltec WSL_V3 just like Heltec WT
|
|
||||||
#define BOARD_I2C_SDA 41
|
|
||||||
#define BOARD_I2C_SCL 42
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915)
|
|
||||||
#define INTERNAL_LED_PIN 2
|
|
||||||
#endif
|
|
||||||
#if defined(ESP32_DIY_LoRa_A7670) || defined(ESP32_DIY_LoRa_A7670_915)
|
|
||||||
#define INTERNAL_LED_PIN 13 // 13 for V1.1 and 12 for V1.0
|
|
||||||
#define BATTERY_PIN 35
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HELTEC_WIRELESS_TRACKER
|
|
||||||
#define INTERNAL_LED_PIN 18
|
|
||||||
#define BATTERY_PIN 1
|
|
||||||
#define ADC_CTRL 2 // HELTEC Wireless Tracker ADC_CTRL = HIGH powers the voltage divider to read BatteryPin. Only on V05 = V1.1
|
|
||||||
#define VEXT_CTRL 3 // To turn on GPS and TFT
|
|
||||||
#define BOARD_I2C_SDA 7
|
|
||||||
#define BOARD_I2C_SCL 6
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ESP32_C3_DIY_LoRa // just testing!
|
|
||||||
#define OLED_SDA 8
|
|
||||||
#define OLED_SCL 9
|
|
||||||
#define OLED_RST 10
|
|
||||||
#define RADIO_SCLK_PIN 4
|
|
||||||
#define RADIO_MISO_PIN 5
|
|
||||||
#define RADIO_MOSI_PIN 6
|
|
||||||
#define RADIO_CS 7
|
|
||||||
#define RADIO_RST_PIN 3
|
|
||||||
#define RADIO_IRQ_PIN 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* (Same pins for LILYGO LoRa32 and ESP32 Wroom Dev )
|
|
||||||
SX1278-------------------> ESP32 ttgo-lora32-v21 and ESP32 WROOM Dev
|
|
||||||
GND GND
|
|
||||||
DIO1 -
|
|
||||||
DIO2 -
|
|
||||||
DIO3 -
|
|
||||||
VCC 3.3V
|
|
||||||
MISO 19
|
|
||||||
MOSI 27
|
|
||||||
SCLK 5
|
|
||||||
NSS 18
|
|
||||||
DIO0 26
|
|
||||||
REST 14
|
|
||||||
GND - */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,118 +1,184 @@
|
|||||||
|
/* 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 <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <SPIFFS.h>
|
#include <SPIFFS.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "board_pinout.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
|
||||||
|
|
||||||
void Configuration::check() {
|
bool shouldSleepStop = true;
|
||||||
if (reload) {
|
|
||||||
show_display("------- UPDATE ------", "config is old", "device will update", "and then reboot", 1000);
|
|
||||||
|
|
||||||
writeFile();
|
|
||||||
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"]["powerOff"] = wifiAutoAP.powerOff;
|
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["digi"]["mode"] = digi.mode;
|
data["aprs_is"]["active"] = aprs_is.active;
|
||||||
|
data["aprs_is"]["passcode"] = aprs_is.passcode;
|
||||||
|
data["aprs_is"]["server"] = aprs_is.server;
|
||||||
|
data["aprs_is"]["port"] = aprs_is.port;
|
||||||
|
data["aprs_is"]["filter"] = aprs_is.filter;
|
||||||
|
data["aprs_is"]["messagesToRF"] = aprs_is.messagesToRF;
|
||||||
|
data["aprs_is"]["objectsToRF"] = aprs_is.objectsToRF;
|
||||||
|
|
||||||
data["lora"]["rxFreq"] = loramodule.rxFreq;
|
data["beacon"]["comment"] = beacon.comment;
|
||||||
data["lora"]["txFreq"] = loramodule.txFreq;
|
data["beacon"]["interval"] = beacon.interval;
|
||||||
data["lora"]["spreadingFactor"] = loramodule.spreadingFactor;
|
data["beacon"]["latitude"] = beacon.latitude;
|
||||||
data["lora"]["signalBandwidth"] = loramodule.signalBandwidth;
|
data["beacon"]["longitude"] = beacon.longitude;
|
||||||
data["lora"]["codingRate4"] = loramodule.codingRate4;
|
data["beacon"]["overlay"] = beacon.overlay;
|
||||||
data["lora"]["power"] = loramodule.power;
|
data["beacon"]["symbol"] = beacon.symbol;
|
||||||
data["lora"]["txActive"] = loramodule.txActive;
|
data["beacon"]["path"] = beacon.path;
|
||||||
data["lora"]["rxActive"] = loramodule.rxActive;
|
|
||||||
|
|
||||||
data["display"]["alwaysOn"] = display.alwaysOn;
|
data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
|
||||||
data["display"]["timeout"] = display.timeout;
|
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
|
||||||
data["display"]["turn180"] = display.turn180;
|
data["beacon"]["beaconFreq"] = beacon.beaconFreq;
|
||||||
|
|
||||||
data["battery"]["sendInternalVoltage"] = battery.sendInternalVoltage;
|
data["beacon"]["statusActive"] = beacon.statusActive;
|
||||||
data["battery"]["monitorInternalVoltage"] = battery.monitorInternalVoltage;
|
data["beacon"]["statusPacket"] = beacon.statusPacket;
|
||||||
data["battery"]["internalSleepVoltage"] = battery.internalSleepVoltage;
|
|
||||||
|
|
||||||
data["battery"]["sendExternalVoltage"] = battery.sendExternalVoltage;
|
data["beacon"]["gpsActive"] = beacon.gpsActive;
|
||||||
data["battery"]["externalVoltagePin"] = battery.externalVoltagePin;
|
data["beacon"]["ambiguityLevel"] = beacon.ambiguityLevel;
|
||||||
data["battery"]["monitorExternalVoltage"] = battery.monitorExternalVoltage;
|
|
||||||
data["battery"]["externalSleepVoltage"] = battery.externalSleepVoltage;
|
|
||||||
|
|
||||||
data["bme"]["active"] = bme.active;
|
data["personalNote"] = personalNote;
|
||||||
data["bme"]["heightCorrection"] = bme.heightCorrection;
|
|
||||||
data["bme"]["temperatureCorrection"] = bme.temperatureCorrection;
|
|
||||||
|
|
||||||
data["syslog"]["active"] = syslog.active;
|
data["blacklist"] = blacklist;
|
||||||
data["syslog"]["server"] = syslog.server;
|
|
||||||
data["syslog"]["port"] = syslog.port;
|
|
||||||
|
|
||||||
data["tnc"]["enableServer"] = tnc.enableServer;
|
data["digi"]["mode"] = digi.mode;
|
||||||
data["tnc"]["enableSerial"] = tnc.enableSerial;
|
data["digi"]["ecoMode"] = digi.ecoMode;
|
||||||
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
|
#if defined(HAS_A7670)
|
||||||
|
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
|
||||||
|
#endif
|
||||||
|
|
||||||
data["other"]["rebootMode"] = rebootMode;
|
data["lora"]["rxActive"] = loramodule.rxActive;
|
||||||
data["other"]["rebootModeTime"] = rebootModeTime;
|
data["lora"]["rxFreq"] = loramodule.rxFreq;
|
||||||
|
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["ota"]["username"] = ota.username;
|
data["display"]["alwaysOn"] = display.alwaysOn;
|
||||||
data["ota"]["password"] = ota.password;
|
data["display"]["timeout"] = display.timeout;
|
||||||
|
data["display"]["turn180"] = display.turn180;
|
||||||
|
|
||||||
data["other"]["rememberStationTime"] = rememberStationTime;
|
data["battery"]["sendInternalVoltage"] = battery.sendInternalVoltage;
|
||||||
|
data["battery"]["monitorInternalVoltage"] = battery.monitorInternalVoltage;
|
||||||
|
data["battery"]["internalSleepVoltage"] = battery.internalSleepVoltage;
|
||||||
|
|
||||||
data["other"]["backupDigiMode"] = backupDigiMode;
|
data["battery"]["sendExternalVoltage"] = battery.sendExternalVoltage;
|
||||||
|
data["battery"]["monitorExternalVoltage"] = battery.monitorExternalVoltage;
|
||||||
|
data["battery"]["externalSleepVoltage"] = battery.externalSleepVoltage;
|
||||||
|
data["battery"]["useExternalI2CSensor"] = battery.useExternalI2CSensor;
|
||||||
|
data["battery"]["voltageDividerR1"] = battery.voltageDividerR1;
|
||||||
|
data["battery"]["voltageDividerR2"] = battery.voltageDividerR2;
|
||||||
|
data["battery"]["externalVoltagePin"] = battery.externalVoltagePin;
|
||||||
|
|
||||||
data["other"]["lowPowerMode"] = lowPowerMode;
|
data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry;
|
||||||
data["other"]["lowVoltageCutOff"] = lowVoltageCutOff;
|
|
||||||
|
|
||||||
serializeJson(data, configFile);
|
data["wxsensor"]["active"] = wxsensor.active;
|
||||||
|
data["wxsensor"]["heightCorrection"] = wxsensor.heightCorrection;
|
||||||
|
data["wxsensor"]["temperatureCorrection"] = wxsensor.temperatureCorrection;
|
||||||
|
|
||||||
configFile.close();
|
data["syslog"]["active"] = syslog.active;
|
||||||
|
data["syslog"]["server"] = syslog.server;
|
||||||
|
data["syslog"]["port"] = syslog.port;
|
||||||
|
data["syslog"]["logBeaconOverTCPIP"] = syslog.logBeaconOverTCPIP;
|
||||||
|
|
||||||
Serial.println("Config saved");
|
data["tnc"]["enableServer"] = tnc.enableServer;
|
||||||
|
data["tnc"]["enableSerial"] = tnc.enableSerial;
|
||||||
|
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
|
||||||
|
data["tnc"]["aprsBridgeActive"] = tnc.aprsBridgeActive;
|
||||||
|
|
||||||
|
data["mqtt"]["active"] = mqtt.active;
|
||||||
|
data["mqtt"]["server"] = mqtt.server;
|
||||||
|
data["mqtt"]["topic"] = mqtt.topic;
|
||||||
|
data["mqtt"]["username"] = mqtt.username;
|
||||||
|
data["mqtt"]["password"] = mqtt.password;
|
||||||
|
data["mqtt"]["port"] = mqtt.port;
|
||||||
|
data["mqtt"]["beaconOverMqtt"] = mqtt.beaconOverMqtt;
|
||||||
|
|
||||||
|
data["ota"]["username"] = ota.username;
|
||||||
|
data["ota"]["password"] = ota.password;
|
||||||
|
|
||||||
|
data["webadmin"]["active"] = webadmin.active;
|
||||||
|
data["webadmin"]["username"] = webadmin.username;
|
||||||
|
data["webadmin"]["password"] = webadmin.password;
|
||||||
|
|
||||||
|
data["remoteManagement"]["managers"] = remoteManagement.managers;
|
||||||
|
data["remoteManagement"]["rfOnly"] = remoteManagement.rfOnly;
|
||||||
|
|
||||||
|
data["ntp"]["server"] = ntp.server;
|
||||||
|
data["ntp"]["gmtCorrection"] = ntp.gmtCorrection;
|
||||||
|
|
||||||
|
data["other"]["rebootMode"] = rebootMode;
|
||||||
|
data["other"]["rebootModeTime"] = rebootModeTime;
|
||||||
|
|
||||||
|
data["other"]["rememberStationTime"] = rememberStationTime;
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -128,129 +194,205 @@ bool Configuration::readFile() {
|
|||||||
wifiAPs.push_back(wifiap);
|
wifiAPs.push_back(wifiap);
|
||||||
}
|
}
|
||||||
|
|
||||||
wifiAutoAP.password = data["wifi"]["autoAP"]["password"].as<String>();
|
if (!data["other"].containsKey("startupDelay")) needsRewrite = true;
|
||||||
wifiAutoAP.powerOff = data["wifi"]["autoAP"]["powerOff"].as<int>();
|
startupDelay = data["other"]["startupDelay"] | 0;
|
||||||
|
|
||||||
callsign = data["callsign"].as<String>();
|
if (!data["wifi"]["autoAP"].containsKey("password") ||
|
||||||
rememberStationTime = data["other"]["rememberStationTime"].as<int>();
|
!data["wifi"]["autoAP"].containsKey("timeout")) needsRewrite = true;
|
||||||
|
wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890";
|
||||||
|
wifiAutoAP.timeout = data["wifi"]["autoAP"]["timeout"] | 10;
|
||||||
|
|
||||||
battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"].as<bool>();
|
if (!data.containsKey("callsign")) needsRewrite = true;
|
||||||
battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"].as<bool>();
|
callsign = data["callsign"] | "NOCALL-10";
|
||||||
battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"].as<float>();
|
|
||||||
|
|
||||||
battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"].as<bool>();
|
|
||||||
battery.externalVoltagePin = data["battery"]["externalVoltagePin"].as<int>();
|
|
||||||
battery.monitorExternalVoltage = data["battery"]["monitorExternalVoltage"].as<bool>();
|
|
||||||
battery.externalSleepVoltage = data["battery"]["externalSleepVoltage"].as<float>();
|
|
||||||
|
|
||||||
aprs_is.passcode = data["aprs_is"]["passcode"].as<String>();
|
|
||||||
aprs_is.server = data["aprs_is"]["server"].as<String>();
|
|
||||||
aprs_is.port = data["aprs_is"]["port"].as<int>();
|
|
||||||
|
|
||||||
loramodule.spreadingFactor = data["lora"]["spreadingFactor"].as<int>();
|
|
||||||
loramodule.signalBandwidth = data["lora"]["signalBandwidth"].as<long>();
|
|
||||||
loramodule.codingRate4 = data["lora"]["codingRate4"].as<int>();
|
|
||||||
loramodule.power = data["lora"]["power"].as<int>();
|
|
||||||
|
|
||||||
display.alwaysOn = data["display"]["alwaysOn"].as<bool>();
|
|
||||||
display.timeout = data["display"]["timeout"].as<int>();
|
|
||||||
display.turn180 = data["display"]["turn180"].as<bool>();
|
|
||||||
|
|
||||||
syslog.active = data["syslog"]["active"].as<bool>();
|
|
||||||
syslog.server = data["syslog"]["server"].as<String>();
|
|
||||||
syslog.port = data["syslog"]["port"].as<int>();
|
|
||||||
|
|
||||||
bme.active = data["bme"]["active"].as<bool>();
|
|
||||||
bme.heightCorrection = data["bme"]["heightCorrection"].as<int>();
|
|
||||||
bme.temperatureCorrection = data["bme"]["temperatureCorrection"].as<float>();
|
|
||||||
|
|
||||||
ota.username = data["ota"]["username"].as<String>();
|
|
||||||
ota.password = data["ota"]["password"].as<String>();
|
|
||||||
|
|
||||||
tnc.enableServer = data["tnc"]["enableServer"].as<bool>();
|
|
||||||
tnc.enableSerial = data["tnc"]["enableSerial"].as<bool>();
|
|
||||||
tnc.acceptOwn = data["tnc"]["acceptOwn"].as<bool>();
|
|
||||||
|
|
||||||
lowPowerMode = data["other"]["lowPowerMode"].as<bool>();
|
|
||||||
lowVoltageCutOff = data["other"]["lowVoltageCutOff"].as<double>();
|
|
||||||
|
|
||||||
backupDigiMode = data["other"]["backupDigiMode"].as<bool>();
|
|
||||||
|
|
||||||
rebootMode = data["other"]["rebootMode"].as<bool>();
|
|
||||||
rebootModeTime = data["other"]["rebootModeTime"].as<int>();
|
|
||||||
|
|
||||||
int stationMode = data["stationMode"].as<int>(); // deprecated but need to specify config version
|
|
||||||
|
|
||||||
if (stationMode == 0) {
|
|
||||||
// Load new settings
|
|
||||||
|
|
||||||
beacon.latitude = data["beacon"]["latitude"].as<double>();
|
|
||||||
beacon.longitude = data["beacon"]["longitude"].as<double>();
|
|
||||||
beacon.comment = data["beacon"]["comment"].as<String>();
|
|
||||||
beacon.overlay = data["beacon"]["overlay"].as<String>();
|
|
||||||
beacon.symbol = data["beacon"]["symbol"].as<String>();
|
|
||||||
beacon.interval = data["beacon"]["interval"].as<int>();
|
|
||||||
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"].as<bool>();
|
|
||||||
beacon.sendViaRF = data["beacon"]["sendViaRF"].as<bool>();
|
|
||||||
beacon.path = data["beacon"]["path"].as<String>();
|
|
||||||
|
|
||||||
digi.mode = data["digi"]["mode"].as<int>();
|
|
||||||
|
|
||||||
aprs_is.active = data["aprs_is"]["active"].as<bool>();
|
|
||||||
aprs_is.filter = data["aprs_is"]["filter"].as<String>();
|
|
||||||
aprs_is.messagesToRF = data["aprs_is"]["messagesToRF"].as<bool>();
|
|
||||||
aprs_is.objectsToRF = data["aprs_is"]["objectsToRF"].as<bool>();
|
|
||||||
|
|
||||||
loramodule.txFreq = data["lora"]["txFreq"].as<long>();
|
|
||||||
loramodule.rxFreq = data["lora"]["rxFreq"].as<long>();
|
|
||||||
loramodule.txActive = data["lora"]["txActive"].as<bool>();
|
|
||||||
loramodule.rxActive = data["lora"]["rxActive"].as<bool>();
|
|
||||||
} else {
|
|
||||||
// Load old settings and put into new variables not actual config
|
|
||||||
|
|
||||||
String iGateComment = data["iGateComment"].as<String>();
|
|
||||||
int beaconInterval = data["other"]["beaconInterval"].as<int>();
|
|
||||||
|
|
||||||
long iGateFreq = data["lora"]["iGateFreq"].as<long>();
|
|
||||||
long digirepeaterTxFreq = data["lora"]["digirepeaterTxFreq"].as<long>();
|
|
||||||
long digirepeaterRxFreq = data["lora"]["digirepeaterRxFreq"].as<long>();
|
|
||||||
|
|
||||||
String digiComment = data["digi"]["comment"].as<String>();
|
if (!data["aprs_is"].containsKey("active") ||
|
||||||
double digiLatitude = data["digi"]["latitude"].as<double>();
|
!data["aprs_is"].containsKey("passcode") ||
|
||||||
double digiLongitude = data["digi"]["longitude"].as<double>();
|
!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;
|
||||||
|
|
||||||
beacon.latitude = digiLatitude;
|
if (!data["beacon"].containsKey("latitude") ||
|
||||||
beacon.longitude = digiLongitude;
|
!data["beacon"].containsKey("longitude") ||
|
||||||
beacon.interval = beaconInterval;
|
!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.longitude = data["beacon"]["longitude"] | 0.0;
|
||||||
|
beacon.comment = data["beacon"]["comment"] | "LoRa APRS";
|
||||||
|
beacon.interval = data["beacon"]["interval"] | 15;
|
||||||
|
beacon.overlay = data["beacon"]["overlay"] | "L";
|
||||||
|
beacon.symbol = data["beacon"]["symbol"] | "a";
|
||||||
|
beacon.path = data["beacon"]["path"] | "WIDE1-1";
|
||||||
|
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false;
|
||||||
|
beacon.sendViaRF = data["beacon"]["sendViaRF"] | false;
|
||||||
|
beacon.beaconFreq = data["beacon"]["beaconFreq"] | 1;
|
||||||
|
beacon.statusActive = data["beacon"]["statusActive"] | false;
|
||||||
|
beacon.statusPacket = data["beacon"]["statusPacket"] | "";
|
||||||
|
beacon.gpsActive = data["beacon"]["gpsActive"] | false;
|
||||||
|
beacon.ambiguityLevel = data["beacon"]["ambiguityLevel"] | 0;
|
||||||
|
|
||||||
loramodule.txFreq = digirepeaterTxFreq;
|
if (!data.containsKey("personalNote")) needsRewrite = true;
|
||||||
loramodule.rxFreq = digirepeaterRxFreq;
|
personalNote = data["personalNote"] | "personal note here";
|
||||||
loramodule.rxActive = true;
|
|
||||||
beacon.sendViaAPRSIS = true;
|
|
||||||
beacon.sendViaRF = false;
|
|
||||||
|
|
||||||
switch (stationMode) {
|
if (!data.containsKey("blacklist")) needsRewrite = true;
|
||||||
case 1: // IGate only
|
blacklist = data["blacklist"] | "station callsign";
|
||||||
// aprs_is.active = true; // better don't do that automatically
|
|
||||||
beacon.comment = iGateComment;
|
|
||||||
loramodule.rxFreq = iGateFreq;
|
|
||||||
break;
|
|
||||||
case 5: // Digi + IGate
|
|
||||||
case 2: // Digi + IGate
|
|
||||||
// aprs_is.active = true; // better don't do that automatically
|
|
||||||
// digi.mode = 2; // better don't do that automatically
|
|
||||||
beacon.comment = digiComment;
|
|
||||||
loramodule.rxFreq = iGateFreq;
|
|
||||||
break;
|
|
||||||
case 3: // Digi
|
|
||||||
case 4: // Digi
|
|
||||||
// digi.mode = 2; // better don't do that automatically
|
|
||||||
beacon.comment = digiComment;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
reload = true;
|
if (!data["digi"].containsKey("mode") ||
|
||||||
}
|
!data["digi"].containsKey("ecoMode")) needsRewrite = true;
|
||||||
|
digi.mode = data["digi"]["mode"] | 0;
|
||||||
|
digi.ecoMode = data["digi"]["ecoMode"] | 0;
|
||||||
|
if (digi.ecoMode == 1) shouldSleepStop = false;
|
||||||
|
#if defined(HAS_A7670)
|
||||||
|
if (digi.ecoMode == 1) digi.ecoMode = 2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!data["lora"].containsKey("rxActive") ||
|
||||||
|
!data["lora"].containsKey("rxFreq") ||
|
||||||
|
!data["lora"].containsKey("rxSpreadingFactor") ||
|
||||||
|
!data["lora"].containsKey("rxCodingRate4") ||
|
||||||
|
!data["lora"].containsKey("rxSignalBandwidth") ||
|
||||||
|
!data["lora"].containsKey("txActive") ||
|
||||||
|
!data["lora"].containsKey("txFreq") ||
|
||||||
|
!data["lora"].containsKey("txSpreadingFactor") ||
|
||||||
|
!data["lora"].containsKey("txCodingRate4") ||
|
||||||
|
!data["lora"].containsKey("txSignalBandwidth") ||
|
||||||
|
!data["lora"].containsKey("power")) needsRewrite = true;
|
||||||
|
loramodule.rxActive = data["lora"]["rxActive"] | true;
|
||||||
|
loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000;
|
||||||
|
loramodule.rxSpreadingFactor = data["lora"]["rxSpreadingFactor"] | 12;
|
||||||
|
loramodule.rxCodingRate4 = data["lora"]["rxCodingRate4"] | 5;
|
||||||
|
loramodule.rxSignalBandwidth = data["lora"]["rxSignalBandwidth"] | 125000;
|
||||||
|
loramodule.txActive = data["lora"]["txActive"] | false;
|
||||||
|
loramodule.txFreq = data["lora"]["txFreq"] | 433775000;
|
||||||
|
loramodule.txSpreadingFactor = data["lora"]["txSpreadingFactor"] | 12;
|
||||||
|
loramodule.txCodingRate4 = data["lora"]["txCodingRate4"] | 5;
|
||||||
|
loramodule.txSignalBandwidth = data["lora"]["txSignalBandwidth"] | 125000;
|
||||||
|
loramodule.power = data["lora"]["power"] | 20;
|
||||||
|
|
||||||
|
if (!data["display"].containsKey("alwaysOn") ||
|
||||||
|
!data["display"].containsKey("timeout") ||
|
||||||
|
!data["display"].containsKey("turn180")) needsRewrite = true;
|
||||||
|
#ifdef HAS_EPAPER
|
||||||
|
display.alwaysOn = true;
|
||||||
|
#else
|
||||||
|
display.alwaysOn = data["display"]["alwaysOn"] | true;
|
||||||
|
#endif
|
||||||
|
display.timeout = data["display"]["timeout"] | 4;
|
||||||
|
display.turn180 = data["display"]["turn180"] | false;
|
||||||
|
|
||||||
|
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.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false;
|
||||||
|
battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 2.9;
|
||||||
|
battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false;
|
||||||
|
battery.monitorExternalVoltage = data["battery"]["monitorExternalVoltage"] | false;
|
||||||
|
battery.externalSleepVoltage = data["battery"]["externalSleepVoltage"] | 10.9;
|
||||||
|
battery.useExternalI2CSensor = data["battery"]["useExternalI2CSensor"] | false;
|
||||||
|
battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0;
|
||||||
|
battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0;
|
||||||
|
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
|
||||||
|
battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | false;
|
||||||
|
|
||||||
|
if (!data["wxsensor"].containsKey("active") ||
|
||||||
|
!data["wxsensor"].containsKey("heightCorrection") ||
|
||||||
|
!data["wxsensor"].containsKey("temperatureCorrection")) needsRewrite = true;
|
||||||
|
wxsensor.active = data["wxsensor"]["active"] | false;
|
||||||
|
wxsensor.heightCorrection = data["wxsensor"]["heightCorrection"] | 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.server = data["syslog"]["server"] | "lora.link9.net";
|
||||||
|
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.enableSerial = data["tnc"]["enableSerial"] | false;
|
||||||
|
tnc.acceptOwn = data["tnc"]["acceptOwn"] | false;
|
||||||
|
tnc.aprsBridgeActive = data["tnc"]["aprsBridgeActive"] | false;
|
||||||
|
|
||||||
|
if (!data["mqtt"].containsKey("active") ||
|
||||||
|
!data["mqtt"].containsKey("server") ||
|
||||||
|
!data["mqtt"].containsKey("topic") ||
|
||||||
|
!data["mqtt"].containsKey("username") ||
|
||||||
|
!data["mqtt"].containsKey("password") ||
|
||||||
|
!data["mqtt"].containsKey("port") ||
|
||||||
|
!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.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.username = data["webadmin"]["username"] | "admin";
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!data["other"].containsKey("rebootMode") ||
|
||||||
|
!data["other"].containsKey("rebootModeTime")) needsRewrite = true;
|
||||||
|
rebootMode = data["other"]["rebootMode"] | false;
|
||||||
|
rebootModeTime = data["other"]["rebootModeTime"] | 6;
|
||||||
|
|
||||||
|
if (!data["other"].containsKey("rememberStationTime")) needsRewrite = true;
|
||||||
|
rememberStationTime = data["other"]["rememberStationTime"] | 30;
|
||||||
|
|
||||||
|
if (!data["other"].containsKey("backupDigiMode")) needsRewrite = true;
|
||||||
|
backupDigiMode = data["other"]["backupDigiMode"] | false;
|
||||||
|
|
||||||
if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP
|
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;
|
||||||
@@ -260,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 {
|
||||||
@@ -268,89 +417,127 @@ bool Configuration::readFile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Configuration::init() {
|
void Configuration::setDefaultValues() {
|
||||||
reload = false;
|
|
||||||
|
|
||||||
WiFi_AP wifiap;
|
WiFi_AP wifiap;
|
||||||
wifiap.ssid = "";
|
wifiap.ssid = "";
|
||||||
wifiap.password = "";
|
wifiap.password = "";
|
||||||
|
|
||||||
wifiAPs.push_back(wifiap);
|
wifiAPs.push_back(wifiap);
|
||||||
|
|
||||||
wifiAutoAP.password = "1234567890";
|
startupDelay = 0;
|
||||||
wifiAutoAP.powerOff = 15;
|
|
||||||
|
|
||||||
callsign = "N0CALL";
|
wifiAutoAP.password = "1234567890";
|
||||||
|
wifiAutoAP.timeout = 10;
|
||||||
|
|
||||||
beacon.comment = "LoRa APRS"; // new
|
callsign = "N0CALL-10";
|
||||||
beacon.latitude = 0.0; // new
|
|
||||||
beacon.longitude = 0.0; // new
|
aprs_is.active = false;
|
||||||
beacon.interval = 15; // new
|
aprs_is.passcode = "XYZVW";
|
||||||
beacon.overlay = "L"; // new
|
aprs_is.server = "rotate.aprs2.net";
|
||||||
beacon.symbol = "#"; // new
|
aprs_is.port = 14580;
|
||||||
beacon.sendViaAPRSIS = true; // new
|
aprs_is.filter = "m/10";
|
||||||
beacon.sendViaRF = false; // new
|
aprs_is.messagesToRF = false;
|
||||||
beacon.path = "WIDE1-1"; // new
|
aprs_is.objectsToRF = false;
|
||||||
|
|
||||||
|
beacon.comment = "LoRa APRS";
|
||||||
|
beacon.latitude = 0.0;
|
||||||
|
beacon.longitude = 0.0;
|
||||||
|
beacon.interval = 15;
|
||||||
|
beacon.overlay = "L";
|
||||||
|
beacon.symbol = "a";
|
||||||
|
beacon.path = "WIDE1-1";
|
||||||
|
|
||||||
|
beacon.sendViaAPRSIS = true;
|
||||||
|
beacon.sendViaRF = false;
|
||||||
|
beacon.beaconFreq = 1;
|
||||||
|
|
||||||
digi.mode = 0;
|
beacon.statusActive = false;
|
||||||
|
beacon.statusPacket = "";
|
||||||
|
|
||||||
tnc.enableServer = false;
|
beacon.gpsActive = false;
|
||||||
tnc.enableSerial = false;
|
beacon.ambiguityLevel = 0;
|
||||||
tnc.acceptOwn = false;
|
|
||||||
|
|
||||||
aprs_is.active = false; // new
|
personalNote = "";
|
||||||
aprs_is.passcode = "XYZVW";
|
|
||||||
aprs_is.server = "rotate.aprs2.net";
|
|
||||||
aprs_is.port = 14580;
|
|
||||||
aprs_is.filter = "m/10"; // new
|
|
||||||
aprs_is.messagesToRF = false;
|
|
||||||
aprs_is.objectsToRF = false;
|
|
||||||
|
|
||||||
loramodule.txFreq = 433775000; // new
|
blacklist = "";
|
||||||
loramodule.rxFreq = 433775000; // new
|
|
||||||
loramodule.spreadingFactor = 12;
|
|
||||||
loramodule.signalBandwidth = 125000;
|
|
||||||
loramodule.codingRate4 = 5;
|
|
||||||
loramodule.power = 20;
|
|
||||||
loramodule.txActive = false; // new
|
|
||||||
loramodule.rxActive = true; // new
|
|
||||||
|
|
||||||
display.alwaysOn = true;
|
digi.mode = 0;
|
||||||
display.timeout = 4;
|
digi.ecoMode = 0;
|
||||||
display.turn180 = false;
|
|
||||||
|
|
||||||
syslog.active = false;
|
loramodule.rxActive = true;
|
||||||
syslog.server = "192.168.0.100";
|
loramodule.rxFreq = 433775000;
|
||||||
syslog.port = 514;
|
loramodule.rxSpreadingFactor = 12;
|
||||||
|
loramodule.rxCodingRate4 = 5;
|
||||||
bme.active = false;
|
loramodule.rxSignalBandwidth = 125000;
|
||||||
bme.heightCorrection = 0;
|
loramodule.txActive = false;
|
||||||
bme.temperatureCorrection = 0.0;
|
loramodule.txFreq = 433775000;
|
||||||
|
loramodule.txSpreadingFactor = 12;
|
||||||
ota.username = "";
|
loramodule.txCodingRate4 = 5;
|
||||||
ota.password = "";
|
loramodule.txSignalBandwidth = 125000;
|
||||||
|
loramodule.power = 20;
|
||||||
|
|
||||||
|
display.alwaysOn = true;
|
||||||
|
display.timeout = 4;
|
||||||
|
display.turn180 = false;
|
||||||
|
|
||||||
rememberStationTime = 30;
|
|
||||||
|
|
||||||
battery.sendInternalVoltage = false;
|
battery.sendInternalVoltage = false;
|
||||||
battery.monitorInternalVoltage = false;
|
battery.monitorInternalVoltage = false;
|
||||||
battery.internalSleepVoltage = 3.0;
|
battery.internalSleepVoltage = 2.9;
|
||||||
|
|
||||||
battery.sendExternalVoltage = false;
|
battery.sendExternalVoltage = false;
|
||||||
battery.externalVoltagePin = 34;
|
|
||||||
battery.monitorExternalVoltage = false;
|
battery.monitorExternalVoltage = false;
|
||||||
battery.externalSleepVoltage = 3.0;
|
battery.externalSleepVoltage = 10.9;
|
||||||
|
battery.useExternalI2CSensor = false;
|
||||||
|
battery.voltageDividerR1 = 100.0;
|
||||||
|
battery.voltageDividerR2 = 27.0;
|
||||||
|
battery.externalVoltagePin = 34;
|
||||||
|
|
||||||
lowPowerMode = false;
|
battery.sendVoltageAsTelemetry = false;
|
||||||
lowVoltageCutOff = 0;
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
Serial.println("All is Written!");
|
tnc.enableServer = false;
|
||||||
|
tnc.enableSerial = false;
|
||||||
|
tnc.acceptOwn = false;
|
||||||
|
tnc.aprsBridgeActive = false;
|
||||||
|
|
||||||
|
mqtt.active = false;
|
||||||
|
mqtt.server = "";
|
||||||
|
mqtt.topic = "aprs-igate";
|
||||||
|
mqtt.username = "";
|
||||||
|
mqtt.password = "";
|
||||||
|
mqtt.port = 1883;
|
||||||
|
mqtt.beaconOverMqtt = false;
|
||||||
|
|
||||||
|
ota.username = "";
|
||||||
|
ota.password = "";
|
||||||
|
|
||||||
|
webadmin.active = false;
|
||||||
|
webadmin.username = "admin";
|
||||||
|
webadmin.password = "";
|
||||||
|
|
||||||
|
remoteManagement.managers = "";
|
||||||
|
remoteManagement.rfOnly = true;
|
||||||
|
|
||||||
|
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() {
|
||||||
@@ -363,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "station_utils.h"
|
#include "station_utils.h"
|
||||||
#include "aprs_is_utils.h"
|
#include "aprs_is_utils.h"
|
||||||
#include "query_utils.h"
|
|
||||||
#include "digi_utils.h"
|
#include "digi_utils.h"
|
||||||
#include "wifi_utils.h"
|
#include "wifi_utils.h"
|
||||||
|
#include "lora_utils.h"
|
||||||
#include "gps_utils.h"
|
#include "gps_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
extern uint32_t lastScreenOn;
|
extern uint32_t lastScreenOn;
|
||||||
extern String iGateBeaconPacket;
|
extern String iGateBeaconPacket;
|
||||||
@@ -24,20 +43,55 @@ extern bool backUpDigiMode;
|
|||||||
|
|
||||||
namespace DIGI_Utils {
|
namespace DIGI_Utils {
|
||||||
|
|
||||||
String buildPacket(const String& path, const String& packet, bool thirdParty) {
|
String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq) {
|
||||||
String packetToRepeat = packet.substring(0, packet.indexOf(",") + 1);
|
if (!crossFreq) {
|
||||||
String tempPath = path;
|
String packetToRepeat = packet.substring(0, packet.indexOf(",") + 1);
|
||||||
tempPath.replace(Config.beacon.path, Config.callsign + "*");
|
String tempPath = path;
|
||||||
packetToRepeat += tempPath;
|
|
||||||
if (thirdParty) {
|
if (path.indexOf("WIDE1-1") != -1 && (Config.digi.mode == 2 || Config.digi.mode == 3)) {
|
||||||
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":}")));
|
tempPath.replace("WIDE1-1", Config.callsign + "*");
|
||||||
} else {
|
} else if (path.indexOf("WIDE2-") != -1 && Config.digi.mode == 3) {
|
||||||
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":")));
|
if (path.indexOf(",WIDE1*") != -1) {
|
||||||
|
tempPath.remove(path.indexOf(",WIDE1*"), 7);
|
||||||
|
}
|
||||||
|
if (path.indexOf("*") != -1) {
|
||||||
|
tempPath.remove(path.indexOf("*"), 1);
|
||||||
|
}
|
||||||
|
if (path.indexOf("WIDE2-1") != -1) {
|
||||||
|
tempPath.replace("WIDE2-1", Config.callsign + "*");
|
||||||
|
} else if (path.indexOf("WIDE2-2") != -1) {
|
||||||
|
tempPath.replace("WIDE2-2", Config.callsign + "*,WIDE2-1");
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packetToRepeat += tempPath;
|
||||||
|
if (thirdParty) {
|
||||||
|
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":}")));
|
||||||
|
} else {
|
||||||
|
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":")));
|
||||||
|
}
|
||||||
|
return packetToRepeat;
|
||||||
|
} else { // CrossFreq Digipeater
|
||||||
|
String suffix = thirdParty ? ":}" : ":";
|
||||||
|
String packetToRepeat = packet.substring(0, packet.indexOf(suffix));
|
||||||
|
|
||||||
|
String terms[] = {",WIDE1*", ",WIDE2*", "*"};
|
||||||
|
for (String term : terms) {
|
||||||
|
int index = packetToRepeat.indexOf(term);
|
||||||
|
if (index != -1) {
|
||||||
|
packetToRepeat.remove(index, term.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packetToRepeat += ",";
|
||||||
|
packetToRepeat += Config.callsign;
|
||||||
|
packetToRepeat += "*";
|
||||||
|
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(suffix)));
|
||||||
|
return packetToRepeat;
|
||||||
}
|
}
|
||||||
return packetToRepeat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateDigiRepeatedPacket(const String& packet, bool thirdParty){
|
String generateDigipeatedPacket(const String& packet, bool thirdParty){
|
||||||
String temp;
|
String temp;
|
||||||
if (thirdParty) { // only header is used
|
if (thirdParty) { // only header is used
|
||||||
const String& header = packet.substring(0, packet.indexOf(":}"));
|
const String& header = packet.substring(0, packet.indexOf(":}"));
|
||||||
@@ -47,53 +101,74 @@ namespace DIGI_Utils {
|
|||||||
}
|
}
|
||||||
if (temp.indexOf(",") > 2) { // checks for path
|
if (temp.indexOf(",") > 2) { // checks for path
|
||||||
const String& path = temp.substring(temp.indexOf(",") + 1); // after tocall
|
const String& path = temp.substring(temp.indexOf(",") + 1); // after tocall
|
||||||
if (path.indexOf(Config.beacon.path) != -1) {
|
if (Config.digi.mode == 2 || backUpDigiMode) {
|
||||||
return buildPacket(path, packet, thirdParty);
|
if (path.indexOf("WIDE1-1") != - 1) {
|
||||||
|
return buildPacket(path, packet, thirdParty, false);
|
||||||
|
} else if (path.indexOf("WIDE1-1") == -1 && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) { // CrossFreq Digi
|
||||||
|
return buildPacket(path, packet, thirdParty, true);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} else if (Config.digi.mode == 3) {
|
||||||
|
if (path.indexOf("WIDE1-1") != -1 || path.indexOf("WIDE2-") != -1) {
|
||||||
|
int wide1Index = path.indexOf("WIDE1-1");
|
||||||
|
int wide2Index = path.indexOf("WIDE2-");
|
||||||
|
|
||||||
|
// WIDE1-1 && WIDE2-n / only WIDE1-1 / only WIDE2-n
|
||||||
|
if ((wide1Index != -1 && wide2Index != -1 && wide1Index < wide2Index) || (wide1Index != -1 && wide2Index == -1) || (wide1Index == -1 && wide2Index != -1)) {
|
||||||
|
return buildPacket(path, packet, thirdParty, false);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
} else if (path.indexOf("WIDE1-1") == -1 && path.indexOf("WIDE2-") == -1 && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) { // CrossFreq Digi
|
||||||
|
return buildPacket(path, packet, thirdParty, true);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
} else if (temp.indexOf(",") == -1 && (Config.digi.mode == 2 || backUpDigiMode || Config.digi.mode == 3) && (abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000)) {
|
||||||
|
return buildPacket("", packet, thirdParty, true);
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void processLoRaPacket(const String& packet) {
|
void processLoRaPacket(const String& packet) {
|
||||||
if (packet != "") {
|
if (packet.indexOf("NOGATE") == -1) {
|
||||||
if ((packet.substring(0, 3) == "\x3c\xff\x01") && (packet.indexOf("NOGATE") == -1)) {
|
bool thirdPartyPacket = false;
|
||||||
bool thirdPartyPacket = false;
|
String temp, Sender;
|
||||||
String temp, Sender;
|
int firstColonIndex = packet.indexOf(":");
|
||||||
int firstColonIndex = packet.indexOf(":");
|
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] == '}' && packet.indexOf("TCPIP") > 0) { // 3rd Party
|
||||||
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] == '}' && packet.indexOf("TCPIP") > 0) { // 3rd Party
|
thirdPartyPacket = true;
|
||||||
thirdPartyPacket = true;
|
temp = packet.substring(packet.indexOf(":}") + 2);
|
||||||
temp = packet.substring(packet.indexOf(":}") + 2);
|
Sender = temp.substring(0, temp.indexOf(">"));
|
||||||
Sender = temp.substring(0, temp.indexOf(">"));
|
} else {
|
||||||
} else {
|
temp = packet.substring(3);
|
||||||
temp = packet.substring(3);
|
Sender = packet.substring(3, packet.indexOf(">"));
|
||||||
Sender = packet.substring(3, packet.indexOf(">"));
|
}
|
||||||
|
if (Sender != Config.callsign) { // Avoid listening to own packets
|
||||||
|
if (!thirdPartyPacket && !Utils::checkValidCallsign(Sender)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (Sender != Config.callsign) { // Avoid listening to own packets
|
if (STATION_Utils::check25SegBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) {
|
||||||
if (!thirdPartyPacket && !Utils::checkValidCallsign(Sender)) {
|
STATION_Utils::updateLastHeard(Sender);
|
||||||
return;
|
Utils::typeOfPacket(temp, 2); // Digi
|
||||||
}
|
bool queryMessage = false;
|
||||||
if (STATION_Utils::check25SegBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) {
|
if (temp.indexOf("::") > 10) { // it's a message
|
||||||
STATION_Utils::updateLastHeard(Sender);
|
String AddresseeAndMessage = temp.substring(temp.indexOf("::") + 2);
|
||||||
Utils::typeOfPacket(temp, 2); // Digi
|
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
||||||
bool queryMessage = false;
|
Addressee.trim();
|
||||||
if (temp.indexOf("::") > 10) { // it's a message
|
if (Addressee == Config.callsign) { // it's a message for me!
|
||||||
String AddresseeAndMessage = temp.substring(temp.indexOf("::") + 2);
|
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket);
|
||||||
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
|
||||||
Addressee.trim();
|
|
||||||
if (Addressee == Config.callsign) { // it's a message for me!
|
|
||||||
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!queryMessage) {
|
}
|
||||||
String loraPacket = generateDigiRepeatedPacket(packet.substring(3), thirdPartyPacket);
|
if (!queryMessage) {
|
||||||
if (loraPacket != "") {
|
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
|
||||||
STATION_Utils::addToOutputPacketBuffer(loraPacket);
|
if (loraPacket != "") {
|
||||||
display_toggle(true);
|
STATION_Utils::addToOutputPacketBuffer(loraPacket);
|
||||||
lastScreenOn = millis();
|
if (Config.digi.ecoMode != 1) displayToggle(true);
|
||||||
}
|
lastScreenOn = millis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
#ifndef DIGI_UTILS_H_
|
|
||||||
#define DIGI_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DIGI_Utils {
|
|
||||||
|
|
||||||
String buildPacket(const String& path, const String& packet, bool thirdParty);
|
|
||||||
String generateDigiRepeatedPacket(const String& packet, bool thirdParty);
|
|
||||||
void processLoRaPacket(const String& packet);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
353
src/display.cpp
@@ -1,6 +1,24 @@
|
|||||||
|
/* 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 <Wire.h>
|
#include <Wire.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "boards_pinout.h"
|
#include "board_pinout.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -9,33 +27,56 @@
|
|||||||
#include <TFT_eSPI.h>
|
#include <TFT_eSPI.h>
|
||||||
|
|
||||||
TFT_eSPI tft = TFT_eSPI();
|
TFT_eSPI tft = TFT_eSPI();
|
||||||
|
TFT_eSprite sprite = TFT_eSprite(&tft);
|
||||||
|
|
||||||
#ifdef HELTEC_WIRELESS_TRACKER
|
#ifdef HELTEC_WIRELESS_TRACKER
|
||||||
#define bigSizeFont 2
|
#define bigSizeFont 2
|
||||||
#define smallSizeFont 1
|
#define smallSizeFont 1
|
||||||
#define lineSpacing 9
|
#define lineSpacing 10
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
|
||||||
|
#define bigSizeFont 5
|
||||||
|
#define smallSizeFont 2
|
||||||
|
#define lineSpacing 25
|
||||||
|
#endif
|
||||||
|
uint16_t redColor = 0xc8a2;
|
||||||
#else
|
#else
|
||||||
#include <Adafruit_GFX.h>
|
#ifdef HAS_EPAPER
|
||||||
#include <Adafruit_SSD1306.h>
|
#include <heltec-eink-modules.h>
|
||||||
#if defined(HELTEC_V3)
|
#include "Fonts/FreeSansBold9pt7b.h"
|
||||||
#define OLED_DISPLAY_HAS_RST_PIN
|
#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;
|
||||||
|
#else
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
|
#include <Adafruit_SH110X.h>
|
||||||
|
Adafruit_SH1106G display(128, 64, &Wire, OLED_RST);
|
||||||
|
#else
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#ifdef HELTEC_WSL_V3_DISPLAY
|
||||||
|
Adafruit_SSD1306 display(128, 64, &Wire1, OLED_RST);
|
||||||
|
#else
|
||||||
|
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern Configuration Config;
|
|
||||||
|
|
||||||
String oldHeader, oldFirstLine, oldSecondLine, oldThirdLine, oldFourthLine, oldFifthLine, oldSixthLine;
|
extern Configuration Config;
|
||||||
|
|
||||||
void cleanTFT() {
|
bool displayFound = false;
|
||||||
#ifdef HAS_TFT
|
|
||||||
tft.fillScreen(TFT_BLACK);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_display() {
|
void displaySetup() {
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
delay(500);
|
delay(500);
|
||||||
#ifdef HAS_TFT
|
#ifdef HAS_TFT
|
||||||
@@ -46,145 +87,229 @@ void setup_display() {
|
|||||||
} else {
|
} else {
|
||||||
tft.setRotation(1);
|
tft.setRotation(1);
|
||||||
}
|
}
|
||||||
|
pinMode(TFT_BL, OUTPUT);
|
||||||
|
digitalWrite(TFT_BL, HIGH);
|
||||||
tft.setTextFont(0);
|
tft.setTextFont(0);
|
||||||
tft.fillScreen(TFT_BLACK);
|
tft.fillScreen(TFT_BLACK);
|
||||||
#else
|
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
|
||||||
#ifdef OLED_DISPLAY_HAS_RST_PIN
|
sprite.createSprite(320, 240);
|
||||||
pinMode(OLED_RST, OUTPUT);
|
#else
|
||||||
digitalWrite(OLED_RST, LOW);
|
sprite.createSprite(160, 80);
|
||||||
delay(20);
|
|
||||||
digitalWrite(OLED_RST, HIGH);
|
|
||||||
#endif
|
#endif
|
||||||
Wire.begin(OLED_SDA, OLED_SCL);
|
#else
|
||||||
|
#ifdef HAS_EPAPER
|
||||||
|
display.landscape();
|
||||||
|
display.printCenter("LoRa APRS iGate Initialising...");
|
||||||
|
if (Config.display.turn180) {
|
||||||
|
#if defined(HELTEC_VM_E290) || defined(HELTEC_WP_V1)
|
||||||
|
display.setRotation(3);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_WP_V1_2)
|
||||||
|
display.setRotation(1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
display.update();
|
||||||
|
#else
|
||||||
|
#ifdef OLED_DISPLAY_HAS_RST_PIN
|
||||||
|
pinMode(OLED_RST, OUTPUT);
|
||||||
|
digitalWrite(OLED_RST, LOW);
|
||||||
|
delay(20);
|
||||||
|
digitalWrite(OLED_RST, HIGH);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
Serial.println(F("SSD1306 allocation failed"));
|
if (display.begin(0x3c, false)) {
|
||||||
for(;;); // Don't proceed, loop forever
|
displayFound = true;
|
||||||
}
|
if (Config.display.turn180) display.setRotation(2);
|
||||||
if (Config.display.turn180) {
|
display.clearDisplay();
|
||||||
display.setRotation(2);
|
display.setTextColor(SH110X_WHITE);
|
||||||
}
|
display.setTextSize(1);
|
||||||
display.clearDisplay();
|
display.setCursor(0, 0);
|
||||||
display.setTextColor(WHITE);
|
display.setContrast(1);
|
||||||
display.setTextSize(1);
|
display.display();
|
||||||
display.setCursor(0, 0);
|
}
|
||||||
display.ssd1306_command(SSD1306_SETCONTRAST);
|
#else
|
||||||
display.ssd1306_command(1);
|
if(display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
||||||
display.display();
|
displayFound = true;
|
||||||
|
if (Config.display.turn180) display.setRotation(2);
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextColor(WHITE);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.ssd1306_command(SSD1306_SETCONTRAST);
|
||||||
|
display.ssd1306_command(1);
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
delay(1000);
|
delay(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_toggle(bool toggle) {
|
void displayToggle(bool toggle) {
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
#ifdef HAS_TFT
|
#ifdef HAS_TFT
|
||||||
digitalWrite(TFT_BL, HIGH);
|
digitalWrite(TFT_BL, HIGH);
|
||||||
#else
|
#else
|
||||||
display.ssd1306_command(SSD1306_DISPLAYON);
|
#ifdef HAS_EPAPER
|
||||||
|
display.printCenter("EPAPER Display Disabled by toggle...");
|
||||||
|
display.update();
|
||||||
|
#else
|
||||||
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
|
if (displayFound) display.oled_command(SH110X_DISPLAYON);
|
||||||
|
#else
|
||||||
|
if (displayFound) display.ssd1306_command(SSD1306_DISPLAYON);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#ifdef HAS_TFT
|
#ifdef HAS_TFT
|
||||||
digitalWrite(TFT_BL, LOW);
|
digitalWrite(TFT_BL, LOW);
|
||||||
#else
|
#else
|
||||||
display.ssd1306_command(SSD1306_DISPLAYOFF);
|
#ifdef HAS_EPAPER
|
||||||
|
display.update();
|
||||||
|
#else
|
||||||
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
|
if (displayFound) display.oled_command(SH110X_DISPLAYOFF);
|
||||||
|
#else
|
||||||
|
if (displayFound) display.ssd1306_command(SSD1306_DISPLAYOFF);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldCleanTFT(const String& header, const String& line1, const String& line2, const String& line3) {
|
void displayShow(const String& header, const String& line1, const String& line2, const String& line3, int wait) {
|
||||||
if (oldHeader != header || oldFirstLine != line1 || oldSecondLine != line2 || oldThirdLine != line3) {
|
|
||||||
oldHeader = header;
|
|
||||||
oldFirstLine = line1;
|
|
||||||
oldSecondLine = line2;
|
|
||||||
oldThirdLine = line3;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shouldCleanTFT(const String& header, const String& line1, const String& line2, const String& line3, const String& line4, const String& line5, const String& line6) {
|
|
||||||
if (oldHeader != header || oldFirstLine != line1 || oldSecondLine != line2 || oldThirdLine != line3 || oldFourthLine != line4 || oldFifthLine != line5 || oldSixthLine != line6) {
|
|
||||||
oldHeader = header;
|
|
||||||
oldFirstLine = line1;
|
|
||||||
oldSecondLine = line2;
|
|
||||||
oldThirdLine = line3;
|
|
||||||
oldFourthLine = line4;
|
|
||||||
oldFifthLine = line5;
|
|
||||||
oldSixthLine = line6;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_display(const String& header, const String& line1, const String& line2, const String& line3, int wait) {
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
const String* const lines[] = {&line1, &line2, &line3};
|
const String* const lines[] = {&line1, &line2, &line3};
|
||||||
#ifdef HAS_TFT
|
#ifdef HAS_TFT
|
||||||
if (shouldCleanTFT(header, line1, line2, line3)) {
|
sprite.fillSprite(TFT_BLACK);
|
||||||
cleanTFT();
|
#if defined(HELTEC_WIRELESS_TRACKER)
|
||||||
}
|
sprite.fillRect(0, 0, 160, 19, redColor);
|
||||||
tft.setTextColor(TFT_WHITE,TFT_BLACK);
|
#endif
|
||||||
tft.setTextSize(bigSizeFont);
|
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
|
||||||
tft.setCursor(0, 0);
|
sprite.fillRect(0, 0, 320, 43, redColor);
|
||||||
tft.print(header);
|
#endif
|
||||||
tft.setTextSize(smallSizeFont);
|
sprite.setTextFont(0);
|
||||||
|
sprite.setTextSize(bigSizeFont);
|
||||||
|
sprite.setTextColor(TFT_WHITE, redColor);
|
||||||
|
sprite.drawString(header, 3, 3);
|
||||||
|
|
||||||
|
sprite.setTextSize(smallSizeFont);
|
||||||
|
sprite.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
tft.setCursor(0, ((lineSpacing * (2 + i)) - 2));
|
sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2);
|
||||||
tft.print(*lines[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sprite.pushSprite(0, 0);
|
||||||
#else
|
#else
|
||||||
display.clearDisplay();
|
#ifdef HAS_EPAPER
|
||||||
display.setTextColor(WHITE);
|
display.clearMemory();
|
||||||
display.setTextSize(1);
|
display.setCursor(5,10);
|
||||||
display.setCursor(0, 0);
|
display.setFont(&FreeSansBold9pt7b);
|
||||||
display.println(header);
|
display.println(header);
|
||||||
for (int i = 0; i < 3; i++) {
|
display.setFont(NULL);
|
||||||
display.setCursor(0, 8 + (8 * i));
|
for (int i = 0; i < 3; i++) {
|
||||||
display.println(*lines[i]);
|
display.setCursor(0, 25 + (14 * i));
|
||||||
}
|
display.println(*lines[i]);
|
||||||
display.ssd1306_command(SSD1306_SETCONTRAST);
|
}
|
||||||
display.ssd1306_command(1);
|
display.update();
|
||||||
display.display();
|
#else
|
||||||
|
if (displayFound) {
|
||||||
|
display.clearDisplay();
|
||||||
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
|
display.setTextColor(SH110X_WHITE);
|
||||||
|
#else
|
||||||
|
display.setTextColor(WHITE);
|
||||||
|
#endif
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println(header);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
display.setCursor(0, 8 + (8 * i));
|
||||||
|
display.println(*lines[i]);
|
||||||
|
}
|
||||||
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
|
display.setContrast(1);
|
||||||
|
#else
|
||||||
|
display.ssd1306_command(SSD1306_SETCONTRAST);
|
||||||
|
display.ssd1306_command(1);
|
||||||
|
#endif
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
delay(wait);
|
delay(wait);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_display(const String& header, const String& line1, const String& line2, const String& line3, const String& line4, const String& line5, const String& line6, int wait) {
|
void displayShow(const String& header, const String& line1, const String& line2, const String& line3, const String& line4, const String& line5, const String& line6, int wait) {
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
const String* const lines[] = {&line1, &line2, &line3, &line4, &line5, &line6};
|
const String* const lines[] = {&line1, &line2, &line3, &line4, &line5, &line6};
|
||||||
#ifdef HAS_TFT
|
#ifdef HAS_TFT
|
||||||
if (shouldCleanTFT(header, line1, line2, line3, line4, line5, line6)) {
|
sprite.fillSprite(TFT_BLACK);
|
||||||
cleanTFT();
|
#if defined(HELTEC_WIRELESS_TRACKER)
|
||||||
}
|
sprite.fillRect(0, 0, 160, 19, redColor);
|
||||||
tft.setTextColor(TFT_WHITE,TFT_BLACK);
|
#endif
|
||||||
tft.setTextSize(bigSizeFont);
|
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
|
||||||
tft.setCursor(0, 0);
|
sprite.fillRect(0, 0, 320, 43, redColor);
|
||||||
tft.print(header);
|
#endif
|
||||||
tft.setTextSize(smallSizeFont);
|
sprite.setTextFont(0);
|
||||||
|
sprite.setTextSize(bigSizeFont);
|
||||||
|
sprite.setTextColor(TFT_WHITE, redColor);
|
||||||
|
sprite.drawString(header, 3, 3);
|
||||||
|
|
||||||
|
sprite.setTextSize(smallSizeFont);
|
||||||
|
sprite.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
tft.setCursor(0, ((lineSpacing * (2 + i)) - 2));
|
sprite.drawString(*lines[i], 3, (lineSpacing * (2 + i)) - 2);
|
||||||
tft.print(*lines[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sprite.pushSprite(0, 0);
|
||||||
#else
|
#else
|
||||||
display.clearDisplay();
|
#ifdef HAS_EPAPER
|
||||||
display.setTextColor(WHITE);
|
lastEpaperText = header + line1 + line2 + line3 + line4 + line5 + line6;
|
||||||
display.setTextSize(2);
|
display.clearMemory();
|
||||||
display.setCursor(0, 0);
|
display.setCursor(5,10);
|
||||||
display.println(header);
|
display.setFont(&FreeSansBold9pt7b);
|
||||||
display.setTextSize(1);
|
display.println(header);
|
||||||
for (int i = 0; i < 6; i++) {
|
display.setFont(NULL);
|
||||||
display.setCursor(0, 16 + (8 * i));
|
for (int i = 0; i < 6; i++) {
|
||||||
display.println(*lines[i]);
|
display.setCursor(0, 25 + (14 * i));
|
||||||
}
|
display.println(*lines[i]);
|
||||||
display.ssd1306_command(SSD1306_SETCONTRAST);
|
}
|
||||||
display.ssd1306_command(1);
|
display.update();
|
||||||
display.display();
|
#else
|
||||||
|
if (displayFound) {
|
||||||
|
display.clearDisplay();
|
||||||
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
|
display.setTextColor(SH110X_WHITE);
|
||||||
|
#else
|
||||||
|
display.setTextColor(WHITE);
|
||||||
|
#endif
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println(header);
|
||||||
|
display.setTextSize(1);
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
display.setCursor(0, 16 + (8 * i));
|
||||||
|
display.println(*lines[i]);
|
||||||
|
}
|
||||||
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
|
display.setContrast(1);
|
||||||
|
#else
|
||||||
|
display.ssd1306_command(SSD1306_SETCONTRAST);
|
||||||
|
display.ssd1306_command(1);
|
||||||
|
#endif
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
delay(wait);
|
delay(wait);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef DISPLAY_H_
|
|
||||||
#define DISPLAY_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
|
|
||||||
|
|
||||||
void cleanTFT();
|
|
||||||
void setup_display();
|
|
||||||
void display_toggle(bool toggle);
|
|
||||||
|
|
||||||
bool shouldCleanTFT(const String& header, const String& line1, const String& line2, const String& line3);
|
|
||||||
bool shouldCleanTFT(const String& header, const String& line1, const String& line2, const String& line3, const String& line4, const String& line5, const String& line6);
|
|
||||||
|
|
||||||
void show_display(const String& header, const String& line1, const String& line2, const String& line3, int wait = 0);
|
|
||||||
void show_display(const String& header, const String& line1, const String& line2, const String& line3, const String& line4, const String& line5, const String& line6, int wait = 0);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,13 +1,40 @@
|
|||||||
|
/* 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 <TinyGPS++.h>
|
#include <TinyGPS++.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "board_pinout.h"
|
||||||
#include "gps_utils.h"
|
#include "gps_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
extern Configuration Config;
|
#ifdef GPS_BAUDRATE
|
||||||
extern WiFiClient espClient;
|
#define GPS_BAUD GPS_BAUDRATE
|
||||||
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
|
#else
|
||||||
|
#define GPS_BAUD 9600
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern Configuration Config;
|
||||||
|
extern HardwareSerial gpsSerial;
|
||||||
|
extern TinyGPSPlus gps;
|
||||||
|
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
|
||||||
|
|
||||||
|
|
||||||
namespace GPS_Utils {
|
namespace GPS_Utils {
|
||||||
@@ -16,64 +43,27 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
|
|
||||||
String encodedData = overlay;
|
|
||||||
uint32_t aprs_lat, aprs_lon;
|
|
||||||
aprs_lat = 900000000 - latitude * 10000000;
|
|
||||||
aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
|
|
||||||
aprs_lon = 900000000 + longitude * 10000000 / 2;
|
|
||||||
aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
|
|
||||||
|
|
||||||
String Ns, Ew, helper;
|
|
||||||
if(latitude < 0) { Ns = "S"; } else { Ns = "N"; }
|
|
||||||
if(latitude < 0) { latitude= -latitude; }
|
|
||||||
|
|
||||||
if(longitude < 0) { Ew = "W"; } else { Ew = "E"; }
|
|
||||||
if(longitude < 0) { longitude= -longitude; }
|
|
||||||
|
|
||||||
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 += " x";
|
|
||||||
encodedData += "\x47";
|
|
||||||
return encodedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
||||||
show_display("***** ERROR ******", "CALLSIGN = NOT VALID!", " Use SSID 0-15", " Or Valid Callsign", 0);
|
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
|
||||||
while (true) {}
|
Config.loramodule.txActive = false;
|
||||||
}
|
Config.aprs_is.messagesToRF = false;
|
||||||
String beaconPacket = Config.callsign;
|
Config.aprs_is.objectsToRF = false;
|
||||||
beaconPacket += ">APLRG1";
|
Config.beacon.sendViaRF = false;
|
||||||
if (Config.beacon.path.indexOf("WIDE") == 0) {
|
Config.digi.mode = 0;
|
||||||
beaconPacket += ",";
|
Config.backupDigiMode = false;
|
||||||
beaconPacket += Config.beacon.path;
|
|
||||||
}
|
}
|
||||||
|
String beaconPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
|
||||||
|
String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(Config.beacon.latitude, Config.beacon.longitude, 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
|
||||||
|
|
||||||
iGateBeaconPacket = beaconPacket;
|
iGateBeaconPacket = beaconPacket;
|
||||||
iGateBeaconPacket += ",qAC:!";
|
iGateBeaconPacket += ",qAC:!";
|
||||||
iGateLoRaBeaconPacket = beaconPacket;
|
iGateBeaconPacket += Config.beacon.overlay;
|
||||||
iGateLoRaBeaconPacket += ":!";
|
iGateBeaconPacket += encodedGPS;
|
||||||
|
|
||||||
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
|
iGateLoRaBeaconPacket = beaconPacket;
|
||||||
iGateBeaconPacket += encodedGPS;
|
iGateLoRaBeaconPacket += ":=";
|
||||||
|
iGateLoRaBeaconPacket += Config.beacon.overlay;
|
||||||
iGateLoRaBeaconPacket += encodedGPS;
|
iGateLoRaBeaconPacket += encodedGPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,101 +71,118 @@ 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 decodeEncodedGPS(const String& packet) {
|
String buildDistanceAndComment(float latitude, float longitude, const String& comment) {
|
||||||
const String& GPSPacket = packet.substring(packet.indexOf(":!")+3);
|
distance = String(calculateDistanceTo(latitude, longitude),1);
|
||||||
const String& encodedLatitude = GPSPacket.substring(0,4);
|
|
||||||
const String& encodedLongtitude = GPSPacket.substring(4,8);
|
|
||||||
const String& comment = GPSPacket.substring(12);
|
|
||||||
|
|
||||||
int Y1 = int(encodedLatitude[0]);
|
String distanceAndComment = String(latitude,5);
|
||||||
int Y2 = int(encodedLatitude[1]);
|
distanceAndComment += "N / ";
|
||||||
int Y3 = int(encodedLatitude[2]);
|
distanceAndComment += String(longitude,5);
|
||||||
int Y4 = int(encodedLatitude[3]);
|
distanceAndComment += "E / ";
|
||||||
float decodedLatitude = 90.0 - ((((Y1-33) * pow(91,3)) + ((Y2-33) * pow(91,2)) + ((Y3-33) * 91) + Y4-33) / 380926.0);
|
distanceAndComment += distance;
|
||||||
|
distanceAndComment += "km";
|
||||||
int X1 = int(encodedLongtitude[0]);
|
|
||||||
int X2 = int(encodedLongtitude[1]);
|
|
||||||
int X3 = int(encodedLongtitude[2]);
|
|
||||||
int X4 = int(encodedLongtitude[3]);
|
|
||||||
float decodedLongitude = -180.0 + ((((X1-33) * pow(91,3)) + ((X2-33) * pow(91,2)) + ((X3-33) * 91) + X4-33) / 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";
|
|
||||||
|
|
||||||
if (comment != "") {
|
if (comment != "") {
|
||||||
decodedGPS += " / ";
|
distanceAndComment += " / ";
|
||||||
decodedGPS += comment;
|
distanceAndComment += comment;
|
||||||
}
|
}
|
||||||
return decodedGPS;
|
return distanceAndComment;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
String decodeEncodedGPS(const String& packet) {
|
||||||
|
int indexOfExclamation = packet.indexOf(":!");
|
||||||
|
int indexOfEqual = packet.indexOf(":=");
|
||||||
|
|
||||||
|
const uint8_t OFFSET = 3; // Offset for encoded data in the packet
|
||||||
|
String infoGPS;
|
||||||
|
if (indexOfExclamation > 10) {
|
||||||
|
infoGPS = packet.substring(indexOfExclamation + OFFSET);
|
||||||
|
} else if (indexOfEqual > 10) {
|
||||||
|
infoGPS = packet.substring(indexOfEqual + OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
float decodedLatitude = APRSPacketLib::decodeBase91EncodedLatitude(infoGPS.substring(0,4));
|
||||||
|
float decodedLongitude = APRSPacketLib::decodeBase91EncodedLongitude(infoGPS.substring(4,8));
|
||||||
|
|
||||||
|
return buildDistanceAndComment(decodedLatitude, decodedLongitude, infoGPS.substring(12));
|
||||||
}
|
}
|
||||||
|
|
||||||
String getReceivedGPS(const String& packet) {
|
String getReceivedGPS(const String& packet) {
|
||||||
|
int indexOfExclamation = packet.indexOf(":!");
|
||||||
|
int indexOfEqual = packet.indexOf(":=");
|
||||||
|
int indexOfAt = packet.indexOf(":@");
|
||||||
|
|
||||||
String infoGPS;
|
String infoGPS;
|
||||||
if (packet.indexOf(":!") > 10) {
|
if (indexOfExclamation > 10) {
|
||||||
infoGPS = packet.substring(packet.indexOf(":!") + 2);
|
infoGPS = packet.substring(indexOfExclamation + 2);
|
||||||
} else if (packet.indexOf(":=") > 10) {
|
} else if (indexOfEqual > 10) {
|
||||||
infoGPS = packet.substring(packet.indexOf(":=") + 2);
|
infoGPS = packet.substring(indexOfEqual + 2);
|
||||||
|
} else if (indexOfAt > 10) {
|
||||||
|
infoGPS = packet.substring(indexOfAt + 9); // 9 = 2+7 (when 7 is timestamp characters)
|
||||||
}
|
}
|
||||||
const String& Latitude = infoGPS.substring(0,8);
|
|
||||||
const String& Longitude = infoGPS.substring(9,18);
|
|
||||||
const String& comment = infoGPS.substring(19);
|
|
||||||
|
|
||||||
float convertedLatitude, convertedLongitude;
|
|
||||||
const String& firstLatPart = Latitude.substring(0,2);
|
|
||||||
const String& secondLatPart = Latitude.substring(2,4);
|
|
||||||
const String& thirdLatPart = Latitude.substring(Latitude.indexOf(".") + 1, Latitude.indexOf(".") + 3);
|
|
||||||
convertedLatitude = firstLatPart.toFloat() + (secondLatPart.toFloat()/60) + (thirdLatPart.toFloat()/(60*100));
|
|
||||||
|
|
||||||
const String& firstLngPart = Longitude.substring(0,3);
|
String Latitude = infoGPS.substring(0,8); // First 8 characters are Latitude
|
||||||
const String& secondLngPart = Longitude.substring(3,5);
|
float convertedLatitude = Latitude.substring(0,2).toFloat(); // First 2 digits (Degrees)
|
||||||
const String& thirdLngPart = Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3);
|
convertedLatitude += Latitude.substring(2,4).toFloat() / 60; // Next 2 digits (Minutes)
|
||||||
convertedLongitude = firstLngPart.toFloat() + (secondLngPart.toFloat()/60) + (thirdLngPart.toFloat()/(60*100));
|
convertedLatitude += Latitude.substring(Latitude.indexOf(".") + 1, Latitude.indexOf(".") + 3).toFloat() / (60*100);
|
||||||
|
if (Latitude.endsWith("S")) convertedLatitude = -convertedLatitude; // Handle Southern Hemisphere
|
||||||
if (String(Latitude[7]) == "S") {
|
|
||||||
convertedLatitude = -convertedLatitude;
|
|
||||||
}
|
|
||||||
if (String(Longitude[8]) == "W") {
|
|
||||||
convertedLongitude = -convertedLongitude;
|
|
||||||
}
|
|
||||||
|
|
||||||
distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1);
|
|
||||||
|
|
||||||
String decodedGPS = String(convertedLatitude,5);
|
String Longitude = infoGPS.substring(9,18); // Next 9 characters are Longitude
|
||||||
decodedGPS += "N / ";
|
float convertedLongitude = Longitude.substring(0,3).toFloat(); // First 3 digits (Degrees)
|
||||||
decodedGPS += String(convertedLongitude,5);
|
convertedLongitude += Longitude.substring(3,5).toFloat() / 60; // Next 2 digits (Minutes)
|
||||||
decodedGPS += "E / ";
|
convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100);
|
||||||
decodedGPS += distance;
|
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
|
||||||
decodedGPS += "km";
|
|
||||||
|
return buildDistanceAndComment(convertedLatitude, convertedLongitude, infoGPS.substring(19));
|
||||||
if (comment != "") {
|
|
||||||
decodedGPS += " / ";
|
|
||||||
decodedGPS += comment;
|
|
||||||
}
|
|
||||||
return decodedGPS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getDistanceAndComment(const String& packet) {
|
String getDistanceAndComment(const String& packet) {
|
||||||
uint8_t encodedBytePosition = 0;
|
int indexOfAt = packet.indexOf(":@");
|
||||||
if (packet.indexOf(":!") > 10) {
|
if (indexOfAt > 10) return getReceivedGPS(packet);
|
||||||
encodedBytePosition = packet.indexOf(":!") + 14;
|
|
||||||
|
const uint8_t nonEncondedLatitudeOffset = 9; // "N" / "S"
|
||||||
|
const uint8_t nonEncondedLongitudeOffset = 19; // "E" / "W"
|
||||||
|
const uint8_t encondedByteOffset = 14;
|
||||||
|
|
||||||
|
int indexOfExclamation = packet.indexOf(":!");
|
||||||
|
int indexOfEqual = packet.indexOf(":=");
|
||||||
|
int baseIndex = - 1;
|
||||||
|
if (indexOfExclamation > 10) {
|
||||||
|
baseIndex = indexOfExclamation;
|
||||||
|
} else if (indexOfEqual > 10) {
|
||||||
|
baseIndex = indexOfEqual;
|
||||||
}
|
}
|
||||||
if (packet.indexOf(":=") > 10) {
|
if (baseIndex == -1) return " _ / _ / _ ";
|
||||||
encodedBytePosition = packet.indexOf(":=") + 14;
|
|
||||||
}
|
int latitudeIndex = baseIndex + nonEncondedLatitudeOffset;
|
||||||
if (encodedBytePosition != 0) {
|
int longitudeIndex = baseIndex + nonEncondedLongitudeOffset;
|
||||||
if (String(packet[encodedBytePosition]) == "G" || String(packet[encodedBytePosition]) == "Q" || String(packet[encodedBytePosition]) == "[" || String(packet[encodedBytePosition]) == "H") {
|
int encondedByteIndex = baseIndex + encondedByteOffset;
|
||||||
return decodeEncodedGPS(packet);
|
int packetLength = packet.length();
|
||||||
} else {
|
|
||||||
return getReceivedGPS(packet);
|
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() {
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) {
|
||||||
|
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_TX, GPS_RX);
|
||||||
}
|
}
|
||||||
} else {
|
#endif
|
||||||
return " _ / _ / _ ";
|
generateBeacons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getData() {
|
||||||
|
while (gpsSerial.available() > 0) {
|
||||||
|
gps.encode(gpsSerial.read());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef GPS_UTILS_H_
|
|
||||||
#define GPS_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace GPS_Utils {
|
|
||||||
|
|
||||||
String getiGateLoRaBeaconPacket();
|
|
||||||
char *ax25_base91enc(char *s, uint8_t n, uint32_t v);
|
|
||||||
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol);
|
|
||||||
void generateBeacons();
|
|
||||||
double calculateDistanceCourse(double latitude, double longitude);
|
|
||||||
String decodeEncodedGPS(const String& packet);
|
|
||||||
String getReceivedGPS(const String& packet);
|
|
||||||
String getDistanceAndComment(const String& packet);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#define DCD_ON 0x03
|
|
||||||
|
|
||||||
#define FEND 0xC0
|
|
||||||
#define FESC 0xDB
|
|
||||||
#define TFEND 0xDC
|
|
||||||
#define TFESC 0xDD
|
|
||||||
|
|
||||||
#define CMD_UNKNOWN 0xFE
|
|
||||||
#define CMD_DATA 0x00
|
|
||||||
#define CMD_HARDWARE 0x06
|
|
||||||
|
|
||||||
#define HW_RSSI 0x21
|
|
||||||
|
|
||||||
#define CMD_ERROR 0x90
|
|
||||||
#define ERROR_INITRADIO 0x01
|
|
||||||
#define ERROR_TXFAILED 0x02
|
|
||||||
#define ERROR_QUEUE_FULL 0x04
|
|
||||||
|
|
||||||
#define APRS_CONTROL_FIELD 0x03
|
|
||||||
#define APRS_INFORMATION_FIELD 0xf0
|
|
||||||
|
|
||||||
#define HAS_BEEN_DIGIPITED_MASK 0b10000000
|
|
||||||
#define IS_LAST_ADDRESS_POSITION_MASK 0b1
|
|
||||||
@@ -1,6 +1,25 @@
|
|||||||
|
/* 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 <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "kiss_protocol.h"
|
#include "kiss_protocol.h"
|
||||||
|
|
||||||
|
|
||||||
bool validateTNC2Frame(const String& tnc2FormattedFrame) {
|
bool validateTNC2Frame(const String& tnc2FormattedFrame) {
|
||||||
return (tnc2FormattedFrame.indexOf(':') != -1) && (tnc2FormattedFrame.indexOf('>') != -1);
|
return (tnc2FormattedFrame.indexOf(':') != -1) && (tnc2FormattedFrame.indexOf('>') != -1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
#ifndef KISS_UTILS_H_
|
|
||||||
#define KISS_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
String encodeAddressAX25(String tnc2Address);
|
|
||||||
String decodeAddressAX25(const String& ax25Address, bool& isLast, bool isRelay);
|
|
||||||
|
|
||||||
String encapsulateKISS(const String& ax25Frame, uint8_t cmd);
|
|
||||||
String decapsulateKISS(const String& frame);
|
|
||||||
|
|
||||||
String encodeKISS(const String& frame);
|
|
||||||
String decodeKISS(const String& inputFrame, bool& dataFrame);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,14 +1,36 @@
|
|||||||
|
/* 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 <RadioLib.h>
|
#include <RadioLib.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "aprs_is_utils.h"
|
#include "aprs_is_utils.h"
|
||||||
#include "boards_pinout.h"
|
#include "station_utils.h"
|
||||||
|
#include "board_pinout.h"
|
||||||
#include "syslog_utils.h"
|
#include "syslog_utils.h"
|
||||||
|
#include "ntp_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -18,22 +40,28 @@ bool transmitFlag = true;
|
|||||||
#ifdef HAS_SX1262
|
#ifdef HAS_SX1262
|
||||||
SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SX1268
|
#ifdef HAS_SX1268
|
||||||
SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
#if defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0)
|
||||||
|
SPIClass loraSPI(FSPI);
|
||||||
|
SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN, loraSPI);
|
||||||
|
#else
|
||||||
|
SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SX1278
|
#ifdef HAS_SX1278
|
||||||
SX1278 radio = new Module(RADIO_CS_PIN, RADIO_BUSY_PIN, RADIO_RST_PIN);
|
SX1278 radio = new Module(RADIO_CS_PIN, RADIO_BUSY_PIN, RADIO_RST_PIN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SX1276
|
#ifdef HAS_SX1276
|
||||||
SX1276 radio = new Module(RADIO_CS_PIN, RADIO_BUSY_PIN, RADIO_RST_PIN);
|
SX1276 radio = new Module(RADIO_CS_PIN, RADIO_BUSY_PIN, RADIO_RST_PIN);
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAS_LLCC68) //LLCC68 supports spreading factor only in range of 5-11!
|
||||||
|
LLCC68 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||||
|
#endif
|
||||||
|
|
||||||
int rssi, freqError;
|
int rssi, freqError;
|
||||||
float snr;
|
float snr;
|
||||||
|
|
||||||
|
|
||||||
namespace LoRa_Utils {
|
namespace LoRa_Utils {
|
||||||
|
|
||||||
void setFlag(void) {
|
void setFlag(void) {
|
||||||
@@ -41,36 +69,56 @@ namespace LoRa_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
|
#if defined (LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0)
|
||||||
|
pinMode(RADIO_VCC_PIN,OUTPUT);
|
||||||
|
digitalWrite(RADIO_VCC_PIN,HIGH);
|
||||||
|
loraSPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN, RADIO_CS_PIN);
|
||||||
|
#else
|
||||||
|
SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
|
||||||
|
#endif
|
||||||
float freq = (float)Config.loramodule.rxFreq / 1000000;
|
float freq = (float)Config.loramodule.rxFreq / 1000000;
|
||||||
|
#if defined(RADIO_HAS_XTAL)
|
||||||
|
radio.XTAL = true;
|
||||||
|
#endif
|
||||||
int state = radio.begin(freq);
|
int state = radio.begin(freq);
|
||||||
if (state == RADIOLIB_ERR_NONE) {
|
if (state == RADIOLIB_ERR_NONE) {
|
||||||
Utils::println("Initializing LoRa Module");
|
Utils::println("Initializing LoRa Module");
|
||||||
} else {
|
} else {
|
||||||
Utils::println("Starting LoRa failed!");
|
Utils::println("Starting LoRa failed! State: " + String(state));
|
||||||
while (true);
|
while (true);
|
||||||
}
|
}
|
||||||
#if defined(HAS_SX1262) || defined(HAS_SX1268)
|
#if defined(HAS_SX1262) || defined(HAS_SX1268) || defined(HAS_LLCC68)
|
||||||
if (!Config.lowPowerMode) {
|
radio.setDio1Action(setFlag);
|
||||||
radio.setDio1Action(setFlag);
|
|
||||||
} else {
|
|
||||||
radio.setDIOMapping(1, RADIOLIB_SX126X_IRQ_RX_DONE);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#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)
|
#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 HAS_1W_LORA // Ebyte E22 400M30S (SX1268) / 900M30S (SX1262)
|
/*#ifdef SX126X_DIO2_AS_RF_SWITCH
|
||||||
|
radio.setRfSwitchPins(RADIO_RXEN, RADIOLIB_NC);
|
||||||
|
radio.setDio2AsRfSwitch(true);
|
||||||
|
#endif*/
|
||||||
|
|
||||||
|
#ifdef HAS_1W_LORA // Ebyte E22 400M30S (SX1268) / 900M30S (SX1262) / Ebyte E220 400M30S (LLCC68)
|
||||||
state = radio.setOutputPower(Config.loramodule.power); // max value 20dB for 1W modules as they have Low Noise Amp
|
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)?
|
||||||
#endif
|
#endif
|
||||||
@@ -83,39 +131,54 @@ namespace LoRa_Utils {
|
|||||||
radio.setCurrentLimit(140);
|
radio.setCurrentLimit(140);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAS_SX1262) || defined(HAS_SX1268)
|
#if defined(HAS_SX1262) || defined(HAS_SX1268) || defined(HAS_LLCC68)
|
||||||
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 {
|
||||||
Utils::println("Starting LoRa failed!");
|
Utils::println("Starting LoRa failed! State: " + String(state));
|
||||||
while (true);
|
while (true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
digitalWrite(INTERNAL_LED_PIN, HIGH);
|
if (Config.digi.ecoMode != 1) digitalWrite(INTERNAL_LED_PIN, HIGH); // disabled in Ultra Eco Mode
|
||||||
#endif
|
#endif
|
||||||
int state = radio.transmit("\x3c\xff\x01" + newPacket);
|
int state = radio.transmit("\x3c\xff\x01" + newPacket);
|
||||||
transmitFlag = true;
|
transmitFlag = true;
|
||||||
@@ -123,36 +186,31 @@ namespace LoRa_Utils {
|
|||||||
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
|
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
|
||||||
SYSLOG_Utils::log(3, newPacket, 0, 0.0, 0); // TX
|
SYSLOG_Utils::log(3, newPacket, 0, 0.0, 0); // TX
|
||||||
}
|
}
|
||||||
Utils::print("---> LoRa Packet Tx : ");
|
Utils::print("---> LoRa Packet Tx : ");
|
||||||
Utils::println(newPacket);
|
Utils::println(newPacket);
|
||||||
} else {
|
} else {
|
||||||
Utils::print(F("failed, code "));
|
Utils::print(F("failed, code "));
|
||||||
Utils::println(String(state));
|
Utils::println(String(state));
|
||||||
}
|
}
|
||||||
#ifdef INTERNAL_LED_PIN
|
#ifdef INTERNAL_LED_PIN
|
||||||
digitalWrite(INTERNAL_LED_PIN, LOW);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*String packetSanitization(const String& packet) {
|
String receivePacketFromSleep() {
|
||||||
String sanitizedPacket = packet;
|
String packet = "";
|
||||||
if (packet.indexOf("\0") > 0) {
|
int state = radio.readData(packet);
|
||||||
sanitizedPacket.replace("\0", "");
|
if (state == RADIOLIB_ERR_NONE) {
|
||||||
|
Utils::println("<--- LoRa Packet Rx : " + packet.substring(3));
|
||||||
|
} else {
|
||||||
|
packet = "";
|
||||||
}
|
}
|
||||||
if (packet.indexOf("\r") > 0) {
|
return packet;
|
||||||
sanitizedPacket.replace("\r", "");
|
|
||||||
}
|
|
||||||
if (packet.indexOf("\n") > 0) {
|
|
||||||
sanitizedPacket.replace("\n", "");
|
|
||||||
}
|
|
||||||
return sanitizedPacket;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void startReceive() {
|
|
||||||
radio.startReceive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String receivePacket() {
|
String receivePacket() {
|
||||||
@@ -166,30 +224,36 @@ namespace LoRa_Utils {
|
|||||||
int state = radio.readData(packet);
|
int state = radio.readData(packet);
|
||||||
if (state == RADIOLIB_ERR_NONE) {
|
if (state == RADIOLIB_ERR_NONE) {
|
||||||
if (packet != "") {
|
if (packet != "") {
|
||||||
rssi = radio.getRSSI();
|
|
||||||
snr = radio.getSNR();
|
|
||||||
freqError = radio.getFrequencyError();
|
|
||||||
Utils::println("<--- LoRa Packet Rx : " + packet.substring(3));
|
|
||||||
Utils::println("(RSSI:" + String(rssi) + " / SNR:" + String(snr) + " / FreqErr:" + String(freqError) + ")");
|
|
||||||
|
|
||||||
if (!Config.lowPowerMode) {
|
String sender = packet.substring(3, packet.indexOf(">"));
|
||||||
ReceivedPacket receivedPacket;
|
if (packet.substring(0,3) == "\x3c\xff\x01" && !STATION_Utils::isBlacklisted(sender)) { // avoid processing BlackListed stations
|
||||||
receivedPacket.millis = millis();
|
rssi = radio.getRSSI();
|
||||||
receivedPacket.packet = packet.substring(3);
|
snr = radio.getSNR();
|
||||||
receivedPacket.RSSI = rssi;
|
freqError = radio.getFrequencyError();
|
||||||
receivedPacket.SNR = snr;
|
Utils::println("<--- LoRa Packet Rx : " + packet.substring(3));
|
||||||
if (receivedPackets.size() >= 20) {
|
Utils::println("(RSSI:" + String(rssi) + " / SNR:" + String(snr) + " / FreqErr:" + String(freqError) + ")");
|
||||||
receivedPackets.erase(receivedPackets.begin());
|
|
||||||
|
if (Config.digi.ecoMode == 0) {
|
||||||
|
if (receivedPackets.size() >= 10) {
|
||||||
|
receivedPackets.erase(receivedPackets.begin());
|
||||||
|
}
|
||||||
|
ReceivedPacket receivedPacket;
|
||||||
|
receivedPacket.rxTime = NTP_Utils::getFormatedTime();
|
||||||
|
receivedPacket.packet = packet.substring(3);
|
||||||
|
receivedPacket.RSSI = rssi;
|
||||||
|
receivedPacket.SNR = snr;
|
||||||
|
receivedPackets.push_back(receivedPacket);
|
||||||
}
|
}
|
||||||
receivedPackets.push_back(receivedPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
|
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
|
||||||
SYSLOG_Utils::log(1, packet, rssi, snr, freqError); // RX
|
SYSLOG_Utils::log(1, packet, rssi, snr, freqError); // RX
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
packet = "";
|
||||||
}
|
}
|
||||||
lastRxTime = millis();
|
lastRxTime = millis();
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
|
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
|
||||||
rssi = radio.getRSSI();
|
rssi = radio.getRSSI();
|
||||||
snr = radio.getSNR();
|
snr = radio.getSNR();
|
||||||
@@ -209,6 +273,10 @@ namespace LoRa_Utils {
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wakeRadio() {
|
||||||
|
radio.startReceive();
|
||||||
|
}
|
||||||
|
|
||||||
void sleepRadio() {
|
void sleepRadio() {
|
||||||
radio.sleep();
|
radio.sleep();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef LORA_UTILS_H_
|
|
||||||
#define LORA_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace LoRa_Utils {
|
|
||||||
|
|
||||||
void setup();
|
|
||||||
void sendNewPacket(const String& newPacket);
|
|
||||||
//String packetSanitization(const String& packet);
|
|
||||||
String receivePacket();
|
|
||||||
void changeFreqTx();
|
|
||||||
void changeFreqRx();
|
|
||||||
void startReceive(); // ???
|
|
||||||
void sleepRadio();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
101
src/mqtt_utils.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
52
src/ntp_utils.cpp
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/* 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 <NTPClient.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "ntp_utils.h"
|
||||||
|
#include "time.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern Configuration Config;
|
||||||
|
|
||||||
|
WiFiUDP ntpUDP;
|
||||||
|
NTPClient* timeClient;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NTP_Utils {
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") {
|
||||||
|
int gmt = Config.ntp.gmtCorrection * 3600;
|
||||||
|
timeClient = new NTPClient(ntpUDP, Config.ntp.server.c_str(), gmt, 15 * 60 * 1000); // Update interval 15 min
|
||||||
|
timeClient->begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") timeClient->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFormatedTime() {
|
||||||
|
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0) return timeClient->getFormattedTime();
|
||||||
|
return "DigiEcoMode Active";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/* 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 <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <ElegantOTA.h>
|
#include <ElegantOTA.h>
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
@@ -5,6 +23,7 @@
|
|||||||
#include "ota_utils.h"
|
#include "ota_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
extern uint32_t lastScreenOn;
|
extern uint32_t lastScreenOn;
|
||||||
extern bool isUpdatingOTA;
|
extern bool isUpdatingOTA;
|
||||||
@@ -13,14 +32,14 @@ unsigned long ota_progress_millis = 0;
|
|||||||
|
|
||||||
|
|
||||||
namespace OTA_Utils {
|
namespace OTA_Utils {
|
||||||
|
|
||||||
void setup(AsyncWebServer *server) {
|
void setup(AsyncWebServer *server) {
|
||||||
if (Config.ota.username != "" && Config.ota.password != "") {
|
if (Config.ota.username != "" && Config.ota.password != "") {
|
||||||
ElegantOTA.begin(server, Config.ota.username.c_str(), Config.ota.password.c_str());
|
ElegantOTA.begin(server, Config.ota.username.c_str(), Config.ota.password.c_str());
|
||||||
} else {
|
} else {
|
||||||
ElegantOTA.begin(server);
|
ElegantOTA.begin(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
ElegantOTA.setAutoReboot(true);
|
ElegantOTA.setAutoReboot(true);
|
||||||
ElegantOTA.onStart(onOTAStart);
|
ElegantOTA.onStart(onOTAStart);
|
||||||
ElegantOTA.onProgress(onOTAProgress);
|
ElegantOTA.onProgress(onOTAProgress);
|
||||||
@@ -29,33 +48,33 @@ namespace OTA_Utils {
|
|||||||
|
|
||||||
void onOTAStart() {
|
void onOTAStart() {
|
||||||
Serial.println("OTA update started!");
|
Serial.println("OTA update started!");
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
show_display("", "", "", " OTA update started!", "", "", "", 1000);
|
displayShow("", "", "", " OTA update started!", "", "", "", 1000);
|
||||||
isUpdatingOTA = true;
|
isUpdatingOTA = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAProgress(size_t current, size_t final) {
|
void onOTAProgress(size_t current, size_t final) {
|
||||||
if (millis() - ota_progress_millis > 1000) {
|
if (millis() - ota_progress_millis > 1000) {
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
ota_progress_millis = millis();
|
ota_progress_millis = millis();
|
||||||
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
||||||
show_display("", "", " OTA Progress : " + String((current*100)/final) + "%", "", "", "", "", 100);
|
displayShow("", "", " OTA Progress : " + String((current*100)/final) + "%", "", "", "", "", 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAEnd(bool success) {
|
void onOTAEnd(bool success) {
|
||||||
display_toggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
if (success) {
|
|
||||||
Serial.println("OTA update finished successfully!");
|
String statusMessage = success ? "OTA update success!" : "OTA update fail!";
|
||||||
show_display("", "", " OTA update success!", "", " Rebooting ...", "", "", 4000);
|
String rebootMessage = success ? "Rebooting ..." : "";
|
||||||
} else {
|
|
||||||
Serial.println("There was an error during OTA update!");
|
Serial.println(success ? "OTA update finished successfully!" : "There was an error during OTA update!");
|
||||||
show_display("", "", " OTA update fail!", "", "", "", "", 4000);
|
displayShow("", "", statusMessage, "", rebootMessage, "", "", 4000);
|
||||||
}
|
|
||||||
isUpdatingOTA = false;
|
isUpdatingOTA = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#ifndef OTA_UTILS_H_
|
|
||||||
#define OTA_UTILS_H_
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace OTA_Utils {
|
|
||||||
|
|
||||||
void setup(AsyncWebServer *server);
|
|
||||||
void onOTAStart();
|
|
||||||
void onOTAProgress(size_t current, size_t final);
|
|
||||||
void onOTAEnd(bool success);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,12 +1,38 @@
|
|||||||
|
/* 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 "configuration.h"
|
#include "configuration.h"
|
||||||
#include "battery_utils.h"
|
#include "battery_utils.h"
|
||||||
#include "boards_pinout.h"
|
#include "board_pinout.h"
|
||||||
#include "power_utils.h"
|
#include "power_utils.h"
|
||||||
|
|
||||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
||||||
#define I2C_SDA 21
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
#define I2C_SCL 22
|
#define I2C0_SDA 17
|
||||||
#define IRQ_PIN 35
|
#define I2C0_SCL 18
|
||||||
|
#define I2C1_SDA 42
|
||||||
|
#define I2C1_SCL 41
|
||||||
|
#define IRQ_PIN 40
|
||||||
|
#else
|
||||||
|
#define I2C_SDA 21
|
||||||
|
#define I2C_SCL 22
|
||||||
|
#define IRQ_PIN 35
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_AXP192
|
#ifdef HAS_AXP192
|
||||||
@@ -21,6 +47,57 @@ extern Configuration Config;
|
|||||||
|
|
||||||
namespace POWER_Utils {
|
namespace POWER_Utils {
|
||||||
|
|
||||||
|
#ifdef VEXT_CTRL
|
||||||
|
void vext_ctrl_ON() {
|
||||||
|
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3) || defined(HELTEC_VM_E290)
|
||||||
|
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
|
||||||
|
#endif
|
||||||
|
#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);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void vext_ctrl_OFF() {
|
||||||
|
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3) || defined(HELTEC_VM_E290)
|
||||||
|
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
|
||||||
|
#endif
|
||||||
|
#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);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ADC_CTRL
|
||||||
|
void adc_ctrl_ON() {
|
||||||
|
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2) || defined(HELTEC_VM_E290)
|
||||||
|
digitalWrite(ADC_CTRL, HIGH);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP_V1) || defined(HELTEC_WP_V1_2)
|
||||||
|
digitalWrite(ADC_CTRL, LOW);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void adc_ctrl_OFF() {
|
||||||
|
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2) || defined(HELTEC_VM_E290)
|
||||||
|
digitalWrite(ADC_CTRL, LOW);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP_V1) || defined(HELTEC_WP_V1_2)
|
||||||
|
digitalWrite(ADC_CTRL, HIGH);
|
||||||
|
#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);
|
||||||
@@ -35,16 +112,45 @@ namespace POWER_Utils {
|
|||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void activateGPS() {
|
||||||
|
#ifdef HAS_AXP192
|
||||||
|
PMU.setLDO3Voltage(3300);
|
||||||
|
PMU.enableLDO3();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_AXP2101
|
||||||
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
|
PMU.setALDO4Voltage(3300);
|
||||||
|
PMU.enableALDO4();
|
||||||
|
#else
|
||||||
|
PMU.setALDO3Voltage(3300);
|
||||||
|
PMU.enableALDO3();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifdef HELTEC_WIRELESS_TRACKER
|
||||||
|
adc_ctrl_ON();
|
||||||
|
#endif
|
||||||
|
//gpsIsActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void activateMeasurement() {
|
void deactivateGPS() {
|
||||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
#ifdef HAS_AXP192
|
||||||
PMU.disableTSPinMeasure();
|
PMU.disableLDO3();
|
||||||
PMU.enableBattDetection();
|
|
||||||
PMU.enableVbusVoltageMeasure();
|
|
||||||
PMU.enableBattVoltageMeasure();
|
|
||||||
PMU.enableSystemVoltageMeasure();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_AXP2101
|
||||||
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
|
PMU.disableALDO4();
|
||||||
|
#else
|
||||||
|
PMU.disableALDO3();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifdef HELTEC_WIRELESS_TRACKER
|
||||||
|
adc_ctrl_OFF();
|
||||||
|
#endif
|
||||||
|
//gpsIsActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void activateLoRa() {
|
void activateLoRa() {
|
||||||
@@ -53,8 +159,13 @@ namespace POWER_Utils {
|
|||||||
PMU.enableLDO2();
|
PMU.enableLDO2();
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_AXP2101
|
#ifdef HAS_AXP2101
|
||||||
PMU.setALDO2Voltage(3300);
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
PMU.enableALDO2();
|
PMU.setALDO3Voltage(3300);
|
||||||
|
PMU.enableALDO3();
|
||||||
|
#else
|
||||||
|
PMU.setALDO2Voltage(3300);
|
||||||
|
PMU.enableALDO2();
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +174,11 @@ namespace POWER_Utils {
|
|||||||
PMU.disableLDO2();
|
PMU.disableLDO2();
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_AXP2101
|
#ifdef HAS_AXP2101
|
||||||
PMU.disableALDO2();
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
|
PMU.disableALDO3();
|
||||||
|
#else
|
||||||
|
PMU.disableALDO2();
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,20 +196,29 @@ namespace POWER_Utils {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
#elif defined(HAS_AXP2101)
|
#elif defined(HAS_AXP2101)
|
||||||
bool result = PMU.begin(Wire, AXP2101_SLAVE_ADDRESS, I2C_SDA, I2C_SCL);
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
|
bool result = PMU.begin(Wire1, AXP2101_SLAVE_ADDRESS, I2C1_SDA, I2C1_SCL);
|
||||||
|
#else
|
||||||
|
bool result = PMU.begin(Wire, AXP2101_SLAVE_ADDRESS, I2C_SDA, I2C_SCL);
|
||||||
|
#endif
|
||||||
if (result) {
|
if (result) {
|
||||||
PMU.disableDC2();
|
PMU.disableDC2();
|
||||||
PMU.disableDC3();
|
PMU.disableDC3();
|
||||||
PMU.disableDC4();
|
PMU.disableDC4();
|
||||||
PMU.disableDC5();
|
PMU.disableDC5();
|
||||||
PMU.disableALDO1();
|
#ifndef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
PMU.disableALDO4();
|
PMU.disableALDO1();
|
||||||
|
PMU.disableALDO4();
|
||||||
|
#endif
|
||||||
PMU.disableBLDO1();
|
PMU.disableBLDO1();
|
||||||
PMU.disableBLDO2();
|
PMU.disableBLDO2();
|
||||||
PMU.disableDLDO1();
|
PMU.disableDLDO1();
|
||||||
PMU.disableDLDO2();
|
PMU.disableDLDO2();
|
||||||
PMU.setDC1Voltage(3300);
|
PMU.setDC1Voltage(3300);
|
||||||
PMU.enableDC1();
|
PMU.enableDC1();
|
||||||
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
|
PMU.setALDO1Voltage(3300);
|
||||||
|
#endif
|
||||||
PMU.setButtonBatteryChargeVoltage(3300);
|
PMU.setButtonBatteryChargeVoltage(3300);
|
||||||
PMU.enableButtonBatteryCharge();
|
PMU.enableButtonBatteryCharge();
|
||||||
PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
||||||
@@ -122,8 +246,16 @@ namespace POWER_Utils {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_AXP2101
|
#ifdef HAS_AXP2101
|
||||||
Wire.begin(SDA, SCL);
|
bool beginStatus = false;
|
||||||
if (begin(Wire)) {
|
#ifdef TTGO_T_Beam_S3_SUPREME_V3
|
||||||
|
Wire1.begin(I2C1_SDA, I2C1_SCL);
|
||||||
|
Wire.begin(I2C0_SDA, I2C0_SCL);
|
||||||
|
if (begin(Wire1)) beginStatus = true;
|
||||||
|
#else
|
||||||
|
Wire.begin(SDA, SCL);
|
||||||
|
if (begin(Wire)) beginStatus = true;
|
||||||
|
#endif
|
||||||
|
if (beginStatus) {
|
||||||
Serial.println("AXP2101 init done!");
|
Serial.println("AXP2101 init done!");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("AXP2101 init failed!");
|
Serial.println("AXP2101 init failed!");
|
||||||
@@ -143,6 +275,7 @@ namespace POWER_Utils {
|
|||||||
|
|
||||||
#ifdef INTERNAL_LED_PIN
|
#ifdef INTERNAL_LED_PIN
|
||||||
pinMode(INTERNAL_LED_PIN, OUTPUT);
|
pinMode(INTERNAL_LED_PIN, OUTPUT);
|
||||||
|
digitalWrite(INTERNAL_LED_PIN, LOW);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) {
|
if (Config.battery.sendExternalVoltage || Config.battery.monitorExternalVoltage) {
|
||||||
@@ -151,26 +284,49 @@ namespace POWER_Utils {
|
|||||||
|
|
||||||
#ifdef VEXT_CTRL
|
#ifdef VEXT_CTRL
|
||||||
pinMode(VEXT_CTRL,OUTPUT); // GPS + TFT on HELTEC Wireless_Tracker and only for Oled in HELTEC V3
|
pinMode(VEXT_CTRL,OUTPUT); // GPS + TFT on HELTEC Wireless_Tracker and only for Oled in HELTEC V3
|
||||||
#ifndef HELTEC_WSL_V3
|
vext_ctrl_ON();
|
||||||
digitalWrite(VEXT_CTRL, HIGH);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ADC_CTRL
|
|
||||||
pinMode(ADC_CTRL, OUTPUT);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HELTEC_WIRELESS_TRACKER
|
#ifdef HAS_GPS
|
||||||
|
if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) activateGPS();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ADC_CTRL
|
||||||
|
pinMode(ADC_CTRL, OUTPUT);
|
||||||
|
adc_ctrl_OFF();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
|
||||||
|
pinMode(BOARD_POWERON, OUTPUT);
|
||||||
|
digitalWrite(BOARD_POWERON, HIGH);
|
||||||
|
|
||||||
|
pinMode(BOARD_SDCARD_CS, OUTPUT);
|
||||||
|
pinMode(RADIO_CS_PIN, OUTPUT);
|
||||||
|
pinMode(TFT_CS, OUTPUT);
|
||||||
|
|
||||||
|
digitalWrite(BOARD_SDCARD_CS, HIGH);
|
||||||
|
digitalWrite(RADIO_CS_PIN, HIGH);
|
||||||
|
digitalWrite(TFT_CS, HIGH);
|
||||||
|
|
||||||
|
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 HELTEC_V3
|
#ifdef USE_WIRE1_WITH_BOARD_I2C_PINS
|
||||||
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
|
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
delay(1000);
|
delay(1000);
|
||||||
|
BATTERY_Utils::setup();
|
||||||
BATTERY_Utils::startupBatteryHealth();
|
BATTERY_Utils::startupBatteryHealth();
|
||||||
|
setCpuFrequencyMhz(80);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#ifndef POWER_UTILS_H_
|
|
||||||
#define POWER_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
|
||||||
#include "XPowersLib.h"
|
|
||||||
#else
|
|
||||||
#include <Wire.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace POWER_Utils {
|
|
||||||
|
|
||||||
double getBatteryVoltage();
|
|
||||||
bool isBatteryConnected();
|
|
||||||
void activateMeasurement();
|
|
||||||
void activateLoRa();
|
|
||||||
void deactivateLoRa();
|
|
||||||
bool begin(TwoWire &port);
|
|
||||||
void setup();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,73 +1,167 @@
|
|||||||
|
/* 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 "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "battery_utils.h"
|
||||||
|
#include "station_utils.h"
|
||||||
#include "query_utils.h"
|
#include "query_utils.h"
|
||||||
#include "lora_utils.h"
|
#include "lora_utils.h"
|
||||||
|
|
||||||
extern Configuration Config;
|
|
||||||
extern std::vector<String> lastHeardStation;
|
extern Configuration Config;
|
||||||
extern String versionDate;
|
extern std::vector<LastHeardStation> lastHeardStations;
|
||||||
extern int rssi;
|
extern String versionDate;
|
||||||
extern float snr;
|
extern int rssi;
|
||||||
extern int freqError;
|
extern float snr;
|
||||||
|
extern int freqError;
|
||||||
|
extern bool shouldSleepLowVoltage;
|
||||||
|
extern bool saveNewDigiEcoModeConfig;
|
||||||
|
extern String versionNumber;
|
||||||
|
|
||||||
|
|
||||||
namespace QUERY_Utils {
|
namespace QUERY_Utils {
|
||||||
|
|
||||||
String process(const String& query, const String& station, const uint8_t queryOrigin) {
|
String process(const String& query, const String& station, bool queryFromAPRSIS, bool thirdParty) {
|
||||||
String answer;
|
String answer;
|
||||||
if (query=="?APRS?" || query=="?aprs?" || query=="?Aprs?" || query=="H" || query=="h" || query=="HELP" || query=="Help" || query=="help" || query=="?") {
|
String queryQuestion = query;
|
||||||
answer.concat("?APRSV ?APRSP ?APRSL ?APRSH ?WHERE callsign");
|
queryQuestion.toUpperCase();
|
||||||
} else if (query=="?APRSV" || query=="?aprsv" || query=="?Aprsv") {
|
if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") {
|
||||||
answer = "CA2RXU_LoRa_iGate 1.3 v";
|
answer.concat("?APRSV ?APRSP ?APRSL ?APRSSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign
|
||||||
answer.concat(versionDate);
|
} else if (queryQuestion == "?APRSV") {
|
||||||
} else if (query=="?APRSP" || query=="?aprsp" || query=="?Aprsp") {
|
answer.concat("CA2RXU_LoRa_iGate v");
|
||||||
answer = "iGate QTH: ";
|
answer.concat(versionNumber);
|
||||||
answer.concat(String(Config.beacon.latitude,2));
|
|
||||||
answer.concat(" ");
|
answer.concat(" ");
|
||||||
answer.concat(String(Config.beacon.longitude,2));
|
answer.concat(versionDate);
|
||||||
} else if (query=="?APRSL" || query=="?aprsl" || query=="?Aprsl") {
|
} else if (queryQuestion == "?APRSP") {
|
||||||
if (lastHeardStation.size() == 0) {
|
answer.concat("iGate QTH: ");
|
||||||
|
answer.concat(String(Config.beacon.latitude,3));
|
||||||
|
answer.concat(" ");
|
||||||
|
answer.concat(String(Config.beacon.longitude,3));
|
||||||
|
} else if (queryQuestion == "?APRSL") {
|
||||||
|
if (lastHeardStations.size() == 0) {
|
||||||
char answerArray[50];
|
char answerArray[50];
|
||||||
snprintf(answerArray, sizeof(answerArray), "No Station Listened in the last %d min.", Config.rememberStationTime);
|
snprintf(answerArray, sizeof(answerArray), "No Station Listened in the last %d min.", Config.rememberStationTime);
|
||||||
answer.concat(answerArray);
|
answer.concat(answerArray);
|
||||||
} else {
|
} else {
|
||||||
for (int i=0; i<lastHeardStation.size(); i++) {
|
for (int i=0; i<lastHeardStations.size(); i++) {
|
||||||
answer += lastHeardStation[i].substring(0,lastHeardStation[i].indexOf(",")) + " ";
|
answer += lastHeardStations[i].station + " ";
|
||||||
}
|
}
|
||||||
answer.trim();
|
answer.trim();
|
||||||
}
|
}
|
||||||
} else if (query=="?APRSSR" || query=="?aprssr" || query=="?Aprssr") {
|
} else if (queryQuestion == "?APRSSR") {
|
||||||
char signalData[35];
|
char signalData[35];
|
||||||
snprintf(signalData, sizeof(signalData), " %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
|
snprintf(signalData, sizeof(signalData), " %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
|
||||||
answer.concat(signalData);
|
answer.concat(signalData);
|
||||||
} else if (query.indexOf("?APRSH") == 0 || query.indexOf("?aprsh") == 0 || query.indexOf("?Aprsh") == 0) {
|
} /*else if (queryQuestion.indexOf("?APRSH") == 0) {
|
||||||
// sacar callsign despues de ?APRSH
|
// sacar callsign despues de ?APRSH
|
||||||
Serial.println("escuchaste a X estacion? en las ultimas 24 o 8 horas?");
|
Serial.println("escuchaste a X estacion? en las ultimas 24 o 8 horas?");
|
||||||
answer.concat("?APRSH on development 73!");
|
answer.concat("?APRSH on development 73!");
|
||||||
} else if (query.indexOf("?WHERE") == 0) {
|
} *//*else if (queryQuestion.indexOf("?WHERE") == 0) {
|
||||||
// agregar callsign para completar donde esta X callsign --> posicion
|
// agregar callsign para completar donde esta X callsign --> posicion
|
||||||
Serial.println("estaciones escuchadas directo (ultimos 30 min)");
|
Serial.println("estaciones escuchadas directo (ultimos 30 min)");
|
||||||
answer.concat("?WHERE on development 73!");
|
answer.concat("?WHERE on development 73!");
|
||||||
|
} */
|
||||||
|
else if (STATION_Utils::isManager(station) && (!queryFromAPRSIS || !Config.remoteManagement.rfOnly)) {
|
||||||
|
if (queryQuestion.indexOf("?EM=OFF") == 0) {
|
||||||
|
if ((Config.digi.mode == 2 || Config.digi.mode == 3) && Config.loramodule.txActive && Config.loramodule.rxActive && !Config.aprs_is.active) {
|
||||||
|
if (Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) { // Exit Digipeater EcoMode or Digipeater without WiFiAP
|
||||||
|
answer = (Config.digi.ecoMode == 1) ? "DigiEcoMode:OFF" : "Digipeater + WiFiAP enabled";
|
||||||
|
Config.digi.ecoMode = 0;
|
||||||
|
Config.display.alwaysOn = true;
|
||||||
|
Config.display.timeout = 10;
|
||||||
|
shouldSleepLowVoltage = true; // to make sure all packets in outputPacketBuffer are sent before restart.
|
||||||
|
saveNewDigiEcoModeConfig = true;
|
||||||
|
} else {
|
||||||
|
answer = "DigiEcoMode was OFF";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
answer = "Digipeater Mode control not possible";
|
||||||
|
}
|
||||||
|
} else if (queryQuestion.indexOf("?EM=ON") == 0) {
|
||||||
|
if ((Config.digi.mode == 2 || Config.digi.mode == 3) && Config.loramodule.txActive && Config.loramodule.rxActive && !Config.aprs_is.active) {
|
||||||
|
if (Config.digi.ecoMode == 0) { // Start Digipeater EcoMode
|
||||||
|
answer = "DigiEcoMode:ON";
|
||||||
|
Config.digi.ecoMode = 1;
|
||||||
|
shouldSleepLowVoltage = true; // to make sure all packets in outputPacketBuffer are sent before restart.
|
||||||
|
saveNewDigiEcoModeConfig = true;
|
||||||
|
} else {
|
||||||
|
answer = "DigiEcoMode was ON";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
answer = "Digipeater Mode control not possible";
|
||||||
|
}
|
||||||
|
} else if (queryQuestion.indexOf("?EM=?") == 0) { // Digipeater EcoMode Status
|
||||||
|
if (Config.digi.ecoMode == 0) {
|
||||||
|
answer = "DigiEcoMode:OFF";
|
||||||
|
} else if (Config.digi.ecoMode == 1) {
|
||||||
|
answer = "DigiEcoMode:ON";
|
||||||
|
} else {
|
||||||
|
answer = "DigiEcoMode:OFF/Only Serial Output";
|
||||||
|
}
|
||||||
|
} else if (queryQuestion.indexOf("?TX=ON") == 0) {
|
||||||
|
if (Config.loramodule.txActive) {
|
||||||
|
answer = "TX was ON";
|
||||||
|
} else {
|
||||||
|
Config.loramodule.txActive = true;
|
||||||
|
answer = "TX=ON";
|
||||||
|
}
|
||||||
|
} else if (queryQuestion.indexOf("?TX=OFF") == 0) {
|
||||||
|
if (!Config.loramodule.txActive) {
|
||||||
|
answer = "TX was OFF";
|
||||||
|
} else {
|
||||||
|
Config.loramodule.txActive = false;
|
||||||
|
answer = "TX=OFF";
|
||||||
|
}
|
||||||
|
} else if (queryQuestion.indexOf("?TX=?") == 0) {
|
||||||
|
answer = (Config.loramodule.txActive) ? "TX=ON" : "TX=OFF";
|
||||||
|
} else if (queryQuestion.indexOf("?COMMIT") == 0) { // saving for next reboot
|
||||||
|
answer = "New Config Saved";
|
||||||
|
Config.writeFile();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (answer == "") return "";
|
||||||
|
|
||||||
|
String queryAnswer = Config.callsign;
|
||||||
|
queryAnswer += ">APLRG1";
|
||||||
|
if (queryFromAPRSIS) {
|
||||||
|
queryAnswer += ",TCPIP,qAC";
|
||||||
|
} else {
|
||||||
|
if (!thirdParty) queryAnswer += ",RFONLY";
|
||||||
|
if (Config.beacon.path != "") {
|
||||||
|
queryAnswer += ",";
|
||||||
|
queryAnswer += Config.beacon.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryAnswer += "::";
|
||||||
|
|
||||||
String processedStation = station;
|
String processedStation = station;
|
||||||
for (int i = station.length(); i < 9; i++) {
|
for (int i = station.length(); i < 9; i++) {
|
||||||
processedStation += ' ';
|
processedStation += ' ';
|
||||||
}
|
}
|
||||||
String queryAnswer = Config.callsign;
|
|
||||||
queryAnswer += ">APLRG1,";
|
|
||||||
if (queryOrigin == 1) { // from APRS-IS
|
|
||||||
queryAnswer += "TCPIP,qAC::";
|
|
||||||
} else { // else == 0 , from LoRa
|
|
||||||
if (Config.beacon.path == "") {
|
|
||||||
queryAnswer += "RFONLY::";
|
|
||||||
} else {
|
|
||||||
queryAnswer += "RFONLY,";
|
|
||||||
queryAnswer += Config.beacon.path;
|
|
||||||
queryAnswer += "::";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queryAnswer += processedStation;
|
queryAnswer += processedStation;
|
||||||
queryAnswer += ":";
|
queryAnswer += ":";
|
||||||
queryAnswer += answer;
|
queryAnswer += answer;
|
||||||
|
|
||||||
|
queryAnswer += " *";
|
||||||
|
queryAnswer += char(random(97, 123));
|
||||||
|
queryAnswer += char(random(97, 123));
|
||||||
|
queryAnswer += "*";
|
||||||
return queryAnswer;
|
return queryAnswer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef QUERY_UTILS_H_
|
|
||||||
#define QUERY_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace QUERY_Utils {
|
|
||||||
|
|
||||||
String process(const String& query, const String& station, const uint8_t queryOrigin);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
85
src/sleep_utils.cpp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/* 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 "configuration.h"
|
||||||
|
#include "board_pinout.h"
|
||||||
|
#include "sleep_utils.h"
|
||||||
|
#include "digi_utils.h"
|
||||||
|
#include "lora_utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern Configuration Config;
|
||||||
|
extern bool shouldSleepStop;
|
||||||
|
extern uint32_t lastBeaconTx;
|
||||||
|
|
||||||
|
bool wakeUpFlag = false;
|
||||||
|
|
||||||
|
|
||||||
|
namespace SLEEP_Utils {
|
||||||
|
|
||||||
|
void wakeUpLoRaPacketReceived() {
|
||||||
|
wakeUpFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkWakeUpFlag() {
|
||||||
|
if (wakeUpFlag) {
|
||||||
|
String packet = LoRa_Utils::receivePacketFromSleep();
|
||||||
|
if (packet != "") {
|
||||||
|
DIGI_Utils::processLoRaPacket(packet);
|
||||||
|
}
|
||||||
|
wakeUpFlag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
#ifndef HAS_A7670
|
||||||
|
if (Config.digi.ecoMode == 1) {
|
||||||
|
pinMode(RADIO_WAKEUP_PIN, INPUT);
|
||||||
|
attachInterrupt(digitalPinToInterrupt(RADIO_WAKEUP_PIN), wakeUpLoRaPacketReceived, RISING);
|
||||||
|
#if defined(TTGO_LORA32_V2_1) || defined(TTGO_LORA32_V2_1_915) || defined(TTGO_LORA32_T3S3_V1_2) || defined(TTGO_T_BEAM_V1_0) || defined(TTGO_T_BEAM_V1_0_915) || defined(TTGO_T_BEAM_V1_0_SX1268) || defined(TTGO_T_BEAM_V1_2) || defined(TTGO_T_BEAM_V1_2_915) || defined(TTGO_T_BEAM_V1_2_SX1262) || defined(TTGO_T_DECK_PLUS) || defined(TTGO_T_DECK_GPS) || defined(TTGO_T_Beam_S3_SUPREME_V3) || defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WP_V1) || defined(HELTEC_WS) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY) || defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V2) || defined(XIAO_ESP32S3_LORA) || defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) || defined(TROY_LoRa_APRS) || defined(OE5HWN_MeshCom) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915) || defined(ESP32_DIY_1W_LoRa_LLCC68) || defined(ESP32_DIY_1W_LoRa_Mesh_V1_2) || defined(WEMOS_S2_MINI_DIY_LoRa) || defined(WEMOS_D1_R32_RA02) || defined(WEMOS_LOLIN32_OLED_DIY_LoRa)
|
||||||
|
esp_sleep_enable_ext1_wakeup(GPIO_WAKEUP_PIN, ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_HTCT62) || defined(ESP32C3_DIY_1W_LoRa) || defined(ESP32C3_DIY_1W_LoRa_915) || defined(ESP32_C3_OctopusLab_LoRa)
|
||||||
|
esp_deep_sleep_enable_gpio_wakeup(1ULL << GPIO_WAKEUP_PIN, ESP_GPIO_WAKEUP_GPIO_HIGH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getSecondsToSleep() {
|
||||||
|
uint32_t elapsedTime = (millis() - lastBeaconTx) / 1000; // in secs
|
||||||
|
uint32_t intervalTime = Config.beacon.interval * 60; // in secs
|
||||||
|
return (elapsedTime < intervalTime) ? (intervalTime - elapsedTime) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSleeping() {
|
||||||
|
if (!shouldSleepStop) {
|
||||||
|
uint32_t timeToSleep = getSecondsToSleep();
|
||||||
|
esp_sleep_enable_timer_wakeup(timeToSleep * 1000000); // 1 min = 60sec
|
||||||
|
Serial.print("(Sleeping : "); Serial.print(timeToSleep); Serial.println("seconds)");
|
||||||
|
delay(100);
|
||||||
|
LoRa_Utils::wakeRadio();
|
||||||
|
esp_light_sleep_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkSerial() {
|
||||||
|
if (Config.digi.ecoMode == 1) Serial.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,34 +1,141 @@
|
|||||||
|
/* 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 "station_utils.h"
|
#include "station_utils.h"
|
||||||
|
#include "battery_utils.h"
|
||||||
#include "aprs_is_utils.h"
|
#include "aprs_is_utils.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "lora_utils.h"
|
#include "lora_utils.h"
|
||||||
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
extern uint32_t lastRxTime;
|
extern uint32_t lastRxTime;
|
||||||
extern String fourthLine;
|
extern String fourthLine;
|
||||||
|
extern bool shouldSleepLowVoltage;
|
||||||
|
|
||||||
uint32_t lastTxTime = millis();
|
uint32_t lastTxTime = millis();
|
||||||
std::vector<String> lastHeardStation;
|
std::vector<LastHeardStation> lastHeardStations;
|
||||||
std::vector<String> outputPacketBuffer;
|
std::vector<String> blacklist;
|
||||||
std::vector<String> packet25SegBuffer;
|
std::vector<String> managers;
|
||||||
|
std::vector<LastHeardStation> lastHeardObjects;
|
||||||
|
|
||||||
|
struct OutputPacketBuffer {
|
||||||
|
String packet;
|
||||||
|
bool isBeacon;
|
||||||
|
};
|
||||||
|
std::vector<OutputPacketBuffer> outputPacketBuffer;
|
||||||
|
|
||||||
|
struct Packet25SegBuffer {
|
||||||
|
uint32_t receivedTime;
|
||||||
|
String station;
|
||||||
|
String payload;
|
||||||
|
};
|
||||||
|
std::vector<Packet25SegBuffer> packet25SegBuffer;
|
||||||
|
|
||||||
|
|
||||||
|
bool saveNewDigiEcoModeConfig = false;
|
||||||
|
bool packetIsBeacon = false;
|
||||||
|
|
||||||
|
|
||||||
namespace STATION_Utils {
|
namespace STATION_Utils {
|
||||||
|
|
||||||
void deleteNotHeard() {
|
std::vector<String> loadCallsignList(const String& list) {
|
||||||
std::vector<String> lastHeardStation_temp;
|
std::vector<String> loadedList;
|
||||||
for (int i = 0; i < lastHeardStation.size(); i++) {
|
|
||||||
String deltaTimeString = lastHeardStation[i].substring(lastHeardStation[i].indexOf(",") + 1);
|
String callsigns = list;
|
||||||
uint32_t deltaTime = deltaTimeString.toInt();
|
callsigns.trim();
|
||||||
if ((millis() - deltaTime) < Config.rememberStationTime * 60 * 1000) {
|
|
||||||
lastHeardStation_temp.push_back(lastHeardStation[i]);
|
while (callsigns.length() > 0) { // != ""
|
||||||
|
int spaceIndex = callsigns.indexOf(" ");
|
||||||
|
if (spaceIndex == -1) { // No more spaces, add the last part
|
||||||
|
loadedList.push_back(callsigns);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadedList.push_back(callsigns.substring(0, spaceIndex));
|
||||||
|
callsigns = callsigns.substring(spaceIndex + 1);
|
||||||
|
callsigns.trim(); // Trim in case of multiple spaces
|
||||||
|
}
|
||||||
|
return loadedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadBlacklistAndManagers() {
|
||||||
|
blacklist = loadCallsignList(Config.blacklist);
|
||||||
|
managers = loadCallsignList(Config.remoteManagement.managers);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkCallsignList(const std::vector<String>& list, const String& callsign) {
|
||||||
|
for (size_t i = 0; i < list.size(); i++) {
|
||||||
|
int wildcardIndex = list[i].indexOf("*");
|
||||||
|
if (wildcardIndex >= 0) {
|
||||||
|
String wildcard = list[i].substring(0, wildcardIndex);
|
||||||
|
if (callsign.startsWith(wildcard)) return true;
|
||||||
|
} else {
|
||||||
|
if (list[i] == callsign) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastHeardStation.clear();
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBlacklisted(const String& callsign) {
|
||||||
|
return checkCallsignList(blacklist, callsign);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isManager(const String& callsign) {
|
||||||
|
return checkCallsignList(managers, callsign);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanObjectsHeard() {
|
||||||
|
for (auto it = lastHeardObjects.begin(); it != lastHeardObjects.end(); ) {
|
||||||
|
if (millis() - it->lastHeardTime >= 9.75 * 60 * 1000) { // 9.75 = 9min 45secs
|
||||||
|
it = lastHeardObjects.erase(it); // erase() returns the next valid iterator
|
||||||
|
} else {
|
||||||
|
++it; // Only increment if not erasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkObjectTime(const String& packet) {
|
||||||
|
cleanObjectsHeard();
|
||||||
|
|
||||||
|
int objectIDIndex = packet.indexOf(":;");
|
||||||
|
String object = packet.substring(objectIDIndex + 2, objectIDIndex + 11);
|
||||||
|
object.trim();
|
||||||
|
|
||||||
|
for (int i = 0; i < lastHeardObjects.size(); i++) { // Check if i should Tx object
|
||||||
|
if (lastHeardObjects[i].station == object) return false;
|
||||||
|
}
|
||||||
|
lastHeardObjects.emplace_back(LastHeardStation{millis(), object}); // Add new object and Tx
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteNotHeard() {
|
||||||
|
std::vector<LastHeardStation> lastHeardStation_temp;
|
||||||
|
for (int i = 0; i < lastHeardStations.size(); i++) {
|
||||||
|
if (millis() - lastHeardStations[i].lastHeardTime < Config.rememberStationTime * 60 * 1000) {
|
||||||
|
lastHeardStation_temp.push_back(lastHeardStations[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastHeardStations.clear();
|
||||||
for (int j = 0; j < lastHeardStation_temp.size(); j++) {
|
for (int j = 0; j < lastHeardStation_temp.size(); j++) {
|
||||||
lastHeardStation.push_back(lastHeardStation_temp[j]);
|
lastHeardStations.push_back(lastHeardStation_temp[j]);
|
||||||
}
|
}
|
||||||
lastHeardStation_temp.clear();
|
lastHeardStation_temp.clear();
|
||||||
}
|
}
|
||||||
@@ -36,83 +143,97 @@ namespace STATION_Utils {
|
|||||||
void updateLastHeard(const String& station) {
|
void updateLastHeard(const String& station) {
|
||||||
deleteNotHeard();
|
deleteNotHeard();
|
||||||
bool stationHeard = false;
|
bool stationHeard = false;
|
||||||
for (int i = 0; i < lastHeardStation.size(); i++) {
|
for (int i = 0; i < lastHeardStations.size(); i++) {
|
||||||
if (lastHeardStation[i].substring(0, lastHeardStation[i].indexOf(",")) == station) {
|
if (lastHeardStations[i].station == station) {
|
||||||
lastHeardStation[i] = station + "," + String(millis());
|
lastHeardStations[i].lastHeardTime = millis();
|
||||||
stationHeard = true;
|
stationHeard = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!stationHeard) {
|
if (!stationHeard) lastHeardStations.emplace_back(LastHeardStation{millis(), station});
|
||||||
lastHeardStation.push_back(station + "," + String(millis()));
|
Utils::showActiveStations();
|
||||||
}
|
|
||||||
|
|
||||||
fourthLine = "Stations (";
|
|
||||||
fourthLine += String(Config.rememberStationTime);
|
|
||||||
fourthLine += "min) = ";
|
|
||||||
if (lastHeardStation.size() < 10) {
|
|
||||||
fourthLine += " ";
|
|
||||||
}
|
|
||||||
fourthLine += String(lastHeardStation.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wasHeard(const String& station) {
|
bool wasHeard(const String& station) {
|
||||||
deleteNotHeard();
|
deleteNotHeard();
|
||||||
for (int i = 0; i < lastHeardStation.size(); i++) {
|
for (int i = 0; i < lastHeardStations.size(); i++) {
|
||||||
if (lastHeardStation[i].substring(0, lastHeardStation[i].indexOf(",")) == station) {
|
if (lastHeardStations[i].station == station) {
|
||||||
Utils::println(" ---> Listened Station");
|
Utils::println(" ---> Listened Station");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Utils::println(" ---> Station not Heard for last 30 min (Not Tx)\n");
|
Utils::println(" ---> Station not Heard in " + String(Config.rememberStationTime) + " min: No Tx");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clean25SegBuffer() {
|
void clean25SegBuffer() {
|
||||||
if (!packet25SegBuffer.empty()) {
|
if (!packet25SegBuffer.empty() && (millis() - packet25SegBuffer[0].receivedTime) > 25 * 1000) packet25SegBuffer.erase(packet25SegBuffer.begin());
|
||||||
String deltaTimeString = packet25SegBuffer[0].substring(0, packet25SegBuffer[0].indexOf(","));
|
|
||||||
uint32_t deltaTime = deltaTimeString.toInt();
|
|
||||||
if ((millis() - deltaTime) > 25 * 1000) {
|
|
||||||
packet25SegBuffer.erase(packet25SegBuffer.begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check25SegBuffer(const String& station, const String& textMessage) {
|
bool check25SegBuffer(const String& station, const String& textMessage) {
|
||||||
if (!packet25SegBuffer.empty()) {
|
if (!packet25SegBuffer.empty()) {
|
||||||
bool shouldBeIgnored = false;
|
|
||||||
for (int i = 0; i < packet25SegBuffer.size(); i++) {
|
for (int i = 0; i < packet25SegBuffer.size(); i++) {
|
||||||
const String& temp = packet25SegBuffer[i].substring(packet25SegBuffer[i].indexOf(",") + 1);
|
if (packet25SegBuffer[i].station == station && packet25SegBuffer[i].payload == textMessage) return false;
|
||||||
const String& bufferStation = temp.substring(0, temp.indexOf(","));
|
|
||||||
const String& bufferMessage = temp.substring(temp.indexOf(",") + 1);
|
|
||||||
if (bufferStation == station && bufferMessage == textMessage) {
|
|
||||||
shouldBeIgnored = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (shouldBeIgnored) {
|
}
|
||||||
return false;
|
packet25SegBuffer.emplace_back(Packet25SegBuffer{millis(), station, textMessage});
|
||||||
} else {
|
return true;
|
||||||
packet25SegBuffer.push_back(String(millis()) + "," + station + "," + textMessage);
|
}
|
||||||
return true;
|
|
||||||
}
|
void processOutputPacketBufferUltraEcoMode() {
|
||||||
} else {
|
size_t currentIndex = 0;
|
||||||
packet25SegBuffer.push_back(String(millis()) + "," + station + "," + textMessage);
|
while (currentIndex < outputPacketBuffer.size()) { // this sends all packets from output buffer
|
||||||
return true;
|
delay(3000); // and cleans buffer to avoid sending packets with time offset
|
||||||
}
|
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = true;
|
||||||
|
LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex].packet); // next time it wakes up
|
||||||
|
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = false;
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
outputPacketBuffer.clear();
|
||||||
|
//
|
||||||
|
if (saveNewDigiEcoModeConfig) {
|
||||||
|
Config.writeFile();
|
||||||
|
delay(1000);
|
||||||
|
displayToggle(false);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
void processOutputPacketBuffer() {
|
void processOutputPacketBuffer() {
|
||||||
int timeToWait = 3 * 1000; // 3 segs between packet Tx and also Rx ???
|
int timeToWait = 3 * 1000; // 3 segs between packet Tx and also Rx ???
|
||||||
uint32_t lastRx = millis() - lastRxTime;
|
uint32_t lastRx = millis() - lastRxTime;
|
||||||
uint32_t lastTx = millis() - lastTxTime;
|
uint32_t lastTx = millis() - lastTxTime;
|
||||||
if (outputPacketBuffer.size() > 0 && lastTx > timeToWait && lastRx > timeToWait) {
|
if (outputPacketBuffer.size() > 0 && lastTx > timeToWait && lastRx > timeToWait) {
|
||||||
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) {
|
||||||
|
while (outputPacketBuffer.size() > 0) {
|
||||||
|
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = true;
|
||||||
|
LoRa_Utils::sendNewPacket(outputPacketBuffer[0].packet);
|
||||||
|
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = false;
|
||||||
|
outputPacketBuffer.erase(outputPacketBuffer.begin());
|
||||||
|
delay(4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (saveNewDigiEcoModeConfig) {
|
||||||
|
Config.writeFile();
|
||||||
|
delay(1000);
|
||||||
|
displayToggle(false);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||