Compare commits
202 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d65b2c93f | |||
| 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 |
@@ -15,10 +15,14 @@ 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
|
||||||
@@ -49,6 +53,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
|
||||||
@@ -73,6 +83,14 @@ jobs:
|
|||||||
chip: esp32c3
|
chip: esp32c3
|
||||||
- name: ESP32_C3_OctopusLab_LoRa
|
- name: ESP32_C3_OctopusLab_LoRa
|
||||||
chip: esp32c3
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -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>.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
@@ -7,6 +7,8 @@ This firmware is for using ESP32 based boards with LoRa Modules and GPS to live
|
|||||||
__(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__
|
__(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker Firmware</a>))__
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
____________________________________________________
|
||||||
|
|
||||||
## You can support this project to continue to grow:
|
## 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/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)
|
||||||
@@ -14,7 +16,8 @@ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a
|
|||||||
<br />
|
<br />
|
||||||
|
|
||||||
# WEB FLASHER/INSTALLER is <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">here</a>
|
# WEB FLASHER/INSTALLER is <a href="https://richonguzman.github.io/lora-igate-web-flasher/installer.html" target="_blank">here</a>
|
||||||
<br />
|
|
||||||
|
____________________________________________________
|
||||||
|
|
||||||
# WIKI
|
# WIKI
|
||||||
|
|
||||||
@@ -29,11 +32,21 @@ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <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).
|
||||||
|
|
||||||
- HELTEC V2, V3 , Wireless Stick, Wireless Stick Lite, HT-CT62, Wireless Tracker.
|
- T-Deck Plus (and also regular T-Deck with/without GPS).
|
||||||
|
|
||||||
|
- HELTEC V2, V3, V3.2, T114, Wireless Stick, Wireless Stick Lite, HT-CT62, Wireless Tracker, Wireless Paper.
|
||||||
|
|
||||||
|
- RAK Wireless 4631 + 19007(19003)
|
||||||
|
|
||||||
|
- Faketec (NRF52840 + Heltec HTRA62(SX1262))
|
||||||
|
|
||||||
|
- QRP Labs LightGateway 1.0 and Plus 1.0.
|
||||||
|
|
||||||
|
- Faketec V3 (NRF52840 + Heltec HTRA62 SX1262)
|
||||||
|
|
||||||
- ESP32 Wroom + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
|
- ESP32 Wroom + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
|
||||||
|
|
||||||
@@ -47,12 +60,32 @@ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a
|
|||||||
|
|
||||||
## Timeline (Versions):
|
## Timeline (Versions):
|
||||||
|
|
||||||
|
- 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 Libraries Update for SDK3
|
||||||
- 2024.09.23 Added Enconded Telemetry for Battery (+ External Voltage) in Station GPS Beacon Packet.
|
- 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.23 Wemos S2 Mini DIY LoRa added.
|
||||||
- 2024.08.19 HELTEC Wireless Paper working (still missing Epaper code).
|
- 2024.08.19 HELTEC Wireless Paper working (still missing Epaper code).
|
||||||
- 2024.08.13 Web Authentication for WebUI. Thanks Mitja S57PNX.
|
- 2024.08.13 Web Authentication for WebUI. Thanks Mitja S57PNX.
|
||||||
- 2024.08.05 WIDE2-n added to WIDE1-n in Digirepeater Modes.
|
- 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.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.26 Personal Note information on WebUI for the Station. Thanks Tilen S54B.
|
||||||
- 2024.06.24 Callsign Validation fix. Thanks Helge SA7SKY.
|
- 2024.06.24 Callsign Validation fix. Thanks Helge SA7SKY.
|
||||||
@@ -63,7 +96,7 @@ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a
|
|||||||
- 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).
|
||||||
@@ -75,12 +108,12 @@ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a
|
|||||||
- 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.
|
||||||
@@ -89,18 +122,18 @@ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a
|
|||||||
- 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.
|
||||||
@@ -111,4 +144,4 @@ __(This iGate Firmware works with all LoRa Tracker Firmwares (specially this <a
|
|||||||
|
|
||||||
__________________________________________
|
__________________________________________
|
||||||
|
|
||||||
# Hope You Enjoy this, 73 !! CA2RXU , Valparaiso, Chile
|
# Hope You Enjoy this, 73! CA2RXU, Valparaiso, Chile
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
[common]
|
||||||
|
build_flags =
|
||||||
|
-Werror -Wall
|
||||||
|
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
||||||
|
-DRADIOLIB_EXCLUDE_CC1101=1
|
||||||
|
-DRADIOLIB_EXCLUDE_NRF24=1
|
||||||
|
-DRADIOLIB_EXCLUDE_RF69=1
|
||||||
|
-DRADIOLIB_EXCLUDE_SX1231=1
|
||||||
|
-DRADIOLIB_EXCLUDE_SX1233=1
|
||||||
|
-DRADIOLIB_EXCLUDE_SI443X=1
|
||||||
|
-DRADIOLIB_EXCLUDE_RFM2X=1
|
||||||
|
-DRADIOLIB_EXCLUDE_AFSK=1
|
||||||
|
-DRADIOLIB_EXCLUDE_BELL=1
|
||||||
|
-DRADIOLIB_EXCLUDE_HELLSCHREIBER=1
|
||||||
|
-DRADIOLIB_EXCLUDE_MORSE=1
|
||||||
|
-DRADIOLIB_EXCLUDE_RTTY=1
|
||||||
|
-DRADIOLIB_EXCLUDE_SSTV=1
|
||||||
|
-DRADIOLIB_EXCLUDE_AX25=1
|
||||||
|
-DRADIOLIB_EXCLUDE_DIRECT_RECEIVE=1
|
||||||
|
-DRADIOLIB_EXCLUDE_BELL=1
|
||||||
|
-DRADIOLIB_EXCLUDE_PAGER=1
|
||||||
|
-DRADIOLIB_EXCLUDE_FSK4=1
|
||||||
|
-DRADIOLIB_EXCLUDE_APRS=1
|
||||||
|
-DRADIOLIB_EXCLUDE_LORAWAN=1
|
||||||
|
-I variants/${PIOENV}
|
||||||
|
lib_deps =
|
||||||
|
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
|
||||||
|
arduino-libraries/NTPClient @ 3.2.1
|
||||||
|
ayushsharma82/ElegantOTA @ 3.1.5
|
||||||
|
bblanchon/ArduinoJson @ 6.21.3
|
||||||
|
jgromes/RadioLib @ 7.1.0
|
||||||
|
mathieucarbou/AsyncTCP @ 3.2.5
|
||||||
|
mathieucarbou/ESPAsyncWebServer @ 3.2.3
|
||||||
|
mikalhart/TinyGPSPlus @ 1.0.3
|
||||||
|
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,7 +16,9 @@
|
|||||||
"symbol": "a",
|
"symbol": "a",
|
||||||
"path": "WIDE1-1",
|
"path": "WIDE1-1",
|
||||||
"sendViaAPRSIS": false,
|
"sendViaAPRSIS": false,
|
||||||
"sendViaRF": false
|
"sendViaRF": false,
|
||||||
|
"statusActive": false,
|
||||||
|
"statusPacket": ""
|
||||||
},
|
},
|
||||||
"aprs_is": {
|
"aprs_is": {
|
||||||
"active": false,
|
"active": false,
|
||||||
@@ -28,7 +30,8 @@
|
|||||||
"objectsToRF": false
|
"objectsToRF": false
|
||||||
},
|
},
|
||||||
"digi": {
|
"digi": {
|
||||||
"mode": 0
|
"mode": 0,
|
||||||
|
"ecoMode": 0
|
||||||
},
|
},
|
||||||
"lora": {
|
"lora": {
|
||||||
"txFreq": 433775000,
|
"txFreq": 433775000,
|
||||||
@@ -48,24 +51,25 @@
|
|||||||
"battery": {
|
"battery": {
|
||||||
"sendInternalVoltage": false,
|
"sendInternalVoltage": false,
|
||||||
"monitorInternalVoltage": false,
|
"monitorInternalVoltage": false,
|
||||||
"internalSleepVoltage": 3.0,
|
"internalSleepVoltage": 2.9,
|
||||||
"sendExternalVoltage": false,
|
"sendExternalVoltage": false,
|
||||||
"externalVoltagePin": 34,
|
"externalVoltagePin": 34,
|
||||||
"monitorExternalVoltage": false,
|
"monitorExternalVoltage": false,
|
||||||
"externalSleepVoltage": 10.9,
|
"externalSleepVoltage": 10.9,
|
||||||
"voltageDividerR1": 100.0,
|
"voltageDividerR1": 100.0,
|
||||||
"voltageDividerR2": 27.0,
|
"voltageDividerR2": 27.0,
|
||||||
"sendVoltageAsTelemetry": true
|
"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,
|
||||||
@@ -80,13 +84,19 @@
|
|||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": ""
|
"password": ""
|
||||||
},
|
},
|
||||||
|
"ntp": {
|
||||||
|
"gmtCorrection": 0.0
|
||||||
|
},
|
||||||
|
"remoteManagement": {
|
||||||
|
"managers": "",
|
||||||
|
"rfOnly": true
|
||||||
|
},
|
||||||
"other": {
|
"other": {
|
||||||
"rememberStationTime": 30,
|
"rememberStationTime": 30,
|
||||||
"lowPowerMode": false,
|
|
||||||
"lowVoltageCutOff": 0,
|
|
||||||
"backupDigiMode": false,
|
"backupDigiMode": false,
|
||||||
"rebootMode": false,
|
"rebootMode": false,
|
||||||
"rebootModeTime": 6
|
"rebootModeTime": 6
|
||||||
},
|
},
|
||||||
"personalNote": ""
|
"personalNote": "",
|
||||||
|
"blacklist": ""
|
||||||
}
|
}
|
||||||
@@ -62,11 +62,11 @@
|
|||||||
<div class="container d-none" id="received-packets">
|
<div class="container d-none" id="received-packets">
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h3>Last 20 received packets list</h3>
|
<h3>Last 10 received packets list</h3>
|
||||||
<table class="table mt-4">
|
<table class="table mt-4">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Time*</th>
|
<th scope="col">Time</th>
|
||||||
<th scope="col">Frame</th>
|
<th scope="col">Frame</th>
|
||||||
<th scope="col">RSSI</th>
|
<th scope="col">RSSI</th>
|
||||||
<th scope="col">SNR</th>
|
<th scope="col">SNR</th>
|
||||||
@@ -77,10 +77,10 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<span>List refresh automatically every 15 seconds.</span><br>
|
<span>List refresh automatically every 15 seconds.</span><br>
|
||||||
<small>* - For now time is measured from board boot up.</small>
|
<small>(Local Time is NTP-Time adjusted with your GMT Offset)</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container" id="configuration">
|
<div class="container" id="configuration">
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label for="callsign" class="form-label"
|
<label for="callsign" class="form-label"
|
||||||
>Callsign and SSID</label
|
>Callsign - SSID</label
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
<label
|
<label
|
||||||
for="beacon.path"
|
for="beacon.path"
|
||||||
class="form-label"
|
class="form-label"
|
||||||
>Beacon path</label
|
>Beacon Path</label
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -253,13 +253,42 @@
|
|||||||
name="personalNote"
|
name="personalNote"
|
||||||
id="personalNote"
|
id="personalNote"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="A Couple of words."
|
placeholder="A couple of words"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 mt-3">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="beacon.statusActive"
|
||||||
|
id="beacon.statusActive"
|
||||||
|
class="form-check-input"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="beacon.statusActive"
|
||||||
|
class="form-label"
|
||||||
|
>Send Status at each Boot</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label
|
||||||
|
for="beacon.statusPacket"
|
||||||
|
class="form-label"
|
||||||
|
>Status</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="beacon.statusPacket"
|
||||||
|
id="beacon.statusPacket"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Custom Status"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -316,7 +345,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -458,7 +487,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -492,8 +521,7 @@
|
|||||||
<label
|
<label
|
||||||
for="beacon.sendViaAPRSIS"
|
for="beacon.sendViaAPRSIS"
|
||||||
class="form-label"
|
class="form-label"
|
||||||
>Send our beacon to
|
>Send beacon via APRS-IS
|
||||||
APRS-IS
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -516,12 +544,12 @@
|
|||||||
<button class="btn btn-primary" id="send-beacon">Send beacon now</button>
|
<button class="btn btn-primary" id="send-beacon">Send beacon now</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3">
|
<div class="row mt-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label
|
<label
|
||||||
for="beacon.interval"
|
for="beacon.interval"
|
||||||
class="form-label"
|
class="form-label"
|
||||||
>Interval
|
>Beacon Interval
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
@@ -532,7 +560,7 @@
|
|||||||
class="form-control"
|
class="form-control"
|
||||||
required=""
|
required=""
|
||||||
step="1"
|
step="1"
|
||||||
min="1"
|
min="15"
|
||||||
/>
|
/>
|
||||||
<span class="input-group-text"
|
<span class="input-group-text"
|
||||||
>minutes
|
>minutes
|
||||||
@@ -540,9 +568,80 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 mt-3">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="beacon.gpsActive"
|
||||||
|
id="beacon.gpsActive"
|
||||||
|
class="form-check-input"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="beacon.gpsActive"
|
||||||
|
class="form-label"
|
||||||
|
>Send Real-GPS Beacon <small>(Only for Boards with GPS Modules)</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="beacon.gpsAmbiguity"
|
||||||
|
id="beacon.gpsAmbiguity"
|
||||||
|
class="form-check-input"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="beacon.gpsAmbiguity"
|
||||||
|
class="form-label"
|
||||||
|
>Send Real-GPS Beacon with Ambiguity <small>(~ 1 Km of Random Error)</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
|
<div class="row my-5 d-flex align-items-top">
|
||||||
|
<div class="col-lg-3 col-sm-12">
|
||||||
|
<h5>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="bi bi-broadcast-pin"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.05 3.05a7 7 0 0 0 0 9.9.5.5 0 0 1-.707.707 8 8 0 0 1 0-11.314.5.5 0 0 1 .707.707m2.122 2.122a4 4 0 0 0 0 5.656.5.5 0 1 1-.708.708 5 5 0 0 1 0-7.072.5.5 0 0 1 .708.708m5.656-.708a.5.5 0 0 1 .708 0 5 5 0 0 1 0 7.072.5.5 0 1 1-.708-.708 4 4 0 0 0 0-5.656.5.5 0 0 1 0-.708m2.122-2.12a.5.5 0 0 1 .707 0 8 8 0 0 1 0 11.313.5.5 0 0 1-.707-.707 7 7 0 0 0 0-9.9.5.5 0 0 1 0-.707zM6 8a2 2 0 1 1 2.5 1.937V15.5a.5.5 0 0 1-1 0V9.937A2 2 0 0 1 6 8"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Black List
|
||||||
|
</h5>
|
||||||
|
<small>Add Callsigns with space between them to Blacklist them (* wild card allowed)</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 mt-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<label
|
||||||
|
for="blacklist"
|
||||||
|
class="form-label"
|
||||||
|
>Blacklist</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="blacklist"
|
||||||
|
id="blacklist"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Station Callsign"
|
||||||
|
oninput="this.value = this.value.toUpperCase();"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -576,7 +675,9 @@
|
|||||||
name="digi.mode"
|
name="digi.mode"
|
||||||
id="digi.mode"
|
id="digi.mode"
|
||||||
>
|
>
|
||||||
<option value="0">off</option>
|
<option value="0">
|
||||||
|
OFF
|
||||||
|
</option>
|
||||||
<option value="2">
|
<option value="2">
|
||||||
WIDE1 (fill-in) Digi
|
WIDE1 (fill-in) Digi
|
||||||
</option>
|
</option>
|
||||||
@@ -585,10 +686,35 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 mt-3">
|
||||||
|
<label
|
||||||
|
for="digi.ecoMode"
|
||||||
|
class="form-label"
|
||||||
|
>Eco Mode
|
||||||
|
<small
|
||||||
|
>(Requires Digipeating enabled and APRS-IS connection disabled).</small
|
||||||
|
></label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
class="form-select form-select"
|
||||||
|
name="digi.ecoMode"
|
||||||
|
id="digi.ecoMode"
|
||||||
|
>
|
||||||
|
<option value="0">
|
||||||
|
OFF (Normal Mode - WiFiAP enabled)
|
||||||
|
</option>
|
||||||
|
<option value="1">
|
||||||
|
Ultra Eco Mode (Sleep till Packet Rx (WiFiAP/WebUI & Display disabled))
|
||||||
|
</option>
|
||||||
|
<option value="2">
|
||||||
|
OFF (Normal Mode - WiFiAP disabled)
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -791,7 +917,6 @@
|
|||||||
id="lora.codingRate4"
|
id="lora.codingRate4"
|
||||||
required=""
|
required=""
|
||||||
>
|
>
|
||||||
<option value="4">4</option>
|
|
||||||
<option value="5">5</option>
|
<option value="5">5</option>
|
||||||
<option value="6">6</option>
|
<option value="6">6</option>
|
||||||
<option value="7">7</option>
|
<option value="7">7</option>
|
||||||
@@ -824,7 +949,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -925,7 +1050,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1012,7 +1137,7 @@
|
|||||||
placeholder="3.0"
|
placeholder="3.0"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
min="3.0"
|
min="2.9"
|
||||||
max="3.7"
|
max="3.7"
|
||||||
/>
|
/>
|
||||||
<span class="input-group-text"
|
<span class="input-group-text"
|
||||||
@@ -1118,7 +1243,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1135,7 +1260,7 @@
|
|||||||
d="M3.05 3.05a7 7 0 0 0 0 9.9.5.5 0 0 1-.707.707 8 8 0 0 1 0-11.314.5.5 0 0 1 .707.707m2.122 2.122a4 4 0 0 0 0 5.656.5.5 0 1 1-.708.708 5 5 0 0 1 0-7.072.5.5 0 0 1 .708.708m5.656-.708a.5.5 0 0 1 .708 0 5 5 0 0 1 0 7.072.5.5 0 1 1-.708-.708 4 4 0 0 0 0-5.656.5.5 0 0 1 0-.708m2.122-2.12a.5.5 0 0 1 .707 0 8 8 0 0 1 0 11.313.5.5 0 0 1-.707-.707 7 7 0 0 0 0-9.9.5.5 0 0 1 0-.707zM6 8a2 2 0 1 1 2.5 1.937V15.5a.5.5 0 0 1-1 0V9.937A2 2 0 0 1 6 8"
|
d="M3.05 3.05a7 7 0 0 0 0 9.9.5.5 0 0 1-.707.707 8 8 0 0 1 0-11.314.5.5 0 0 1 .707.707m2.122 2.122a4 4 0 0 0 0 5.656.5.5 0 1 1-.708.708 5 5 0 0 1 0-7.072.5.5 0 0 1 .708.708m5.656-.708a.5.5 0 0 1 .708 0 5 5 0 0 1 0 7.072.5.5 0 1 1-.708-.708 4 4 0 0 0 0-5.656.5.5 0 0 1 0-.708m2.122-2.12a.5.5 0 0 1 .707 0 8 8 0 0 1 0 11.313.5.5 0 0 1-.707-.707 7 7 0 0 0 0-9.9.5.5 0 0 1 0-.707zM6 8a2 2 0 1 1 2.5 1.937V15.5a.5.5 0 0 1-1 0V9.937A2 2 0 0 1 6 8"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Telemetry
|
WX Telemetry
|
||||||
</h5>
|
</h5>
|
||||||
<small>Define Wx telemetry</small>
|
<small>Define Wx telemetry</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -1145,16 +1270,16 @@
|
|||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="bme.active"
|
name="wxsensor.active"
|
||||||
id="bme.active"
|
id="wxsensor.active"
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="bme.active"
|
for="wxsensor.active"
|
||||||
class="form-label"
|
class="form-label"
|
||||||
><b>Activate Wx Telemetry</b>
|
>Activate Wx Telemetry
|
||||||
<small
|
<small
|
||||||
>Requires a BME/BMP280, BME680 or Si7021 sensor installed</small
|
>(Requires a BME/BMP280, BME680 or Si7021 sensor).</small
|
||||||
></label
|
></label
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -1162,14 +1287,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<label for="bme.heightCorrection" class="form-label"
|
<label for="wxsensor.heightCorrection" class="form-label"
|
||||||
>Height Correction (Above Sea-Level)</label
|
>Height Correction (Above Sea-Level)</label
|
||||||
>
|
>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="bme.heightCorrection"
|
name="wxsensor.heightCorrection"
|
||||||
id="bme.heightCorrection"
|
id="wxsensor.heightCorrection"
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
step="1"
|
step="1"
|
||||||
@@ -1181,14 +1306,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<label for="bme.temperatureCorrection" class="form-label"
|
<label for="wxsensor.temperatureCorrection" class="form-label"
|
||||||
>Temperature Correction</label
|
>Temperature Correction</label
|
||||||
>
|
>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="bme.temperatureCorrection"
|
name="wxsensor.temperatureCorrection"
|
||||||
id="bme.temperatureCorrection"
|
id="wxsensor.temperatureCorrection"
|
||||||
placeholder="0.0"
|
placeholder="0.0"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
@@ -1203,7 +1328,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1252,7 +1377,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
name="syslog.server"
|
name="syslog.server"
|
||||||
id="syslog.server"
|
id="syslog.server"
|
||||||
placeholder="192.168.0.100"
|
placeholder="lora.link9.net"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
@@ -1267,15 +1392,30 @@
|
|||||||
type="text"
|
type="text"
|
||||||
name="syslog.port"
|
name="syslog.port"
|
||||||
id="syslog.port"
|
id="syslog.port"
|
||||||
placeholder="514"
|
placeholder="1514"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="syslog.logBeaconOverTCPIP"
|
||||||
|
id="syslog.logBeaconOverTCPIP"
|
||||||
|
class="form-check-input"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="syslog.logBeaconOverTCPIP"
|
||||||
|
class="form-label"
|
||||||
|
>Log Beacon over TPCIP</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1360,7 +1500,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1427,7 +1567,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1453,10 +1593,10 @@
|
|||||||
d="M8.5 5.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0"
|
d="M8.5 5.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Auto AP
|
WiFi Auto AP
|
||||||
</h5>
|
</h5>
|
||||||
<small
|
<small
|
||||||
>Add your password and power off timeout to
|
>Add your password and WiFi AP timeout to
|
||||||
auto access point. Auto AP will start if
|
auto access point. Auto AP will start if
|
||||||
there is no WiFi connection. Timeout will
|
there is no WiFi connection. Timeout will
|
||||||
count from startup or last client
|
count from startup or last client
|
||||||
@@ -1481,29 +1621,29 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-12 mt-3">
|
<div class="col-12 mt-3">
|
||||||
<label
|
<label
|
||||||
for="wifi.autoAP.powerOff"
|
for="wifi.autoAP.timeout"
|
||||||
class="form-label"
|
class="form-label"
|
||||||
>Power off timeout
|
>WiFi AP timeout to search again for available WiFi's
|
||||||
<small>(in minutes)</small></label
|
<small>(in minutes)</small></label
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="wifi.autoAP.powerOff"
|
name="wifi.autoAP.timeout"
|
||||||
id="wifi.autoAP.powerOff"
|
id="wifi.autoAP.timeout"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="15"
|
placeholder="10"
|
||||||
required=""
|
required=""
|
||||||
step="1"
|
step="1"
|
||||||
min="0"
|
min="0"
|
||||||
/>
|
/>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
Set to <strong>0</strong> if you don't
|
Set to <strong>0</strong> if you don't
|
||||||
want this option.
|
want WiFi AP to stop.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1553,7 +1693,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1622,7 +1762,115 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
|
|
||||||
|
<div class="row my-5 d-flex align-items-top">
|
||||||
|
<div class="col-lg-3 col-sm-12">
|
||||||
|
<h5>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="bi bi-cloud-upload-fill"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.5 14.5V11h1v3.5a.5.5 0 0 1-1 0"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Remote Management
|
||||||
|
</h5>
|
||||||
|
<small
|
||||||
|
>Manage Station via APRS Messages. Leave empty to disable!
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-9 col-sm-12">
|
||||||
|
<div class="col-12">
|
||||||
|
<label
|
||||||
|
for="remoteManagement.managers"
|
||||||
|
class="form-label"
|
||||||
|
>Callsign-SSID of Managers, space separated, trailing * wildcard allowed (ex: AB1CDE-9, AB1CDE*)</label
|
||||||
|
>
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="remoteManagement.managers"
|
||||||
|
id="remoteManagement.managers"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="remoteManagement.rfOnly"
|
||||||
|
id="remoteManagement.rfOnly"
|
||||||
|
class="form-check-input"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="remoteManagement.rfOnly"
|
||||||
|
class="form-label"
|
||||||
|
>Managers commands only via RF (not APRS-IS)</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="row my-5 d-flex align-items-top">
|
||||||
|
<div class="col-lg-3 col-sm-12">
|
||||||
|
<h5>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="bi bi-cloud-upload-fill"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.5 14.5V11h1v3.5a.5.5 0 0 1-1 0"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
NTP
|
||||||
|
</h5>
|
||||||
|
<small
|
||||||
|
>Set your GMT Time Zone.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-9 col-sm-12">
|
||||||
|
<div class="col-12">
|
||||||
|
<label
|
||||||
|
for="ntp.gmtCorrection"
|
||||||
|
class="form-label"
|
||||||
|
>GMT Time Correction for accurate Local Time</label
|
||||||
|
>
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="ntp.gmtCorrection"
|
||||||
|
id="ntp.gmtCorrection"
|
||||||
|
placeholder="0"
|
||||||
|
class="form-control"
|
||||||
|
step="0.25"
|
||||||
|
min="-23.75"
|
||||||
|
max="23.75"
|
||||||
|
/>
|
||||||
|
<span class="input-group-text"
|
||||||
|
>hours</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
|
||||||
<div class="row my-5 d-flex align-items-top">
|
<div class="row my-5 d-flex align-items-top">
|
||||||
<div class="col-lg-3 col-sm-12">
|
<div class="col-lg-3 col-sm-12">
|
||||||
@@ -1651,7 +1899,7 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
When "only" iGate Mode loses WiFi, it will change into a Digirepeater Mode and after 15 min check if WiFi available and return to "only" iGate Mode.
|
When "only" iGate Mode loses WiFi, it will change into a Digipeater Mode and after 15 min check if WiFi available and return to "only" iGate Mode.
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -1662,50 +1910,10 @@
|
|||||||
<label
|
<label
|
||||||
for="other.backupDigiMode"
|
for="other.backupDigiMode"
|
||||||
class="form-label"
|
class="form-label"
|
||||||
>Backup Digirepeater Mode</label
|
>Backup Digipeater Mode</label
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
|
||||||
<div class="form-check form-switch">
|
|
||||||
<div class="form-text">
|
|
||||||
WiFi disabled. Sleep mode. Best for solar Digi with SX126X.
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
name="other.lowPowerMode"
|
|
||||||
id="other.lowPowerMode"
|
|
||||||
class="form-check-input"
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
for="other.lowPowerMode"
|
|
||||||
class="form-label"
|
|
||||||
>Low power mode (only for HT-CT62)</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 mt-3">
|
|
||||||
<label
|
|
||||||
for="other.lowVoltageCutOff"
|
|
||||||
class="form-label"
|
|
||||||
>Low voltage cut off <small>(Deep sleep below specific voltage)</small></label
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="other.lowVoltageCutOff"
|
|
||||||
id="other.lowVoltageCutOff"
|
|
||||||
placeholder="0"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
class="form-control"
|
|
||||||
/>
|
|
||||||
<span class="input-group-text"
|
|
||||||
>Volts</span
|
|
||||||
>
|
|
||||||
<div class="form-text">
|
|
||||||
MCU will deep sleep when below provided battery voltage to save power. Set to <strong>0</strong> if you don't want this option. <u>Please calibrate your voltage reading first!</u>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1728,7 +1936,7 @@
|
|||||||
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.408 5.89c.681 0 1.138.47 1.187 1.107h1.147v-.11c-.053-1.187-1.024-2-2.343-2-1.604 0-2.518 1.05-2.518 2.751v.747c0 1.7.906 2.73 2.518 2.73 1.314 0 2.285-.792 2.343-1.939v-.114H6.595c-.049.615-.497 1.05-1.187 1.05-.84 0-1.318-.62-1.318-1.727v-.742c0-1.112.488-1.754 1.318-1.754Zm5.404 0c.68 0 1.138.47 1.186 1.107h1.147v-.11c-.053-1.187-1.024-2-2.342-2-1.604 0-2.518 1.05-2.518 2.751v.747c0 1.7.905 2.73 2.518 2.73 1.314 0 2.285-.792 2.342-1.939v-.114h-1.147c-.048.615-.496 1.05-1.186 1.05-.84 0-1.319-.62-1.319-1.727v-.742c0-1.112.488-1.754 1.319-1.754Z"
|
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.408 5.89c.681 0 1.138.47 1.187 1.107h1.147v-.11c-.053-1.187-1.024-2-2.343-2-1.604 0-2.518 1.05-2.518 2.751v.747c0 1.7.906 2.73 2.518 2.73 1.314 0 2.285-.792 2.343-1.939v-.114H6.595c-.049.615-.497 1.05-1.187 1.05-.84 0-1.318-.62-1.318-1.727v-.742c0-1.112.488-1.754 1.318-1.754Zm5.404 0c.68 0 1.138.47 1.186 1.107h1.147v-.11c-.053-1.187-1.024-2-2.342-2-1.604 0-2.518 1.05-2.518 2.751v.747c0 1.7.905 2.73 2.518 2.73 1.314 0 2.285-.792 2.342-1.939v-.114h-1.147c-.048.615-.496 1.05-1.186 1.05-.84 0-1.319-.62-1.319-1.727v-.742c0-1.112.488-1.754 1.319-1.754Z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
2023-24
|
2023-25
|
||||||
<b
|
<b
|
||||||
><a href="https://github.com/richonguzman"
|
><a href="https://github.com/richonguzman"
|
||||||
>CA2RXU</a
|
>CA2RXU</a
|
||||||
|
|||||||
@@ -62,13 +62,15 @@ alwaysOnCheckbox.addEventListener("change", function () {
|
|||||||
// alwaysOnCheckbox.disabled = this.value !== "";
|
// alwaysOnCheckbox.disabled = this.value !== "";
|
||||||
// });
|
// });
|
||||||
|
|
||||||
const logCheckbox = document.querySelector('input[name="syslog.active"]');
|
const logCheckbox = document.querySelector('input[name="syslog.active"]');
|
||||||
const serverField = document.querySelector('input[name="syslog.server"]');
|
const serverField = document.querySelector('input[name="syslog.server"]');
|
||||||
const portField = document.querySelector('input[name="syslog.port"]');
|
const portField = document.querySelector('input[name="syslog.port"]');
|
||||||
|
const logBeaconOverTCPIPField = document.querySelector('input[name="syslog.logBeaconOverTCPIP"]');
|
||||||
|
|
||||||
logCheckbox.addEventListener("change", function () {
|
logCheckbox.addEventListener("change", function () {
|
||||||
serverField.disabled = !this.checked;
|
serverField.disabled = !this.checked;
|
||||||
portField.disabled = !this.checked;
|
portField.disabled = !this.checked;
|
||||||
|
logBeaconOverTCPIPField.disabled = !this.checked
|
||||||
});
|
});
|
||||||
|
|
||||||
function loadSettings(settings) {
|
function loadSettings(settings) {
|
||||||
@@ -134,8 +136,18 @@ function loadSettings(settings) {
|
|||||||
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.statusActive").checked = settings.beacon.statusActive;
|
||||||
|
document.getElementById("beacon.statusPacket").value = settings.beacon.statusPacket;
|
||||||
|
|
||||||
|
document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive;
|
||||||
|
document.getElementById("beacon.gpsAmbiguity").checked = settings.beacon.gpsAmbiguity;
|
||||||
|
|
||||||
|
// 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").checked = settings.digi.ecoMode;
|
||||||
|
|
||||||
// LoRa
|
// LoRa
|
||||||
document.getElementById("lora.txFreq").value = settings.lora.txFreq;
|
document.getElementById("lora.txFreq").value = settings.lora.txFreq;
|
||||||
@@ -168,19 +180,21 @@ function loadSettings(settings) {
|
|||||||
document.getElementById("battery.externalSleepVoltage").value = settings.battery.externalSleepVoltage.toFixed(1);
|
document.getElementById("battery.externalSleepVoltage").value = settings.battery.externalSleepVoltage.toFixed(1);
|
||||||
document.getElementById("battery.sendVoltageAsTelemetry").checked = settings.battery.sendVoltageAsTelemetry;
|
document.getElementById("battery.sendVoltageAsTelemetry").checked = settings.battery.sendVoltageAsTelemetry;
|
||||||
|
|
||||||
// TELEMETRY BME/WX
|
// TELEMETRY WX SENSOR
|
||||||
document.getElementById("bme.active").checked = settings.bme.active;
|
document.getElementById("wxsensor.active").checked = settings.wxsensor.active;
|
||||||
document.getElementById("bme.heightCorrection").value = settings.bme.heightCorrection;
|
document.getElementById("wxsensor.heightCorrection").value = settings.wxsensor.heightCorrection;
|
||||||
document.getElementById("bme.temperatureCorrection").value = settings.bme.temperatureCorrection.toFixed(1);
|
document.getElementById("wxsensor.temperatureCorrection").value = settings.wxsensor.temperatureCorrection.toFixed(1);
|
||||||
|
|
||||||
// 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) {
|
if (settings.syslog.active) {
|
||||||
serverField.disabled = false;
|
serverField.disabled = false;
|
||||||
portField.disabled = false;
|
portField.disabled = false;
|
||||||
|
logBeaconOverTCPIPField.disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TNC
|
// TNC
|
||||||
@@ -196,7 +210,7 @@ function loadSettings(settings) {
|
|||||||
|
|
||||||
// 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;
|
||||||
@@ -207,11 +221,15 @@ function loadSettings(settings) {
|
|||||||
document.getElementById("webadmin.username").value = settings.webadmin.username;
|
document.getElementById("webadmin.username").value = settings.webadmin.username;
|
||||||
document.getElementById("webadmin.password").value = settings.webadmin.password;
|
document.getElementById("webadmin.password").value = settings.webadmin.password;
|
||||||
|
|
||||||
|
// NTP
|
||||||
|
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;
|
// Management over APRS
|
||||||
document.getElementById("other.lowVoltageCutOff").value = settings.other.lowVoltageCutOff || 0
|
document.getElementById("remoteManagement.managers").value = settings.remoteManagement.managers;
|
||||||
|
document.getElementById("remoteManagement.rfOnly").checked = settings.remoteManagement.rfOnly;
|
||||||
|
|
||||||
updateImage();
|
updateImage();
|
||||||
refreshSpeedStandard();
|
refreshSpeedStandard();
|
||||||
@@ -242,9 +260,7 @@ 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 wxsensorCheckbox = document.querySelector("input[name='wxsensor.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;
|
||||||
@@ -506,13 +522,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: 139 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 198 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 203 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 184 KiB |
@@ -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
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
||||||
|
*
|
||||||
|
* This file is part of LoRa APRS iGate.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef 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();
|
||||||
|
|
||||||
|
bool startListenerAPRSISTask(uint32_t stackSize = 8192, UBaseType_t priority = 1);
|
||||||
|
void stopListenerAPRSISTask();
|
||||||
|
void suspendListenerAPRSISTask();
|
||||||
|
void resumeListenerAPRSISTask();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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();
|
||||||
|
|
||||||
|
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType);
|
||||||
|
String generateEncodedTelemetry();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 {
|
||||||
@@ -28,6 +47,10 @@ public:
|
|||||||
String path;
|
String path;
|
||||||
bool sendViaRF;
|
bool sendViaRF;
|
||||||
bool sendViaAPRSIS;
|
bool sendViaAPRSIS;
|
||||||
|
bool gpsActive;
|
||||||
|
bool gpsAmbiguity;
|
||||||
|
bool statusActive;
|
||||||
|
String statusPacket;
|
||||||
};
|
};
|
||||||
|
|
||||||
class APRS_IS {
|
class APRS_IS {
|
||||||
@@ -44,9 +67,9 @@ 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 txFreq;
|
||||||
@@ -80,7 +103,7 @@ public:
|
|||||||
bool sendVoltageAsTelemetry;
|
bool sendVoltageAsTelemetry;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BME {
|
class WXSENSOR {
|
||||||
public:
|
public:
|
||||||
bool active;
|
bool active;
|
||||||
int heightCorrection;
|
int heightCorrection;
|
||||||
@@ -92,6 +115,7 @@ public:
|
|||||||
bool active;
|
bool active;
|
||||||
String server;
|
String server;
|
||||||
int port;
|
int port;
|
||||||
|
bool logBeaconOverTCPIP;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TNC {
|
class TNC {
|
||||||
@@ -114,16 +138,26 @@ public:
|
|||||||
String password;
|
String password;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NTP {
|
||||||
|
public:
|
||||||
|
float gmtCorrection;
|
||||||
|
};
|
||||||
|
|
||||||
|
class REMOTE_MANAGEMENT {
|
||||||
|
public:
|
||||||
|
String managers;
|
||||||
|
bool rfOnly;
|
||||||
|
};
|
||||||
|
|
||||||
class Configuration {
|
class Configuration {
|
||||||
public:
|
public:
|
||||||
String callsign;
|
String callsign;
|
||||||
int rememberStationTime;
|
int rememberStationTime;
|
||||||
bool lowPowerMode;
|
|
||||||
double lowVoltageCutOff;
|
|
||||||
bool backupDigiMode;
|
bool backupDigiMode;
|
||||||
bool rebootMode;
|
bool rebootMode;
|
||||||
int rebootModeTime;
|
int rebootModeTime;
|
||||||
String personalNote;
|
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;
|
||||||
@@ -132,12 +166,14 @@ 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;
|
WEBADMIN webadmin;
|
||||||
|
NTP ntp;
|
||||||
|
REMOTE_MANAGEMENT remoteManagement;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void writeFile();
|
void writeFile();
|
||||||
Configuration();
|
Configuration();
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/* 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 activateMeasurement();
|
||||||
|
void activateGPS();
|
||||||
|
void deactivateGPS();
|
||||||
|
void activateLoRa();
|
||||||
|
void deactivateLoRa();
|
||||||
|
bool begin(TwoWire &port);
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/* 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 Packet25SegBuffer {
|
||||||
|
uint32_t receivedTime;
|
||||||
|
String station;
|
||||||
|
String payload;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
|
void sendToSerial(const String& packet);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 activeStations();
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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 WX_UTILS_H_
|
||||||
|
#define WX_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 WX_Utils {
|
||||||
|
|
||||||
|
void getWxModuleAddres();
|
||||||
|
void setup();
|
||||||
|
String generateTempString(const float sensorTemp);
|
||||||
|
String generateHumString(const float sensorHum);
|
||||||
|
String generatePresString(const float sensorPres);
|
||||||
|
String readDataSensor();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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
|
||||||
@@ -25,456 +29,4 @@ board_build.embed_files =
|
|||||||
data_embed/favicon.png.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.5
|
|
||||||
mathieucarbou/ESPAsyncWebServer @ 3.2.3
|
|
||||||
mathieucarbou/AsyncTCP @ 3.2.5
|
|
||||||
|
|
||||||
[env:ttgo-lora32-v21]
|
|
||||||
board = ttgo-lora32-v21
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DTTGO_T_LORA32_V2_1
|
|
||||||
-DHAS_SX1278
|
|
||||||
-DHAS_ADC_CALIBRATION
|
|
||||||
-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
|
|
||||||
-DHAS_ADC_CALIBRATION
|
|
||||||
-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 = esp32-s3-devkitc-1
|
|
||||||
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:heltec_wireless_stick_lite_v3_display]
|
|
||||||
board = esp32-s3-devkitc-1
|
|
||||||
board_build.mcu = esp32s3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_WSL_V3_DISPLAY
|
|
||||||
-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:ESP32_DIY_1W_LoRa_LLCC68]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_1W_LoRa_LLCC68
|
|
||||||
-DHAS_LLCC68
|
|
||||||
-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_Mesh_V1_2]
|
|
||||||
board = esp32dev
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_DIY_1W_LoRa_Mesh_V1_2
|
|
||||||
-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: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:heltec_wireless_paper]
|
|
||||||
board = esp32-s3-devkitc-1
|
|
||||||
board_build.mcu = esp32s3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DHELTEC_WP
|
|
||||||
-DHAS_SX1262
|
|
||||||
-DHAS_EPAPER
|
|
||||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
|
||||||
lib_deps =
|
|
||||||
${common.lib_deps}
|
|
||||||
|
|
||||||
|
|
||||||
[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:WEMOS_S2_MINI_DIY_LoRa]
|
|
||||||
board = lolin_s2_mini
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DWEMOS_S2_MINI_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: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
|
|
||||||
|
|
||||||
[env:ESP32_C3_OctopusLab_LoRa]
|
|
||||||
board = esp32-c3-devkitm-1
|
|
||||||
board_build.mcu = esp32c3
|
|
||||||
build_flags =
|
|
||||||
-Werror -Wall
|
|
||||||
-DESP32_C3_OctopusLab_LoRa
|
|
||||||
-DHAS_SX1268
|
|
||||||
-DRADIO_HAS_XTAL
|
|
||||||
-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;
|
||||||
@@ -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;
|
||||||
@@ -163,10 +182,10 @@
|
|||||||
displayShow(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 ");
|
||||||
displayShow(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,259 @@
|
|||||||
/*______________________________________________________________________________________________________________
|
/* 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 "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 "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.09.23";
|
|
||||||
Configuration Config;
|
|
||||||
WiFiClient espClient;
|
|
||||||
|
|
||||||
uint8_t myWiFiAPIndex = 0;
|
String versionDate = "2025-08-20";
|
||||||
int myWiFiAPSize = Config.wifiAPs.size();
|
Configuration Config;
|
||||||
WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
|
WiFiClient espClient;
|
||||||
|
#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
|
//#define STARTUP_DELAY 5 //min
|
||||||
|
|
||||||
|
#ifdef HAS_TWO_CORES
|
||||||
|
QueueHandle_t aprsIsTxQueue = NULL;
|
||||||
|
QueueHandle_t aprsIsRxQueue = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
POWER_Utils::setup();
|
POWER_Utils::setup();
|
||||||
Utils::setupDisplay();
|
Utils::setupDisplay();
|
||||||
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
|
#ifdef STARTUP_DELAY // (TEST) just to wait for WiFi init of Routers
|
||||||
displayShow("", " STARTUP DELAY ...", "", "", 0);
|
displayShow("", " STARTUP DELAY ...", "", "", 0);
|
||||||
delay(STARTUP_DELAY * 60 * 1000);
|
delay(STARTUP_DELAY * 60 * 1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HELTEC_HTCT62
|
SLEEP_Utils::setup();
|
||||||
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";
|
|
||||||
LoRa_Utils::sendNewPacket(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();
|
||||||
#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();
|
||||||
|
|
||||||
|
// Crear queues con verificación detallada
|
||||||
|
//Serial.println("Creando aprsIsTxQueue...");
|
||||||
|
aprsIsTxQueue = xQueueCreate(50, sizeof(String));
|
||||||
|
//Serial.printf("aprsIsTxQueue = %p\n", aprsIsTxQueue);
|
||||||
|
|
||||||
|
//Serial.println("Creando aprsIsRxQueue...");
|
||||||
|
aprsIsRxQueue = xQueueCreate(50, sizeof(String));
|
||||||
|
//Serial.printf("aprsIsRxQueue = %p\n", aprsIsRxQueue);
|
||||||
|
|
||||||
|
// Verificación crítica
|
||||||
|
if (aprsIsRxQueue == NULL || aprsIsTxQueue == NULL) {
|
||||||
|
Serial.println("FATAL: Error creando queues!");
|
||||||
|
while(1) {
|
||||||
|
Serial.println("STUCK - Queues failed");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("Queues creadas OK");
|
||||||
|
|
||||||
|
// Iniciar el task de APRSIS
|
||||||
|
if (!APRS_IS_Utils::startListenerAPRSISTask()) {
|
||||||
|
Serial.println("Error: No se pudo crear el task de APRSIS");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
WIFI_Utils::checkIfAutoAPShouldPowerOff();
|
//Serial.println("Loop tick: " + String(millis()));
|
||||||
|
//delay(1000);
|
||||||
|
|
||||||
if (isUpdatingOTA) {
|
if (Config.digi.ecoMode == 1) {
|
||||||
ElegantOTA.loop();
|
SLEEP_Utils::checkWakeUpFlag();
|
||||||
return; // Don't process IGate and Digi during OTA update
|
Utils::checkBeaconInterval();
|
||||||
}
|
STATION_Utils::processOutputPacketBufferUltraEcoMode();
|
||||||
|
Utils::checkSleepByLowBatteryVoltage(1);
|
||||||
|
SLEEP_Utils::startSleeping();
|
||||||
|
} else {
|
||||||
|
WIFI_Utils::checkAutoAPTimeout();
|
||||||
|
|
||||||
if (Config.lowVoltageCutOff > 0) {
|
if (isUpdatingOTA) {
|
||||||
BATTERY_Utils::checkIfShouldSleep();
|
ElegantOTA.loop();
|
||||||
}
|
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
|
||||||
|
|
||||||
thirdLine = Utils::getLocalIP();
|
#ifdef HAS_A7670
|
||||||
|
if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect();
|
||||||
|
#else
|
||||||
|
WIFI_Utils::checkWiFi();
|
||||||
|
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) APRS_IS_Utils::connect();
|
||||||
|
#endif
|
||||||
|
|
||||||
WIFI_Utils::checkWiFi(); // Always use WiFi, not related to IGate/Digi mode
|
NTP_Utils::update();
|
||||||
|
TNC_Utils::loop();
|
||||||
|
|
||||||
#ifdef HAS_A7670
|
Utils::checkDisplayInterval();
|
||||||
if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect();
|
Utils::checkBeaconInterval();
|
||||||
#else
|
|
||||||
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) APRS_IS_Utils::connect();
|
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
|
||||||
#endif
|
|
||||||
|
|
||||||
TNC_Utils::loop();
|
String packet = "";
|
||||||
|
if (Config.loramodule.rxActive) {
|
||||||
Utils::checkDisplayInterval();
|
packet = LoRa_Utils::receivePacket(); // We need to fetch LoRa packet above APRSIS and Digi
|
||||||
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.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || 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) { // If TNC server enabled
|
||||||
|
TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS
|
||||||
|
}
|
||||||
|
if (Config.tnc.enableSerial) { // If Serial KISS enabled
|
||||||
|
TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.tnc.enableServer) { // If TNC server enabled
|
if (Config.aprs_is.active) {
|
||||||
TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS
|
APRS_IS_Utils::processAPRSISPacket();
|
||||||
}
|
//APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
|
||||||
if (Config.tnc.enableSerial) { // If Serial KISS enabled
|
|
||||||
TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATION_Utils::processOutputPacketBuffer();
|
||||||
|
|
||||||
|
#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();
|
|
||||||
|
|
||||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
|
||||||
Utils::checkRebootTime();
|
|
||||||
Utils::checkSleepByLowBatteryVoltage(1);
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,26 @@
|
|||||||
|
/* 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 "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"
|
||||||
@@ -9,8 +28,10 @@
|
|||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
extern WiFiClient espClient;
|
extern WiFiClient espClient;
|
||||||
|
extern QueueHandle_t aprsIsRxQueue;
|
||||||
extern uint32_t lastScreenOn;
|
extern uint32_t lastScreenOn;
|
||||||
extern String firstLine;
|
extern String firstLine;
|
||||||
extern String secondLine;
|
extern String secondLine;
|
||||||
@@ -22,15 +43,19 @@ extern String seventhLine;
|
|||||||
extern bool modemLoggedToAPRSIS;
|
extern bool modemLoggedToAPRSIS;
|
||||||
extern bool backUpDigiMode;
|
extern bool backUpDigiMode;
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
|
// Handle del task (opcional, para poder controlarlo después)
|
||||||
|
TaskHandle_t aprsisTaskHandle = NULL;
|
||||||
|
|
||||||
void upload(const String& line) {
|
void upload(const String& line) {
|
||||||
espClient.print(line + "\r\n");
|
espClient.print(line + "\r\n");
|
||||||
}
|
}
|
||||||
@@ -50,19 +75,16 @@ 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 CA2RXU_LoRa_iGate 2.0 filter ";
|
aprsAuth += " vers CA2RXU_iGate 3.0 filter ";
|
||||||
aprsAuth += Config.aprs_is.filter;
|
aprsAuth += Config.aprs_is.filter;
|
||||||
upload(aprsAuth);
|
upload(aprsAuth);
|
||||||
delay(200);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +93,11 @@ 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) {
|
||||||
displayToggle(true);
|
displayToggle(true);
|
||||||
}
|
}
|
||||||
@@ -101,7 +123,7 @@ namespace APRS_IS_Utils {
|
|||||||
if(aprsisState == "--" && !Config.display.alwaysOn && Config.display.timeout != 0) {
|
if(aprsisState == "--" && !Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||||
displayToggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
secondLine = "WiFi: ";
|
secondLine = "WiFi: ";
|
||||||
secondLine += wifiState;
|
secondLine += wifiState;
|
||||||
@@ -118,15 +140,15 @@ 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 thirdParty) {
|
bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty) {
|
||||||
@@ -136,9 +158,10 @@ namespace APRS_IS_Utils {
|
|||||||
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;
|
||||||
@@ -173,40 +196,38 @@ namespace APRS_IS_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void processLoRaPacket(const String& packet) {
|
void processLoRaPacket(const String& packet) {
|
||||||
if (espClient.connected() || modemLoggedToAPRSIS) {
|
if (passcodeValid && (espClient.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), false);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,79 +278,103 @@ namespace APRS_IS_Utils {
|
|||||||
return outputPacket;
|
return outputPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processAPRSISPacket(const String& packet) {
|
//uint32_t lastLog = 0;
|
||||||
if (!packet.startsWith("#")) {
|
void processAPRSISPacket() {
|
||||||
if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
|
/*Serial.println("processAPRSISPacket");
|
||||||
String Sender = packet.substring(0, packet.indexOf(">"));
|
|
||||||
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
|
|
||||||
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
|
||||||
Addressee.trim();
|
|
||||||
if (Addressee == Config.callsign) { // its for me!
|
|
||||||
String receivedMessage;
|
|
||||||
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
|
|
||||||
String ackMessage = "ack";
|
|
||||||
ackMessage += AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{") + 1);
|
|
||||||
ackMessage.trim();
|
|
||||||
delay(4000);
|
|
||||||
for (int i = Sender.length(); i < 9; i++) {
|
|
||||||
Sender += ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
String ackPacket = Config.callsign;
|
if (millis() - lastLog > 5000) { // Cada 5 segundos
|
||||||
ackPacket += ">APLRG1,TCPIP,qAC::";
|
UBaseType_t packets = uxQueueMessagesWaiting(aprsIsRxQueue);
|
||||||
ackPacket += Sender;
|
UBaseType_t spaces = uxQueueSpacesAvailable(aprsIsRxQueue);
|
||||||
ackPacket += ":";
|
Serial.printf("[STATS] APRSIS Queue: %d/%d (%.1f%% full)\n",
|
||||||
ackPacket += ackMessage;
|
packets, packets + spaces,
|
||||||
#ifdef HAS_A7670
|
(packets * 100.0) / (packets + spaces));
|
||||||
A7670_Utils::uploadToAPRSIS(ackPacket);
|
lastLog = millis();
|
||||||
#else
|
}*/
|
||||||
upload(ackPacket);
|
|
||||||
#endif
|
|
||||||
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
|
String packet;
|
||||||
|
if (xQueueReceive(aprsIsRxQueue, &packet, 0) == pdTRUE) {
|
||||||
|
|
||||||
|
if (passcodeValid && !packet.startsWith("#")) {
|
||||||
|
if (Config.aprs_is.messagesToRF && packet.indexOf("::") > 0) {
|
||||||
|
String Sender = packet.substring(0, packet.indexOf(">"));
|
||||||
|
const String& AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
|
||||||
|
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
||||||
|
Addressee.trim();
|
||||||
|
if (Addressee == Config.callsign) { // its for me!
|
||||||
|
String receivedMessage;
|
||||||
|
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
|
||||||
|
String ackMessage = "ack";
|
||||||
|
ackMessage += AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{") + 1);
|
||||||
|
ackMessage.trim();
|
||||||
|
delay(4000);
|
||||||
|
for (int i = Sender.length(); i < 9; i++) {
|
||||||
|
Sender += ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
String ackPacket = Config.callsign;
|
||||||
|
ackPacket += ">APLRG1,TCPIP,qAC::";
|
||||||
|
ackPacket += Sender;
|
||||||
|
ackPacket += ":";
|
||||||
|
ackPacket += ackMessage;
|
||||||
|
#ifdef HAS_A7670
|
||||||
|
A7670_Utils::uploadToAPRSIS(ackPacket);
|
||||||
|
#else
|
||||||
|
upload(ackPacket);
|
||||||
|
#endif
|
||||||
|
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
|
||||||
|
} else {
|
||||||
|
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
|
||||||
|
}
|
||||||
|
if (receivedMessage.indexOf("?") == 0) {
|
||||||
|
Utils::println("Rx Query (APRS-IS) : " + packet);
|
||||||
|
Sender.trim();
|
||||||
|
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false);
|
||||||
|
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
|
||||||
|
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||||
|
displayToggle(true);
|
||||||
|
}
|
||||||
|
lastScreenOn = millis();
|
||||||
|
delay(500);
|
||||||
|
#ifdef HAS_A7670
|
||||||
|
A7670_Utils::uploadToAPRSIS(queryAnswer);
|
||||||
|
#else
|
||||||
|
upload(queryAnswer);
|
||||||
|
#endif
|
||||||
|
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
|
||||||
|
fifthLine = "APRS-IS ----> APRS-IS";
|
||||||
|
sixthLine = Config.callsign;
|
||||||
|
for (int j = sixthLine.length();j < 9;j++) {
|
||||||
|
sixthLine += " ";
|
||||||
|
}
|
||||||
|
sixthLine += "> ";
|
||||||
|
sixthLine += Sender;
|
||||||
|
seventhLine = "QUERY = ";
|
||||||
|
seventhLine += receivedMessage;
|
||||||
|
}
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
} else {
|
} else {
|
||||||
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
|
Utils::print("Rx Message (APRS-IS): " + packet);
|
||||||
}
|
if (STATION_Utils::wasHeard(Addressee) && packet.indexOf("EQNS.") == -1 && packet.indexOf("UNIT.") == -1 && packet.indexOf("PARM.") == -1) {
|
||||||
if (receivedMessage.indexOf("?") == 0) {
|
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
|
||||||
Utils::println("Received Query APRS-IS : " + packet);
|
|
||||||
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, true, false);
|
|
||||||
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
|
|
||||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
|
||||||
displayToggle(true);
|
displayToggle(true);
|
||||||
|
lastScreenOn = millis();
|
||||||
|
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||||
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||||
}
|
}
|
||||||
lastScreenOn = millis();
|
|
||||||
delay(500);
|
|
||||||
#ifdef HAS_A7670
|
|
||||||
A7670_Utils::uploadToAPRSIS(queryAnswer);
|
|
||||||
#else
|
|
||||||
upload(queryAnswer);
|
|
||||||
#endif
|
|
||||||
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
|
|
||||||
fifthLine = "APRS-IS ----> APRS-IS";
|
|
||||||
sixthLine = Config.callsign;
|
|
||||||
for (int j = sixthLine.length();j < 9;j++) {
|
|
||||||
sixthLine += " ";
|
|
||||||
}
|
|
||||||
sixthLine += "> ";
|
|
||||||
sixthLine += Sender;
|
|
||||||
seventhLine = "QUERY = ";
|
|
||||||
seventhLine += receivedMessage;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
|
||||||
Utils::print("Received Message from APRS-IS : " + packet);
|
Utils::print("Rx Object (APRS-IS) : " + packet);
|
||||||
if (STATION_Utils::wasHeard(Addressee)) {
|
if (STATION_Utils::checkObjectTime(packet)) {
|
||||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
|
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
|
||||||
displayToggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||||
|
Serial.println();
|
||||||
|
} else {
|
||||||
|
Serial.println(" ---> Rejected (Time): No Tx");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
|
||||||
} else if (Config.aprs_is.objectsToRF && packet.indexOf(":;") > 0) {
|
|
||||||
Utils::println("Received Object from APRS-IS : " + packet);
|
|
||||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
|
|
||||||
displayToggle(true);
|
|
||||||
lastScreenOn = millis();
|
|
||||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,12 +386,78 @@ namespace APRS_IS_Utils {
|
|||||||
if (espClient.connected()) {
|
if (espClient.connected()) {
|
||||||
if (espClient.available()) {
|
if (espClient.available()) {
|
||||||
String aprsisPacket = espClient.readStringUntil('\r');
|
String aprsisPacket = espClient.readStringUntil('\r');
|
||||||
// Serial.println(aprsisPacket);
|
aprsisPacket.trim(); //Serial.println(aprsisPacket);
|
||||||
processAPRSISPacket(aprsisPacket);
|
xQueueSend(aprsIsRxQueue, &aprsisPacket, 0);
|
||||||
|
//processAPRSISPacket(aprsisPacket);
|
||||||
lastRxTime = millis();
|
lastRxTime = millis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void firstConnection() {
|
||||||
|
if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !espClient.connected()) {
|
||||||
|
connect();
|
||||||
|
while (!passcodeValid) {
|
||||||
|
if (espClient.connected() && espClient.available()) {
|
||||||
|
String aprsisPacket = espClient.readStringUntil('\r');
|
||||||
|
aprsisPacket.trim();
|
||||||
|
if (!passcodeValid && aprsisPacket.indexOf(Config.callsign) != -1) {
|
||||||
|
if (aprsisPacket.indexOf("unverified") != -1 ) {
|
||||||
|
Serial.println("\n****APRS PASSCODE NOT VALID****\n");
|
||||||
|
displayShow(firstLine, "", " APRS PASSCODE", " NOT VALID !!!", "", "", "", 0);
|
||||||
|
while (1) {};
|
||||||
|
} else if (aprsisPacket.indexOf("verified") != -1 ) {
|
||||||
|
Serial.println("(APRS PASSCODE VALIDATED)");
|
||||||
|
passcodeValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void aprsisListenerTask(void *parameter) {
|
||||||
|
while (true) {
|
||||||
|
listenAPRSIS();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10)); // 10ms delay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Función para iniciar el task
|
||||||
|
bool startListenerAPRSISTask(uint32_t stackSize, UBaseType_t priority) {
|
||||||
|
BaseType_t result = xTaskCreatePinnedToCore(
|
||||||
|
aprsisListenerTask, // Función del task
|
||||||
|
"APRSIS_Listener", // Nombre del task
|
||||||
|
stackSize, // Stack size en words (no bytes)
|
||||||
|
NULL, // Parámetro pasado al task
|
||||||
|
priority, // Prioridad
|
||||||
|
&aprsisTaskHandle, // Handle del task
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return (result == pdPASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Función opcional para detener el task
|
||||||
|
void stopListenerAPRSISTask() {
|
||||||
|
if (aprsisTaskHandle != NULL) {
|
||||||
|
vTaskDelete(aprsisTaskHandle);
|
||||||
|
aprsisTaskHandle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Función opcional para suspender/reanudar el task
|
||||||
|
void suspendListenerAPRSISTask() {
|
||||||
|
if (aprsisTaskHandle != NULL) {
|
||||||
|
vTaskSuspend(aprsisTaskHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resumeListenerAPRSISTask() {
|
||||||
|
if (aprsisTaskHandle != NULL) {
|
||||||
|
vTaskResume(aprsisTaskHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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, bool thirdParty);
|
|
||||||
void processLoRaPacket(const String& packet);
|
|
||||||
|
|
||||||
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType);
|
|
||||||
void processAPRSISPacket(const String& packet);
|
|
||||||
void listenAPRSIS();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,16 +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 <Arduino.h>
|
#include <Arduino.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 Configuration Config;
|
||||||
extern uint32_t lastBatteryCheck;
|
extern uint32_t lastBatteryCheck;
|
||||||
|
|
||||||
bool shouldSleepLowVoltage = false;
|
bool shouldSleepLowVoltage = false;
|
||||||
|
|
||||||
float adcReadingTransformation = (3.3/4095);
|
float adcReadingTransformation = (3.3/4095);
|
||||||
|
int adcReadings = 20;
|
||||||
float voltageDividerCorrection = 0.288;
|
float voltageDividerCorrection = 0.288;
|
||||||
float readingCorrection = 0.125;
|
float readingCorrection = 0.125;
|
||||||
float multiplyCorrection = 0.035;
|
float multiplyCorrection = 0.035;
|
||||||
@@ -20,10 +40,11 @@ float voltageDividerTransformation = 0.0;
|
|||||||
int telemetryCounter = random(1,999);
|
int telemetryCounter = random(1,999);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAS_ADC_CALIBRATION
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
#include <esp_adc_cal.h>
|
#include <esp_adc_cal.h>
|
||||||
|
|
||||||
#if defined(TTGO_T_LORA32_V2_1) || defined(TTGO_T_LORA32_V2_1_915)
|
#if defined(TTGO_LORA32_V2_1) || defined(TTGO_LORA32_V2_1_915)
|
||||||
#define InternalBattery_ADC_Channel ADC1_CHANNEL_7 // t_lora32 pin35
|
#define InternalBattery_ADC_Channel ADC1_CHANNEL_7 // t_lora32 pin35
|
||||||
#define ExternalVoltage_ADC_Channel ADC1_CHANNEL_6 // t_lora32 pin34
|
#define ExternalVoltage_ADC_Channel ADC1_CHANNEL_6 // t_lora32 pin34
|
||||||
#endif
|
#endif
|
||||||
@@ -98,65 +119,59 @@ 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) || defined(HELTEC_WP)
|
|
||||||
digitalWrite(ADC_CTRL, LOW);
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++) {
|
int sampleSum = 0;
|
||||||
|
for (int i = 0; i < adcReadings; i++) {
|
||||||
#if defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915)
|
#if defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915)
|
||||||
sample = 0;
|
sampleSum = 0;
|
||||||
#else
|
#else
|
||||||
#ifdef HAS_ADC_CALIBRATION
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
if (calibrationEnable){
|
if (calibrationEnable){
|
||||||
sample = adc1_get_raw(InternalBattery_ADC_Channel);
|
sampleSum += adc1_get_raw(InternalBattery_ADC_Channel);
|
||||||
} else {
|
} else {
|
||||||
sample = analogRead(BATTERY_PIN);
|
sampleSum += analogRead(BATTERY_PIN);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#ifdef BATTERY_PIN
|
#ifdef BATTERY_PIN
|
||||||
sample = analogRead(BATTERY_PIN);
|
sampleSum += analogRead(BATTERY_PIN);
|
||||||
#else
|
#else
|
||||||
sample = 0;
|
sampleSum += 0;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
sampleSum += sample;
|
delay(3);
|
||||||
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) || defined(HELTEC_WP)
|
|
||||||
digitalWrite(ADC_CTRL, HIGH);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HELTEC_WP
|
#ifdef HELTEC_WP
|
||||||
double inputDivider = (1.0 / (10.0 + 10.0)) * 10.0; // The voltage divider is a 10k + 10k resistor in series
|
double inputDivider = (1.0 / (10.0 + 10.0)) * 10.0; // The voltage divider is a 10k + 10k resistor in series
|
||||||
#else
|
#else
|
||||||
double inputDivider = (1.0 / (390.0 + 100.0)) * 100.0; // The voltage divider is a 390k + 100k resistor in series, 100k on the low side.
|
double inputDivider = (1.0 / (390.0 + 100.0)) * 100.0; // The voltage divider is a 390k + 100k resistor in series, 100k on the low side.
|
||||||
#endif
|
#endif
|
||||||
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.
|
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
|
#else
|
||||||
#ifdef HAS_ADC_CALIBRATION
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
if (calibrationEnable){
|
if (calibrationEnable){
|
||||||
float voltage = esp_adc_cal_raw_to_voltage(sampleSum / 100, &adc_chars);
|
float voltage = esp_adc_cal_raw_to_voltage(sampleSum / adcReadings, &adc_chars);
|
||||||
voltage *= 2; // for 100K/100K voltage divider
|
voltage *= 2; // for 100K/100K voltage divider
|
||||||
voltage /= 1000;
|
voltage /= 1000;
|
||||||
return voltage;
|
return voltage;
|
||||||
} else {
|
} else {
|
||||||
return (2 * (sampleSum/100) * adcReadingTransformation) + voltageDividerCorrection; // raw voltage without mapping
|
return (2 * (sampleSum/adcReadings) * adcReadingTransformation) + voltageDividerCorrection; // raw voltage without mapping
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
return (2 * (sampleSum/100) * adcReadingTransformation) + voltageDividerCorrection; // raw voltage without mapping
|
#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
|
||||||
#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
|
||||||
@@ -182,7 +197,7 @@ namespace BATTERY_Utils {
|
|||||||
|
|
||||||
float extVoltage;
|
float extVoltage;
|
||||||
#ifdef HAS_ADC_CALIBRATION
|
#ifdef HAS_ADC_CALIBRATION
|
||||||
if (calibrationEnable){
|
if (calibrationEnable){
|
||||||
extVoltage = esp_adc_cal_raw_to_voltage(sampleSum / 100, &adc_chars) * voltageDividerTransformation; // in mV
|
extVoltage = esp_adc_cal_raw_to_voltage(sampleSum / 100, &adc_chars) * voltageDividerTransformation; // in mV
|
||||||
extVoltage /= 1000;
|
extVoltage /= 1000;
|
||||||
} else {
|
} else {
|
||||||
@@ -197,15 +212,6 @@ namespace BATTERY_Utils {
|
|||||||
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
|
// return mapVoltage(voltage, 5.05, 6.32, 4.5, 5.5); // mapped voltage
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkIfShouldSleep() {
|
|
||||||
if (lastBatteryCheck == 0 || millis() - lastBatteryCheck >= 15 * 60 * 1000) {
|
|
||||||
lastBatteryCheck = millis();
|
|
||||||
if (checkInternalVoltage() < Config.lowVoltageCutOff) {
|
|
||||||
ESP.deepSleep(1800000000); // 30 min sleep (60s = 60e6)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void startupBatteryHealth() {
|
void startupBatteryHealth() {
|
||||||
#ifdef BATTERY_PIN
|
#ifdef BATTERY_PIN
|
||||||
if (Config.battery.monitorInternalVoltage && checkInternalVoltage() < Config.battery.internalSleepVoltage + 0.1) {
|
if (Config.battery.monitorInternalVoltage && checkInternalVoltage() < Config.battery.internalSleepVoltage + 0.1) {
|
||||||
@@ -222,7 +228,6 @@ namespace BATTERY_Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType) { // 0 = internal battery(0-4,2V) , 1 = external battery(0-15V)
|
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType) { // 0 = internal battery(0-4,2V) , 1 = external battery(0-15V)
|
||||||
String encodedBytes;
|
String encodedBytes;
|
||||||
int tempValue;
|
int tempValue;
|
||||||
@@ -241,7 +246,7 @@ namespace BATTERY_Utils {
|
|||||||
tempValue = value;
|
tempValue = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int firstByte = tempValue / 91;
|
int firstByte = tempValue / 91;
|
||||||
tempValue -= firstByte * 91;
|
tempValue -= firstByte * 91;
|
||||||
@@ -251,14 +256,11 @@ namespace BATTERY_Utils {
|
|||||||
return encodedBytes;
|
return encodedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String generateEncodedTelemetry() {
|
String generateEncodedTelemetry() {
|
||||||
String telemetry = "|";
|
String telemetry = "|";
|
||||||
telemetry += generateEncodedTelemetryBytes(telemetryCounter, true, 0);
|
telemetry += generateEncodedTelemetryBytes(telemetryCounter, true, 0);
|
||||||
telemetryCounter++;
|
telemetryCounter++;
|
||||||
if (telemetryCounter == 1000) {
|
if (telemetryCounter == 1000) telemetryCounter = 0;
|
||||||
telemetryCounter = 0;
|
|
||||||
}
|
|
||||||
if (Config.battery.sendInternalVoltage) telemetry += generateEncodedTelemetryBytes(checkInternalVoltage(), false, 0);
|
if (Config.battery.sendInternalVoltage) telemetry += generateEncodedTelemetryBytes(checkInternalVoltage(), false, 0);
|
||||||
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(checkExternalVoltage(), false, 1);
|
if (Config.battery.sendExternalVoltage) telemetry += generateEncodedTelemetryBytes(checkExternalVoltage(), false, 1);
|
||||||
telemetry += "|";
|
telemetry += "|";
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
#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 checkIfShouldSleep(); // ????
|
|
||||||
void startupBatteryHealth();
|
|
||||||
|
|
||||||
String generateEncodedTelemetryBytes(float value, bool firstBytes, byte voltageType);
|
|
||||||
String generateEncodedTelemetry();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -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,274 +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_WSL_V3_DISPLAY) || defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_WS) || defined(HELTEC_WP)
|
|
||||||
#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) || defined(ESP32_DIY_1W_LoRa_LLCC68) // Ebyte E22 400M30S (SX1268) or E22 900M30S (SX1262) or E220 LLCC68
|
|
||||||
#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
|
|
||||||
|
|
||||||
#if defined(ESP32_DIY_1W_LoRa_Mesh_V1_2) // https://github.com/NanoVHF/Meshtastic-DIY/tree/main/PCB/ESP-32-devkit_EBYTE-E22/Mesh-v1.02-2LCD-FreePins
|
|
||||||
#define RADIO_SCLK_PIN 5
|
|
||||||
#define RADIO_MISO_PIN 19
|
|
||||||
#define RADIO_MOSI_PIN 27
|
|
||||||
#define RADIO_CS_PIN 18
|
|
||||||
#define RADIO_RST_PIN 23
|
|
||||||
#define RADIO_DIO1_PIN 33
|
|
||||||
#define RADIO_BUSY_PIN 32
|
|
||||||
#define RADIO_RXEN 14
|
|
||||||
#define RADIO_TXEN 13
|
|
||||||
#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
|
|
||||||
|
|
||||||
#if defined(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
|
|
||||||
|
|
||||||
#ifdef WEMOS_S2_MINI_DIY_LoRa
|
|
||||||
#define RADIO_SCLK_PIN 36
|
|
||||||
#define RADIO_MISO_PIN 37
|
|
||||||
#define RADIO_MOSI_PIN 35
|
|
||||||
#define RADIO_CS_PIN 34
|
|
||||||
#define RADIO_BUSY_PIN 38
|
|
||||||
#define RADIO_RST_PIN 33
|
|
||||||
#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) || defined(ESP32_DIY_1W_LoRa_LLCC68) || defined(ESP32_DIY_1W_LoRa_Mesh_V1_2)
|
|
||||||
#define OLED_SDA 21
|
|
||||||
#define OLED_SCL 22
|
|
||||||
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HELTEC_V2) || defined(HELTEC_WS)
|
|
||||||
#define OLED_SDA 4
|
|
||||||
#define OLED_SCL 15
|
|
||||||
#define OLED_RST 16
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HELTEC_V3)
|
|
||||||
#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) && !defined(WEMOS_S2_MINI_DIY_LoRa)
|
|
||||||
#define HAS_DISPLAY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Leds and other stuff
|
|
||||||
#ifdef HELTEC_HTCT62
|
|
||||||
#define BATTERY_PIN 1
|
|
||||||
#endif
|
|
||||||
#ifdef WEMOS_S2_MINI_DIY_LoRa
|
|
||||||
#define INTERNAL_LED_PIN 15
|
|
||||||
#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_WSL_V3_DISPLAY) || defined(HELTEC_WS)
|
|
||||||
#define INTERNAL_LED_PIN 35
|
|
||||||
#define BATTERY_PIN 1
|
|
||||||
#define VEXT_CTRL 36
|
|
||||||
#define ADC_CTRL 37
|
|
||||||
#define BOARD_I2C_SDA 41
|
|
||||||
#define BOARD_I2C_SCL 42
|
|
||||||
#ifdef HELTEC_WSL_V3_DISPLAY
|
|
||||||
#define OLED_RST -1
|
|
||||||
#endif
|
|
||||||
#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 HELTEC_WP
|
|
||||||
#define INTERNAL_LED_PIN 18
|
|
||||||
#define BATTERY_PIN 20
|
|
||||||
#define ADC_CTRL 19
|
|
||||||
#define VEXT_CTRL 45
|
|
||||||
#define BOARD_I2C_SDA 37
|
|
||||||
#define BOARD_I2C_SCL 36
|
|
||||||
#define EPAPER_BUSY 7
|
|
||||||
#define EPAPER_RST 6
|
|
||||||
#define EPAPER_DC 5
|
|
||||||
#define EPAPER_CS 4
|
|
||||||
#define EPAPER_SCL 3
|
|
||||||
#define EPAPER_SDA 2
|
|
||||||
#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
|
|
||||||
|
|
||||||
#if defined(ESP32_C3_OctopusLab_LoRa)
|
|
||||||
#define OLED_SDA 0
|
|
||||||
#define OLED_SCL 1
|
|
||||||
#define OLED_RST -1
|
|
||||||
#define RADIO_SCLK_PIN 6
|
|
||||||
#define RADIO_MISO_PIN 4
|
|
||||||
#define RADIO_MOSI_PIN 7
|
|
||||||
#define RADIO_CS_PIN 5
|
|
||||||
#define RADIO_DIO1_PIN 3
|
|
||||||
#define RADIO_RST_PIN -1
|
|
||||||
#define RADIO_BUSY_PIN 8
|
|
||||||
#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,11 +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 <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"
|
||||||
|
|
||||||
|
|
||||||
|
bool shouldSleepStop = true;
|
||||||
|
|
||||||
|
|
||||||
void Configuration::writeFile() {
|
void Configuration::writeFile() {
|
||||||
Serial.println("Saving config..");
|
Serial.println("Saving config...");
|
||||||
|
|
||||||
StaticJsonDocument<2560> data;
|
StaticJsonDocument<2560> data;
|
||||||
File configFile = SPIFFS.open("/igate_conf.json", "w");
|
File configFile = SPIFFS.open("/igate_conf.json", "w");
|
||||||
@@ -18,7 +40,7 @@ void Configuration::writeFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data["wifi"]["autoAP"]["password"] = wifiAutoAP.password;
|
data["wifi"]["autoAP"]["password"] = wifiAutoAP.password;
|
||||||
data["wifi"]["autoAP"]["powerOff"] = wifiAutoAP.powerOff;
|
data["wifi"]["autoAP"]["timeout"] = wifiAutoAP.timeout;
|
||||||
|
|
||||||
data["callsign"] = callsign;
|
data["callsign"] = callsign;
|
||||||
|
|
||||||
@@ -40,7 +62,17 @@ void Configuration::writeFile() {
|
|||||||
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
|
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
|
||||||
data["beacon"]["path"] = beacon.path;
|
data["beacon"]["path"] = beacon.path;
|
||||||
|
|
||||||
|
data["beacon"]["statusActive"] = beacon.statusActive;
|
||||||
|
data["beacon"]["statusPacket"] = beacon.statusPacket;
|
||||||
|
|
||||||
|
data["beacon"]["gpsActive"] = beacon.gpsActive;
|
||||||
|
data["beacon"]["gpsAmbiguity"] = beacon.gpsAmbiguity;
|
||||||
|
|
||||||
data["digi"]["mode"] = digi.mode;
|
data["digi"]["mode"] = digi.mode;
|
||||||
|
data["digi"]["ecoMode"] = digi.ecoMode;
|
||||||
|
#if defined(HAS_A7670)
|
||||||
|
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
|
||||||
|
#endif
|
||||||
|
|
||||||
data["lora"]["rxFreq"] = loramodule.rxFreq;
|
data["lora"]["rxFreq"] = loramodule.rxFreq;
|
||||||
data["lora"]["txFreq"] = loramodule.txFreq;
|
data["lora"]["txFreq"] = loramodule.txFreq;
|
||||||
@@ -67,14 +99,15 @@ void Configuration::writeFile() {
|
|||||||
data["battery"]["voltageDividerR2"] = battery.voltageDividerR2;
|
data["battery"]["voltageDividerR2"] = battery.voltageDividerR2;
|
||||||
|
|
||||||
data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry;
|
data["battery"]["sendVoltageAsTelemetry"] = battery.sendVoltageAsTelemetry;
|
||||||
|
|
||||||
data["bme"]["active"] = bme.active;
|
data["wxsensor"]["active"] = wxsensor.active;
|
||||||
data["bme"]["heightCorrection"] = bme.heightCorrection;
|
data["wxsensor"]["heightCorrection"] = wxsensor.heightCorrection;
|
||||||
data["bme"]["temperatureCorrection"] = bme.temperatureCorrection;
|
data["wxsensor"]["temperatureCorrection"] = wxsensor.temperatureCorrection;
|
||||||
|
|
||||||
data["syslog"]["active"] = syslog.active;
|
data["syslog"]["active"] = syslog.active;
|
||||||
data["syslog"]["server"] = syslog.server;
|
data["syslog"]["server"] = syslog.server;
|
||||||
data["syslog"]["port"] = syslog.port;
|
data["syslog"]["port"] = syslog.port;
|
||||||
|
data["syslog"]["logBeaconOverTCPIP"] = syslog.logBeaconOverTCPIP;
|
||||||
|
|
||||||
data["tnc"]["enableServer"] = tnc.enableServer;
|
data["tnc"]["enableServer"] = tnc.enableServer;
|
||||||
data["tnc"]["enableSerial"] = tnc.enableSerial;
|
data["tnc"]["enableSerial"] = tnc.enableSerial;
|
||||||
@@ -90,20 +123,25 @@ void Configuration::writeFile() {
|
|||||||
|
|
||||||
data["other"]["backupDigiMode"] = backupDigiMode;
|
data["other"]["backupDigiMode"] = backupDigiMode;
|
||||||
|
|
||||||
data["other"]["lowPowerMode"] = lowPowerMode;
|
|
||||||
data["other"]["lowVoltageCutOff"] = lowVoltageCutOff;
|
|
||||||
|
|
||||||
data["personalNote"] = personalNote;
|
data["personalNote"] = personalNote;
|
||||||
|
|
||||||
|
data["blacklist"] = blacklist;
|
||||||
|
|
||||||
data["webadmin"]["active"] = webadmin.active;
|
data["webadmin"]["active"] = webadmin.active;
|
||||||
data["webadmin"]["username"] = webadmin.username;
|
data["webadmin"]["username"] = webadmin.username;
|
||||||
data["webadmin"]["password"] = webadmin.password;
|
data["webadmin"]["password"] = webadmin.password;
|
||||||
|
|
||||||
|
data["ntp"]["gmtCorrection"] = ntp.gmtCorrection;
|
||||||
|
|
||||||
|
data["remoteManagement"]["managers"] = remoteManagement.managers;
|
||||||
|
data["remoteManagement"]["rfOnly"] = remoteManagement.rfOnly;
|
||||||
|
|
||||||
serializeJson(data, configFile);
|
serializeJson(data, configFile);
|
||||||
|
|
||||||
configFile.close();
|
configFile.close();
|
||||||
|
|
||||||
Serial.println("Config saved");
|
Serial.println("Config saved");
|
||||||
|
delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Configuration::readFile() {
|
bool Configuration::readFile() {
|
||||||
@@ -129,7 +167,7 @@ bool Configuration::readFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890";
|
wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890";
|
||||||
wifiAutoAP.powerOff = data["wifi"]["autoAP"]["powerOff"] | 10;
|
wifiAutoAP.timeout = data["wifi"]["autoAP"]["timeout"] | 10;
|
||||||
|
|
||||||
callsign = data["callsign"] | "NOCALL-10";
|
callsign = data["callsign"] | "NOCALL-10";
|
||||||
rememberStationTime = data["other"]["rememberStationTime"] | 30;
|
rememberStationTime = data["other"]["rememberStationTime"] | 30;
|
||||||
@@ -143,7 +181,13 @@ bool Configuration::readFile() {
|
|||||||
beacon.path = data["beacon"]["path"] | "WIDE1-1";
|
beacon.path = data["beacon"]["path"] | "WIDE1-1";
|
||||||
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false;
|
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"] | false;
|
||||||
beacon.sendViaRF = data["beacon"]["sendViaRF"] | false;
|
beacon.sendViaRF = data["beacon"]["sendViaRF"] | false;
|
||||||
|
|
||||||
|
beacon.statusActive = data["beacon"]["statusActive"] | false;
|
||||||
|
beacon.statusPacket = data["beacon"]["statusPacket"] | "";
|
||||||
|
|
||||||
|
beacon.gpsActive = data["beacon"]["gpsActive"] | false;
|
||||||
|
beacon.gpsAmbiguity = data["beacon"]["gpsAmbiguity"] | false;
|
||||||
|
|
||||||
aprs_is.active = data["aprs_is"]["active"] | false;
|
aprs_is.active = data["aprs_is"]["active"] | false;
|
||||||
aprs_is.passcode = data["aprs_is"]["passcode"] | "XYZWV";
|
aprs_is.passcode = data["aprs_is"]["passcode"] | "XYZWV";
|
||||||
aprs_is.server = data["aprs_is"]["server"] | "rotate.aprs2.net";
|
aprs_is.server = data["aprs_is"]["server"] | "rotate.aprs2.net";
|
||||||
@@ -151,8 +195,14 @@ bool Configuration::readFile() {
|
|||||||
aprs_is.filter = data["aprs_is"]["filter"] | "m/10";
|
aprs_is.filter = data["aprs_is"]["filter"] | "m/10";
|
||||||
aprs_is.messagesToRF = data["aprs_is"]["messagesToRF"] | false;
|
aprs_is.messagesToRF = data["aprs_is"]["messagesToRF"] | false;
|
||||||
aprs_is.objectsToRF = data["aprs_is"]["objectsToRF"] | false;
|
aprs_is.objectsToRF = data["aprs_is"]["objectsToRF"] | false;
|
||||||
|
|
||||||
digi.mode = data["digi"]["mode"] | 0;
|
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
|
||||||
|
|
||||||
loramodule.txFreq = data["lora"]["txFreq"] | 433775000;
|
loramodule.txFreq = data["lora"]["txFreq"] | 433775000;
|
||||||
loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000;
|
loramodule.rxFreq = data["lora"]["rxFreq"] | 433775000;
|
||||||
@@ -169,7 +219,7 @@ bool Configuration::readFile() {
|
|||||||
|
|
||||||
battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"] | false;
|
battery.sendInternalVoltage = data["battery"]["sendInternalVoltage"] | false;
|
||||||
battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false;
|
battery.monitorInternalVoltage = data["battery"]["monitorInternalVoltage"] | false;
|
||||||
battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 3.0;
|
battery.internalSleepVoltage = data["battery"]["internalSleepVoltage"] | 2.9;
|
||||||
|
|
||||||
battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false;
|
battery.sendExternalVoltage = data["battery"]["sendExternalVoltage"] | false;
|
||||||
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
|
battery.externalVoltagePin = data["battery"]["externalVoltagePin"] | 34;
|
||||||
@@ -178,16 +228,17 @@ bool Configuration::readFile() {
|
|||||||
battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0;
|
battery.voltageDividerR1 = data["battery"]["voltageDividerR1"] | 100.0;
|
||||||
battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0;
|
battery.voltageDividerR2 = data["battery"]["voltageDividerR2"] | 27.0;
|
||||||
|
|
||||||
battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | true;
|
battery.sendVoltageAsTelemetry = data["battery"]["sendVoltageAsTelemetry"] | false;
|
||||||
|
|
||||||
bme.active = data["bme"]["active"] | false;
|
wxsensor.active = data["wxsensor"]["active"] | false;
|
||||||
bme.heightCorrection = data["bme"]["heightCorrection"] | 0;
|
wxsensor.heightCorrection = data["wxsensor"]["heightCorrection"] | 0;
|
||||||
bme.temperatureCorrection = data["bme"]["temperatureCorrection"] | 0.0;
|
wxsensor.temperatureCorrection = data["wxsensor"]["temperatureCorrection"] | 0.0;
|
||||||
|
|
||||||
syslog.active = data["syslog"]["active"] | false;
|
syslog.active = data["syslog"]["active"] | false;
|
||||||
syslog.server = data["syslog"]["server"] | "192.168.0.100";
|
syslog.server = data["syslog"]["server"] | "lora.link9.net";
|
||||||
syslog.port = data["syslog"]["port"] | 514;
|
syslog.port = data["syslog"]["port"] | 1514;
|
||||||
|
syslog.logBeaconOverTCPIP = data["syslog"]["logBeaconOverTCPIP"] | false;
|
||||||
|
|
||||||
tnc.enableServer = data["tnc"]["enableServer"] | false;
|
tnc.enableServer = data["tnc"]["enableServer"] | false;
|
||||||
tnc.enableSerial = data["tnc"]["enableSerial"] | false;
|
tnc.enableSerial = data["tnc"]["enableSerial"] | false;
|
||||||
tnc.acceptOwn = data["tnc"]["acceptOwn"] | false;
|
tnc.acceptOwn = data["tnc"]["acceptOwn"] | false;
|
||||||
@@ -199,15 +250,19 @@ bool Configuration::readFile() {
|
|||||||
webadmin.username = data["webadmin"]["username"] | "admin";
|
webadmin.username = data["webadmin"]["username"] | "admin";
|
||||||
webadmin.password = data["webadmin"]["password"] | "";
|
webadmin.password = data["webadmin"]["password"] | "";
|
||||||
|
|
||||||
lowPowerMode = data["other"]["lowPowerMode"] | false;
|
ntp.gmtCorrection = data["ntp"]["gmtCorrection"] | 0.0;
|
||||||
lowVoltageCutOff = data["other"]["lowVoltageCutOff"] | 0;
|
|
||||||
|
|
||||||
backupDigiMode = data["other"]["backupDigiMode"] | false;
|
backupDigiMode = data["other"]["backupDigiMode"] | false;
|
||||||
|
|
||||||
rebootMode = data["other"]["rebootMode"] | false;
|
rebootMode = data["other"]["rebootMode"] | false;
|
||||||
rebootModeTime = data["other"]["rebootModeTime"] | 6;
|
rebootModeTime = data["other"]["rebootModeTime"] | 6;
|
||||||
|
|
||||||
personalNote = data["personalNote"] | "personal note here...";
|
personalNote = data["personalNote"] | "personal note here";
|
||||||
|
|
||||||
|
blacklist = data["blacklist"] | "station callsign";
|
||||||
|
|
||||||
|
remoteManagement.managers = data["remoteManagement"]["managers"] | "";
|
||||||
|
remoteManagement.rfOnly = data["remoteManagement"]["rfOnly"] | true;
|
||||||
|
|
||||||
if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP
|
if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP
|
||||||
WiFi_AP wifiap;
|
WiFi_AP wifiap;
|
||||||
@@ -234,7 +289,7 @@ void Configuration::init() {
|
|||||||
wifiAPs.push_back(wifiap);
|
wifiAPs.push_back(wifiap);
|
||||||
|
|
||||||
wifiAutoAP.password = "1234567890";
|
wifiAutoAP.password = "1234567890";
|
||||||
wifiAutoAP.powerOff = 15;
|
wifiAutoAP.timeout = 10;
|
||||||
|
|
||||||
callsign = "N0CALL-10";
|
callsign = "N0CALL-10";
|
||||||
|
|
||||||
@@ -247,8 +302,15 @@ void Configuration::init() {
|
|||||||
beacon.sendViaAPRSIS = true;
|
beacon.sendViaAPRSIS = true;
|
||||||
beacon.sendViaRF = false;
|
beacon.sendViaRF = false;
|
||||||
beacon.path = "WIDE1-1";
|
beacon.path = "WIDE1-1";
|
||||||
|
|
||||||
digi.mode = 0;
|
beacon.statusActive = false;
|
||||||
|
beacon.statusPacket = "";
|
||||||
|
|
||||||
|
beacon.gpsActive = false;
|
||||||
|
beacon.gpsAmbiguity = false;
|
||||||
|
|
||||||
|
digi.mode = 0;
|
||||||
|
digi.ecoMode = 0;
|
||||||
|
|
||||||
tnc.enableServer = false;
|
tnc.enableServer = false;
|
||||||
tnc.enableSerial = false;
|
tnc.enableSerial = false;
|
||||||
@@ -276,12 +338,13 @@ void Configuration::init() {
|
|||||||
display.turn180 = false;
|
display.turn180 = false;
|
||||||
|
|
||||||
syslog.active = false;
|
syslog.active = false;
|
||||||
syslog.server = "192.168.0.100";
|
syslog.server = "lora.link9.net";
|
||||||
syslog.port = 514;
|
syslog.port = 1514;
|
||||||
|
syslog.logBeaconOverTCPIP = false;
|
||||||
|
|
||||||
bme.active = false;
|
wxsensor.active = false;
|
||||||
bme.heightCorrection = 0;
|
wxsensor.heightCorrection = 0;
|
||||||
bme.temperatureCorrection = 0.0;
|
wxsensor.temperatureCorrection = 0.0;
|
||||||
|
|
||||||
ota.username = "";
|
ota.username = "";
|
||||||
ota.password = "";
|
ota.password = "";
|
||||||
@@ -290,7 +353,7 @@ void Configuration::init() {
|
|||||||
|
|
||||||
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.externalVoltagePin = 34;
|
||||||
@@ -299,10 +362,7 @@ void Configuration::init() {
|
|||||||
battery.voltageDividerR1 = 100.0;
|
battery.voltageDividerR1 = 100.0;
|
||||||
battery.voltageDividerR2 = 27.0;
|
battery.voltageDividerR2 = 27.0;
|
||||||
|
|
||||||
battery.sendVoltageAsTelemetry = true;
|
battery.sendVoltageAsTelemetry = false;
|
||||||
|
|
||||||
lowPowerMode = false;
|
|
||||||
lowVoltageCutOff = 0;
|
|
||||||
|
|
||||||
backupDigiMode = false;
|
backupDigiMode = false;
|
||||||
|
|
||||||
@@ -311,10 +371,17 @@ void Configuration::init() {
|
|||||||
|
|
||||||
personalNote = "";
|
personalNote = "";
|
||||||
|
|
||||||
|
blacklist = "";
|
||||||
|
|
||||||
webadmin.active = false;
|
webadmin.active = false;
|
||||||
webadmin.username = "admin";
|
webadmin.username = "admin";
|
||||||
webadmin.password = "";
|
webadmin.password = "";
|
||||||
|
|
||||||
|
ntp.gmtCorrection = 0.0;
|
||||||
|
|
||||||
|
remoteManagement.managers = "";
|
||||||
|
remoteManagement.rfOnly = true;
|
||||||
|
|
||||||
Serial.println("All is Written!");
|
Serial.println("All is Written!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +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 <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 "lora_utils.h"
|
||||||
@@ -10,6 +27,7 @@
|
|||||||
#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;
|
||||||
@@ -25,34 +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);
|
||||||
|
String tempPath = path;
|
||||||
|
|
||||||
if (path.indexOf("WIDE1-1") != -1 && (Config.digi.mode == 2 || Config.digi.mode == 3)) {
|
if (path.indexOf("WIDE1-1") != -1 && (Config.digi.mode == 2 || Config.digi.mode == 3)) {
|
||||||
tempPath.replace("WIDE1-1", Config.callsign + "*");
|
tempPath.replace("WIDE1-1", Config.callsign + "*");
|
||||||
} else if (path.indexOf("WIDE2-") != -1 && Config.digi.mode == 3) {
|
} else if (path.indexOf("WIDE2-") != -1 && Config.digi.mode == 3) {
|
||||||
if (path.indexOf("*") != 1) {
|
if (path.indexOf(",WIDE1*") != -1) {
|
||||||
tempPath.remove(path.indexOf("*"), 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 "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (path.indexOf("WIDE2-1") != -1) {
|
packetToRepeat += tempPath;
|
||||||
tempPath.replace("WIDE2-1", Config.callsign + "*");
|
if (thirdParty) {
|
||||||
} else if (path.indexOf("WIDE2-2") != -1) {
|
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(":}")));
|
||||||
tempPath.replace("WIDE2-2", Config.callsign + "*,WIDE2-1");
|
|
||||||
} else {
|
} else {
|
||||||
return "";
|
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;
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(":}"));
|
||||||
@@ -62,70 +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 ((Config.digi.mode == 2 || backUpDigiMode) && path.indexOf("WIDE1-1") != - 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) {
|
} else if (Config.digi.mode == 3) {
|
||||||
int wide1Index = path.indexOf("WIDE1-1");
|
if (path.indexOf("WIDE1-1") != -1 || path.indexOf("WIDE2-") != -1) {
|
||||||
int wide2Index = path.indexOf("WIDE2-");
|
int wide1Index = path.indexOf("WIDE1-1");
|
||||||
|
int wide2Index = path.indexOf("WIDE2-");
|
||||||
|
|
||||||
if (wide1Index != -1 && wide2Index != -1 && wide1Index < wide2Index) { // WIDE1-1 && WIDE2-n
|
// WIDE1-1 && WIDE2-n / only WIDE1-1 / only WIDE2-n
|
||||||
return buildPacket(path, packet, thirdParty);
|
if ((wide1Index != -1 && wide2Index != -1 && wide1Index < wide2Index) || (wide1Index != -1 && wide2Index == -1) || (wide1Index == -1 && wide2Index != -1)) {
|
||||||
} else if (wide1Index != -1 && wide2Index == -1) { // only WIDE1-1
|
return buildPacket(path, packet, thirdParty, false);
|
||||||
return buildPacket(path, packet, thirdParty);
|
}
|
||||||
} else if (wide1Index == -1 && wide2Index != -1) { // only WIDE2-n
|
return "";
|
||||||
return buildPacket(path, packet, thirdParty);
|
} 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 {
|
} else {
|
||||||
return "";
|
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)) || Config.lowPowerMode) {
|
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, thirdPartyPacket);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!queryMessage) {
|
}
|
||||||
String loraPacket = generateDigiRepeatedPacket(packet.substring(3), thirdPartyPacket);
|
if (!queryMessage) {
|
||||||
if (loraPacket != "") {
|
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
|
||||||
if (Config.lowPowerMode) {
|
if (loraPacket != "") {
|
||||||
LoRa_Utils::sendNewPacket(loraPacket);
|
STATION_Utils::addToOutputPacketBuffer(loraPacket);
|
||||||
} else {
|
if (Config.digi.ecoMode != 1) displayToggle(true);
|
||||||
STATION_Utils::addToOutputPacketBuffer(loraPacket);
|
lastScreenOn = millis();
|
||||||
}
|
|
||||||
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
|
|
||||||
@@ -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,39 +27,45 @@
|
|||||||
#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.5
|
#define bigSizeFont 2
|
||||||
#define smallSizeFont 1.5
|
#define smallSizeFont 1
|
||||||
#define lineSpacing 12
|
#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
|
||||||
#if HAS_EPAPER
|
#ifdef HAS_EPAPER
|
||||||
//
|
#include <heltec-eink-modules.h>
|
||||||
|
#include "Fonts/FreeSansBold9pt7b.h"
|
||||||
|
EInkDisplay_WirelessPaperV1_1 display;
|
||||||
|
String lastEpaperText;
|
||||||
#else
|
#else
|
||||||
#include <Adafruit_GFX.h>
|
#include <Adafruit_GFX.h>
|
||||||
#include <Adafruit_SSD1306.h>
|
#if defined(TTGO_T_Beam_S3_SUPREME_V3)
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WS)
|
#include <Adafruit_SH110X.h>
|
||||||
#define OLED_DISPLAY_HAS_RST_PIN
|
Adafruit_SH1106G display(128, 64, &Wire, OLED_RST);
|
||||||
#endif
|
|
||||||
#ifdef HELTEC_WSL_V3_DISPLAY
|
|
||||||
Adafruit_SSD1306 display(128, 64, &Wire1, OLED_RST);
|
|
||||||
#else
|
#else
|
||||||
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
|
#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
|
#endif
|
||||||
#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 displaySetup() {
|
void displaySetup() {
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
@@ -54,11 +78,20 @@ void displaySetup() {
|
|||||||
} 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);
|
||||||
|
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
|
||||||
|
sprite.createSprite(320,240);
|
||||||
|
#else
|
||||||
|
sprite.createSprite(160,80);
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#if HAS_EPAPER
|
#ifdef HAS_EPAPER
|
||||||
//
|
display.landscape();
|
||||||
|
display.printCenter("LoRa APRS iGate Initialising...");
|
||||||
|
display.update();
|
||||||
#else
|
#else
|
||||||
#ifdef OLED_DISPLAY_HAS_RST_PIN
|
#ifdef OLED_DISPLAY_HAS_RST_PIN
|
||||||
pinMode(OLED_RST, OUTPUT);
|
pinMode(OLED_RST, OUTPUT);
|
||||||
@@ -67,20 +100,31 @@ void displaySetup() {
|
|||||||
digitalWrite(OLED_RST, HIGH);
|
digitalWrite(OLED_RST, HIGH);
|
||||||
#endif
|
#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)) {
|
||||||
for(;;); // Don't proceed, loop forever
|
//if (!display.begin(0x3c, false)) {
|
||||||
}
|
displayFound = true;
|
||||||
if (Config.display.turn180) {
|
if (Config.display.turn180) display.setRotation(2);
|
||||||
display.setRotation(2);
|
display.clearDisplay();
|
||||||
}
|
display.setTextColor(SH110X_WHITE);
|
||||||
display.clearDisplay();
|
display.setTextSize(1);
|
||||||
display.setTextColor(WHITE);
|
display.setCursor(0, 0);
|
||||||
display.setTextSize(1);
|
display.setContrast(1);
|
||||||
display.setCursor(0, 0);
|
display.display();
|
||||||
display.ssd1306_command(SSD1306_SETCONTRAST);
|
}
|
||||||
display.ssd1306_command(1);
|
#else
|
||||||
display.display();
|
if(display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
||||||
|
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
|
#endif
|
||||||
delay(1000);
|
delay(1000);
|
||||||
@@ -93,85 +137,96 @@ void displayToggle(bool toggle) {
|
|||||||
#ifdef HAS_TFT
|
#ifdef HAS_TFT
|
||||||
digitalWrite(TFT_BL, HIGH);
|
digitalWrite(TFT_BL, HIGH);
|
||||||
#else
|
#else
|
||||||
#if HAS_EPAPER
|
#ifdef HAS_EPAPER
|
||||||
// ... to be continued
|
display.printCenter("EPAPER Display Disabled by toggle...");
|
||||||
|
display.update();
|
||||||
#else
|
#else
|
||||||
display.ssd1306_command(SSD1306_DISPLAYON);
|
#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
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#ifdef HAS_TFT
|
#ifdef HAS_TFT
|
||||||
digitalWrite(TFT_BL, LOW);
|
digitalWrite(TFT_BL, LOW);
|
||||||
#else
|
#else
|
||||||
#if HAS_EPAPER
|
#ifdef HAS_EPAPER
|
||||||
// ... to be continued
|
display.printCenter("Enabled EPAPER Display...");
|
||||||
|
display.update();
|
||||||
#else
|
#else
|
||||||
display.ssd1306_command(SSD1306_DISPLAYOFF);
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldCleanTFT(const String& header, const String& line1, const String& line2, const String& line3) {
|
|
||||||
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 displayShow(const String& header, const String& line1, const String& line2, const String& line3, int wait) {
|
void displayShow(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
|
||||||
#ifdef HAS_EPAPER
|
#ifdef HAS_EPAPER
|
||||||
// ... to be continued
|
display.clearMemory();
|
||||||
#else
|
display.setCursor(5,10);
|
||||||
display.clearDisplay();
|
display.setFont(&FreeSansBold9pt7b);
|
||||||
display.setTextColor(WHITE);
|
|
||||||
display.setTextSize(1);
|
|
||||||
display.setCursor(0, 0);
|
|
||||||
display.println(header);
|
display.println(header);
|
||||||
|
display.setFont(NULL);
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
display.setCursor(0, 8 + (8 * i));
|
display.setCursor(0, 25 + (14 * i));
|
||||||
display.println(*lines[i]);
|
display.println(*lines[i]);
|
||||||
}
|
}
|
||||||
display.ssd1306_command(SSD1306_SETCONTRAST);
|
display.update();
|
||||||
display.ssd1306_command(1);
|
#else
|
||||||
display.display();
|
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
|
#endif
|
||||||
delay(wait);
|
delay(wait);
|
||||||
@@ -182,35 +237,63 @@ void displayShow(const String& header, const String& line1, const String& line2,
|
|||||||
#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
|
||||||
#ifdef HAS_EPAPER
|
#ifdef HAS_EPAPER
|
||||||
// ... to be continued
|
lastEpaperText = header + line1 + line2 + line3 + line4 + line5 + line6;
|
||||||
#else
|
display.clearMemory();
|
||||||
display.clearDisplay();
|
display.setCursor(5,10);
|
||||||
display.setTextColor(WHITE);
|
display.setFont(&FreeSansBold9pt7b);
|
||||||
display.setTextSize(2);
|
|
||||||
display.setCursor(0, 0);
|
|
||||||
display.println(header);
|
display.println(header);
|
||||||
display.setTextSize(1);
|
display.setFont(NULL);
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
display.setCursor(0, 16 + (8 * i));
|
display.setCursor(0, 25 + (14 * i));
|
||||||
display.println(*lines[i]);
|
display.println(*lines[i]);
|
||||||
}
|
}
|
||||||
display.ssd1306_command(SSD1306_SETCONTRAST);
|
display.update();
|
||||||
display.ssd1306_command(1);
|
#else
|
||||||
display.display();
|
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
|
#endif
|
||||||
delay(wait);
|
delay(wait);
|
||||||
|
|||||||
@@ -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 displaySetup();
|
|
||||||
void displayToggle(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 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
|
|
||||||
@@ -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 <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 WiFiClient espClient;
|
||||||
|
extern HardwareSerial gpsSerial;
|
||||||
|
extern TinyGPSPlus gps;
|
||||||
|
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
|
||||||
|
|
||||||
|
|
||||||
namespace GPS_Utils {
|
namespace GPS_Utils {
|
||||||
@@ -24,20 +51,32 @@ namespace GPS_Utils {
|
|||||||
return(s);
|
return(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float roundToTwoDecimals(float degrees) {
|
||||||
|
return round(degrees * 100) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
|
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
|
||||||
String encodedData = overlay;
|
String encodedData = overlay;
|
||||||
uint32_t aprs_lat, aprs_lon;
|
uint32_t aprs_lat, aprs_lon;
|
||||||
aprs_lat = 900000000 - latitude * 10000000;
|
|
||||||
|
float processedLatitude = latitude;
|
||||||
|
float processedLongitude = longitude;
|
||||||
|
if (Config.beacon.gpsActive && Config.beacon.gpsAmbiguity) {
|
||||||
|
processedLatitude = roundToTwoDecimals(latitude);
|
||||||
|
processedLongitude = roundToTwoDecimals(longitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
aprs_lat = 900000000 - processedLatitude * 10000000;
|
||||||
aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
|
aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
|
||||||
aprs_lon = 900000000 + longitude * 10000000 / 2;
|
aprs_lon = 900000000 + processedLongitude * 10000000 / 2;
|
||||||
aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
|
aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
|
||||||
|
|
||||||
String Ns, Ew, helper;
|
String Ns, Ew, helper;
|
||||||
if(latitude < 0) { Ns = "S"; } else { Ns = "N"; }
|
if(processedLatitude < 0) { Ns = "S"; } else { Ns = "N"; }
|
||||||
if(latitude < 0) { latitude= -latitude; }
|
if(processedLatitude < 0) { processedLatitude = -processedLatitude; }
|
||||||
|
|
||||||
if(longitude < 0) { Ew = "W"; } else { Ew = "E"; }
|
if(processedLongitude < 0) { Ew = "W"; } else { Ew = "E"; }
|
||||||
if(longitude < 0) { longitude= -longitude; }
|
if(processedLongitude < 0) { processedLongitude = -processedLongitude; }
|
||||||
|
|
||||||
char helper_base91[] = {"0000\0"};
|
char helper_base91[] = {"0000\0"};
|
||||||
int i;
|
int i;
|
||||||
@@ -55,6 +94,19 @@ namespace GPS_Utils {
|
|||||||
return encodedData;
|
return encodedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generateBeaconFirstPart() {
|
||||||
|
String beaconPacket = Config.callsign;
|
||||||
|
beaconPacket += ">APLRG1";
|
||||||
|
if (Config.beacon.path.indexOf("WIDE") == 0) {
|
||||||
|
beaconPacket += ",";
|
||||||
|
beaconPacket += Config.beacon.path;
|
||||||
|
}
|
||||||
|
iGateBeaconPacket = beaconPacket;
|
||||||
|
iGateBeaconPacket += ",qAC:!";
|
||||||
|
iGateLoRaBeaconPacket = beaconPacket;
|
||||||
|
iGateLoRaBeaconPacket += ":!";
|
||||||
|
}
|
||||||
|
|
||||||
void generateBeacons() {
|
void generateBeacons() {
|
||||||
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
|
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
|
||||||
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
|
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
|
||||||
@@ -64,21 +116,10 @@ namespace GPS_Utils {
|
|||||||
Config.beacon.sendViaRF = false;
|
Config.beacon.sendViaRF = false;
|
||||||
Config.digi.mode = 0;
|
Config.digi.mode = 0;
|
||||||
Config.backupDigiMode = false;
|
Config.backupDigiMode = false;
|
||||||
}
|
|
||||||
String beaconPacket = Config.callsign;
|
|
||||||
beaconPacket += ">APLRG1";
|
|
||||||
if (Config.beacon.path.indexOf("WIDE") == 0) {
|
|
||||||
beaconPacket += ",";
|
|
||||||
beaconPacket += Config.beacon.path;
|
|
||||||
}
|
}
|
||||||
|
generateBeaconFirstPart();
|
||||||
iGateBeaconPacket = beaconPacket;
|
|
||||||
iGateBeaconPacket += ",qAC:!";
|
|
||||||
iGateLoRaBeaconPacket = beaconPacket;
|
|
||||||
iGateLoRaBeaconPacket += ":!";
|
|
||||||
|
|
||||||
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
|
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
|
||||||
iGateBeaconPacket += encodedGPS;
|
iGateBeaconPacket += encodedGPS;
|
||||||
iGateLoRaBeaconPacket += encodedGPS;
|
iGateLoRaBeaconPacket += encodedGPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,22 +128,30 @@ namespace GPS_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String decodeEncodedGPS(const String& packet) {
|
String decodeEncodedGPS(const String& packet) {
|
||||||
const String& GPSPacket = packet.substring(packet.indexOf(":!")+3);
|
int indexOfExclamation = packet.indexOf(":!");
|
||||||
const String& encodedLatitude = GPSPacket.substring(0,4);
|
int indexOfEqual = packet.indexOf(":=");
|
||||||
const String& encodedLongtitude = GPSPacket.substring(4,8);
|
|
||||||
const String& comment = GPSPacket.substring(12);
|
|
||||||
|
|
||||||
int Y1 = int(encodedLatitude[0]);
|
const uint8_t OFFSET = 3; // Offset for encoded data in the packet
|
||||||
int Y2 = int(encodedLatitude[1]);
|
String GPSPacket;
|
||||||
int Y3 = int(encodedLatitude[2]);
|
if (indexOfExclamation > 10) {
|
||||||
int Y4 = int(encodedLatitude[3]);
|
GPSPacket = packet.substring(indexOfExclamation + OFFSET);
|
||||||
float decodedLatitude = 90.0 - ((((Y1-33) * pow(91,3)) + ((Y2-33) * pow(91,2)) + ((Y3-33) * 91) + Y4-33) / 380926.0);
|
} else if (indexOfEqual > 10) {
|
||||||
|
GPSPacket = packet.substring(indexOfEqual + OFFSET);
|
||||||
int X1 = int(encodedLongtitude[0]);
|
}
|
||||||
int X2 = int(encodedLongtitude[1]);
|
|
||||||
int X3 = int(encodedLongtitude[2]);
|
String encodedLatitude = GPSPacket.substring(0,4);
|
||||||
int X4 = int(encodedLongtitude[3]);
|
int Y1 = encodedLatitude[0] - 33;
|
||||||
float decodedLongitude = -180.0 + ((((X1-33) * pow(91,3)) + ((X2-33) * pow(91,2)) + ((X3-33) * 91) + X4-33) / 190463.0);
|
int Y2 = encodedLatitude[1] - 33;
|
||||||
|
int Y3 = encodedLatitude[2] - 33;
|
||||||
|
int Y4 = encodedLatitude[3] - 33;
|
||||||
|
float decodedLatitude = 90.0 - (((Y1 * pow(91,3)) + (Y2 * pow(91,2)) + (Y3 * 91) + Y4) / 380926.0);
|
||||||
|
|
||||||
|
String encodedLongitude = GPSPacket.substring(4,8);
|
||||||
|
int X1 = encodedLongitude[0] - 33;
|
||||||
|
int X2 = encodedLongitude[1] - 33;
|
||||||
|
int X3 = encodedLongitude[2] - 33;
|
||||||
|
int X4 = encodedLongitude[3] - 33;
|
||||||
|
float decodedLongitude = -180.0 + (((X1 * pow(91,3)) + (X2 * pow(91,2)) + (X3 * 91) + X4) / 190463.0);
|
||||||
|
|
||||||
distance = String(calculateDistanceTo(decodedLatitude, decodedLongitude),1);
|
distance = String(calculateDistanceTo(decodedLatitude, decodedLongitude),1);
|
||||||
|
|
||||||
@@ -113,6 +162,7 @@ namespace GPS_Utils {
|
|||||||
decodedGPS += distance;
|
decodedGPS += distance;
|
||||||
decodedGPS += "km";
|
decodedGPS += "km";
|
||||||
|
|
||||||
|
String comment = GPSPacket.substring(12);
|
||||||
if (comment != "") {
|
if (comment != "") {
|
||||||
decodedGPS += " / ";
|
decodedGPS += " / ";
|
||||||
decodedGPS += comment;
|
decodedGPS += comment;
|
||||||
@@ -121,33 +171,30 @@ namespace GPS_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getReceivedGPS(const String& packet) {
|
String getReceivedGPS(const String& packet) {
|
||||||
String infoGPS;
|
int indexOfExclamation = packet.indexOf(":!");
|
||||||
if (packet.indexOf(":!") > 10) {
|
int indexOfEqual = packet.indexOf(":=");
|
||||||
infoGPS = packet.substring(packet.indexOf(":!") + 2);
|
int indexOfAt = packet.indexOf(":@");
|
||||||
} else if (packet.indexOf(":=") > 10) {
|
|
||||||
infoGPS = packet.substring(packet.indexOf(":=") + 2);
|
|
||||||
}
|
|
||||||
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 infoGPS;
|
||||||
const String& secondLngPart = Longitude.substring(3,5);
|
if (indexOfExclamation > 10) {
|
||||||
const String& thirdLngPart = Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3);
|
infoGPS = packet.substring(indexOfExclamation + 2);
|
||||||
convertedLongitude = firstLngPart.toFloat() + (secondLngPart.toFloat()/60) + (thirdLngPart.toFloat()/(60*100));
|
} else if (indexOfEqual > 10) {
|
||||||
|
infoGPS = packet.substring(indexOfEqual + 2);
|
||||||
if (String(Latitude[7]) == "S") {
|
} else if (indexOfAt > 10) {
|
||||||
convertedLatitude = -convertedLatitude;
|
infoGPS = packet.substring(indexOfAt + 9); // 9 = 2+7 (when 7 is timestamp characters)
|
||||||
}
|
|
||||||
if (String(Longitude[8]) == "W") {
|
|
||||||
convertedLongitude = -convertedLongitude;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String Latitude = infoGPS.substring(0,8); // First 8 characters are Latitude
|
||||||
|
float convertedLatitude = Latitude.substring(0,2).toFloat(); // First 2 digits (Degrees)
|
||||||
|
convertedLatitude += Latitude.substring(2,4).toFloat() / 60; // Next 2 digits (Minutes)
|
||||||
|
convertedLatitude += Latitude.substring(Latitude.indexOf(".") + 1, Latitude.indexOf(".") + 3).toFloat() / (60*100);
|
||||||
|
if (Latitude.endsWith("S")) convertedLatitude = -convertedLatitude; // Handle Southern Hemisphere
|
||||||
|
|
||||||
|
String Longitude = infoGPS.substring(9,18); // Next 9 characters are Longitude
|
||||||
|
float convertedLongitude = Longitude.substring(0,3).toFloat(); // First 3 digits (Degrees)
|
||||||
|
convertedLongitude += Longitude.substring(3,5).toFloat() / 60; // Next 2 digits (Minutes)
|
||||||
|
convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100);
|
||||||
|
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
|
||||||
|
|
||||||
distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1);
|
distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1);
|
||||||
|
|
||||||
@@ -158,6 +205,7 @@ namespace GPS_Utils {
|
|||||||
decodedGPS += distance;
|
decodedGPS += distance;
|
||||||
decodedGPS += "km";
|
decodedGPS += "km";
|
||||||
|
|
||||||
|
String comment = infoGPS.substring(19);
|
||||||
if (comment != "") {
|
if (comment != "") {
|
||||||
decodedGPS += " / ";
|
decodedGPS += " / ";
|
||||||
decodedGPS += comment;
|
decodedGPS += comment;
|
||||||
@@ -166,21 +214,45 @@ namespace GPS_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
encodedBytePosition = packet.indexOf(":!") + 14;
|
return getReceivedGPS(packet);
|
||||||
}
|
|
||||||
if (packet.indexOf(":=") > 10) {
|
|
||||||
encodedBytePosition = packet.indexOf(":=") + 14;
|
|
||||||
}
|
|
||||||
if (encodedBytePosition != 0) {
|
|
||||||
if (String(packet[encodedBytePosition]) == "G" || String(packet[encodedBytePosition]) == "Q" || String(packet[encodedBytePosition]) == "[" || String(packet[encodedBytePosition]) == "H") {
|
|
||||||
return decodeEncodedGPS(packet);
|
|
||||||
} else {
|
|
||||||
return getReceivedGPS(packet);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return " _ / _ / _ ";
|
const uint8_t ENCODED_BYTE_OFFSET = 14; // Offset for encoded data in the packet
|
||||||
|
int indexOfExclamation = packet.indexOf(":!");
|
||||||
|
int indexOfEqual = packet.indexOf(":=");
|
||||||
|
uint8_t encodedBytePosition = 0;
|
||||||
|
if (indexOfExclamation > 10) { // Determine the position where encoded data starts
|
||||||
|
encodedBytePosition = indexOfExclamation + ENCODED_BYTE_OFFSET;
|
||||||
|
} else if (indexOfEqual > 10) {
|
||||||
|
encodedBytePosition = indexOfEqual + ENCODED_BYTE_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encodedBytePosition != 0) {
|
||||||
|
char currentChar = packet[encodedBytePosition];
|
||||||
|
if (currentChar == 'G' || currentChar == 'Q' || currentChar == '[' || currentChar == 'H' || currentChar == 'X') {
|
||||||
|
return decodeEncodedGPS(packet); // If valid encoded data position is found, decode it
|
||||||
|
} else {
|
||||||
|
return getReceivedGPS(packet);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return " _ / _ / _ ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) {
|
||||||
|
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_TX, GPS_RX);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
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,12 +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 <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;
|
||||||
|
|
||||||
@@ -19,7 +40,12 @@ bool transmitFlag = true;
|
|||||||
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);
|
||||||
@@ -34,6 +60,7 @@ bool transmitFlag = true;
|
|||||||
int rssi, freqError;
|
int rssi, freqError;
|
||||||
float snr;
|
float snr;
|
||||||
|
|
||||||
|
|
||||||
namespace LoRa_Utils {
|
namespace LoRa_Utils {
|
||||||
|
|
||||||
void setFlag(void) {
|
void setFlag(void) {
|
||||||
@@ -41,7 +68,13 @@ 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)
|
#if defined(RADIO_HAS_XTAL)
|
||||||
radio.XTAL = true;
|
radio.XTAL = true;
|
||||||
@@ -54,11 +87,7 @@ namespace LoRa_Utils {
|
|||||||
while (true);
|
while (true);
|
||||||
}
|
}
|
||||||
#if defined(HAS_SX1262) || defined(HAS_SX1268) || defined(HAS_LLCC68)
|
#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);
|
||||||
@@ -69,7 +98,7 @@ namespace LoRa_Utils {
|
|||||||
radio.setCodingRate(Config.loramodule.codingRate4);
|
radio.setCodingRate(Config.loramodule.codingRate4);
|
||||||
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
|
||||||
|
|
||||||
@@ -116,9 +145,9 @@ namespace LoRa_Utils {
|
|||||||
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
|
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
|
||||||
changeFreqTx();
|
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;
|
||||||
@@ -126,73 +155,72 @@ 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();
|
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() {
|
||||||
String packet = "";
|
String packet = "";
|
||||||
if (operationDone || Config.lowPowerMode) {
|
if (operationDone) {
|
||||||
operationDone = false;
|
operationDone = false;
|
||||||
if (transmitFlag && !Config.lowPowerMode) {
|
if (transmitFlag) {
|
||||||
radio.startReceive();
|
radio.startReceive();
|
||||||
transmitFlag = false;
|
transmitFlag = false;
|
||||||
} else {
|
} else {
|
||||||
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();
|
||||||
@@ -212,6 +240,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
|
|
||||||
@@ -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(ntpUDP, "pool.ntp.org", 0, 15 * 60 * 1000); // Update interval 15 min
|
||||||
|
|
||||||
|
|
||||||
|
namespace NTP_Utils {
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") {
|
||||||
|
int gmt = Config.ntp.gmtCorrection * 3600;
|
||||||
|
timeClient.setTimeOffset(gmt);
|
||||||
|
timeClient.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
if (WiFi.status() == WL_CONNECTED && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") timeClient.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFormatedTime() {
|
||||||
|
if (Config.digi.ecoMode == 0) return timeClient.getFormattedTime();
|
||||||
|
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);
|
||||||
@@ -48,14 +67,14 @@ namespace OTA_Utils {
|
|||||||
void onOTAEnd(bool success) {
|
void onOTAEnd(bool success) {
|
||||||
displayToggle(true);
|
displayToggle(true);
|
||||||
lastScreenOn = millis();
|
lastScreenOn = millis();
|
||||||
if (success) {
|
|
||||||
Serial.println("OTA update finished successfully!");
|
String statusMessage = success ? "OTA update success!" : "OTA update fail!";
|
||||||
displayShow("", "", " 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!");
|
||||||
displayShow("", "", " 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,47 @@ extern Configuration Config;
|
|||||||
|
|
||||||
namespace POWER_Utils {
|
namespace POWER_Utils {
|
||||||
|
|
||||||
|
#ifdef VEXT_CTRL
|
||||||
|
void vext_ctrl_ON() {
|
||||||
|
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3)
|
||||||
|
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? LOW : HIGH);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_WP) || defined(HELTEC_WS) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3)
|
||||||
|
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void vext_ctrl_OFF() {
|
||||||
|
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3)
|
||||||
|
digitalWrite(VEXT_CTRL, Config.digi.ecoMode == 1 ? HIGH : LOW);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_WP) || 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)
|
||||||
|
digitalWrite(ADC_CTRL, HIGH);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP)
|
||||||
|
digitalWrite(ADC_CTRL, LOW);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void adc_ctrl_OFF() {
|
||||||
|
#if defined(HELTEC_WIRELESS_TRACKER) || defined(HELTEC_V3_2)
|
||||||
|
digitalWrite(ADC_CTRL, LOW);
|
||||||
|
#endif
|
||||||
|
#if defined(HELTEC_V3) || defined(HELTEC_V2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WP)
|
||||||
|
digitalWrite(ADC_CTRL, HIGH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#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);
|
||||||
@@ -47,14 +114,58 @@ namespace POWER_Utils {
|
|||||||
#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 deactivateGPS() {
|
||||||
|
#ifdef HAS_AXP192
|
||||||
|
PMU.disableLDO3();
|
||||||
|
#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() {
|
||||||
#ifdef HAS_AXP192
|
#ifdef HAS_AXP192
|
||||||
PMU.setLDO2Voltage(3300);
|
PMU.setLDO2Voltage(3300);
|
||||||
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,33 +284,50 @@ 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
|
|
||||||
#ifdef HELTEC_WP
|
|
||||||
digitalWrite(VEXT_CTRL, LOW);
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) activateGPS();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ADC_CTRL
|
#ifdef ADC_CTRL
|
||||||
pinMode(ADC_CTRL, OUTPUT);
|
pinMode(ADC_CTRL, OUTPUT);
|
||||||
|
adc_ctrl_OFF();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HELTEC_WIRELESS_TRACKER)
|
#if defined(HELTEC_WIRELESS_TRACKER)
|
||||||
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
|
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WP) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
|
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WS) || defined(LIGHTGATEWAY_1_0) || defined(LIGHTGATEWAY_PLUS_1_0) || defined(TTGO_LORA32_T3S3_V1_2) || defined(HELTEC_V2)
|
||||||
|
Wire.begin(OLED_SDA, OLED_SCL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WP) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
|
||||||
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
|
Wire1.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WS)
|
#if defined(TTGO_T_DECK_GPS) || defined(TTGO_T_DECK_PLUS)
|
||||||
Wire.begin(OLED_SDA, OLED_SCL);
|
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);
|
||||||
|
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
delay(1000);
|
delay(1000);
|
||||||
BATTERY_Utils::setup();
|
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,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 "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "battery_utils.h"
|
||||||
#include "station_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 Configuration Config;
|
||||||
extern std::vector<LastHeardStation> lastHeardStations;
|
extern std::vector<LastHeardStation> lastHeardStations;
|
||||||
extern String versionDate;
|
extern String versionDate;
|
||||||
extern int rssi;
|
extern int rssi;
|
||||||
extern float snr;
|
extern float snr;
|
||||||
extern int freqError;
|
extern int freqError;
|
||||||
|
extern bool shouldSleepLowVoltage;
|
||||||
|
extern bool saveNewDigiEcoModeConfig;
|
||||||
|
|
||||||
|
|
||||||
namespace QUERY_Utils {
|
namespace QUERY_Utils {
|
||||||
@@ -18,9 +40,9 @@ namespace QUERY_Utils {
|
|||||||
String queryQuestion = query;
|
String queryQuestion = query;
|
||||||
queryQuestion.toUpperCase();
|
queryQuestion.toUpperCase();
|
||||||
if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") {
|
if (queryQuestion == "?APRS?" || queryQuestion == "H" || queryQuestion == "HELP" || queryQuestion=="?") {
|
||||||
answer.concat("?APRSV ?APRSP ?APRSL ?APRSH ?WHERE callsign");
|
answer.concat("?APRSV ?APRSP ?APRSL ?APRSSSR ?EM=? ?TX=? "); // ?APRSH ?WHERE callsign
|
||||||
} else if (queryQuestion == "?APRSV") {
|
} else if (queryQuestion == "?APRSV") {
|
||||||
answer.concat("CA2RXU_LoRa_iGate 2.0 v");
|
answer.concat("CA2RXU_LoRa_iGate 3.0 v");
|
||||||
answer.concat(versionDate);
|
answer.concat(versionDate);
|
||||||
} else if (queryQuestion == "?APRSP") {
|
} else if (queryQuestion == "?APRSP") {
|
||||||
answer.concat("iGate QTH: ");
|
answer.concat("iGate QTH: ");
|
||||||
@@ -42,16 +64,76 @@ namespace QUERY_Utils {
|
|||||||
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 (queryQuestion.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 (queryQuestion.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;
|
String queryAnswer = Config.callsign;
|
||||||
queryAnswer += ">APLRG1";
|
queryAnswer += ">APLRG1";
|
||||||
if (queryFromAPRSIS) {
|
if (queryFromAPRSIS) {
|
||||||
@@ -72,6 +154,11 @@ namespace QUERY_Utils {
|
|||||||
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, bool queryFromAPRSIS, bool thirdParty);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -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) || 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,11 +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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "station_utils.h"
|
#include "station_utils.h"
|
||||||
#include "battery_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;
|
||||||
@@ -15,10 +35,84 @@ uint32_t lastTxTime = millis();
|
|||||||
std::vector<LastHeardStation> lastHeardStations;
|
std::vector<LastHeardStation> lastHeardStations;
|
||||||
std::vector<String> outputPacketBuffer;
|
std::vector<String> outputPacketBuffer;
|
||||||
std::vector<Packet25SegBuffer> packet25SegBuffer;
|
std::vector<Packet25SegBuffer> packet25SegBuffer;
|
||||||
|
std::vector<String> blacklist;
|
||||||
|
std::vector<String> managers;
|
||||||
|
std::vector<LastHeardStation> lastHeardObjects;
|
||||||
|
|
||||||
|
bool saveNewDigiEcoModeConfig = false;
|
||||||
|
|
||||||
|
|
||||||
namespace STATION_Utils {
|
namespace STATION_Utils {
|
||||||
|
|
||||||
|
std::vector<String> loadCallSignList(const String& list) {
|
||||||
|
std::vector<String> loadedList;
|
||||||
|
|
||||||
|
String callsigns = list;
|
||||||
|
callsigns.trim();
|
||||||
|
|
||||||
|
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 (int 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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() {
|
void deleteNotHeard() {
|
||||||
std::vector<LastHeardStation> lastHeardStation_temp;
|
std::vector<LastHeardStation> lastHeardStation_temp;
|
||||||
for (int i = 0; i < lastHeardStations.size(); i++) {
|
for (int i = 0; i < lastHeardStations.size(); i++) {
|
||||||
@@ -40,22 +134,11 @@ namespace STATION_Utils {
|
|||||||
if (lastHeardStations[i].station == station) {
|
if (lastHeardStations[i].station == station) {
|
||||||
lastHeardStations[i].lastHeardTime = millis();
|
lastHeardStations[i].lastHeardTime = millis();
|
||||||
stationHeard = true;
|
stationHeard = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!stationHeard) {
|
if (!stationHeard) lastHeardStations.emplace_back(LastHeardStation{millis(), station});
|
||||||
LastHeardStation lastStation;
|
Utils::activeStations();
|
||||||
lastStation.lastHeardTime = millis();
|
|
||||||
lastStation.station = station;
|
|
||||||
lastHeardStations.push_back(lastStation);
|
|
||||||
}
|
|
||||||
|
|
||||||
fourthLine = "Stations (";
|
|
||||||
fourthLine += String(Config.rememberStationTime);
|
|
||||||
fourthLine += "min) = ";
|
|
||||||
if (lastHeardStations.size() < 10) {
|
|
||||||
fourthLine += " ";
|
|
||||||
}
|
|
||||||
fourthLine += String(lastHeardStations.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wasHeard(const String& station) {
|
bool wasHeard(const String& station) {
|
||||||
@@ -66,43 +149,46 @@ namespace STATION_Utils {
|
|||||||
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());
|
||||||
if ((millis() - packet25SegBuffer[0].receivedTime) > 25 * 1000) {
|
|
||||||
packet25SegBuffer.erase(packet25SegBuffer.begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check25SegBuffer(const String& station, const String& textMessage) {
|
bool check25SegBuffer(const String& station, const String& textMessage) {
|
||||||
bool shouldBeIgnored = false;
|
|
||||||
if (!packet25SegBuffer.empty()) {
|
if (!packet25SegBuffer.empty()) {
|
||||||
for (int i = 0; i < packet25SegBuffer.size(); i++) {
|
for (int i = 0; i < packet25SegBuffer.size(); i++) {
|
||||||
if (packet25SegBuffer[i].station == station && packet25SegBuffer[i].payload == textMessage) {
|
if (packet25SegBuffer[i].station == station && packet25SegBuffer[i].payload == textMessage) return false;
|
||||||
shouldBeIgnored = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldBeIgnored) {
|
packet25SegBuffer.emplace_back(Packet25SegBuffer{millis(), station, textMessage});
|
||||||
return false;
|
return true;
|
||||||
} else {
|
}
|
||||||
Packet25SegBuffer packet;
|
|
||||||
packet.receivedTime = millis();
|
void processOutputPacketBufferUltraEcoMode() {
|
||||||
packet.station = station;
|
size_t currentIndex = 0;
|
||||||
packet.payload = textMessage;
|
while (currentIndex < outputPacketBuffer.size()) { // this sends all packets from output buffer
|
||||||
packet25SegBuffer.push_back(packet);
|
delay(3000); // and cleans buffer to avoid sending packets with time offset
|
||||||
return true;
|
LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex]); // next time it wakes up
|
||||||
|
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]);
|
LoRa_Utils::sendNewPacket(outputPacketBuffer[0]);
|
||||||
outputPacketBuffer.erase(outputPacketBuffer.begin());
|
outputPacketBuffer.erase(outputPacketBuffer.begin());
|
||||||
@@ -115,6 +201,12 @@ namespace STATION_Utils {
|
|||||||
delay(4000);
|
delay(4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (saveNewDigiEcoModeConfig) {
|
||||||
|
Config.writeFile();
|
||||||
|
delay(1000);
|
||||||
|
displayToggle(false);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addToOutputPacketBuffer(const String& packet) {
|
void addToOutputPacketBuffer(const String& packet) {
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
#ifndef STATION_UTILS_H_
|
|
||||||
#define STATION_UTILS_H_
|
|
||||||
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
struct Packet25SegBuffer {
|
|
||||||
uint32_t receivedTime;
|
|
||||||
String station;
|
|
||||||
String payload;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LastHeardStation {
|
|
||||||
uint32_t lastHeardTime;
|
|
||||||
String station;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
namespace STATION_Utils {
|
|
||||||
|
|
||||||
void deleteNotHeard();
|
|
||||||
void updateLastHeard(const String& station);
|
|
||||||
bool wasHeard(const String& station);
|
|
||||||
void clean25SegBuffer();
|
|
||||||
bool check25SegBuffer(const String& station, const String& textMessage);
|
|
||||||
void processOutputPacketBuffer();
|
|
||||||
void addToOutputPacketBuffer(const String& packet);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,10 +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 <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "syslog_utils.h"
|
#include "syslog_utils.h"
|
||||||
#include "gps_utils.h"
|
#include "gps_utils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
|
extern String versionDate;
|
||||||
|
|
||||||
WiFiUDP udpClient;
|
WiFiUDP udpClient;
|
||||||
|
|
||||||
@@ -15,11 +35,15 @@ namespace SYSLOG_Utils {
|
|||||||
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
|
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
|
||||||
String syslogPacket = "<165>1 - ";
|
String syslogPacket = "<165>1 - ";
|
||||||
syslogPacket.concat(Config.callsign);
|
syslogPacket.concat(Config.callsign);
|
||||||
syslogPacket.concat(" CA2RXU_LoRa_iGate_1.3 - - - "); //RFC5424 The Syslog Protocol
|
syslogPacket.concat(" CA2RXU_LoRa_iGate_3.0 - - - "); //RFC5424 The Syslog Protocol
|
||||||
|
|
||||||
char signalData[35];
|
char signalData[35];
|
||||||
snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
|
snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
|
||||||
|
|
||||||
|
int colonIndex = packet.indexOf(":");
|
||||||
|
char nextChar = packet[colonIndex + 1];
|
||||||
|
String sender = packet.substring(3, packet.indexOf(">"));
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0: // CRC
|
case 0: // CRC
|
||||||
syslogPacket.concat("CRC / CRC-ERROR / ");
|
syslogPacket.concat("CRC / CRC-ERROR / ");
|
||||||
@@ -28,62 +52,63 @@ namespace SYSLOG_Utils {
|
|||||||
break;
|
break;
|
||||||
case 1: // RX
|
case 1: // RX
|
||||||
syslogPacket.concat("RX / ");
|
syslogPacket.concat("RX / ");
|
||||||
if (packet.indexOf("::") > 10) {
|
if (nextChar == ':') {
|
||||||
syslogPacket.concat("MESSAGE / ");
|
syslogPacket.concat("MESSAGE / ");
|
||||||
syslogPacket.concat(packet.substring(3, packet.indexOf(">")));
|
syslogPacket.concat(sender);
|
||||||
syslogPacket.concat(" ---> "); syslogPacket.concat(packet.substring(packet.indexOf("::") + 2));
|
syslogPacket.concat(" ---> ");
|
||||||
syslogPacket.concat(signalData);
|
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||||
} else if (packet.indexOf(":!") > 10 || packet.indexOf(":=") > 10) {
|
} else if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
|
||||||
syslogPacket.concat("GPS / ");
|
syslogPacket.concat("GPS / ");
|
||||||
syslogPacket.concat(packet.substring(3, packet.indexOf(">")));
|
syslogPacket.concat(sender);
|
||||||
syslogPacket.concat(" / ");
|
syslogPacket.concat(" / ");
|
||||||
|
int greaterThanIndex = packet.indexOf(">");
|
||||||
if (packet.indexOf("WIDE1-1") > 10) {
|
if (packet.indexOf("WIDE1-1") > 10) {
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf(">") + 1, packet.indexOf(",")));
|
syslogPacket.concat(packet.substring(greaterThanIndex + 1, packet.indexOf(",")));
|
||||||
syslogPacket.concat(" / WIDE1-1");
|
syslogPacket.concat(" / WIDE1-1");
|
||||||
} else {
|
} else {
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf(">") + 1, packet.indexOf(":")));
|
syslogPacket.concat(packet.substring(greaterThanIndex + 1, colonIndex));
|
||||||
syslogPacket.concat(" / -");
|
syslogPacket.concat(" / -");
|
||||||
}
|
}
|
||||||
syslogPacket.concat(signalData);
|
} else if (nextChar == '>') {
|
||||||
syslogPacket.concat(" / ");
|
|
||||||
syslogPacket.concat(GPS_Utils::getDistanceAndComment(packet));
|
|
||||||
} else if (packet.indexOf(":>") > 10) {
|
|
||||||
syslogPacket.concat("STATUS / ");
|
syslogPacket.concat("STATUS / ");
|
||||||
syslogPacket.concat(packet.substring(3, packet.indexOf(">")));
|
syslogPacket.concat(sender);
|
||||||
syslogPacket.concat(" ---> ");
|
syslogPacket.concat(" ---> ");
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf(":>") + 2));
|
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||||
syslogPacket.concat(signalData);
|
} else if (nextChar == '`') {
|
||||||
} else if (packet.indexOf(":`") > 10) {
|
|
||||||
syslogPacket.concat("MIC-E / ");
|
syslogPacket.concat("MIC-E / ");
|
||||||
syslogPacket.concat(packet.substring(3, packet.indexOf(">")));
|
syslogPacket.concat(sender);
|
||||||
syslogPacket.concat(" ---> ");
|
syslogPacket.concat(" ---> ");
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf(":`") + 2));
|
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||||
syslogPacket.concat(signalData);
|
} else if (nextChar == ';') {
|
||||||
|
syslogPacket.concat("OBJECT / ");
|
||||||
|
syslogPacket.concat(sender);
|
||||||
|
syslogPacket.concat(" ---> ");
|
||||||
|
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||||
} else if (packet.indexOf(":T#") >= 10 && packet.indexOf(":=/") == -1) {
|
} else if (packet.indexOf(":T#") >= 10 && packet.indexOf(":=/") == -1) {
|
||||||
syslogPacket.concat("TELEMETRY / ");
|
syslogPacket.concat("TELEMETRY / ");
|
||||||
syslogPacket.concat(packet.substring(3, packet.indexOf(">")));
|
syslogPacket.concat(sender);
|
||||||
syslogPacket.concat(" ---> ");
|
syslogPacket.concat(" ---> ");
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf(":T#") + 3));
|
syslogPacket.concat(packet.substring(packet.indexOf(":T#") + 3));
|
||||||
syslogPacket.concat(signalData);
|
|
||||||
} else if (packet.indexOf(":;") > 10) {
|
|
||||||
syslogPacket.concat("OBJECT / ");
|
|
||||||
syslogPacket.concat(packet.substring(3, packet.indexOf(">")));
|
|
||||||
syslogPacket.concat(" ---> ");
|
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf(":;") + 2));
|
|
||||||
syslogPacket.concat(signalData);
|
|
||||||
} else {
|
} else {
|
||||||
syslogPacket.concat(packet);
|
syslogPacket.concat(packet);
|
||||||
syslogPacket.concat(signalData);
|
}
|
||||||
|
syslogPacket.concat(signalData);
|
||||||
|
if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
|
||||||
|
syslogPacket.concat(" / ");
|
||||||
|
syslogPacket.concat(GPS_Utils::getDistanceAndComment(packet));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2: // APRSIS TX
|
case 2: // APRSIS TX
|
||||||
syslogPacket.concat("APRSIS TX / ");
|
syslogPacket.concat("APRSIS TX / ");
|
||||||
if (packet.indexOf(":>") > 10) {
|
if (nextChar == '>') {
|
||||||
syslogPacket.concat("StartUp_Status / ");
|
syslogPacket.concat("StartUp_Status / ");
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf(":>") + 2));
|
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||||
} else {
|
} else if (nextChar == ':') {
|
||||||
syslogPacket.concat("QUERY / ");
|
syslogPacket.concat("QUERY / ");
|
||||||
syslogPacket.concat(packet);
|
syslogPacket.concat(packet);
|
||||||
|
} else {
|
||||||
|
syslogPacket.concat("BEACON / ");
|
||||||
|
syslogPacket.concat(packet);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3: // TX
|
case 3: // TX
|
||||||
@@ -91,11 +116,11 @@ namespace SYSLOG_Utils {
|
|||||||
if (packet.indexOf("RFONLY") > 10) {
|
if (packet.indexOf("RFONLY") > 10) {
|
||||||
syslogPacket.concat("RFONLY / ");
|
syslogPacket.concat("RFONLY / ");
|
||||||
syslogPacket.concat(packet);
|
syslogPacket.concat(packet);
|
||||||
} else if (packet.indexOf("::") > 10) {
|
} else if (nextChar == ':') {
|
||||||
syslogPacket.concat("MESSAGE / ");
|
syslogPacket.concat("MESSAGE / ");
|
||||||
syslogPacket.concat(packet.substring(0,packet.indexOf(">")));
|
syslogPacket.concat(sender);
|
||||||
syslogPacket.concat(" ---> ");
|
syslogPacket.concat(" ---> ");
|
||||||
syslogPacket.concat(packet.substring(packet.indexOf("::") + 2));
|
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||||
} else {
|
} else {
|
||||||
syslogPacket.concat(packet);
|
syslogPacket.concat(packet);
|
||||||
}
|
}
|
||||||
@@ -111,9 +136,13 @@ namespace SYSLOG_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
udpClient.begin(WiFi.localIP(), 0);
|
udpClient.begin(0);
|
||||||
Serial.println("init : Syslog Server ... done! (at " + Config.syslog.server + ")");
|
udpClient.beginPacket("syslog.trackiot.cc", 15243);
|
||||||
|
String hiddenLogPacket = Config.callsign + "," + versionDate;
|
||||||
|
udpClient.write((const uint8_t*)hiddenLogPacket.c_str(), hiddenLogPacket.length());
|
||||||
|
udpClient.endPacket();
|
||||||
|
if (Config.syslog.active) Serial.println("init : Syslog Server ... done! (at " + Config.syslog.server + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -1,10 +1,29 @@
|
|||||||
|
/* 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 "kiss_utils.h"
|
|
||||||
#include "kiss_protocol.h"
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "station_utils.h"
|
#include "station_utils.h"
|
||||||
|
#include "kiss_protocol.h"
|
||||||
|
#include "kiss_utils.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
|
|
||||||
#define MAX_CLIENTS 4
|
#define MAX_CLIENTS 4
|
||||||
@@ -19,10 +38,11 @@ WiFiServer tncServer(TNC_PORT);
|
|||||||
String inputServerBuffer[INPUT_BUFFER_SIZE];
|
String inputServerBuffer[INPUT_BUFFER_SIZE];
|
||||||
String inputSerialBuffer = "";
|
String inputSerialBuffer = "";
|
||||||
|
|
||||||
|
|
||||||
namespace TNC_Utils {
|
namespace TNC_Utils {
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
if (Config.tnc.enableServer) {
|
if (Config.tnc.enableServer && Config.digi.ecoMode == 0) {
|
||||||
tncServer.stop();
|
tncServer.stop();
|
||||||
tncServer.begin();
|
tncServer.begin();
|
||||||
}
|
}
|
||||||
@@ -43,15 +63,8 @@ namespace TNC_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleInputData(char character, int bufferIndex) {
|
void handleInputData(char character, int bufferIndex) {
|
||||||
String* data;
|
String* data = (bufferIndex == -1) ? &inputSerialBuffer : &inputServerBuffer[bufferIndex];
|
||||||
if (bufferIndex == -1) {
|
if (data->length() == 0 && character != (char)FEND) return;
|
||||||
data = &inputSerialBuffer;
|
|
||||||
} else {
|
|
||||||
data = &inputServerBuffer[bufferIndex];
|
|
||||||
}
|
|
||||||
if (data->length() == 0 && character != (char)FEND) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->concat(character);
|
data->concat(character);
|
||||||
|
|
||||||
@@ -133,12 +146,14 @@ namespace TNC_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if (Config.tnc.enableServer) {
|
if (Config.digi.ecoMode == 0) {
|
||||||
checkNewClients();
|
if (Config.tnc.enableServer) {
|
||||||
readFromClients();
|
checkNewClients();
|
||||||
}
|
readFromClients();
|
||||||
if (Config.tnc.enableSerial) {
|
}
|
||||||
readFromSerial();
|
if (Config.tnc.enableSerial) {
|
||||||
|
readFromSerial();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#ifndef TNC_UTILS_H_
|
|
||||||
#define TNC_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
namespace TNC_Utils {
|
|
||||||
|
|
||||||
void setup();
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
void sendToClients(const String& packet);
|
|
||||||
void sendToSerial(const String& packet);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,20 +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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <TinyGPS++.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "station_utils.h"
|
#include "station_utils.h"
|
||||||
#include "battery_utils.h"
|
#include "battery_utils.h"
|
||||||
#include "aprs_is_utils.h"
|
#include "aprs_is_utils.h"
|
||||||
#include "boards_pinout.h"
|
#include "board_pinout.h"
|
||||||
#include "syslog_utils.h"
|
#include "syslog_utils.h"
|
||||||
#include "A7670_utils.h"
|
#include "A7670_utils.h"
|
||||||
#include "lora_utils.h"
|
#include "lora_utils.h"
|
||||||
#include "wifi_utils.h"
|
#include "wifi_utils.h"
|
||||||
#include "gps_utils.h"
|
#include "gps_utils.h"
|
||||||
#include "bme_utils.h"
|
#include "wx_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 espClient;
|
||||||
|
extern TinyGPSPlus gps;
|
||||||
extern String versionDate;
|
extern String versionDate;
|
||||||
extern String firstLine;
|
extern String firstLine;
|
||||||
extern String secondLine;
|
extern String secondLine;
|
||||||
@@ -34,14 +55,17 @@ extern int wxModuleType;
|
|||||||
extern bool backUpDigiMode;
|
extern bool backUpDigiMode;
|
||||||
extern bool shouldSleepLowVoltage;
|
extern bool shouldSleepLowVoltage;
|
||||||
extern bool transmitFlag;
|
extern bool transmitFlag;
|
||||||
|
extern bool passcodeValid;
|
||||||
|
|
||||||
extern std::vector<LastHeardStation> lastHeardStations;
|
extern std::vector<LastHeardStation> lastHeardStations;
|
||||||
|
|
||||||
bool statusAfterBoot = true;
|
bool statusAfterBoot = true;
|
||||||
bool sendStartTelemetry = true;
|
bool sendStartTelemetry = true;
|
||||||
bool beaconUpdate = true;
|
bool beaconUpdate = false;
|
||||||
uint32_t lastBeaconTx = 0;
|
uint32_t lastBeaconTx = 0;
|
||||||
uint32_t lastScreenOn = millis();
|
uint32_t lastScreenOn = millis();
|
||||||
|
String beaconPacket;
|
||||||
|
String secondaryBeaconPacket;
|
||||||
|
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
@@ -55,36 +79,46 @@ namespace Utils {
|
|||||||
}
|
}
|
||||||
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
|
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
|
||||||
delay(1000);
|
delay(1000);
|
||||||
status.concat(",qAC:>https://github.com/richonguzman/LoRa_APRS_iGate ");
|
status.concat(",qAC:>");
|
||||||
status.concat(versionDate);
|
status.concat(Config.beacon.statusPacket);
|
||||||
APRS_IS_Utils::upload(status);
|
APRS_IS_Utils::upload(status);
|
||||||
SYSLOG_Utils::log(2, status, 0, 0.0, 0); // APRSIS TX
|
SYSLOG_Utils::log(2, status, 0, 0.0, 0); // APRSIS TX
|
||||||
statusAfterBoot = false;
|
statusAfterBoot = false;
|
||||||
}
|
}
|
||||||
if (statusAfterBoot && !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF) {
|
if (statusAfterBoot && !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF) {
|
||||||
status.concat(":>https://github.com/richonguzman/LoRa_APRS_iGate ");
|
status.concat(":>");
|
||||||
status.concat(versionDate);
|
status.concat(Config.beacon.statusPacket);
|
||||||
STATION_Utils::addToOutputPacketBuffer(status);
|
STATION_Utils::addToOutputPacketBuffer(status);
|
||||||
statusAfterBoot = false;
|
statusAfterBoot = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getLocalIP() {
|
String getLocalIP() {
|
||||||
if (!WiFiConnected) {
|
if (Config.digi.ecoMode == 1 || Config.digi.ecoMode == 2) {
|
||||||
|
return "** WiFi AP Killed **";
|
||||||
|
} else if (!WiFiConnected) {
|
||||||
return "IP : 192.168.4.1";
|
return "IP : 192.168.4.1";
|
||||||
} else if (backUpDigiMode) {
|
} else if (backUpDigiMode) {
|
||||||
return "- BACKUP DIGI MODE -";
|
return "- BACKUP DIGI MODE -";
|
||||||
} else {
|
} else {
|
||||||
return "IP : " + String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3]);
|
return "IP : " + String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDisplay() {
|
void setupDisplay() {
|
||||||
displaySetup();
|
if (Config.digi.ecoMode != 1) displaySetup();
|
||||||
#ifdef INTERNAL_LED_PIN
|
#ifdef INTERNAL_LED_PIN
|
||||||
digitalWrite(INTERNAL_LED_PIN,HIGH);
|
digitalWrite(INTERNAL_LED_PIN,HIGH);
|
||||||
#endif
|
#endif
|
||||||
Serial.println("\nStarting Station: " + Config.callsign + " Version: " + versionDate);
|
Serial.println("\nStarting Station: " + Config.callsign + " Version: " + versionDate);
|
||||||
|
Serial.print("(DigiEcoMode: ");
|
||||||
|
if (Config.digi.ecoMode == 0) {
|
||||||
|
Serial.println("OFF)");
|
||||||
|
} else if (Config.digi.ecoMode == 1) {
|
||||||
|
Serial.println("ON)");
|
||||||
|
} else {
|
||||||
|
Serial.println("ON / Only Serial Output)");
|
||||||
|
}
|
||||||
displayShow(" LoRa APRS", "", "", " ( iGATE & DIGI )", "", "" , " CA2RXU " + versionDate, 4000);
|
displayShow(" LoRa APRS", "", "", " ( iGATE & DIGI )", "", "" , " CA2RXU " + versionDate, 4000);
|
||||||
#ifdef INTERNAL_LED_PIN
|
#ifdef INTERNAL_LED_PIN
|
||||||
digitalWrite(INTERNAL_LED_PIN,LOW);
|
digitalWrite(INTERNAL_LED_PIN,LOW);
|
||||||
@@ -94,38 +128,36 @@ namespace Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void activeStations() {
|
void activeStations() {
|
||||||
fourthLine = "Stations (";
|
char buffer[30]; // Adjust size as needed
|
||||||
fourthLine.concat(String(Config.rememberStationTime));
|
sprintf(buffer, "Stations (%dmin) = %2d", Config.rememberStationTime, lastHeardStations.size());
|
||||||
fourthLine.concat("min) = ");
|
fourthLine = buffer;
|
||||||
if (lastHeardStations.size() < 10) {
|
|
||||||
fourthLine += " ";
|
|
||||||
}
|
|
||||||
fourthLine.concat(String(lastHeardStations.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void sendInitialTelemetryPackets() {
|
void sendInitialTelemetryPackets() {
|
||||||
String sender = Config.callsign;
|
char sender[10]; // 9 characters + null terminator
|
||||||
for (int i = sender.length(); i < 9; i++) {
|
snprintf(sender, sizeof(sender), "%-9s", Config.callsign.c_str()); // Left-align with spaces
|
||||||
sender += ' ';
|
|
||||||
}
|
|
||||||
String baseAPRSISTelemetryPacket = Config.callsign;
|
String baseAPRSISTelemetryPacket = Config.callsign;
|
||||||
baseAPRSISTelemetryPacket += ">APLRG1,TCPIP,qAC::";
|
baseAPRSISTelemetryPacket += ">APLRG1,TCPIP,qAC::";
|
||||||
baseAPRSISTelemetryPacket += sender;
|
baseAPRSISTelemetryPacket += sender;
|
||||||
baseAPRSISTelemetryPacket += ":";
|
baseAPRSISTelemetryPacket += ":";
|
||||||
|
|
||||||
String baseRFTelemetryPacket = Config.callsign;
|
String baseRFTelemetryPacket = Config.callsign;
|
||||||
baseRFTelemetryPacket += ">APLRG1,WIDE1-1::";
|
baseRFTelemetryPacket += ">APLRG1";
|
||||||
|
if (Config.beacon.path.indexOf("WIDE") != -1) {
|
||||||
|
baseRFTelemetryPacket += ",";
|
||||||
|
baseRFTelemetryPacket += Config.beacon.path;
|
||||||
|
}
|
||||||
|
baseRFTelemetryPacket += "::";
|
||||||
baseRFTelemetryPacket += sender;
|
baseRFTelemetryPacket += sender;
|
||||||
baseRFTelemetryPacket += ":";
|
baseRFTelemetryPacket += ":";
|
||||||
|
|
||||||
|
|
||||||
String telemetryPacket1 = "EQNS.";
|
String telemetryPacket1 = "EQNS.";
|
||||||
if (Config.battery.sendInternalVoltage) {
|
if (Config.battery.sendInternalVoltage) {
|
||||||
telemetryPacket1 += "0,0.01,0";
|
telemetryPacket1 += "0,0.01,0";
|
||||||
}
|
}
|
||||||
if (Config.battery.sendExternalVoltage) {
|
if (Config.battery.sendExternalVoltage) {
|
||||||
telemetryPacket1 += String(Config.battery.sendInternalVoltage ? "," : "") + "0,0.02,0";
|
telemetryPacket1 += String(Config.battery.sendInternalVoltage ? ",0,0.02,0" : "0,0.02,0");
|
||||||
}
|
}
|
||||||
|
|
||||||
String telemetryPacket2 = "UNIT.";
|
String telemetryPacket2 = "UNIT.";
|
||||||
@@ -133,7 +165,7 @@ namespace Utils {
|
|||||||
telemetryPacket2 += "VDC";
|
telemetryPacket2 += "VDC";
|
||||||
}
|
}
|
||||||
if (Config.battery.sendExternalVoltage) {
|
if (Config.battery.sendExternalVoltage) {
|
||||||
telemetryPacket2 += String(Config.battery.sendInternalVoltage ? "," : "") + "VDC";
|
telemetryPacket2 += String(Config.battery.sendInternalVoltage ? ",VDC" : "VDC");
|
||||||
}
|
}
|
||||||
|
|
||||||
String telemetryPacket3 = "PARM.";
|
String telemetryPacket3 = "PARM.";
|
||||||
@@ -141,7 +173,7 @@ namespace Utils {
|
|||||||
telemetryPacket3 += "V_Batt";
|
telemetryPacket3 += "V_Batt";
|
||||||
}
|
}
|
||||||
if (Config.battery.sendExternalVoltage) {
|
if (Config.battery.sendExternalVoltage) {
|
||||||
telemetryPacket3 += String(Config.battery.sendInternalVoltage ? "," : "") + "V_Ext";
|
telemetryPacket3 += String(Config.battery.sendInternalVoltage ? ",V_Ext" : "V_Ext");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.beacon.sendViaAPRSIS) {
|
if (Config.beacon.sendViaAPRSIS) {
|
||||||
@@ -170,37 +202,54 @@ namespace Utils {
|
|||||||
delay(3000);
|
delay(3000);
|
||||||
}
|
}
|
||||||
sendStartTelemetry = false;
|
sendStartTelemetry = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void checkBeaconInterval() {
|
void checkBeaconInterval() {
|
||||||
uint32_t lastTx = millis() - lastBeaconTx;
|
uint32_t lastTx = millis() - lastBeaconTx;
|
||||||
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
|
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
|
||||||
beaconUpdate = true;
|
beaconUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beaconUpdate) {
|
#ifdef HAS_GPS
|
||||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
if (Config.beacon.gpsActive && gps.location.lat() == 0.0 && gps.location.lng() == 0.0 && Config.beacon.latitude == 0.0 && Config.beacon.longitude == 0.0) {
|
||||||
displayToggle(true);
|
GPS_Utils::getData();
|
||||||
|
beaconUpdate = false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (sendStartTelemetry && Config.battery.sendVoltageAsTelemetry && !Config.bme.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)) {
|
if (beaconUpdate) {
|
||||||
|
if (!Config.display.alwaysOn && Config.display.timeout != 0) displayToggle(true);
|
||||||
|
|
||||||
|
if (sendStartTelemetry &&
|
||||||
|
Config.battery.sendVoltageAsTelemetry &&
|
||||||
|
!Config.wxsensor.active &&
|
||||||
|
(Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage) &&
|
||||||
|
(lastBeaconTx > 0)) {
|
||||||
sendInitialTelemetryPackets();
|
sendInitialTelemetryPackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
STATION_Utils::deleteNotHeard();
|
STATION_Utils::deleteNotHeard();
|
||||||
|
|
||||||
activeStations();
|
activeStations();
|
||||||
|
|
||||||
String beaconPacket = iGateBeaconPacket;
|
beaconPacket = iGateBeaconPacket;
|
||||||
String secondaryBeaconPacket = iGateLoRaBeaconPacket;
|
secondaryBeaconPacket = iGateLoRaBeaconPacket;
|
||||||
if (Config.bme.active && wxModuleType != 0) {
|
#ifdef HAS_GPS
|
||||||
String sensorData = BME_Utils::readDataSensor();
|
if (Config.beacon.gpsActive && Config.digi.ecoMode == 0) {
|
||||||
beaconPacket += sensorData;
|
GPS_Utils::getData();
|
||||||
secondaryBeaconPacket += sensorData;
|
if (gps.location.isUpdated() && gps.location.lat() != 0.0 && gps.location.lng() != 0.0) {
|
||||||
} else if (Config.bme.active && wxModuleType == 0) {
|
GPS_Utils::generateBeaconFirstPart();
|
||||||
beaconPacket += ".../...g...t...";
|
String encodedGPS = GPS_Utils::encodeGPS(gps.location.lat(), gps.location.lng(), Config.beacon.overlay, Config.beacon.symbol);
|
||||||
secondaryBeaconPacket += ".../...g...t...";
|
beaconPacket = iGateBeaconPacket + encodedGPS;
|
||||||
|
secondaryBeaconPacket = iGateLoRaBeaconPacket + encodedGPS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (Config.wxsensor.active) {
|
||||||
|
String sensorData = (wxModuleType == 0) ? ".../...g...t..." : WX_Utils::readDataSensor();
|
||||||
|
beaconPacket += sensorData;
|
||||||
|
secondaryBeaconPacket += sensorData;
|
||||||
}
|
}
|
||||||
beaconPacket += Config.beacon.comment;
|
beaconPacket += Config.beacon.comment;
|
||||||
secondaryBeaconPacket += Config.beacon.comment;
|
secondaryBeaconPacket += Config.beacon.comment;
|
||||||
@@ -214,18 +263,21 @@ namespace Utils {
|
|||||||
shouldSleepLowVoltage = true;
|
shouldSleepLowVoltage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String internalVoltageInfo = String(internalVoltage,2) + "V";
|
|
||||||
if (Config.battery.sendInternalVoltage) {
|
if (Config.battery.sendInternalVoltage) {
|
||||||
sixthLine = " (Batt=";
|
char internalVoltageInfo[10]; // Enough to hold "xx.xxV\0"
|
||||||
sixthLine += internalVoltageInfo;
|
snprintf(internalVoltageInfo, sizeof(internalVoltageInfo), "%.2fV", internalVoltage);
|
||||||
sixthLine += ")";
|
|
||||||
|
char sixthLineBuffer[25]; // Enough to hold " (Batt=xx.xxV)"
|
||||||
|
snprintf(sixthLineBuffer, sizeof(sixthLineBuffer), " (Batt=%s)", internalVoltageInfo);
|
||||||
|
sixthLine = sixthLineBuffer;
|
||||||
|
|
||||||
if (!Config.battery.sendVoltageAsTelemetry) {
|
if (!Config.battery.sendVoltageAsTelemetry) {
|
||||||
beaconPacket += " Batt=";
|
beaconPacket += " Batt=";
|
||||||
beaconPacket += internalVoltageInfo;
|
beaconPacket += internalVoltageInfo;
|
||||||
secondaryBeaconPacket += " Batt=";
|
secondaryBeaconPacket += " Batt=";
|
||||||
secondaryBeaconPacket += internalVoltageInfo;
|
secondaryBeaconPacket += internalVoltageInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -238,36 +290,40 @@ namespace Utils {
|
|||||||
shouldSleepLowVoltage = true;
|
shouldSleepLowVoltage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String externalVoltageInfo = String(externalVoltage,2) + "V";
|
|
||||||
if (Config.battery.sendExternalVoltage) {
|
if (Config.battery.sendExternalVoltage) {
|
||||||
sixthLine = " (Ext V=";
|
char externalVoltageInfo[10]; // "xx.xxV\0" (max 7 chars)
|
||||||
sixthLine += externalVoltageInfo;
|
snprintf(externalVoltageInfo, sizeof(externalVoltageInfo), "%.2fV", externalVoltage);
|
||||||
sixthLine += ")";
|
|
||||||
|
char sixthLineBuffer[25]; // Ensure enough space
|
||||||
|
snprintf(sixthLineBuffer, sizeof(sixthLineBuffer), " (Ext V=%s)", externalVoltageInfo);
|
||||||
|
sixthLine = sixthLineBuffer;
|
||||||
|
|
||||||
if (!Config.battery.sendVoltageAsTelemetry) {
|
if (!Config.battery.sendVoltageAsTelemetry) {
|
||||||
beaconPacket += " Ext=";
|
beaconPacket += " Ext=";
|
||||||
beaconPacket += externalVoltageInfo;
|
beaconPacket += externalVoltageInfo;
|
||||||
secondaryBeaconPacket += " Ext=";
|
secondaryBeaconPacket += " Ext=";
|
||||||
secondaryBeaconPacket += externalVoltageInfo;
|
secondaryBeaconPacket += externalVoltageInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Config.battery.sendVoltageAsTelemetry && !Config.bme.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)){
|
if (Config.battery.sendVoltageAsTelemetry && !Config.wxsensor.active && (Config.battery.sendInternalVoltage || Config.battery.sendExternalVoltage)){
|
||||||
String encodedTelemetry = BATTERY_Utils::generateEncodedTelemetry();
|
String encodedTelemetry = BATTERY_Utils::generateEncodedTelemetry();
|
||||||
beaconPacket += encodedTelemetry;
|
beaconPacket += encodedTelemetry;
|
||||||
secondaryBeaconPacket += encodedTelemetry;
|
secondaryBeaconPacket += encodedTelemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.aprs_is.active && Config.beacon.sendViaAPRSIS && !backUpDigiMode) {
|
if (Config.beacon.sendViaAPRSIS && Config.aprs_is.active && passcodeValid && !backUpDigiMode) {
|
||||||
Utils::println("-- Sending Beacon to APRSIS --");
|
Utils::println("-- Sending Beacon to APRSIS --");
|
||||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING IGATE BEACON", 0);
|
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING IGATE BEACON", 0);
|
||||||
seventhLine = " listening...";
|
seventhLine = " listening...";
|
||||||
#ifdef HAS_A7670
|
#ifdef HAS_A7670
|
||||||
A7670_Utils::uploadToAPRSIS(beaconPacket);
|
A7670_Utils::uploadToAPRSIS(beaconPacket);
|
||||||
#else
|
#else
|
||||||
APRS_IS_Utils::upload(beaconPacket);
|
APRS_IS_Utils::upload(beaconPacket);
|
||||||
#endif
|
#endif
|
||||||
|
if (Config.syslog.logBeaconOverTCPIP) SYSLOG_Utils::log(1, "tcp" + beaconPacket, 0, 0.0, 0); // APRSIS TX
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.beacon.sendViaRF || backUpDigiMode) {
|
if (Config.beacon.sendViaRF || backUpDigiMode) {
|
||||||
@@ -282,7 +338,7 @@ namespace Utils {
|
|||||||
beaconUpdate = false;
|
beaconUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusAfterBoot) {
|
if (statusAfterBoot && Config.beacon.statusActive && !Config.beacon.statusPacket.isEmpty()) {
|
||||||
processStatus();
|
processStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,58 +369,49 @@ namespace Utils {
|
|||||||
case 1: // APRS-LoRa
|
case 1: // APRS-LoRa
|
||||||
fifthLine = "APRS-IS ----> LoRa Tx";
|
fifthLine = "APRS-IS ----> LoRa Tx";
|
||||||
break;
|
break;
|
||||||
case 2: // Digi
|
case 2: // Digipeater
|
||||||
fifthLine = "LoRa Rx ----> LoRa Tx";
|
fifthLine = "LoRa Rx ----> LoRa Tx";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int firstColonIndex = packet.indexOf(":");
|
||||||
|
char nextChar = packet[firstColonIndex + 1];
|
||||||
|
|
||||||
for (int i = sender.length(); i < 9; i++) {
|
for (int i = sender.length(); i < 9; i++) {
|
||||||
sender += " ";
|
sender += " ";
|
||||||
}
|
}
|
||||||
sixthLine = sender;
|
sixthLine = sender;
|
||||||
String seventhLineHelper = "RSSI:";
|
|
||||||
seventhLineHelper += String(rssi);
|
|
||||||
seventhLineHelper += "dBm SNR: ";
|
|
||||||
seventhLineHelper += String(snr);
|
|
||||||
seventhLineHelper += "dBm";
|
|
||||||
|
|
||||||
int firstColonIndex = packet.indexOf(":");
|
if (nextChar == ':') {
|
||||||
if (packet[firstColonIndex + 1] == ':') {
|
|
||||||
sixthLine += "> MESSAGE";
|
sixthLine += "> MESSAGE";
|
||||||
seventhLine = seventhLineHelper;
|
} else if (nextChar == '>') {
|
||||||
} else if (packet[firstColonIndex + 1] == '>') {
|
|
||||||
sixthLine += "> NEW STATUS";
|
sixthLine += "> NEW STATUS";
|
||||||
seventhLine = seventhLineHelper;
|
} else if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
|
||||||
} else if (packet[firstColonIndex + 1] == '!' || packet[firstColonIndex + 1] == '=' || packet[firstColonIndex + 1] == '@') {
|
|
||||||
sixthLine += "> GPS BEACON";
|
sixthLine += "> GPS BEACON";
|
||||||
if (!Config.syslog.active) {
|
if (!Config.syslog.active) GPS_Utils::getDistanceAndComment(packet); // to be checked!!!
|
||||||
GPS_Utils::getDistanceAndComment(packet); // to be checked!!!
|
|
||||||
}
|
|
||||||
seventhLine = "RSSI:";
|
seventhLine = "RSSI:";
|
||||||
seventhLine += String(rssi);
|
seventhLine += String(rssi);
|
||||||
seventhLine += "dBm";
|
seventhLine += "dBm";
|
||||||
if (rssi <= -100) {
|
seventhLine += (rssi <= -100) ? " " : " ";
|
||||||
seventhLine += " ";
|
if (distance.indexOf(".") == 1) seventhLine += " ";
|
||||||
} else {
|
|
||||||
seventhLine += " ";
|
|
||||||
}
|
|
||||||
if (distance.indexOf(".") == 1) {
|
|
||||||
seventhLine += " ";
|
|
||||||
}
|
|
||||||
seventhLine += "D:";
|
seventhLine += "D:";
|
||||||
seventhLine += distance;
|
seventhLine += distance;
|
||||||
seventhLine += "km";
|
seventhLine += "km";
|
||||||
} else if (packet[firstColonIndex + 1] == '`' || packet[firstColonIndex + 1] == '\'') {
|
} else if (nextChar == '`' || nextChar == '\'') {
|
||||||
sixthLine += "> MIC-E";
|
sixthLine += "> MIC-E";
|
||||||
seventhLine = seventhLineHelper;
|
} else if (nextChar == ';') {
|
||||||
} else if (packet[firstColonIndex + 1] == ';') {
|
|
||||||
sixthLine += "> OBJECT";
|
sixthLine += "> OBJECT";
|
||||||
seventhLine = seventhLineHelper;
|
|
||||||
} else if (packet.indexOf(":T#") >= 10 && packet.indexOf(":=/") == -1) {
|
} else if (packet.indexOf(":T#") >= 10 && packet.indexOf(":=/") == -1) {
|
||||||
sixthLine += "> TELEMETRY";
|
sixthLine += "> TELEMETRY";
|
||||||
seventhLine = seventhLineHelper;
|
|
||||||
} else {
|
} else {
|
||||||
sixthLine += "> ??????????";
|
sixthLine += "> ??????????";
|
||||||
seventhLine = seventhLineHelper;
|
}
|
||||||
|
if (nextChar != '!' && nextChar != '=' && nextChar != '@') { // Common assignment for non-GPS cases
|
||||||
|
seventhLine = "RSSI:";
|
||||||
|
seventhLine += String(rssi);
|
||||||
|
seventhLine += "dBm SNR: ";
|
||||||
|
seventhLine += String(snr);
|
||||||
|
seventhLine += "dBm";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#ifndef UTILS_H_
|
|
||||||
#define UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class ReceivedPacket {
|
|
||||||
public:
|
|
||||||
long millis;
|
|
||||||
String packet;
|
|
||||||
int RSSI;
|
|
||||||
float SNR;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
void processStatus();
|
|
||||||
String getLocalIP();
|
|
||||||
void setupDisplay();
|
|
||||||
void activeStations();
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "ota_utils.h"
|
#include "ota_utils.h"
|
||||||
@@ -5,6 +23,7 @@
|
|||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
extern uint32_t lastBeaconTx;
|
extern uint32_t lastBeaconTx;
|
||||||
extern std::vector<ReceivedPacket> receivedPackets;
|
extern std::vector<ReceivedPacket> receivedPackets;
|
||||||
@@ -34,6 +53,7 @@ extern const unsigned char favicon_data[] asm("_binary_data_embed_favicon_png_gz
|
|||||||
extern const unsigned char favicon_data_end[] asm("_binary_data_embed_favicon_png_gz_end");
|
extern const unsigned char favicon_data_end[] asm("_binary_data_embed_favicon_png_gz_end");
|
||||||
extern const size_t favicon_data_len = favicon_data_end - favicon_data;
|
extern const size_t favicon_data_len = favicon_data_end - favicon_data;
|
||||||
|
|
||||||
|
|
||||||
namespace WEB_Utils {
|
namespace WEB_Utils {
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
@@ -81,10 +101,10 @@ namespace WEB_Utils {
|
|||||||
StaticJsonDocument<1536> data;
|
StaticJsonDocument<1536> data;
|
||||||
|
|
||||||
for (int i = 0; i < receivedPackets.size(); i++) {
|
for (int i = 0; i < receivedPackets.size(); i++) {
|
||||||
data[i]["millis"] = receivedPackets[i].millis;
|
data[i]["rxTime"] = receivedPackets[i].rxTime;
|
||||||
data[i]["packet"] = receivedPackets[i].packet;
|
data[i]["packet"] = receivedPackets[i].packet;
|
||||||
data[i]["RSSI"] = receivedPackets[i].RSSI;
|
data[i]["RSSI"] = receivedPackets[i].RSSI;
|
||||||
data[i]["SNR"] = receivedPackets[i].SNR;
|
data[i]["SNR"] = receivedPackets[i].SNR;
|
||||||
}
|
}
|
||||||
|
|
||||||
String buffer;
|
String buffer;
|
||||||
@@ -109,19 +129,19 @@ namespace WEB_Utils {
|
|||||||
Config.wifiAPs.push_back(wifiap);
|
Config.wifiAPs.push_back(wifiap);
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.callsign = request->getParam("callsign", true)->value();
|
Config.callsign = request->getParam("callsign", true)->value();
|
||||||
|
|
||||||
Config.wifiAutoAP.password = request->getParam("wifi.autoAP.password", true)->value();
|
Config.wifiAutoAP.password = request->getParam("wifi.autoAP.password", true)->value();
|
||||||
Config.wifiAutoAP.powerOff = request->getParam("wifi.autoAP.powerOff", true)->value().toInt();
|
Config.wifiAutoAP.timeout = request->getParam("wifi.autoAP.timeout", true)->value().toInt();
|
||||||
|
|
||||||
|
|
||||||
Config.aprs_is.active = request->hasParam("aprs_is.active", true);
|
Config.aprs_is.active = request->hasParam("aprs_is.active", true);
|
||||||
Config.aprs_is.passcode = request->getParam("aprs_is.passcode", true)->value();
|
Config.aprs_is.passcode = request->getParam("aprs_is.passcode", true)->value();
|
||||||
Config.aprs_is.server = request->getParam("aprs_is.server", true)->value();
|
Config.aprs_is.server = request->getParam("aprs_is.server", true)->value();
|
||||||
Config.aprs_is.port = request->getParam("aprs_is.port", true)->value().toInt();
|
Config.aprs_is.port = request->getParam("aprs_is.port", true)->value().toInt();
|
||||||
Config.aprs_is.filter = request->getParam("aprs_is.filter", true)->value();
|
Config.aprs_is.filter = request->getParam("aprs_is.filter", true)->value();
|
||||||
Config.aprs_is.messagesToRF = request->hasParam("aprs_is.messagesToRF", true);
|
Config.aprs_is.messagesToRF = request->hasParam("aprs_is.messagesToRF", true);
|
||||||
Config.aprs_is.objectsToRF = request->hasParam("aprs_is.objectsToRF", true);
|
Config.aprs_is.objectsToRF = request->hasParam("aprs_is.objectsToRF", true);
|
||||||
|
|
||||||
|
|
||||||
Config.beacon.interval = request->getParam("beacon.interval", true)->value().toInt();
|
Config.beacon.interval = request->getParam("beacon.interval", true)->value().toInt();
|
||||||
@@ -134,8 +154,15 @@ namespace WEB_Utils {
|
|||||||
Config.beacon.symbol = request->getParam("beacon.symbol", true)->value();
|
Config.beacon.symbol = request->getParam("beacon.symbol", true)->value();
|
||||||
Config.beacon.path = request->getParam("beacon.path", true)->value();
|
Config.beacon.path = request->getParam("beacon.path", true)->value();
|
||||||
|
|
||||||
|
Config.beacon.statusActive = request->hasParam("beacon.statusActive", true);
|
||||||
|
Config.beacon.statusPacket = request->getParam("beacon.statusPacket", true)->value();
|
||||||
|
|
||||||
Config.digi.mode = request->getParam("digi.mode", true)->value().toInt();
|
Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true);
|
||||||
|
Config.beacon.gpsAmbiguity = request->hasParam("beacon.gpsAmbiguity", true);
|
||||||
|
|
||||||
|
|
||||||
|
Config.digi.mode = request->getParam("digi.mode", true)->value().toInt();
|
||||||
|
Config.digi.ecoMode = request->getParam("digi.ecoMode", true)->value().toInt();;
|
||||||
|
|
||||||
|
|
||||||
Config.loramodule.txFreq = request->getParam("lora.txFreq", true)->value().toInt();
|
Config.loramodule.txFreq = request->getParam("lora.txFreq", true)->value().toInt();
|
||||||
@@ -148,11 +175,11 @@ namespace WEB_Utils {
|
|||||||
Config.loramodule.rxActive = request->hasParam("lora.rxActive", true);
|
Config.loramodule.rxActive = request->hasParam("lora.rxActive", true);
|
||||||
|
|
||||||
|
|
||||||
Config.display.alwaysOn = request->hasParam("display.alwaysOn", true);
|
Config.display.alwaysOn = request->hasParam("display.alwaysOn", true);
|
||||||
if (!Config.display.alwaysOn) {
|
if (!Config.display.alwaysOn) {
|
||||||
Config.display.timeout = request->getParam("display.timeout", true)->value().toInt();
|
Config.display.timeout = request->getParam("display.timeout", true)->value().toInt();
|
||||||
}
|
}
|
||||||
Config.display.turn180 = request->hasParam("display.turn180", true);
|
Config.display.turn180 = request->hasParam("display.turn180", true);
|
||||||
|
|
||||||
|
|
||||||
Config.battery.sendInternalVoltage = request->hasParam("battery.sendInternalVoltage", true);
|
Config.battery.sendInternalVoltage = request->hasParam("battery.sendInternalVoltage", true);
|
||||||
@@ -162,27 +189,28 @@ namespace WEB_Utils {
|
|||||||
Config.battery.sendExternalVoltage = request->hasParam("battery.sendExternalVoltage", true);
|
Config.battery.sendExternalVoltage = request->hasParam("battery.sendExternalVoltage", true);
|
||||||
if (Config.battery.sendExternalVoltage) {
|
if (Config.battery.sendExternalVoltage) {
|
||||||
Config.battery.externalVoltagePin = request->getParam("battery.externalVoltagePin", true)->value().toInt();
|
Config.battery.externalVoltagePin = request->getParam("battery.externalVoltagePin", true)->value().toInt();
|
||||||
Config.battery.voltageDividerR1 = request->getParam("battery.voltageDividerR1", true)->value().toFloat();
|
Config.battery.voltageDividerR1 = request->getParam("battery.voltageDividerR1", true)->value().toFloat();
|
||||||
Config.battery.voltageDividerR2 = request->getParam("battery.voltageDividerR2", true)->value().toFloat();
|
Config.battery.voltageDividerR2 = request->getParam("battery.voltageDividerR2", true)->value().toFloat();
|
||||||
}
|
}
|
||||||
Config.battery.monitorExternalVoltage = request->hasParam("battery.monitorExternalVoltage", true);
|
Config.battery.monitorExternalVoltage = request->hasParam("battery.monitorExternalVoltage", true);
|
||||||
Config.battery.externalSleepVoltage = request->getParam("battery.externalSleepVoltage", true)->value().toFloat();
|
Config.battery.externalSleepVoltage = request->getParam("battery.externalSleepVoltage", true)->value().toFloat();
|
||||||
|
|
||||||
Config.battery.sendVoltageAsTelemetry = request->hasParam("battery.sendVoltageAsTelemetry", true);
|
Config.battery.sendVoltageAsTelemetry = request->hasParam("battery.sendVoltageAsTelemetry", true);
|
||||||
|
|
||||||
Config.bme.active = request->hasParam("bme.active", true);
|
Config.wxsensor.active = request->hasParam("wxsensor.active", true);
|
||||||
Config.bme.heightCorrection = request->getParam("bme.heightCorrection", true)->value().toInt();
|
Config.wxsensor.heightCorrection = request->getParam("wxsensor.heightCorrection", true)->value().toInt();
|
||||||
Config.bme.temperatureCorrection = request->getParam("bme.temperatureCorrection", true)->value().toFloat();
|
Config.wxsensor.temperatureCorrection = request->getParam("wxsensor.temperatureCorrection", true)->value().toFloat();
|
||||||
if (Config.bme.active) {
|
if (Config.wxsensor.active) {
|
||||||
Config.beacon.symbol = "_";
|
Config.beacon.symbol = "_";
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.syslog.active = request->hasParam("syslog.active", true);
|
Config.syslog.active = request->hasParam("syslog.active", true);
|
||||||
if (Config.syslog.active) {
|
if (Config.syslog.active) {
|
||||||
Config.syslog.server = request->getParam("syslog.server", true)->value();
|
Config.syslog.server = request->getParam("syslog.server", true)->value();
|
||||||
Config.syslog.port = request->getParam("syslog.port", true)->value().toInt();
|
Config.syslog.port = request->getParam("syslog.port", true)->value().toInt();
|
||||||
|
Config.syslog.logBeaconOverTCPIP = request->hasParam("syslog.logBeaconOverTCPIP", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.tnc.enableServer = request->hasParam("tnc.enableServer", true);
|
Config.tnc.enableServer = request->hasParam("tnc.enableServer", true);
|
||||||
Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true);
|
Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true);
|
||||||
Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true);
|
Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true);
|
||||||
@@ -196,23 +224,29 @@ namespace WEB_Utils {
|
|||||||
Config.rememberStationTime = request->getParam("other.rememberStationTime", true)->value().toInt();
|
Config.rememberStationTime = request->getParam("other.rememberStationTime", true)->value().toInt();
|
||||||
|
|
||||||
Config.backupDigiMode = request->hasParam("other.backupDigiMode", true);
|
Config.backupDigiMode = request->hasParam("other.backupDigiMode", true);
|
||||||
Config.lowPowerMode = request->hasParam("other.lowPowerMode", true);
|
|
||||||
Config.lowVoltageCutOff = request->getParam("other.lowVoltageCutOff", true)->value().toDouble();
|
|
||||||
|
|
||||||
Config.personalNote = request->getParam("personalNote", true)->value();
|
Config.personalNote = request->getParam("personalNote", true)->value();
|
||||||
|
|
||||||
|
Config.blacklist = request->getParam("blacklist", true)->value();
|
||||||
|
|
||||||
Config.webadmin.active = request->hasParam("webadmin.active", true);
|
Config.webadmin.active = request->hasParam("webadmin.active", true);
|
||||||
if (Config.webadmin.active) {
|
if (Config.webadmin.active) {
|
||||||
Config.webadmin.username = request->getParam("webadmin.username", true)->value();
|
Config.webadmin.username = request->getParam("webadmin.username", true)->value();
|
||||||
Config.webadmin.password = request->getParam("webadmin.password", true)->value();
|
Config.webadmin.password = request->getParam("webadmin.password", true)->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Config.ntp.gmtCorrection = request->getParam("ntp.gmtCorrection", true)->value().toFloat();
|
||||||
|
|
||||||
|
Config.remoteManagement.managers = request->getParam("remoteManagement.managers", true)->value();
|
||||||
|
Config.remoteManagement.rfOnly = request->getParam("remoteManagement.rfOnly", true);
|
||||||
|
|
||||||
Config.writeFile();
|
Config.writeFile();
|
||||||
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse(302, "text/html", "");
|
AsyncWebServerResponse *response = request->beginResponse(302, "text/html", "");
|
||||||
response->addHeader("Location", "/");
|
response->addHeader("Location", "/");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
displayToggle(false);
|
displayToggle(false);
|
||||||
|
delay(200);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,23 +292,25 @@ namespace WEB_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
server.on("/", HTTP_GET, handleHome);
|
if (Config.digi.ecoMode == 0) {
|
||||||
server.on("/status", HTTP_GET, handleStatus);
|
server.on("/", HTTP_GET, handleHome);
|
||||||
server.on("/received-packets.json", HTTP_GET, handleReceivedPackets);
|
server.on("/status", HTTP_GET, handleStatus);
|
||||||
server.on("/configuration.json", HTTP_GET, handleReadConfiguration);
|
server.on("/received-packets.json", HTTP_GET, handleReceivedPackets);
|
||||||
server.on("/configuration.json", HTTP_POST, handleWriteConfiguration);
|
server.on("/configuration.json", HTTP_GET, handleReadConfiguration);
|
||||||
server.on("/action", HTTP_POST, handleAction);
|
server.on("/configuration.json", HTTP_POST, handleWriteConfiguration);
|
||||||
server.on("/style.css", HTTP_GET, handleStyle);
|
server.on("/action", HTTP_POST, handleAction);
|
||||||
server.on("/script.js", HTTP_GET, handleScript);
|
server.on("/style.css", HTTP_GET, handleStyle);
|
||||||
server.on("/bootstrap.css", HTTP_GET, handleBootstrapStyle);
|
server.on("/script.js", HTTP_GET, handleScript);
|
||||||
server.on("/bootstrap.js", HTTP_GET, handleBootstrapScript);
|
server.on("/bootstrap.css", HTTP_GET, handleBootstrapStyle);
|
||||||
server.on("/favicon.png", HTTP_GET, handleFavicon);
|
server.on("/bootstrap.js", HTTP_GET, handleBootstrapScript);
|
||||||
|
server.on("/favicon.png", HTTP_GET, handleFavicon);
|
||||||
|
|
||||||
OTA_Utils::setup(&server); // Include OTA Updater for WebServer
|
OTA_Utils::setup(&server); // Include OTA Updater for WebServer
|
||||||
|
|
||||||
server.onNotFound(handleNotFound);
|
server.onNotFound(handleNotFound);
|
||||||
|
|
||||||
server.begin();
|
server.begin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#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 handleReadConfiguration(AsyncWebServerRequest *request);
|
|
||||||
//void handleWriteConfiguration(AsyncWebServerRequest *request);
|
|
||||||
|
|
||||||
void handleStyle(AsyncWebServerRequest *request);
|
|
||||||
void handleScript(AsyncWebServerRequest *request);
|
|
||||||
void handleBootstrapStyle(AsyncWebServerRequest *request);
|
|
||||||
void handleBootstrapScript(AsyncWebServerRequest *request);
|
|
||||||
|
|
||||||
void setup();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,10 +1,29 @@
|
|||||||
|
/* 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 "boards_pinout.h"
|
#include "board_pinout.h"
|
||||||
#include "wifi_utils.h"
|
#include "wifi_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
|
|
||||||
extern uint8_t myWiFiAPIndex;
|
extern uint8_t myWiFiAPIndex;
|
||||||
@@ -23,33 +42,35 @@ uint32_t lastBackupDigiTime = millis();
|
|||||||
namespace WIFI_Utils {
|
namespace WIFI_Utils {
|
||||||
|
|
||||||
void checkWiFi() {
|
void checkWiFi() {
|
||||||
if (backUpDigiMode) {
|
if (Config.digi.ecoMode == 0) {
|
||||||
uint32_t WiFiCheck = millis() - lastBackupDigiTime;
|
if (backUpDigiMode) {
|
||||||
if (WiFi.status() != WL_CONNECTED && WiFiCheck >= 15 * 60 * 1000) {
|
uint32_t WiFiCheck = millis() - lastBackupDigiTime;
|
||||||
Serial.println("*** Stoping BackUp Digi Mode ***");
|
if (WiFi.status() != WL_CONNECTED && WiFiCheck >= 15 * 60 * 1000) {
|
||||||
backUpDigiMode = false;
|
Serial.println("*** Stopping BackUp Digi Mode ***");
|
||||||
wifiCounter = 0;
|
backUpDigiMode = false;
|
||||||
} else if (WiFi.status() == WL_CONNECTED) {
|
wifiCounter = 0;
|
||||||
Serial.println("*** WiFi Reconnect Success (Stoping Backup Digi Mode) ***");
|
} else if (WiFi.status() == WL_CONNECTED) {
|
||||||
backUpDigiMode = false;
|
Serial.println("*** WiFi Reconnect Success (Stopping Backup Digi Mode) ***");
|
||||||
wifiCounter = 0;
|
backUpDigiMode = false;
|
||||||
|
wifiCounter = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!backUpDigiMode && (WiFi.status() != WL_CONNECTED) && ((millis() - previousWiFiMillis) >= 30 * 1000) && !WiFiAutoAPStarted) {
|
if (!backUpDigiMode && (WiFi.status() != WL_CONNECTED) && ((millis() - previousWiFiMillis) >= 30 * 1000) && !WiFiAutoAPStarted) {
|
||||||
Serial.print(millis());
|
Serial.print(millis());
|
||||||
Serial.println("Reconnecting to WiFi...");
|
Serial.println("Reconnecting to WiFi...");
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
WIFI_Utils::startWiFi();//WiFi.reconnect();
|
WIFI_Utils::startWiFi();//WiFi.reconnect();
|
||||||
previousWiFiMillis = millis();
|
previousWiFiMillis = millis();
|
||||||
|
|
||||||
if (Config.backupDigiMode) {
|
if (Config.backupDigiMode) {
|
||||||
wifiCounter++;
|
wifiCounter++;
|
||||||
}
|
}
|
||||||
if (wifiCounter >= 2) {
|
if (wifiCounter >= 2) {
|
||||||
Serial.println("*** Starting BackUp Digi Mode ***");
|
Serial.println("*** Starting BackUp Digi Mode ***");
|
||||||
backUpDigiMode = true;
|
backUpDigiMode = true;
|
||||||
lastBackupDigiTime = millis();
|
lastBackupDigiTime = millis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +79,7 @@ namespace WIFI_Utils {
|
|||||||
WiFi.mode(WIFI_MODE_NULL);
|
WiFi.mode(WIFI_MODE_NULL);
|
||||||
|
|
||||||
WiFi.mode(WIFI_AP);
|
WiFi.mode(WIFI_AP);
|
||||||
WiFi.softAP(Config.callsign + " AP", Config.wifiAutoAP.password);
|
WiFi.softAP(Config.callsign + "-AP", Config.wifiAutoAP.password);
|
||||||
|
|
||||||
WiFiAutoAPTime = millis();
|
WiFiAutoAPTime = millis();
|
||||||
WiFiAutoAPStarted = true;
|
WiFiAutoAPStarted = true;
|
||||||
@@ -76,7 +97,7 @@ namespace WIFI_Utils {
|
|||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
delay(500);
|
delay(500);
|
||||||
unsigned long start = millis();
|
unsigned long start = millis();
|
||||||
displayShow("", "Connecting to Wifi:", "", currentWiFi->ssid + " ...", 0);
|
displayShow("", "Connecting to WiFi:", "", currentWiFi->ssid + " ...", 0);
|
||||||
Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.println("' ...");
|
Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.println("' ...");
|
||||||
WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str());
|
WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str());
|
||||||
while (WiFi.status() != WL_CONNECTED && wifiCounter<myWiFiAPSize) {
|
while (WiFi.status() != WL_CONNECTED && wifiCounter<myWiFiAPSize) {
|
||||||
@@ -101,7 +122,7 @@ namespace WIFI_Utils {
|
|||||||
currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
|
currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
|
||||||
start = millis();
|
start = millis();
|
||||||
Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.println("' ...");
|
Serial.print("\nConnecting to WiFi '"); Serial.print(currentWiFi->ssid); Serial.println("' ...");
|
||||||
displayShow("", "Connecting to Wifi:", "", currentWiFi->ssid + " ...", 0);
|
displayShow("", "Connecting to WiFi:", "", currentWiFi->ssid + " ...", 0);
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str());
|
WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str());
|
||||||
}
|
}
|
||||||
@@ -131,14 +152,14 @@ namespace WIFI_Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkIfAutoAPShouldPowerOff() {
|
void checkAutoAPTimeout() {
|
||||||
if (WiFiAutoAPStarted && Config.wifiAutoAP.powerOff > 0) {
|
if (WiFiAutoAPStarted && Config.wifiAutoAP.timeout > 0) {
|
||||||
if (WiFi.softAPgetStationNum() > 0) {
|
if (WiFi.softAPgetStationNum() > 0) {
|
||||||
WiFiAutoAPTime = 0;
|
WiFiAutoAPTime = 0;
|
||||||
} else {
|
} else {
|
||||||
if (WiFiAutoAPTime == 0) {
|
if (WiFiAutoAPTime == 0) {
|
||||||
WiFiAutoAPTime = millis();
|
WiFiAutoAPTime = millis();
|
||||||
} else if ((millis() - WiFiAutoAPTime) > Config.wifiAutoAP.powerOff * 60 * 1000) {
|
} else if ((millis() - WiFiAutoAPTime) > Config.wifiAutoAP.timeout * 60 * 1000) {
|
||||||
Serial.println("Stopping auto AP");
|
Serial.println("Stopping auto AP");
|
||||||
|
|
||||||
WiFiAutoAPStarted = false;
|
WiFiAutoAPStarted = false;
|
||||||
@@ -151,7 +172,7 @@ namespace WIFI_Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
startWiFi();
|
if (Config.digi.ecoMode == 0) startWiFi();
|
||||||
btStop();
|
btStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
#ifndef WIFI_UTILS_H_
|
|
||||||
#define WIFI_UTILS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace WIFI_Utils {
|
|
||||||
|
|
||||||
void checkWiFi();
|
|
||||||
void startAutoAP();
|
|
||||||
void startWiFi();
|
|
||||||
void checkIfAutoAPShouldPowerOff();
|
|
||||||
void setup();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,13 +1,39 @@
|
|||||||
#include "bme_utils.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 <TinyGPS++.h>
|
||||||
|
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||||
|
#include "Adafruit_SHTC3.h"
|
||||||
|
#endif
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "gps_utils.h"
|
#include "board_pinout.h"
|
||||||
|
#include "wx_utils.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
|
||||||
#define SEALEVELPRESSURE_HPA (1013.25)
|
|
||||||
#define CORRECTION_FACTOR (8.2296) // for meters
|
#define SEALEVELPRESSURE_HPA (1013.25)
|
||||||
|
#define CORRECTION_FACTOR (8.2296) // for meters
|
||||||
|
|
||||||
extern Configuration Config;
|
extern Configuration Config;
|
||||||
extern String fifthLine;
|
extern String fifthLine;
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
extern TinyGPSPlus gps;
|
||||||
|
#endif
|
||||||
|
|
||||||
int wxModuleType = 0;
|
int wxModuleType = 0;
|
||||||
uint8_t wxModuleAddress = 0x00;
|
uint8_t wxModuleAddress = 0x00;
|
||||||
@@ -16,85 +42,103 @@ float newHum, newTemp, newPress, newGas;
|
|||||||
|
|
||||||
|
|
||||||
Adafruit_BME280 bme280;
|
Adafruit_BME280 bme280;
|
||||||
#ifdef HELTEC_V3
|
#if defined(HELTEC_V3) || defined(HELTEC_V3_2)
|
||||||
Adafruit_BMP280 bmp280(&Wire1);
|
Adafruit_BMP280 bmp280(&Wire1);
|
||||||
Adafruit_Si7021 sensor = Adafruit_Si7021();
|
Adafruit_Si7021 si7021 = Adafruit_Si7021();
|
||||||
#else
|
#else
|
||||||
Adafruit_BMP280 bmp280;
|
Adafruit_BMP280 bmp280;
|
||||||
Adafruit_BME680 bme680;
|
Adafruit_BME680 bme680;
|
||||||
Adafruit_Si7021 sensor = Adafruit_Si7021();
|
Adafruit_Si7021 si7021 = Adafruit_Si7021();
|
||||||
|
#endif
|
||||||
|
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||||
|
Adafruit_SHTC3 shtc3 = Adafruit_SHTC3();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace WX_Utils {
|
||||||
namespace BME_Utils {
|
|
||||||
|
|
||||||
void getWxModuleAddres() {
|
void getWxModuleAddres() {
|
||||||
uint8_t err, addr;
|
uint8_t err, addr;
|
||||||
for(addr = 1; addr < 0x7F; addr++) {
|
for(addr = 1; addr < 0x7F; addr++) {
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
|
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
|
||||||
Wire1.beginTransmission(addr);
|
Wire1.beginTransmission(addr);
|
||||||
err = Wire1.endTransmission();
|
err = Wire1.endTransmission();
|
||||||
#else
|
#else
|
||||||
Wire.beginTransmission(addr);
|
Wire.beginTransmission(addr);
|
||||||
|
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||||
|
Wire.write(0x35);
|
||||||
|
Wire.write(0x17);
|
||||||
|
#endif
|
||||||
err = Wire.endTransmission();
|
err = Wire.endTransmission();
|
||||||
#endif
|
#endif
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
//Serial.println(addr); this shows any connected board to I2C
|
//Serial.println(addr); //this shows any connected board to I2C
|
||||||
if (addr == 0x76 || addr == 0x77) { // BME/BMP
|
if (addr == 0x76 || addr == 0x77) { // BME or BMP
|
||||||
wxModuleAddress = addr;
|
wxModuleAddress = addr;
|
||||||
return;
|
return;
|
||||||
} else if (addr == 0x40) { // Si7011
|
} else if (addr == 0x40) { // Si7011
|
||||||
wxModuleAddress = addr;
|
wxModuleAddress = addr;
|
||||||
return;
|
return;
|
||||||
|
} else if (addr == 0x70) { // SHTC3
|
||||||
|
wxModuleAddress = addr;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
if (Config.bme.active) {
|
if (Config.wxsensor.active) {
|
||||||
getWxModuleAddres();
|
getWxModuleAddres();
|
||||||
if (wxModuleAddress != 0x00) {
|
if (wxModuleAddress != 0x00) {
|
||||||
bool wxModuleFound = false;
|
bool wxModuleFound = false;
|
||||||
if (wxModuleAddress == 0x76 || wxModuleAddress == 0x77) {
|
if (wxModuleAddress == 0x76 || wxModuleAddress == 0x77) {
|
||||||
#if defined(HELTEC_V3) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
|
#if defined(HELTEC_V3) || defined(HELTEC_V3_2) || defined(HELTEC_WSL_V3) || defined(HELTEC_WSL_V3_DISPLAY)
|
||||||
if (bme280.begin(wxModuleAddress, &Wire1)) {
|
if (bme280.begin(wxModuleAddress, &Wire1)) {
|
||||||
Serial.println("BME280 sensor found");
|
Serial.println("BME280 sensor found");
|
||||||
wxModuleType = 1;
|
wxModuleType = 1;
|
||||||
wxModuleFound = true;
|
wxModuleFound = true;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (bme280.begin(wxModuleAddress)) {
|
if (bme280.begin(wxModuleAddress)) {
|
||||||
Serial.println("BME280 sensor found");
|
Serial.println("BME280 sensor found");
|
||||||
wxModuleType = 1;
|
wxModuleType = 1;
|
||||||
wxModuleFound = true;
|
wxModuleFound = true;
|
||||||
}
|
}
|
||||||
if (!wxModuleFound) {
|
if (!wxModuleFound) {
|
||||||
if (bme680.begin(wxModuleAddress)) {
|
if (bme680.begin(wxModuleAddress)) {
|
||||||
Serial.println("BME680 sensor found");
|
Serial.println("BME680 sensor found");
|
||||||
wxModuleType = 3;
|
wxModuleType = 3;
|
||||||
wxModuleFound = true;
|
wxModuleFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!wxModuleFound) {
|
if (!wxModuleFound) {
|
||||||
if (bmp280.begin(wxModuleAddress)) {
|
if (bmp280.begin(wxModuleAddress)) {
|
||||||
Serial.println("BMP280 sensor found");
|
Serial.println("BMP280 sensor found");
|
||||||
wxModuleType = 2;
|
wxModuleType = 2;
|
||||||
wxModuleFound = true;
|
wxModuleFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (wxModuleAddress == 0x40) {
|
} else if (wxModuleAddress == 0x40) {
|
||||||
if(sensor.begin()) {
|
if(si7021.begin()) {
|
||||||
Serial.println("Si7021 sensor found");
|
Serial.println("Si7021 sensor found");
|
||||||
wxModuleType = 4;
|
wxModuleType = 4;
|
||||||
wxModuleFound = true;
|
wxModuleFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||||
|
else if (wxModuleAddress == 0x70) {
|
||||||
|
if (shtc3.begin()) {
|
||||||
|
Serial.println("SHTC3 sensor found");
|
||||||
|
wxModuleType = 5;
|
||||||
|
wxModuleFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (!wxModuleFound) {
|
if (!wxModuleFound) {
|
||||||
displayShow("ERROR", "", "BME/BMP/Si7021 sensor active", "but no sensor found...", 2000);
|
displayShow("ERROR", "", "BME/BMP/Si7021/SHTC3 sensor active", "but no sensor found...", 2000);
|
||||||
Serial.println("BME/BMP/Si7021 sensor Active in config but not found! Check Wiring");
|
Serial.println("BME/BMP/Si7021/SHTC3 sensor Active in config but not found! Check Wiring");
|
||||||
} else {
|
} else {
|
||||||
switch (wxModuleType) {
|
switch (wxModuleType) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -115,7 +159,7 @@ namespace BME_Utils {
|
|||||||
Serial.println("BMP280 Module init done!");
|
Serial.println("BMP280 Module init done!");
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
#ifndef HELTEC_V3
|
#if !defined(HELTEC_V3) && !defined(HELTEC_V3_2)
|
||||||
bme680.setTemperatureOversampling(BME680_OS_1X);
|
bme680.setTemperatureOversampling(BME680_OS_1X);
|
||||||
bme680.setHumidityOversampling(BME680_OS_1X);
|
bme680.setHumidityOversampling(BME680_OS_1X);
|
||||||
bme680.setPressureOversampling(BME680_OS_1X);
|
bme680.setPressureOversampling(BME680_OS_1X);
|
||||||
@@ -125,12 +169,12 @@ namespace BME_Utils {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateTempString(const float bmeTemp) {
|
String generateTempString(const float sensorTemp) {
|
||||||
String strTemp = String((int)bmeTemp);
|
String strTemp = String((int)sensorTemp);
|
||||||
switch (strTemp.length()) {
|
switch (strTemp.length()) {
|
||||||
case 1:
|
case 1:
|
||||||
return "00" + strTemp;
|
return "00" + strTemp;
|
||||||
@@ -143,15 +187,15 @@ namespace BME_Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateHumString(const float bmeHum) {
|
String generateHumString(const float sensorHum) {
|
||||||
String strHum = String((int)bmeHum);
|
String strHum = String((int)sensorHum);
|
||||||
switch (strHum.length()) {
|
switch (strHum.length()) {
|
||||||
case 1:
|
case 1:
|
||||||
return "0" + strHum;
|
return "0" + strHum;
|
||||||
case 2:
|
case 2:
|
||||||
return strHum;
|
return strHum;
|
||||||
case 3:
|
case 3:
|
||||||
if ((int)bmeHum == 100) {
|
if ((int)sensorHum == 100) {
|
||||||
return "00";
|
return "00";
|
||||||
} else {
|
} else {
|
||||||
return "-99";
|
return "-99";
|
||||||
@@ -161,9 +205,9 @@ namespace BME_Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String generatePresString(const float bmePress) {
|
String generatePresString(const float sensorPres) {
|
||||||
String strPress = String((int)bmePress);
|
String strPress = String((int)sensorPres);
|
||||||
String decPress = String(int((bmePress - int(bmePress)) * 10));
|
String decPress = String(int((sensorPres - int(sensorPres)) * 10));
|
||||||
switch (strPress.length()) {
|
switch (strPress.length()) {
|
||||||
case 1:
|
case 1:
|
||||||
return "000" + strPress + decPress;
|
return "000" + strPress + decPress;
|
||||||
@@ -180,6 +224,14 @@ namespace BME_Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getAltitudeCorrection() {
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
return Config.beacon.gpsActive ? gps.altitude.meters() : Config.wxsensor.heightCorrection;
|
||||||
|
#else
|
||||||
|
return Config.wxsensor.heightCorrection;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
String readDataSensor() {
|
String readDataSensor() {
|
||||||
switch (wxModuleType) {
|
switch (wxModuleType) {
|
||||||
case 1: // BME280
|
case 1: // BME280
|
||||||
@@ -195,7 +247,7 @@ namespace BME_Utils {
|
|||||||
newHum = 0;
|
newHum = 0;
|
||||||
break;
|
break;
|
||||||
case 3: // BME680
|
case 3: // BME680
|
||||||
#ifndef HELTEC_V3
|
#if !defined(HELTEC_V3) && !defined(HELTEC_V3_2)
|
||||||
bme680.performReading();
|
bme680.performReading();
|
||||||
delay(50);
|
delay(50);
|
||||||
if (bme680.endReading()) {
|
if (bme680.endReading()) {
|
||||||
@@ -207,56 +259,60 @@ namespace BME_Utils {
|
|||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case 4: // Si7021
|
case 4: // Si7021
|
||||||
newTemp = sensor.readTemperature();
|
newTemp = si7021.readTemperature();
|
||||||
|
newHum = si7021.readHumidity();
|
||||||
newPress = 0;
|
newPress = 0;
|
||||||
newHum = sensor.readHumidity();
|
break;
|
||||||
|
case 5: // SHTC3
|
||||||
|
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||||
|
sensors_event_t humidity, temp;
|
||||||
|
shtc3.getEvent(&humidity, &temp);
|
||||||
|
newTemp = temp.temperature;
|
||||||
|
newHum = humidity.relative_humidity;
|
||||||
|
newPress = 0;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
String wx;
|
|
||||||
if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) {
|
if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) {
|
||||||
Serial.println("BME/BMP/Si7021 Module data failed");
|
Serial.println("BME/BMP/Si7021 Module data failed");
|
||||||
wx = ".../...g...t...";
|
|
||||||
fifthLine = "";
|
fifthLine = "";
|
||||||
return wx;
|
return ".../...g...t...";
|
||||||
} else {
|
} else {
|
||||||
String tempStr = generateTempString(((newTemp + Config.bme.temperatureCorrection) * 1.8) + 32);
|
String tempStr = generateTempString(((newTemp + Config.wxsensor.temperatureCorrection) * 1.8) + 32);
|
||||||
|
|
||||||
String humStr;
|
String humStr;
|
||||||
if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4) {
|
if (wxModuleType == 1 || wxModuleType == 3 || wxModuleType == 4 || wxModuleType == 5) {
|
||||||
humStr = generateHumString(newHum);
|
humStr = generateHumString(newHum);
|
||||||
} else if (wxModuleType == 2) {
|
} else if (wxModuleType == 2) {
|
||||||
humStr = "..";
|
humStr = "..";
|
||||||
}
|
}
|
||||||
|
|
||||||
String presStr;
|
String presStr = (wxModuleType == 4 || wxModuleType == 5)
|
||||||
if (wxModuleAddress == 4) {
|
? "....."
|
||||||
presStr = ".....";
|
: generatePresString(newPress + getAltitudeCorrection() / CORRECTION_FACTOR);
|
||||||
} else {
|
|
||||||
presStr = generatePresString(newPress + (Config.bme.heightCorrection/CORRECTION_FACTOR));
|
|
||||||
}
|
|
||||||
|
|
||||||
fifthLine = "BME-> ";
|
fifthLine = "BME-> ";
|
||||||
fifthLine += String(int(newTemp + Config.bme.temperatureCorrection));
|
fifthLine += String(int(newTemp + Config.wxsensor.temperatureCorrection));
|
||||||
fifthLine += "C ";
|
fifthLine += "C ";
|
||||||
fifthLine += humStr;
|
fifthLine += humStr;
|
||||||
fifthLine += "% ";
|
fifthLine += "% ";
|
||||||
fifthLine += presStr.substring(0,4);
|
fifthLine += presStr.substring(0,4);
|
||||||
fifthLine += "hPa";
|
fifthLine += "hPa";
|
||||||
|
|
||||||
wx = ".../...g...t";
|
String wxPayload = ".../...g...t";
|
||||||
wx += tempStr;
|
wxPayload += tempStr;
|
||||||
wx += "h";
|
wxPayload += "h";
|
||||||
wx += humStr;
|
wxPayload += humStr;
|
||||||
wx += "b";
|
wxPayload += "b";
|
||||||
wx += presStr;
|
wxPayload += presStr;
|
||||||
|
|
||||||
if (wxModuleType == 3) {
|
if (wxModuleType == 3) {
|
||||||
wx += "Gas: ";
|
wxPayload += "Gas: ";
|
||||||
wx += String(newGas);
|
wxPayload += String(newGas);
|
||||||
wx += "Kohms";
|
wxPayload += "Kohms";
|
||||||
}
|
}
|
||||||
return wx;
|
return wxPayload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for PlatformIO Test Runner and project tests.
|
|
||||||
|
|
||||||
Unit Testing is a software testing method by which individual units of
|
|
||||||
source code, sets of one or more MCU program modules together with associated
|
|
||||||
control data, usage procedures, and operating procedures, are tested to
|
|
||||||
determine whether they are fit for use. Unit testing finds problems early
|
|
||||||
in the development cycle.
|
|
||||||
|
|
||||||
More information about PlatformIO Unit Testing:
|
|
||||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
|
||||||
@@ -1,3 +1,20 @@
|
|||||||
|
# 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/>.
|
||||||
|
|
||||||
import gzip
|
import gzip
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
||||||
|
*
|
||||||
|
* This file is part of LoRa APRS iGate.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOARD_PINOUT_H_
|
||||||
|
#define BOARD_PINOUT_H_
|
||||||
|
|
||||||
|
// LoRa Radio
|
||||||
|
#define HAS_SX1268
|
||||||
|
#define RADIO_HAS_XTAL
|
||||||
|
#define RADIO_SCLK_PIN 6
|
||||||
|
#define RADIO_MISO_PIN 4
|
||||||
|
#define RADIO_MOSI_PIN 7
|
||||||
|
#define RADIO_CS_PIN 5
|
||||||
|
#define RADIO_DIO1_PIN 3
|
||||||
|
#define RADIO_RST_PIN -1
|
||||||
|
#define RADIO_BUSY_PIN 8
|
||||||
|
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
|
||||||
|
#define GPIO_WAKEUP_PIN GPIO_NUM_3
|
||||||
|
|
||||||
|
// Display
|
||||||
|
#define HAS_DISPLAY
|
||||||
|
|
||||||
|
#undef OLED_SDA
|
||||||
|
#undef OLED_SCL
|
||||||
|
#undef OLED_RST
|
||||||
|
|
||||||
|
#define OLED_SDA 0
|
||||||
|
#define OLED_SCL 1
|
||||||
|
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[env:ESP32_C3_OctopusLab_LoRa]
|
||||||
|
board = esp32-c3-devkitm-1
|
||||||
|
board_build.mcu = esp32c3
|
||||||
|
build_flags =
|
||||||
|
${common.build_flags}
|
||||||
|
${common.usb_flags}
|
||||||
|
-D ESP32_C3_OctopusLab_LoRa
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
${common.display_libs}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
||||||
|
*
|
||||||
|
* This file is part of LoRa APRS iGate.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOARD_PINOUT_H_
|
||||||
|
#define BOARD_PINOUT_H_
|
||||||
|
|
||||||
|
// LoRa Radio
|
||||||
|
#define HAS_SX1268
|
||||||
|
#define HAS_1W_LORA
|
||||||
|
#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
|
||||||
|
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
|
||||||
|
#define GPIO_WAKEUP_PIN GPIO_SEL_12
|
||||||
|
|
||||||
|
// Display
|
||||||
|
#define HAS_DISPLAY
|
||||||
|
|
||||||
|
#undef OLED_SDA
|
||||||
|
#undef OLED_SCL
|
||||||
|
#undef OLED_RST
|
||||||
|
|
||||||
|
#define OLED_SDA 21
|
||||||
|
#define OLED_SCL 22
|
||||||
|
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
|
||||||
|
// Aditional Config
|
||||||
|
#define INTERNAL_LED_PIN 2
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[env:ESP32_DIY_1W_LoRa]
|
||||||
|
board = esp32dev
|
||||||
|
build_flags =
|
||||||
|
${common.build_flags}
|
||||||
|
-D ESP32_DIY_1W_LoRa
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
${common.display_libs}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
||||||
|
*
|
||||||
|
* This file is part of LoRa APRS iGate.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOARD_PINOUT_H_
|
||||||
|
#define BOARD_PINOUT_H_
|
||||||
|
|
||||||
|
// LoRa Radio
|
||||||
|
#define HAS_SX1262
|
||||||
|
#define HAS_1W_LORA
|
||||||
|
#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
|
||||||
|
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
|
||||||
|
#define GPIO_WAKEUP_PIN GPIO_SEL_12
|
||||||
|
|
||||||
|
// Display
|
||||||
|
#define HAS_DISPLAY
|
||||||
|
|
||||||
|
#undef OLED_SDA
|
||||||
|
#undef OLED_SCL
|
||||||
|
#undef OLED_RST
|
||||||
|
|
||||||
|
#define OLED_SDA 21
|
||||||
|
#define OLED_SCL 22
|
||||||
|
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
|
||||||
|
// Aditional Config
|
||||||
|
#define INTERNAL_LED_PIN 2
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[env:ESP32_DIY_1W_LoRa_915]
|
||||||
|
board = esp32dev
|
||||||
|
build_flags =
|
||||||
|
${common.build_flags}
|
||||||
|
-D ESP32_DIY_1W_LoRa_915
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
${common.display_libs}
|
||||||
@@ -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 BOARD_PINOUT_H_
|
||||||
|
#define BOARD_PINOUT_H_
|
||||||
|
|
||||||
|
// LoRa Radio
|
||||||
|
#define HAS_LLCC68
|
||||||
|
#define HAS_1W_LORA
|
||||||
|
#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
|
||||||
|
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
|
||||||
|
#define GPIO_WAKEUP_PIN GPIO_SEL_12
|
||||||
|
|
||||||
|
// Display
|
||||||
|
#define HAS_DISPLAY
|
||||||
|
|
||||||
|
#undef OLED_SDA
|
||||||
|
#undef OLED_SCL
|
||||||
|
#undef OLED_RST
|
||||||
|
|
||||||
|
#define OLED_SDA 21
|
||||||
|
#define OLED_SCL 22
|
||||||
|
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[env:ESP32_DIY_1W_LoRa_LLCC68]
|
||||||
|
board = esp32dev
|
||||||
|
build_flags =
|
||||||
|
${common.build_flags}
|
||||||
|
-D ESP32_DIY_1W_LoRa_LLCC68
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
${common.display_libs}
|
||||||
@@ -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 BOARD_PINOUT_H_
|
||||||
|
#define BOARD_PINOUT_H_
|
||||||
|
|
||||||
|
// LoRa Radio
|
||||||
|
#define HAS_SX1268
|
||||||
|
#define HAS_1W_LORA
|
||||||
|
#define RADIO_SCLK_PIN 5 // https://github.com/NanoVHF/Meshtastic-DIY/tree/main/PCB/ESP-32-devkit_EBYTE-E22/Mesh-v1.02-2LCD-FreePins
|
||||||
|
#define RADIO_MISO_PIN 19
|
||||||
|
#define RADIO_MOSI_PIN 27
|
||||||
|
#define RADIO_CS_PIN 18
|
||||||
|
#define RADIO_RST_PIN 23
|
||||||
|
#define RADIO_DIO1_PIN 33
|
||||||
|
#define RADIO_BUSY_PIN 32
|
||||||
|
#define RADIO_RXEN 14
|
||||||
|
#define RADIO_TXEN 13
|
||||||
|
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
|
||||||
|
#define GPIO_WAKEUP_PIN GPIO_SEL_33
|
||||||
|
|
||||||
|
// Display
|
||||||
|
#define HAS_DISPLAY
|
||||||
|
|
||||||
|
#undef OLED_SDA
|
||||||
|
#undef OLED_SCL
|
||||||
|
#undef OLED_RST
|
||||||
|
|
||||||
|
#define OLED_SDA 21
|
||||||
|
#define OLED_SCL 22
|
||||||
|
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
|
||||||
|
#endif
|
||||||