Compare commits

...

75 Commits

Author SHA1 Message Date
Ricardo Guzman (Richonguzman) b00b29a5da Merge pull request #76 from SQ2CPA/main
Merged bins for web flash
2024-04-16 15:25:52 -04:00
SQ2CPA a64899d44c chore: workflow for merged bins 2024-04-16 21:16:28 +02:00
richonguzman af9f3fd09b json memory fix 2024-04-16 15:01:53 -04:00
richonguzman 0fb2c95338 just small typo 2024-04-15 11:00:35 -04:00
richonguzman 3885a43324 update 2024-04-13 13:06:13 -04:00
Ricardo Guzman (Richonguzman) 0695e6643e Merge pull request #74 from SQ2CPA/main
Received packets list
2024-04-13 12:48:54 -04:00
SQ2CPA 71473164f4 feat: received packets list 2024-04-13 18:20:10 +02:00
richonguzman 8e505df768 duplicated comment fix 2024-04-11 11:07:28 -04:00
richonguzman 81295bc3e2 removing LoRa dot h library 2024-04-10 09:13:56 -04:00
richonguzman 16f5338953 testing syslog 2024-04-09 12:51:51 -04:00
richonguzman 0b22d27b40 status mod for TCPIP and RF 2024-04-09 12:20:31 -04:00
richonguzman aea0aa21af rx Fix 2024-04-09 11:20:09 -04:00
richonguzman a6ce44f8d3 new Compressed GPS data instead of classic NMEA 2024-04-09 11:08:23 -04:00
richonguzman 3ce5d08c44 typeOfPacket fix 2024-04-09 10:01:44 -04:00
richonguzman 51b59ab555 crc mismatch fix 2024-04-07 08:16:28 -04:00
richonguzman b9024d34cb RFONLY coma fix 2024-04-05 12:10:12 -03:00
richonguzman e4bc8eecbe RFONLY update 2024-04-04 15:54:49 -03:00
Ricardo Guzman (Richonguzman) c10645c2c0 Merge pull request #68 from SQ2CPA/main
Migrate from LoRa.h into RadioLib and more
2024-04-04 14:09:13 -03:00
SQ2CPA 0d64c65aa5 feat: add build env and date to web ui footer 2024-04-04 18:28:38 +02:00
SQ2CPA d16e407269 improvement: use LoRa WX icon when using BME 2024-04-04 17:58:07 +02:00
SQ2CPA c8cde0a404 fix: use path from config everywhere 2024-04-04 17:56:37 +02:00
SQ2CPA c1cc7c9ab0 fix: use battery settings for comment in low power mode 2024-04-04 17:55:20 +02:00
SQ2CPA 4d23681935 improvement: migrate from lora.h to radiolib 2024-04-04 17:42:36 +02:00
richonguzman 1bcb119cad 1 2024-03-29 10:35:49 -03:00
richonguzman 43c0021ba7 supported boards text add and link 2024-03-29 10:35:09 -03:00
richonguzman daa245ff7c supported boards add 2024-03-29 10:33:45 -03:00
richonguzman e52ac86e3e sender vs Sender fix 2024-03-28 16:28:14 -03:00
Ricardo Guzman (Richonguzman) c1bb6e39ef Merge pull request #64 from SQ2CPA/main
Experimental Low power mode and Low voltage cut off
2024-03-28 16:20:08 -03:00
SQ2CPA 0e94444198 fix: rename heltec wireless lite to ht-ct62 2024-03-28 18:33:11 +01:00
SQ2CPA 968d9188be improvement: low voltage cut off as web ui option 2024-03-28 18:28:31 +01:00
SQ2CPA db21a08904 chore: ota update binary in installer zip 2024-03-28 18:01:42 +01:00
SQ2CPA 14999d1b66 feat: low power mode 2024-03-28 18:01:42 +01:00
SQ2CPA a8a7c3e3a3 fix: missing sender variable 2024-03-28 18:01:42 +01:00
richonguzman ca3c1eaf9f uint8_t update 2024-03-28 10:22:54 -03:00
richonguzman dec018f232 new picture 2024-03-28 00:11:36 -03:00
richonguzman f49472efd1 new igate picture 2024-03-28 00:05:46 -03:00
richonguzman df3b15ff35 just small code cleaning 2024-03-27 15:28:27 -03:00
richonguzman d004efb193 version Update 2024-03-27 13:12:47 -03:00
richonguzman d98334a489 preparing for Notification LowVoltBattery 2024-03-27 11:42:04 -03:00
richonguzman 1365ae67a1 update in APRS listening 2024-03-27 11:26:02 -03:00
richonguzman 110c6d600b esp32 super mini killed 2024-03-26 17:43:50 -03:00
richonguzman 9557df983b test1 2024-03-26 16:43:17 -03:00
richonguzman 0e9dba76a8 low_voltage_cutoff settings 2024-03-24 10:01:54 -03:00
richonguzman caa48694b4 new date 2024-03-23 23:48:32 -03:00
Ricardo Guzman (Richonguzman) 0ac2705609 fix lora problem 2024-03-23 23:36:18 -03:00
Ricardo Guzman (Richonguzman) ed9d80fd78 Add files via upload 2024-03-23 09:48:12 -03:00
Ricardo Guzman (Richonguzman) cb7e9f3235 Merge pull request #57 from SQ2CPA/main
Hotfix TNC config only if available
2024-03-22 16:45:54 -03:00
SQ2CPA 8fdab82684 fix: tnc config only if available 2024-03-22 20:42:55 +01:00
Ricardo Guzman (Richonguzman) 0d1162786c Merge pull request #56 from SQ2CPA/main
TNC and few more changes and fixes
2024-03-22 15:28:14 -03:00
SQ2CPA 0b40cb0889 fix: path is not required 2024-03-22 19:07:35 +01:00
SQ2CPA e33b639f12 fix: saving empty APs 2024-03-22 19:07:35 +01:00
SQ2CPA 15be5fc611 feat: battery voltage cutoff monitor 2024-03-22 19:07:35 +01:00
SQ2CPA a44c6ca96c chore: commit test build workflow 2024-03-22 19:07:35 +01:00
SQ2CPA 7897e9a5be feat: heltec wireless stick lite board 2024-03-22 19:07:35 +01:00
SQ2CPA a33875a608 fix: send battery voltage as separate 2024-03-22 19:07:35 +01:00
SQ2CPA 5337cb2d46 feat: send beacon and reboot buttons 2024-03-22 19:07:35 +01:00
SQ2CPA 67e1c528f3 refactor: tnc handle input data 2024-03-22 19:07:35 +01:00
SQ2CPA 9f617c406e feat: new navbar 2024-03-22 19:07:35 +01:00
SQ2CPA d9629d0929 feat: serial kiss 2024-03-22 19:07:32 +01:00
SQ2CPA 2a5cc33711 feat: tnc server 2024-03-22 19:05:09 +01:00
richonguzman 6bd6cce82b digiMode self hearing fix 2024-03-21 15:21:29 -03:00
richonguzman 54bbede56c digimode fix 2024-03-21 15:19:30 -03:00
richonguzman 85b7596cbb minor code update 2024-03-21 00:09:35 -03:00
richonguzman fb4b6a25bd signalBandwidth fixed 2024-03-19 22:41:07 -03:00
richonguzman 4c30422635 refactor queries 2024-03-18 14:48:56 -03:00
richonguzman e059c65a99 testing Queries for DigiMode 2024-03-18 10:38:16 -03:00
richonguzman 3fda399ecf OE5HWN_MeshCom readme update 2024-03-18 09:40:57 -03:00
richonguzman 492f571183 OE5HWN_MeshCom added 2024-03-18 09:40:05 -03:00
richonguzman b287704293 start 2024-03-18 08:42:18 -03:00
richonguzman d802fb4c91 readme update 2024-03-12 16:45:22 -03:00
richonguzman 4247d00be4 TNC Text cleaning 2024-03-10 12:16:08 -03:00
richonguzman 7401c36fdf readme update 2024-03-10 09:32:50 -03:00
Ricardo Guzman (Richonguzman) cc2d4a669d Merge pull request #44 from richonguzman/adding-HELTEC-Wireless-Stick
Adding heltec wireless stick
2024-03-09 20:18:51 -03:00
richonguzman 706d84cf07 adding WebUI pics 2024-03-09 20:14:24 -03:00
richonguzman 4ae33a8362 first Test Heltec WS 2024-03-09 09:43:51 -03:00
49 changed files with 1565 additions and 689 deletions
+8 -1
View File
@@ -20,7 +20,8 @@ jobs:
- "ttgo-t-beam-v1"
- "ttgo-t-beam-v1_SX1268"
- "ttgo-t-beam-v1_2_SX1262"
# - "heltec_wireless_stick_lite" # NOT FULLY TESTED
- "heltec_wireless_stick"
- "heltec_ht-ct62"
steps:
- uses: actions/checkout@v3
@@ -40,11 +41,17 @@ jobs:
- name: Move Files
run: |
mkdir -p installer/firmware
cp .pio/build/${{ matrix.target }}/firmware.bin installer/ota_update.bin
cp .pio/build/${{ matrix.target }}/firmware.bin installer/firmware/
cp .pio/build/${{ matrix.target }}/bootloader.bin installer/firmware/
cp .pio/build/${{ matrix.target }}/partitions.bin installer/firmware/
cp .pio/build/${{ matrix.target }}/spiffs.bin installer/firmware/
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin installer/firmware/
- name: Merge for web flashing
run: |
python installer/bin/esptool/esptool.py --chip esp32 merge_bin -o installer/web_upgrade.bin --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 installer/firmware/bootloader.bin 0x8000 installer/firmware/partitions.bin 0xe000 installer/firmware/boot_app0.bin 0x10000 installer/firmware/firmware.bin
python installer/bin/esptool/esptool.py --chip esp32 merge_bin -o installer/web_factory.bin --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 installer/firmware/bootloader.bin 0x8000 installer/firmware/partitions.bin 0xe000 installer/firmware/boot_app0.bin 0x10000 installer/firmware/firmware.bin 2686976 installer/firmware/spiffs.bin
- name: Install Zip
run: sudo apt-get install zip
+40
View File
@@ -0,0 +1,40 @@
name: Commit Test Build
on:
push:
branches:
- '*'
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- "ttgo-lora32-v21"
- "heltec-lora32-v2"
- "heltec_wifi_lora_32_V3"
- "ESP32_DIY_LoRa"
- "ESP32_DIY_1W_LoRa"
- "ttgo-t-beam-v1_2"
- "ttgo-t-beam-v1"
- "ttgo-t-beam-v1_SX1268"
- "ttgo-t-beam-v1_2_SX1262"
- "heltec_wireless_stick"
- "heltec_ht-ct62"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Build target
run: pio run -e ${{ matrix.target }}
- name: Build FS
run: pio run --target buildfs -e ${{ matrix.target }}
+2 -2
View File
@@ -1,4 +1,4 @@
{
"editor.tabSize": 4,
"editor.formatOnSave": true
}
"editor.formatOnSave": false
}
+7 -56
View File
@@ -2,7 +2,7 @@
This firmware is for using ESP32 based boards with LoRa Modules and GPS to live in the APRS world.
![Screenshot](https://github.com/richonguzman/LoRa_APRS_iGate/blob/main/images/OledScreen.jpeg)
![Screenshot](https://github.com/richonguzman/LoRa_APRS_iGate/blob/main/images/iGateOledScreen.jpeg)
__(NOTE: This iGate Firmware was develop to work with all LoRa APRS Trackers and specially with this firmware <a href="https://github.com/richonguzman/LoRa_APRS_Tracker" target="_blank">LoRa APRS Tracker</a>)__
@@ -38,19 +38,18 @@ ____________________________________________________
# WIKI
### 1. Installation Guide --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/1.-Installation-Guide" target="_blank">here</a>.
### Supported Boards and buying links --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/Z.-------Supported-Boards-and-Buying-Links" target="_blank">here</a>.
### 2. iGate Configuration and Explanation for each setting --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/2.-iGate-Configuration" target="_blank">here</a>.
### Installation Guide --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/01.-Installation-Guide" target="_blank">here</a>.
### 3. Supported Boards and Environment Selection --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/3.-Supported-Boards-and-Environment-Selection" target="_blank">here</a>.
### 4. Upload Firmware and Filesystem --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/4.-Upload-Firmware-and-Filesystem" target="_blank">here</a>.
### 5. Adding BME280 Module --> <a href="https://github.com/richonguzman/LoRa_APRS_iGate/wiki/5.-Adding-BME280-Module" target="_blank">here</a>.
*(Wiki has all configuration explanation, supported boards list, adding BME/BMP Wx modules and more)*
____________________________________________________
## Timeline (Versions):
- 2024.04.13 Received Packets added on WebUI.
- 2024.04.09 iGate/Digirepeater own GPS beacon is encoded (Base91) now.
- 2024.03.18 OE5HWN MeshCom board support added.
- 2024.02.25 New Web Configuration UI with WiFi AP (thanks Damian SQ2CPA).
- 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)
@@ -83,54 +82,6 @@ ____________________________________________________
- 2023.02.17 Receiving Feed from APRS-IS.
- 2023.02.10 First Beta (receiving LoRa Beacon/Packets and uploading to APRS-IS).
____________________________________________________
Instructions (add your information into the '/data/igate_conf.json'):
a) Change _callsign_ from "NOCALL-10" to your CALLSIGN + SSID.
b) Choose _stationMode_:
1 = RX iGate, black "L" as symbol
2 = Rx + TX iGate, red "L" as symbol, HAM only. RX will be sent to APRS-IS, Messages will be sent via Lora. Same frequency for RX and TX. By using this feature you have comply with the regulations of your country.
3 = Digipeater simplex, green "L" as symbol, HAM only. Received packets containing WIDEx-x in path will be digipeated on the same frequency. By using this feature you have comply with the regulations of your country.
4 = Digipeater split frequency, green "L" as symbol, HAM only. Received packets will be digipeated on a different frequency. Frequency separation must be 125kHz or more. By using this feature you have comply with the regulations of your country.
IgateComment and DigirepeaterComment will be sent to APRS-IS or via RF, depending on your stationmode
c) WiFi section:
adjust SSID and Password to you WiFi, add the GPS to "Latitude" and "Longitude" (info from GoogleMaps) of your new LoRa iGate. (If stationMode 3 or 4 selected, add also GPS info to Digirepeater Section).
d) APRS_IS section:
change "passcode" from "VWXYZ" to yours (remember that is 5 digits integer) and choose a server close to your location (see https://www.aprs2.net/)
e) LORA section:
adjust TX frequency and RX frequency matching your stationmode and country. Remember,
at stationmode 1, 2, and 3, RX and TX frequency shall be set to 433775000 (443.775MHz, deviations possible, depending on your country)
at stationmode 4, RX frequency shall be set to 433775000, TX frequency shall be set to 433900000 (deviations possible, depending on your country). There must be a frequency separation of 125kHz or more.
adjust power to your need, valid values are from 1 to 20
f) Syslog section:
adjust server and port to a suitable value if needed.
g) BME section:
adjust to "active" if BME280 sensor connected through I2C pins
__________________________________________
Special Thanks to the help in testing and developing to Manfred (DC2MH) , for showing me the "way of good coding" to Tihomir (CA3TSK) and much more Ham Licence Ops all over the world.
# Hope You Enjoy this, 73 !! CA2RXU , Valparaiso, Chile
+10 -3
View File
@@ -13,7 +13,7 @@
"comment": "LoRa APRS",
"interval": 15,
"overlay": "L",
"symbol": "#",
"symbol": "a",
"path": "WIDE1-1",
"sendViaAPRSIS": false,
"sendViaRF": false
@@ -21,6 +21,11 @@
"digi": {
"mode": 0
},
"tnc": {
"enableServer": false,
"enableSerial": false,
"acceptOwn": false
},
"aprs_is": {
"active": false,
"passcode": "XYZVW",
@@ -60,6 +65,8 @@
"rememberStationTime": 30,
"sendBatteryVoltage": false,
"externalVoltageMeasurement": false,
"externalVoltagePin": 34
"externalVoltagePin": 34,
"lowPowerMode": false,
"lowVoltageCutOff": 0
}
}
}
+232 -125
View File
@@ -11,101 +11,80 @@
<input type="file" accept="text/json,.json" style="display: none" />
<form autocomplete="off" action="/configuration.json" method="POST">
<nav
class="navbar bg-body-secondary shadow-sm border-bottom sticky-top"
class="navbar bg-body-secondary shadow-sm border-bottom sticky-top navbar-expand-lg"
>
<div class="container">
<a class="navbar-brand"
>CA2RXU's LoRa iGate Configuration</a
<a class="navbar-brand" href=""
>CA2RXU's LoRa</a
>
<div class="d-flex">
<a href="/update" class="btn btn-warning mx-2"> OTA </a>
<button
class="btn btn-primary mx-2"
type="button"
id="backup"
>
Backup
</button>
<button
class="btn btn-primary mx-2"
type="button"
id="restore"
>
Restore
</button>
<button
class="btn btn-success mx-2"
type="submit"
id="save"
>
Save
</button>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor01">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="/">
Configuration
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/update">
Update <small>OTA</small>
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Backup</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" id="backup">Download</a>
<a class="dropdown-item" href="#" id="restore">Restore</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Device</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" id="reboot">Reboot</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="/received-packets">
Received packets
</a>
</li>
</ul>
<div class="d-flex">
<button class="btn btn-success my-2 my-sm-0" type="submit">Save</button>
</div>
</div>
</div>
</nav>
<div class="container">
<div class="container d-none" id="received-packets">
<div class="row my-5 d-flex align-items-top">
<div class="col-12">
<h3>Last 20 received packets list</h3>
<table class="table mt-4">
<thead>
<tr>
<th scope="col">Time*</th>
<th scope="col">Frame</th>
<th scope="col">RSSI</th>
<th scope="col">SNR</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<span>List refresh automatically every 15 seconds.</span><br>
<small>* - For now time is measured from board boot up.</small>
</div>
</div>
<hr />
</div>
<div class="container" id="configuration">
<main>
<div class="col-10 my-5 mx-auto">
<!-- <div class="row">
<div class="d-flex align-items-center">
<img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ6SURBVHgB7d0/axRBGMfx50IMWPoKkpA02gUsLfZdqOCLOPBFWAliHbCUVNpaxQNrIY0WVjZaJlUsIsZ5uDu9bGb/zMwzs/PM/L7wIG4Ocflwl7ndvVsihBBCmTajunpk5uHG33+aOSGkvhdmLs1cW+bKzDEhle2a+UZ22Pacrx6PlMRYXc/arrkkIKvIBxfISgrBBXLmSeACOdMkcYGcWTFwgZxJMXGBPHEpcIE8USlxgSzcbGD2KD0ukIXKGRfIAuWOC+TANOACOSAtuEAemQ10a2P2KV9cIA+0ZZltMzurOaT8cYHcUx+wJlwgd9QFrBEXyJZswAekFxfIrdq4GhZUQO7Itlq+QzoXVEC21AdcIm51yF3AJeNWhWwDLmFBBeRVmg4/Atmj2nGLR/bCbZrm2qWx/+7Ekwx5i9LGO/XFzF2qO97/r5QAOSUwcG+WBDkVMHDtRUdOAcz/ed4J4NqLihwbGLjjioYcExi4bkVBjgUMXL/EkWMAAzcsUWRpYODKJIYsCQxc2USQpYCBG6dgZAlg4MYtCDkUGLhp8kYOAQZu2ryQfYGT4S7PAKJVzsg+wEmfubNZbd+XOpgTsiswXpbzaDSyCzBw82oU8lhg4ObZIPIYYODmXS/yEDBwdcQ+72w/6AMGrq6OaHnLghv1AQNXX6/bG7qAPxNwNbbf3mAD5qf5EaEisgG/IVRMNuA9QsVkA94mpLWP7Q2pP5uE4vayvQHA5bQw86m90Qb8i5C22Kyx/cAG/IGQphj3ftcPbcBzM78JaWiN+73rATZgfvAzWn4SHeXbIC7Xtcg6MfOUgJxro3C5vlU0kPNsNC439DYJyHnlhMuNeR8M5DxyxuXGHugA8rR54XIuR7KAPE3euJzroUogpy0Il/M5Fg3kNAXjcr4nG4AcNxFcLuTc78nqz7e0/A7KLDo9PbV+nkn6Q2xnZ2c0n88pQmK4XOjJ/eyQm6YhxYnichLng/FyLZM4Lid1wh/IYUXB5SSv6ACyX9FwOelLdoDsVlRcLsY1WUAeV3RcLtZFd0Duj3EfUGRcLuZVlUC2lwyXi32Re/D75IuLC1osFtYDFXxAo73dts21MV/8wgc6PEqKm7LHZv5QnndASTV8p5U9unl7oaJ6QvUi23CLA+YdqhGZcQ/Jfmu/olrvVE3Ia9wdqgB4sxp+JzPuPv2/+XV1lYzcxq32Q34lvlzbcKsF5h0vCZlxD+g2btXApSCvF1TbBOB/ba4oNSOvn7lr3KpWzC5pXHgVf4RKOk3P5CqOUEmn5eW6CxfAA2n4ndyHC+AR5YwMXOFyWngx7i4h8XJABm7kpkQGbqKmQAZu4lIiA3eiUiADd+JiIgM3k2IgAzezJJGBm2kSyMDNvBBk4CrJBxm4ynJB/kHAVRnf3OucumGvzBxTBZV+eouhn5u5t7HtvZlXhBBCCCEUsb+qGDWF0d8l5AAAAABJRU5ErkJggg=="
width="60"
height="60"
class="me-2"
/>
<div>
<label for="stationMode" class="form-label"
>Station Mode</label
>
<select
class="form-select form-select-lg"
name="stationMode"
id="stationMode"
>
<option value="1">
Rx (only) iGate with black diamond +
"L" as symbol.
</option>
<option value="2">
[HAM only] Rx + Tx iGate with red
diamond + "L" as symbol.
</option>
<option value="3">
[HAM only] Digipeater simplex,
green-star + "L" as symbol.
</option>
<option value="4">
[HAM only] Digipeater split
frequency, green-star + "L" as
symbol.
</option>
<option value="5">
[HAM only] Rx + Tx iGate connected
to WiFi, Digipeater mode as fallback
</option>
</select>
<div class="form-check form-switch mt-3">
<input
type="checkbox"
name="bme.active"
id="bme.active"
class="form-check-input"
/>
<label
for="bme.active"
class="form-check-label"
><b>Activate Wx Telemetry</b>
<small
>Requires a BME/BMP280 or BME680
sensor installed</small
></label
>
</div>
</div>
</div>
</div> -->
<div class="row my-5 d-flex align-items-top">
<div class="col-lg-3 col-sm-12">
<h5>
@@ -174,8 +153,7 @@
name="beacon.path"
id="beacon.path"
class="form-control"
placeholder="WIDE1-1"
required=""
placeholder="We prefer WIDE1-1"
/>
</div>
<div class="col-8 mt-3">
@@ -540,6 +518,91 @@
</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-database-fill"
viewBox="0 0 16 16"
>
<path
d="M3.904 1.777C4.978 1.289 6.427 1 8 1s3.022.289 4.096.777C13.125 2.245 14 2.993 14 4s-.875 1.755-1.904 2.223C11.022 6.711 9.573 7 8 7s-3.022-.289-4.096-.777C2.875 5.755 2 5.007 2 4s.875-1.755 1.904-2.223"
/>
<path
d="M2 6.161V7c0 1.007.875 1.755 1.904 2.223C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777C13.125 8.755 14 8.007 14 7v-.839c-.457.432-1.004.751-1.49.972C11.278 7.693 9.682 8 8 8s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"
/>
<path
d="M2 9.161V10c0 1.007.875 1.755 1.904 2.223C4.978 12.711 6.427 13 8 13s3.022-.289 4.096-.777C13.125 11.755 14 11.007 14 10v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"
/>
<path
d="M2 12.161V13c0 1.007.875 1.755 1.904 2.223C4.978 15.711 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"
/>
</svg>
TNC
</h5>
<small
>TNC and KISS configuration</small
>
</div>
<div class="col-lg-9 col-sm-12">
<div class="row">
<div class="col-12">
<div class="form-check form-switch">
<div class="form-text">
Server will be available at port <strong>8001</strong>
</div>
<input
type="checkbox"
name="tnc.enableServer"
id="tnc.enableServer"
class="form-check-input"
/>
<label
for="tnc.enableServer"
class="form-label"
>Enable TNC server</label
>
</div>
</div>
<div class="col-12">
<div class="form-check form-switch">
<input
type="checkbox"
name="tnc.enableSerial"
id="tnc.enableSerial"
class="form-check-input"
/>
<label
for="tnc.enableSerial"
class="form-label"
>Enable Serial KISS</label
>
</div>
</div>
<div class="col-12">
<div class="form-check form-switch">
<input
type="checkbox"
name="tnc.acceptOwn"
id="tnc.acceptOwn"
class="form-check-input"
/>
<label
for="tnc.acceptOwn"
class="form-label"
>Accept own frames via KISS</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>
@@ -680,39 +743,6 @@
</select>
</div>
</div>
<!-- <div class="row mt-3">
<div class="col-6">
<div class="form-check form-switch">
<input
type="checkbox"
name="beacon.igateSendsLoRaBeacon"
id="beacon.igateSendsLoRaBeacon"
class="form-check-input"
/>
<label
for="beacon.igateSendsLoRaBeacon"
class="form-label"
>IGate sends LoRa beacon</label
>
</div>
</div>
<div class="col-6">
<div class="form-check form-switch">
<input
type="checkbox"
name="beacon.igateRepeatLoRaPackets"
id="beacon.igateRepeatLoRaPackets"
class="form-check-input"
/>
<label
for="beacon.igateRepeatLoRaPackets"
class="form-label"
>IGate repeat LoRa
packets</label
>
</div>
</div>
</div> -->
</div>
</div>
<hr />
@@ -837,7 +867,7 @@
</div>
<div class="col-lg-9 col-sm-12">
<div class="row">
<div class="col-12">
<div class="col-6">
<div class="form-check form-switch">
<input
type="checkbox"
@@ -852,6 +882,9 @@
>
</div>
</div>
<div class="col-6 d-grid gap-2">
<button class="btn btn-primary" id="send-beacon">Send beacon now</button>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
@@ -900,7 +933,6 @@
name="other.externalVoltageMeasurement"
id="other.externalVoltageMeasurement"
class="form-check-input"
disabled
/>
<label
for="other.externalVoltageMeasurement"
@@ -916,7 +948,6 @@
id="other.externalVoltagePin"
value="34"
class="form-control"
disabled
/>
<label
for="other.externalVoltagePin"
@@ -1235,11 +1266,78 @@
</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-heart-pulse-fill"
viewBox="0 0 16 16"
>
<path
d="M1.475 9C2.702 10.84 4.779 12.871 8 15c3.221-2.129 5.298-4.16 6.525-6H12a.5.5 0 0 1-.464-.314l-1.457-3.642-1.598 5.593a.5.5 0 0 1-.945.049L5.889 6.568l-1.473 2.21A.5.5 0 0 1 4 9z"
/>
<path
d="M.88 8C-2.427 1.68 4.41-2 7.823 1.143q.09.083.176.171a3 3 0 0 1 .176-.17C11.59-2 18.426 1.68 15.12 8h-2.783l-1.874-4.686a.5.5 0 0 0-.945.049L7.921 8.956 6.464 5.314a.5.5 0 0 0-.88-.091L3.732 8z"
/>
</svg>
Experimental
</h5>
<small>You can test new features. <u>Use at your own risk!</u></small>
</div>
<div class="col-lg-9 col-sm-12">
<div class="row">
<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</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"
/>
<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>
</main>
<footer
class="my-5 py-5 text-body-secondary text-center text-small"
>
<p class="mb-4 small">%BUILD_INFO%</p>
<p class="mb-1 small">
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -1358,6 +1456,15 @@
</div>
</div>
</div>
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
<div id="toast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">System Message</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body text-center"></div>
</div>
</div>
</body>
<script src="/bootstrap.js"></script>
<script src="/script.js"></script>
+95 -15
View File
@@ -86,7 +86,7 @@ function loadSettings(settings) {
document.querySelector(".list-networks").innerHTML = "";
// Networks
const wifiNetworks = settings.wifi.AP;
const wifiNetworks = settings.wifi.AP || [];
const networksContainer = document.querySelector(".list-networks");
let networkCount = 0;
@@ -150,6 +150,13 @@ function loadSettings(settings) {
// document.getElementById("digi.longitude").value = settings.digi.longitude;
document.getElementById("digi.mode").value = settings.digi.mode;
// TNC
if (settings.tnc) {
document.getElementById("tnc.enableServer").checked = settings.tnc.enableServer;
document.getElementById("tnc.enableSerial").checked = settings.tnc.enableSerial;
document.getElementById("tnc.acceptOwn").checked = settings.tnc.acceptOwn;
}
// OTA
document.getElementById("ota.username").value = settings.ota.username;
document.getElementById("ota.password").value = settings.ota.password;
@@ -162,6 +169,7 @@ function loadSettings(settings) {
settings.other.sendBatteryVoltage;
document.getElementById("other.externalVoltageMeasurement").checked =
settings.other.externalVoltageMeasurement;
document.getElementById("other.externalVoltagePin").value =
settings.other.externalVoltagePin;
// document.getElementById("beacon.igateSendsLoRaBeacon").value =
@@ -205,10 +213,39 @@ function loadSettings(settings) {
settings.lora.codingRate4;
document.getElementById("lora.power").value = settings.lora.power;
// Experimental
document.getElementById("other.lowPowerMode").checked = settings.other.lowPowerMode;
document.getElementById("other.lowVoltageCutOff").value = settings.other.lowVoltageCutOff || 0
updateImage();
refreshSpeedStandard();
toggleFields();
}
function showToast(message) {
const el = document.querySelector('#toast');
el.querySelector('.toast-body').innerHTML = message;
(new bootstrap.Toast(el)).show();
}
document.getElementById('send-beacon').addEventListener('click', function (e) {
e.preventDefault();
fetch("/action?type=send-beacon", { method: "POST" });
showToast("Your beacon will be sent in a moment. <br> <u>This action will be ignored if you have APRSIS and LoRa TX disabled!</u>");
});
document.getElementById('reboot').addEventListener('click', function (e) {
e.preventDefault();
fetch("/action?type=reboot", { method: "POST" });
showToast("Your device will be rebooted in a while");
});
const bmeCheckbox = document.querySelector("input[name='bme.active']");
const stationModeSelect = document.querySelector("select[name='stationMode']");
@@ -239,9 +276,6 @@ function updateImage() {
}
function toggleFields() {
const sendBatteryVoltageCheckbox = document.querySelector(
'input[name="other.sendBatteryVoltage"]'
);
const externalVoltageMeasurementCheckbox = document.querySelector(
'input[name="other.externalVoltageMeasurement"]'
);
@@ -249,15 +283,10 @@ function toggleFields() {
'input[name="other.externalVoltagePin"]'
);
externalVoltageMeasurementCheckbox.disabled =
!sendBatteryVoltageCheckbox.checked;
externalVoltagePinInput.disabled =
!externalVoltageMeasurementCheckbox.checked;
}
const sendBatteryVoltageCheckbox = document.querySelector(
'input[name="other.sendBatteryVoltage"]'
);
const externalVoltageMeasurementCheckbox = document.querySelector(
'input[name="other.externalVoltageMeasurement"]'
);
@@ -265,10 +294,8 @@ const externalVoltagePinInput = document.querySelector(
'input[name="other.externalVoltagePin"]'
);
sendBatteryVoltageCheckbox.addEventListener("change", function () {
externalVoltageMeasurementCheckbox.disabled = !this.checked;
externalVoltagePinInput.disabled =
!externalVoltageMeasurementCheckbox.checked;
externalVoltageMeasurementCheckbox.addEventListener("change", function () {
externalVoltagePinInput.disabled = !this.checked;
});
document.querySelector(".new button").addEventListener("click", function () {
@@ -378,8 +405,6 @@ document.getElementById("action.speed").addEventListener("change", function () {
}
});
toggleFields();
const form = document.querySelector("form");
const saveModal = new bootstrap.Modal(document.getElementById("saveModal"), {
@@ -431,3 +456,58 @@ form.addEventListener("submit", async (event) => {
});
fetchSettings();
function loadReceivedPackets(packets) {
if (packets) {
document.querySelector('#received-packets tbody').innerHTML = '';
const container = document.querySelector("#received-packets tbody");
container.innerHTML = '';
const date = new Date();
packets.forEach((packet) => {
const element = document.createElement("tr");
date.setTime(packet.millis);
const p = date.toUTCString().split(' ')
element.innerHTML = `
<td>${p[p.length-2]}</td>
<td>${packet.packet}</td>
<td>${packet.RSSI}</td>
<td>${packet.SNR}</td>
`;
container.appendChild(element);
})
}
setTimeout(fetchReceivedPackets, 15000);
}
function fetchReceivedPackets() {
fetch("/received-packets.json")
.then((response) => response.json())
.then((packets) => {
loadReceivedPackets(packets);
})
.catch((err) => {
console.error(err);
console.error(`Failed to load received packets`);
});
}
document.querySelector('a[href="/received-packets"]').addEventListener('click', function (e) {
e.preventDefault();
document.getElementById('received-packets').classList.remove('d-none');
document.getElementById('configuration').classList.add('d-none');
document.querySelector('button[type=submit]').remove();
fetchReceivedPackets();
})
+8
View File
@@ -0,0 +1,8 @@
.alert-fixed {
position:fixed;
top: 0px;
left: 0px;
width: 100%;
z-index:9999;
border-radius:0px
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

+84 -14
View File
@@ -23,7 +23,6 @@ board_build.embed_files =
data_embed/bootstrap.js.gz
lib_deps =
bblanchon/ArduinoJson@^6.20.2
sandeepmistry/LoRa@^0.8.0
adafruit/Adafruit GFX Library @ 1.11.5
adafruit/Adafruit SSD1306 @ 2.5.7
mikalhart/TinyGPSPlus @ 1.0.3
@@ -44,44 +43,115 @@ debug_tool = esp-prog
[env:ttgo-lora32-v21]
board = ttgo-lora32-v21
build_flags = -Werror -Wall -DTTGO_T_LORA32_V2_1 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DTTGO_T_LORA32_V2_1
-DHAS_SX127X
-DHAS_SX1278
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:heltec-lora32-v2]
board = ttgo-lora32-v21
build_flags = -Werror -Wall -DHELTEC_V2 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DHELTEC_V2
-DHAS_SX127X
-DHAS_SX1278
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:heltec_wifi_lora_32_V3]
board = heltec_wifi_lora_32_V3
build_flags = -Werror -Wall -DHELTEC_V3 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
board_build.mcu = esp32s3
build_flags =
-Werror -Wall
-DHELTEC_V3
-DHAS_SX126X
-DHAS_SX1262
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:ESP32_DIY_LoRa]
board = esp32dev
build_flags = -Werror -Wall -DESP32_DIY_LoRa -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DESP32_DIY_LoRa
-DHAS_SX127X
-DHAS_SX1278
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:ESP32_DIY_1W_LoRa]
board = esp32dev
build_flags = -Werror -Wall -DESP32_DIY_1W_LoRa -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DESP32_DIY_1W_LoRa
-DHAS_SX126X
-DHAS_SX1268
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:ttgo-t-beam-v1_2]
board = ttgo-t-beam
build_flags = -Werror -Wall -DTTGO_T_Beam_V1_2 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DTTGO_T_Beam_V1_2
-DHAS_SX127X
-DHAS_SX1278
-DHAS_AXP2101
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:ttgo-t-beam-v1]
board = ttgo-t-beam
build_flags = -Werror -Wall -DTTGO_T_Beam_V1_0 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DTTGO_T_Beam_V1_0
-DHAS_SX127X
-DHAS_SX1278
-DHAS_AXP192
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:ttgo-t-beam-v1_SX1268]
board = ttgo-t-beam
build_flags = -Werror -Wall -DTTGO_T_Beam_V1_0_SX1268 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DTTGO_T_Beam_V1_0_SX1268
-DHAS_SX126X
-DHAS_SX1268
-DHAS_AXP192
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:ttgo-t-beam-v1_2_SX1262]
board = ttgo-t-beam
build_flags = -Werror -Wall -DTTGO_T_Beam_V1_2_SX1262 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DTTGO_T_Beam_V1_2_SX1262
-DHAS_SX126X
-DHAS_SX1262
-DHAS_AXP2101
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:heltec_wireless_stick_lite]
platform = espressif32
[env:OE5HWN_MeshCom]
board = esp32dev
build_flags =
-Werror -Wall
-DOE5HWN_MeshCom
-DHAS_SX126X
-DHAS_SX1268
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:heltec_wireless_stick]
board = heltec_wifi_lora_32_V3
board_build.mcu = esp32s3
build_flags =
-Werror -Wall
-DHELTEC_WS
-DHAS_SX126X
-DHAS_SX1262
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
[env:heltec_ht-ct62]
board = heltec_wireless_stick_lite
board_build.mcu = esp32c3
board_build.f_cpu = 240000000L
build_flags = -Werror -Wall -DHELTEC_CT62 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1
build_flags =
-Werror -Wall
-DHELTEC_HTCT62
-DHAS_SX126X
-DHAS_SX1262
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
+110 -15
View File
@@ -14,16 +14,17 @@
#include "gps_utils.h"
#include "bme_utils.h"
#include "web_utils.h"
#include "tnc_utils.h"
#include "display.h"
#include "utils.h"
#include <ElegantOTA.h>
#include "battery_utils.h"
Configuration Config;
WiFiClient espClient;
String versionDate = "2024.03.08";
int myWiFiAPIndex = 0;
String versionDate = "2024.04.13";
uint8_t myWiFiAPIndex = 0;
int myWiFiAPSize = Config.wifiAPs.size();
WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
@@ -41,6 +42,8 @@ bool WiFiConnected = false;
bool WiFiAutoAPStarted = false;
long WiFiAutoAPTime = false;
uint32_t lastBatteryCheck = 0;
uint32_t bmeLastReading = -60000;
String batteryVoltage;
@@ -50,15 +53,17 @@ std::vector<String> lastHeardStation_temp;
std::vector<String> packetBuffer;
std::vector<String> packetBuffer_temp;
std::vector<ReceivedPacket> receivedPackets;
String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, iGateBeaconPacket, iGateLoRaBeaconPacket;
void setup() {
Serial.begin(115200);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_HTCT62)
pinMode(batteryPin, INPUT);
#endif
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
#ifdef HAS_INTERNAL_LED
pinMode(internalLedPin, OUTPUT);
#endif
if (Config.externalVoltageMeasurement) {
@@ -72,16 +77,85 @@ void setup() {
Config.check();
WIFI_Utils::setup();
LoRa_Utils::setup();
Utils::validateFreqs();
iGateBeaconPacket = GPS_Utils::generateBeacon();
iGateLoRaBeaconPacket = GPS_Utils::generateiGateLoRaBeacon();
#ifdef HELTEC_HTCT62
if (Config.lowPowerMode) {
gpio_wakeup_enable(GPIO_NUM_3, GPIO_INTR_HIGH_LEVEL);
esp_deep_sleep_enable_gpio_wakeup(GPIO_NUM_3, ESP_GPIO_WAKEUP_GPIO_HIGH);
long lastBeacon = 0;
LoRa_Utils::startReceive();
while (true) {
auto wakeup_reason = esp_sleep_get_wakeup_cause();
if (wakeup_reason == 7) { // packet received
Serial.println("Received packet");
String packet = LoRa_Utils::receivePacket();
Serial.println(packet);
if (Config.digi.mode == 2) { // If Digi enabled
DIGI_Utils::loop(packet); // Send received packet to Digi
}
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.sendBatteryVoltage) {
comment += " Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V";
}
if (Config.externalVoltageMeasurement) {
comment += " Ext=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V";
}
LoRa_Utils::sendNewPacket("APRS", iGateLoRaBeaconPacket + 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();
SYSLOG_Utils::setup();
BME_Utils::setup();
WEB_Utils::setup();
TNC_Utils::setup();
}
void loop() {
@@ -92,6 +166,10 @@ void loop() {
return; // Don't process IGate and Digi during OTA update
}
if (Config.lowVoltageCutOff > 0) {
BATTERY_Utils::checkIfShouldSleep();
}
thirdLine = Utils::getLocalIP();
WIFI_Utils::checkWiFi(); // Always use WiFi, not related to IGate/Digi mode
@@ -101,23 +179,40 @@ void loop() {
APRS_IS_Utils::connect();
}
TNC_Utils::loop();
Utils::checkDisplayInterval();
Utils::checkBeaconInterval();
String packet;
if (Config.loramodule.rxActive) {
packet = LoRa_Utils::receivePacket(); // We need to fetch LoRa packet above APRSIS and Digi
}
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
if (Config.aprs_is.active) { // If APRSIS enabled
APRS_IS_Utils::loop(packet); // Send received packet to APRSIS
String packet = "";
if (Config.loramodule.rxActive) {
packet = LoRa_Utils::receivePacket(); // We need to fetch LoRa packet above APRSIS and Digi
}
if (packet != "") {
if (Config.aprs_is.active) { // If APRSIS enabled
APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS
}
if (Config.digi.mode == 2) { // If Digi enabled
DIGI_Utils::loop(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.digi.mode == 2) { // If Digi enabled
DIGI_Utils::loop(packet); // Send received packet to Digi
if (Config.aprs_is.active) { // If APRSIS enabled
APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS
}
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
+94 -95
View File
@@ -11,7 +11,6 @@
extern Configuration Config;
extern WiFiClient espClient;
extern int internalLedPin;
extern uint32_t lastScreenOn;
extern String firstLine;
extern String secondLine;
@@ -28,8 +27,8 @@ namespace APRS_IS_Utils {
espClient.print(line + "\r\n");
}
void connect(){
int count = 0;
void connect() {
uint8_t count = 0;
String aprsauth;
Serial.print("Connecting to APRS-IS ... ");
while (!espClient.connect(Config.aprs_is.server.c_str(), Config.aprs_is.port) && count < 20) {
@@ -44,12 +43,13 @@ namespace APRS_IS_Utils {
}
if (count == 20) {
Serial.println("Tried: " + String(count) + " FAILED!");
} else {
Serial.println("Connected!\n(Server: " + String(Config.aprs_is.server) + " / Port: " + String(Config.aprs_is.port) +")");
}
else {
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;
aprsauth = "user " + Config.callsign + " pass " + Config.aprs_is.passcode + " vers CA2RXU_LoRa_iGate 1.3 filter " + Config.aprs_is.filter;// + "\r\n";
aprsauth = "user " + Config.callsign + " pass " + Config.aprs_is.passcode + " vers CA2RXU_LoRa_iGate 1.3 filter " + Config.aprs_is.filter;
upload(aprsauth);
delay(200);
}
@@ -58,8 +58,9 @@ namespace APRS_IS_Utils {
void checkStatus() {
String wifiState, aprsisState;
if (WiFi.status() == WL_CONNECTED) {
wifiState = "OK";
} else {
wifiState = "OK";
}
else {
wifiState = "AP";
if (!Config.display.alwaysOn) {
@@ -70,10 +71,12 @@ namespace APRS_IS_Utils {
}
if (!Config.aprs_is.active) {
aprsisState = "OFF";
} else if (espClient.connected()) {
aprsisState = "OK";
} else {
aprsisState = "OFF";
}
else if (espClient.connected()) {
aprsisState = "OK";
}
else {
aprsisState = "--";
if (!Config.display.alwaysOn) {
@@ -82,112 +85,114 @@ namespace APRS_IS_Utils {
lastScreenOn = millis();
}
secondLine = "WiFi: " + wifiState + " APRS-IS: " + aprsisState;
}
String createPacket(String packet) {
if (!(Config.aprs_is.active && Config.digi.mode == 0)) { // Check if NOT only IGate
return packet.substring(3, packet.indexOf(":")) + ",qAR," + Config.callsign + packet.substring(packet.indexOf(":"));// + "\n";
} else {
return packet.substring(3, packet.indexOf(":")) + ",qAO," + Config.callsign + packet.substring(packet.indexOf(":"));// + "\n";
return packet.substring(3, packet.indexOf(":")) + ",qAR," + Config.callsign + packet.substring(packet.indexOf(":"));
}
else {
return packet.substring(3, packet.indexOf(":")) + ",qAO," + Config.callsign + packet.substring(packet.indexOf(":"));
}
}
bool processReceivedLoRaMessage(String sender, String packet) {
String ackMessage, receivedMessage;
if (packet.indexOf("{") > 0) { // ack?
ackMessage = "ack" + packet.substring(packet.indexOf("{") + 1);
ackMessage.trim();
delay(4000);
//Serial.println(ackMessage);
for (int i = sender.length(); i < 9; i++) {
sender += ' ';
}
if (Config.beacon.path == "") {
LoRa_Utils::sendNewPacket("APRS", Config.callsign + ">APLRG1,RFONLY::" + sender + ":" + ackMessage);
} else {
LoRa_Utils::sendNewPacket("APRS", Config.callsign + ">APLRG1,RFONLY," + Config.beacon.path + "::" + sender + ":" + ackMessage);
}
receivedMessage = packet.substring(packet.indexOf(":") + 1, packet.indexOf("{"));
}
else {
receivedMessage = packet.substring(packet.indexOf(":") + 1);
}
if (receivedMessage.indexOf("?") == 0) {
delay(2000);
if (!Config.display.alwaysOn) {
display_toggle(true);
}
LoRa_Utils::sendNewPacket("APRS", QUERY_Utils::process(receivedMessage, sender, "LoRa"));
lastScreenOn = millis();
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, "Callsign = " + sender, "TYPE --> QUERY", 0);
return true;
}
else {
return false;
}
}
void processLoRaPacket(String packet) {
bool queryMessage = false;
String aprsPacket, Sender, AddresseeAndMessage, Addressee, ackMessage, receivedMessage;
if (packet != "") {
#ifdef TextSerialOutputForApp
Serial.println(packet.substring(3));
#else
Serial.print("Received Lora Packet : " + String(packet));
#endif
if ((packet.substring(0,3) == "\x3c\xff\x01") && (packet.indexOf("TCPIP") == -1) && (packet.indexOf("NOGATE") == -1) && (packet.indexOf("RFONLY") == -1)) {
#ifndef TextSerialOutputForApp
Serial.print(" ---> APRS LoRa Packet!");
#endif
Sender = packet.substring(3,packet.indexOf(">"));
if (Sender != Config.callsign) { // avoid listening yourself by digirepeating
AddresseeAndMessage = packet.substring(packet.indexOf("::")+2);
Addressee = AddresseeAndMessage.substring(0,AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
if (AddresseeAndMessage.indexOf("{")>0) { // ack?
ackMessage = "ack" + AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{")+1);
ackMessage.trim();
delay(4000);
//Serial.println(ackMessage);
for(int i = Sender.length(); i < 9; i++) {
Sender += ' ';
}
LoRa_Utils::sendNewPacket("APRS", Config.callsign + ">APLRG1,RFONLY,WIDE1-1::" + Sender + ":" + ackMessage);
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":")+1, AddresseeAndMessage.indexOf("{"));
} else {
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":")+1);
if (espClient.connected()) {
bool queryMessage = false;
String aprsPacket, Sender, AddresseeAndMessage, Addressee;
if (packet != "") {
if ((packet.substring(0, 3) == "\x3c\xff\x01") && (packet.indexOf("TCPIP") == -1) && (packet.indexOf("NOGATE") == -1) && (packet.indexOf("RFONLY") == -1)) {
Sender = packet.substring(3, packet.indexOf(">"));
STATION_Utils::updateLastHeard(Sender);
//STATION_Utils::updatePacketBuffer(packet);
Utils::typeOfPacket(aprsPacket, "LoRa-APRS");
if (Sender != Config.callsign) { // avoid listening yourself by digirepeating
AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
queryMessage = processReceivedLoRaMessage(Sender, AddresseeAndMessage);
}
if (receivedMessage.indexOf("?") == 0) {
queryMessage = true;
delay(2000);
if (!queryMessage) {
aprsPacket = createPacket(packet);
if (!Config.display.alwaysOn) {
display_toggle(true);
}
LoRa_Utils::sendNewPacket("APRS", QUERY_Utils::process(receivedMessage, Sender, "LoRa"));
lastScreenOn = millis();
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, "Callsign = " + Sender, "TYPE --> QUERY", 0);
upload(aprsPacket);
Utils::println("---> Uploaded to APRS-IS");
STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(aprsPacket, "LoRa-APRS");
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
}
}
if (!queryMessage) {
aprsPacket = createPacket(packet);
if (!Config.display.alwaysOn) {
display_toggle(true);
}
lastScreenOn = millis();
upload(aprsPacket);
#ifndef TextSerialOutputForApp
Serial.println(" ---> Uploaded to APRS-IS");
#endif
STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(aprsPacket, "LoRa-APRS");
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
}
}
} else {
#ifndef TextSerialOutputForApp
Serial.println(" ---> LoRa Packet Ignored (first 3 bytes or TCPIP/NOGATE/RFONLY)\n");
#endif
}
}
}
}
void processAPRSISPacket(String packet) {
String Sender, AddresseeAndMessage, Addressee, receivedMessage;
if (!packet.startsWith("#")){
if (packet.indexOf("::")>0) {
Sender = packet.substring(0,packet.indexOf(">"));
AddresseeAndMessage = packet.substring(packet.indexOf("::")+2);
if (!packet.startsWith("#")) {
if (packet.indexOf("::") > 0) {
Sender = packet.substring(0, packet.indexOf(">"));
AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (Addressee == Config.callsign) { // its for me!
if (AddresseeAndMessage.indexOf("{")>0) { // ack?
String ackMessage = "ack" + AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{")+1);
if (AddresseeAndMessage.indexOf("{") > 0) { // ack?
String ackMessage = "ack" + AddresseeAndMessage.substring(AddresseeAndMessage.indexOf("{") + 1);
ackMessage.trim();
delay(4000);
//Serial.println(ackMessage);
for(int i = Sender.length(); i < 9; i++) {
for (int i = Sender.length(); i < 9; i++) {
Sender += ' ';
}
String ackPacket = Config.callsign + ">APLRG1,TCPIP,qAC::" + Sender + ":" + ackMessage;// + "\n";
upload(ackPacket);
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":")+1, AddresseeAndMessage.indexOf("{"));
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1, AddresseeAndMessage.indexOf("{"));
} else {
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":")+1);
receivedMessage = AddresseeAndMessage.substring(AddresseeAndMessage.indexOf(":") + 1);
}
if (receivedMessage.indexOf("?") == 0) {
#ifndef TextSerialOutputForApp
Serial.println("Received Query APRS-IS : " + packet);
#endif
Utils::println("Received Query APRS-IS : " + packet);
String queryAnswer = QUERY_Utils::process(receivedMessage, Sender, "APRSIS");
//Serial.println("---> QUERY Answer : " + queryAnswer.substring(0,queryAnswer.indexOf("\n")));
if (!Config.display.alwaysOn) {
@@ -196,19 +201,17 @@ namespace APRS_IS_Utils {
lastScreenOn = millis();
delay(500);
upload(queryAnswer);
SYSLOG_Utils::log("APRSIS Tx", queryAnswer,0,0,0);
SYSLOG_Utils::log("APRSIS Tx", queryAnswer, 0, 0, 0);
fifthLine = "APRS-IS ----> APRS-IS";
sixthLine = Config.callsign;
for (int j=sixthLine.length();j<9;j++) {
for (int j = sixthLine.length();j < 9;j++) {
sixthLine += " ";
}
sixthLine += "> " + Sender;
seventhLine = "QUERY = " + receivedMessage;
}
} else {
#ifndef TextSerialOutputForApp
Serial.print("Received from APRS-IS : " + packet);
#endif
Utils::print("Received from APRS-IS : " + packet);
if (Config.aprs_is.toRF && STATION_Utils::wasHeard(Addressee)) {
LoRa_Utils::sendNewPacket("APRS", LoRa_Utils::generatePacket(packet));
@@ -218,19 +221,15 @@ namespace APRS_IS_Utils {
}
}
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
}
}
}
}
void loop(String packet) {
void listenAPRSIS() {
if (espClient.connected()) {
processLoRaPacket(packet);
if (espClient.available()) {
String aprsisPacket = espClient.readStringUntil('\r');
// Serial.println(aprsisPacket);
processAPRSISPacket(aprsisPacket);
}
}
+2 -8
View File
@@ -4,22 +4,16 @@
#include <Arduino.h>
//#define TextSerialOutputForApp
/* uncomment the previous line to get text from Serial-Output over USB into PC for:
- PinPoint App ( https://www.pinpointaprs.com )
- APRSIS32 App ( http://aprsisce.wikidot.com )
*/
namespace APRS_IS_Utils {
void upload(String line);
void connect();
void checkStatus();
String createPacket(String unprocessedPacket);
bool processReceivedLoRaMessage(String sender, String packet);
void processLoRaPacket(String packet);
void processAPRSISPacket(String packet);
void loop(String packet);
void listenAPRSIS();
}
+32 -5
View File
@@ -3,6 +3,7 @@
#include "pins_config.h"
extern Configuration Config;
extern uint32_t lastBatteryCheck;
float adcReadingTransformation = (3.3/4095);
float voltageDividerCorrection = 0.288;
@@ -16,11 +17,15 @@ float multiplyCorrection = 0.035;
namespace BATTERY_Utils {
float mapVoltage(float voltage, float in_min, float in_max, float out_min, float out_max) {
return (voltage - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float checkBattery() {
int sample;
int sampleSum = 0;
for (int i=0; i<100; i++) {
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
for (int i = 0; i < 100; i++) {
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_HTCT62)
sample = analogRead(batteryPin);
#endif
#if defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
@@ -29,18 +34,40 @@ namespace BATTERY_Utils {
sampleSum += sample;
delayMicroseconds(50);
}
return (2 * (sampleSum/100) * adcReadingTransformation) + voltageDividerCorrection;
float voltage = (2 * (sampleSum/100) * adcReadingTransformation) + voltageDividerCorrection;
return voltage; // raw voltage without mapping
// return mapVoltage(voltage, 3.34, 4.71, 3.0, 4.2); // mapped voltage
}
float checkExternalVoltage() {
int sample;
int sampleSum = 0;
for (int i=0; i<100; i++) {
for (int i = 0; i < 100; i++) {
sample = analogRead(Config.externalVoltagePin);
sampleSum += sample;
delayMicroseconds(50);
}
return ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * ((R1+R2)/R2)) - multiplyCorrection;
float voltage = ((((sampleSum/100)* adcReadingTransformation) + readingCorrection) * ((R1+R2)/R2)) - multiplyCorrection;
return voltage; // raw voltage without mapping
// 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();
float voltage = checkBattery();
if (voltage < Config.lowVoltageCutOff) {
ESP.deepSleep(1800000000); // 30 min sleep (60s = 60e6)
}
}
}
}
+1
View File
@@ -8,6 +8,7 @@ namespace BATTERY_Utils {
float checkBattery();
float checkExternalVoltage();
void checkIfShouldSleep();
}
+23 -2
View File
@@ -16,7 +16,7 @@ void Configuration::check() {
void Configuration::writeFile() {
Serial.println("Saving config..");
StaticJsonDocument<1536> data;
StaticJsonDocument<2048> data;
File configFile = SPIFFS.open("/igate_conf.json", "w");
if (wifiAPs[0].ssid != "") { // We don't want to save Auto AP empty SSID
@@ -48,6 +48,10 @@ void Configuration::writeFile() {
// data["digi"]["latitude"] = digi.latitude;
// data["digi"]["longitude"] = digi.longitude;
data["tnc"]["enableServer"] = tnc.enableServer;
data["tnc"]["enableSerial"] = tnc.enableSerial;
data["tnc"]["acceptOwn"] = tnc.acceptOwn;
data["aprs_is"]["active"] = aprs_is.active;
data["aprs_is"]["passcode"] = aprs_is.passcode;
data["aprs_is"]["server"] = aprs_is.server;
@@ -92,6 +96,9 @@ void Configuration::writeFile() {
data["ota"]["username"] = ota.username;
data["ota"]["password"] = ota.password;
data["other"]["lowPowerMode"] = lowPowerMode;
data["other"]["lowVoltageCutOff"] = lowVoltageCutOff;
serializeJson(data, configFile);
configFile.close();
@@ -105,7 +112,7 @@ bool Configuration::readFile() {
File configFile = SPIFFS.open("/igate_conf.json", "r");
if (configFile) {
StaticJsonDocument<1536> data;
StaticJsonDocument<2048> data;
DeserializationError error = deserializeJson(data, configFile);
if (error) {
@@ -152,6 +159,13 @@ bool Configuration::readFile() {
ota.username = data["ota"]["username"].as<String>();
ota.password = data["ota"]["password"].as<String>();
tnc.enableServer = data["tnc"]["enableServer"].as<bool>();
tnc.enableSerial = data["tnc"]["enableSerial"].as<bool>();
tnc.acceptOwn = data["tnc"]["acceptOwn"].as<bool>();
lowPowerMode = data["other"]["lowPowerMode"].as<bool>();
lowVoltageCutOff = data["other"]["lowVoltageCutOff"].as<double>();
int stationMode = data["stationMode"].as<int>(); // deprecated but need to specify config version
if (stationMode == 0) {
@@ -279,6 +293,10 @@ void Configuration::init() {
// digi.latitude = 0.0; // deprecated
// digi.longitude = 0.0; // deprecated
tnc.enableServer = false;
tnc.enableSerial = false;
tnc.acceptOwn = false;
aprs_is.active = false; // new
aprs_is.passcode = "XYZVW";
aprs_is.server = "rotate.aprs2.net";
@@ -319,6 +337,9 @@ void Configuration::init() {
externalVoltageMeasurement = false;
externalVoltagePin = 34;
lowPowerMode = false;
lowVoltageCutOff = 0;
Serial.println("todo escrito");
}
+10
View File
@@ -73,6 +73,13 @@ public:
bool turn180;
};
class TNC {
public:
bool enableServer;
bool enableSerial;
bool acceptOwn;
};
class SYSLOG {
public:
bool active;
@@ -107,10 +114,13 @@ public:
bool sendBatteryVoltage;
bool externalVoltageMeasurement;
int externalVoltagePin;
bool lowPowerMode;
double lowVoltageCutOff;
std::vector<WiFi_AP> wifiAPs;
WiFi_Auto_AP wifiAutoAP;
Beacon beacon; // new
DIGI digi;
TNC tnc; // new
APRS_IS aprs_is;
LoraModule loramodule;
Display display;
+39 -30
View File
@@ -2,6 +2,7 @@
#include "configuration.h"
#include "station_utils.h"
#include "aprs_is_utils.h"
#include "query_utils.h"
#include "lora_utils.h"
#include "digi_utils.h"
#include "wifi_utils.h"
@@ -25,54 +26,62 @@ namespace DIGI_Utils {
String generateDigiRepeatedPacket(String packet, String callsign) {
String sender, temp0, tocall, path;
sender = packet.substring(0,packet.indexOf(">"));
temp0 = packet.substring(packet.indexOf(">")+1,packet.indexOf(":"));
sender = packet.substring(0, packet.indexOf(">"));
temp0 = packet.substring(packet.indexOf(">") + 1, packet.indexOf(":"));
if (temp0.indexOf(",") > 2) {
tocall = temp0.substring(0,temp0.indexOf(","));
path = temp0.substring(temp0.indexOf(",")+1,temp0.indexOf(":"));
if (path.indexOf("WIDE1-")>=0) {
String hop = path.substring(path.indexOf("WIDE1-")+6, path.indexOf("WIDE1-")+7);
if (hop.toInt()>=1 && hop.toInt()<=7) {
if (hop.toInt()==1) {
tocall = temp0.substring(0, temp0.indexOf(","));
path = temp0.substring(temp0.indexOf(",") + 1, temp0.indexOf(":"));
if (path.indexOf("WIDE1-") >= 0) {
String hop = path.substring(path.indexOf("WIDE1-") + 6, path.indexOf("WIDE1-") + 7);
if (hop.toInt() >= 1 && hop.toInt() <= 7) {
if (hop.toInt() == 1) {
path.replace("WIDE1-1", callsign + "*");
} else {
path.replace("WIDE1-" + hop , callsign + "*,WIDE1-" + String(hop.toInt()-1));
}
else {
path.replace("WIDE1-" + hop, callsign + "*,WIDE1-" + String(hop.toInt() - 1));
}
String repeatedPacket = sender + ">" + tocall + "," + path + packet.substring(packet.indexOf(":"));
return repeatedPacket;
} else {
}
else {
return "";
}
} else {
}
else {
return "";
}
} else {
}
else {
return "";
}
}
void processPacket(String packet) {
String loraPacket;
bool queryMessage = false;
String loraPacket, Sender, AddresseeAndMessage, Addressee;
if (packet != "") {
Serial.print("Received Lora Packet : " + String(packet));
if ((packet.substring(0, 3) == "\x3c\xff\x01") && (packet.indexOf("NOGATE") == -1)) {
Serial.println(" ---> APRS LoRa Packet");
String sender = packet.substring(3,packet.indexOf(">"));
STATION_Utils::updateLastHeard(sender);
STATION_Utils::updatePacketBuffer(packet);
Utils::typeOfPacket(packet.substring(3), "Digi");
if (packet.indexOf("WIDE1-") > 10 && Config.digi.mode == 2) { // If should repeat packet (WIDE1 Digi)
loraPacket = generateDigiRepeatedPacket(packet.substring(3), Config.callsign);
if (loraPacket != "") {
delay(500);
Serial.println(loraPacket);
LoRa_Utils::sendNewPacket("APRS", loraPacket);
display_toggle(true);
lastScreenOn = millis();
Sender = packet.substring(3, packet.indexOf(">"));
if (Sender != Config.callsign) {
STATION_Utils::updateLastHeard(Sender);
// STATION_Utils::updatePacketBuffer(packet);
Utils::typeOfPacket(packet.substring(3), "Digi");
AddresseeAndMessage = packet.substring(packet.indexOf("::") + 2);
Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
Addressee.trim();
if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage);
}
if (!queryMessage && packet.indexOf("WIDE1-") > 10 && Config.digi.mode == 2) { // If should repeat packet (WIDE1 Digi)
loraPacket = generateDigiRepeatedPacket(packet.substring(3), Config.callsign);
if (loraPacket != "") {
delay(500);
LoRa_Utils::sendNewPacket("APRS", loraPacket);
display_toggle(true);
lastScreenOn = millis();
}
}
}
} else {
Serial.println(" ---> LoRa Packet Ignored (first 3 bytes or NOGATE)\n");
}
}
}
+21 -1
View File
@@ -5,11 +5,14 @@
#include "pins_config.h"
#include "display.h"
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#ifdef HAS_DISPLAY
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST);
#endif
extern Configuration Config;
void setup_display() {
#ifdef HAS_DISPLAY
Wire.begin(OLED_SDA, OLED_SCL);
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
@@ -27,17 +30,21 @@ void setup_display() {
display.ssd1306_command(1);
display.display();
delay(1000);
#endif
}
void display_toggle(bool toggle) {
#ifdef HAS_DISPLAY
if (toggle) {
display.ssd1306_command(SSD1306_DISPLAYON);
} else {
display.ssd1306_command(SSD1306_DISPLAYOFF);
}
#endif
}
void show_display(String line1, int wait) {
#ifdef HAS_DISPLAY
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
@@ -47,9 +54,11 @@ void show_display(String line1, int wait) {
display.ssd1306_command(1);
display.display();
delay(wait);
#endif
}
void show_display(String line1, String line2, int wait) {
#ifdef HAS_DISPLAY
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
@@ -61,9 +70,11 @@ void show_display(String line1, String line2, int wait) {
display.ssd1306_command(1);
display.display();
delay(wait);
#endif
}
void show_display(String line1, String line2, String line3, int wait) {
#ifdef HAS_DISPLAY
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
@@ -77,9 +88,11 @@ void show_display(String line1, String line2, String line3, int wait) {
display.ssd1306_command(1);
display.display();
delay(wait);
#endif
}
void show_display(String line1, String line2, String line3, String line4, int wait) {
#ifdef HAS_DISPLAY
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
@@ -95,9 +108,11 @@ void show_display(String line1, String line2, String line3, String line4, int wa
display.ssd1306_command(1);
display.display();
delay(wait);
#endif
}
void show_display(String line1, String line2, String line3, String line4, String line5, int wait) {
#ifdef HAS_DISPLAY
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
@@ -115,9 +130,11 @@ void show_display(String line1, String line2, String line3, String line4, String
display.ssd1306_command(1);
display.display();
delay(wait);
#endif
}
void show_display(String line1, String line2, String line3, String line4, String line5, String line6, int wait) {
#ifdef HAS_DISPLAY
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
@@ -137,9 +154,11 @@ void show_display(String line1, String line2, String line3, String line4, String
display.ssd1306_command(1);
display.display();
delay(wait);
#endif
}
void show_display(String line1, String line2, String line3, String line4, String line5, String line6, String line7, int wait) {
#ifdef HAS_DISPLAY
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
@@ -162,4 +181,5 @@ void show_display(String line1, String line2, String line3, String line4, String
display.ssd1306_command(1);
display.display();
delay(wait);
#endif
}
+32 -74
View File
@@ -10,96 +10,54 @@ String distance;
namespace GPS_Utils {
String double2string(double n, int ndec) {
String r = "";
if (n>-1 && n<0) {
r = "-";
}
int v = n;
r += v;
r += '.';
for (int i=0;i<ndec;i++) {
n -= v;
n = 10 * abs(n);
v = n;
r += v;
char *ax25_base91enc(char *s, uint8_t n, uint32_t v) {
for(s += n, *s = '\0'; n; n--) {
*(--s) = v % 91 + 33;
v /= 91;
}
return r;
return(s);
}
String processLatitudeAPRS(double lat) {
String degrees = double2string(lat,6);
String north_south, latitude, convDeg3;
float convDeg, convDeg2;
String encodeGPS(float latitude, float longitude, String overlay, String symbol) {
String encodedData = overlay;
uint32_t aprs_lat, aprs_lon;
aprs_lat = 900000000 - latitude * 10000000;
aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
aprs_lon = 900000000 + longitude * 10000000 / 2;
aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
if (abs(degrees.toFloat()) < 10) {
latitude += "0";
}
Serial.println(latitude);
if (degrees.indexOf("-") == 0) {
north_south = "S";
latitude += degrees.substring(1,degrees.indexOf("."));
} else {
north_south = "N";
latitude += degrees.substring(0,degrees.indexOf("."));
}
convDeg = abs(degrees.toFloat()) - abs(int(degrees.toFloat()));
convDeg2 = (convDeg * 60)/100;
convDeg3 = String(convDeg2,6);
latitude += convDeg3.substring(convDeg3.indexOf(".")+1,convDeg3.indexOf(".")+3) + "." + convDeg3.substring(convDeg3.indexOf(".")+3,convDeg3.indexOf(".")+5);
latitude += north_south;
return latitude;
}
String Ns, Ew, helper;
if(latitude < 0) { Ns = "S"; } else { Ns = "N"; }
if(latitude < 0) { latitude= -latitude; }
String processLongitudeAPRS(double lon) {
String degrees = double2string(lon,6);
String east_west, longitude, convDeg3;
float convDeg, convDeg2;
if (abs(degrees.toFloat()) < 100) {
longitude += "0";
if(longitude < 0) { Ew = "W"; } else { Ew = "E"; }
if(longitude < 0) { longitude= -longitude; }
char helper_base91[] = {"0000\0"};
int i;
ax25_base91enc(helper_base91, 4, aprs_lat);
for (i = 0; i < 4; i++) {
encodedData += helper_base91[i];
}
if (abs(degrees.toFloat()) < 10) {
longitude += "0";
ax25_base91enc(helper_base91, 4, aprs_lon);
for (i = 0; i < 4; i++) {
encodedData += helper_base91[i];
}
if (degrees.indexOf("-") == 0) {
east_west = "W";
longitude += degrees.substring(1,degrees.indexOf("."));
} else {
east_west = "E";
longitude += degrees.substring(0,degrees.indexOf("."));
}
convDeg = abs(degrees.toFloat()) - abs(int(degrees.toFloat()));
convDeg2 = (convDeg * 60)/100;
convDeg3 = String(convDeg2,6);
longitude += convDeg3.substring(convDeg3.indexOf(".")+1,convDeg3.indexOf(".")+3) + "." + convDeg3.substring(convDeg3.indexOf(".")+3,convDeg3.indexOf(".")+5);
longitude += east_west;
return longitude;
encodedData += symbol + " x" + "\x47";
return encodedData;
}
String generateBeacon() {
String stationLatitude = processLatitudeAPRS(Config.beacon.latitude);
String stationLongitude = processLongitudeAPRS(Config.beacon.longitude);
String beaconPacket = Config.callsign + ">APLRG1," + Config.beacon.path;
if (Config.aprs_is.active && Config.digi.mode == 0) { // If APRSIS enabled and Digi disabled
beaconPacket += ",qAC";
} else {}
beaconPacket += ":=" + stationLatitude + Config.beacon.overlay + stationLongitude + Config.beacon.symbol;
beaconPacket += Config.beacon.comment;
return beaconPacket;
}
return beaconPacket + ":!" + encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);;
}
String generateiGateLoRaBeacon() {
String stationLatitude = processLatitudeAPRS(Config.beacon.latitude);
String stationLongitude = processLongitudeAPRS(Config.beacon.longitude);
String beaconPacket = Config.callsign + ">APLRG1," + Config.beacon.path + ":=" + stationLatitude + Config.beacon.overlay + stationLongitude + Config.beacon.symbol;
return beaconPacket;
return Config.callsign + ">APLRG1," + Config.beacon.path + ":!" + encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
}
double calculateDistanceTo(double latitude, double longitude) {
@@ -159,7 +117,7 @@ namespace GPS_Utils {
}
String getDistance(String packet) {
int encodedBytePosition = 0;
uint8_t encodedBytePosition = 0;
if (packet.indexOf(":!") > 10) {
encodedBytePosition = packet.indexOf(":!") + 14;
}
+2 -3
View File
@@ -6,9 +6,8 @@
namespace GPS_Utils {
String double2string(double n, int ndec);
String processLatitudeAPRS();
String processLongitudeAPRS();
char *ax25_base91enc(char *s, uint8_t n, uint32_t v);
String encodeGPS(float latitude, float longitude, String overlay, String symbol);
String generateBeacon();
String generateiGateLoRaBeacon();
double calculateDistanceCourse(double latitude, double longitude);
+23
View File
@@ -0,0 +1,23 @@
#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
+170
View File
@@ -0,0 +1,170 @@
#include <Arduino.h>
#include "kiss_protocol.h"
bool validateTNC2Frame(const String& tnc2FormattedFrame) {
return (tnc2FormattedFrame.indexOf(':') != -1) && (tnc2FormattedFrame.indexOf('>') != -1);
}
bool validateKISSFrame(const String& kissFormattedFrame) {
return kissFormattedFrame.charAt(0) == (char)FEND && kissFormattedFrame.charAt(kissFormattedFrame.length() - 1) == (char)FEND;
}
String encodeAddressAX25(String tnc2Address) {
bool hasBeenDigipited = tnc2Address.indexOf('*') != -1;
if (tnc2Address.indexOf('-') == -1) {
if (hasBeenDigipited) {
tnc2Address = tnc2Address.substring(0, tnc2Address.length() - 1);
}
tnc2Address += "-0";
}
int separatorIndex = tnc2Address.indexOf('-');
int ssid = tnc2Address.substring(separatorIndex + 1).toInt();
String kissAddress = "";
for (int i = 0; i < 6; ++i) {
char addressChar;
if (tnc2Address.length() > i && i < separatorIndex) {
addressChar = tnc2Address.charAt(i);
} else {
addressChar = ' ';
}
kissAddress += (char)(addressChar << 1);
}
kissAddress += (char)((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0));
return kissAddress;
}
String decodeAddressAX25(const String& ax25Address, bool& isLast, bool isRelay) {
String address = "";
for (int i = 0; i < 6; ++i) {
uint8_t currentCharacter = ax25Address.charAt(i);
currentCharacter >>= 1;
if (currentCharacter != ' ') {
address += (char)currentCharacter;
}
}
auto ssidChar = (uint8_t)ax25Address.charAt(6);
bool hasBeenDigipited = ssidChar & HAS_BEEN_DIGIPITED_MASK;
isLast = ssidChar & IS_LAST_ADDRESS_POSITION_MASK;
ssidChar >>= 1;
int ssid = 0b1111 & ssidChar;
if (ssid) {
address += '-';
address += ssid;
}
if (isRelay && hasBeenDigipited) {
address += '*';
}
return address;
}
String encapsulateKISS(const String& ax25Frame, uint8_t cmd) {
String kissFrame = "";
kissFrame += (char)FEND;
kissFrame += (char)(0x0f & cmd);
for (int i = 0; i < ax25Frame.length(); ++i) {
char currentChar = ax25Frame.charAt(i);
if (currentChar == (char)FEND) {
kissFrame += (char)FESC;
kissFrame += (char)TFEND;
} else if (currentChar == (char)FESC) {
kissFrame += (char)FESC;
kissFrame += (char)TFESC;
} else {
kissFrame += currentChar;
}
}
kissFrame += (char)FEND; // end of frame
return kissFrame;
}
String decapsulateKISS(const String& frame) {
String ax25Frame = "";
for (int i = 2; i < frame.length() - 1; ++i) {
char currentChar = frame.charAt(i);
if (currentChar == (char)FESC) {
char nextChar = frame.charAt(i + 1);
if (nextChar == (char)TFEND) {
ax25Frame += (char)FEND;
} else if (nextChar == (char)TFESC) {
ax25Frame += (char)FESC;
}
i++;
} else {
ax25Frame += currentChar;
}
}
return ax25Frame;
}
String encodeKISS(const String& frame) {
String ax25Frame = "";
if (validateTNC2Frame(frame)) {
String address = "";
bool dstAddresWritten = false;
for (int p = 0; p <= frame.indexOf(':'); p++) {
char currentChar = frame.charAt(p);
if (currentChar == ':' || currentChar == '>' || currentChar == ',') {
if (!dstAddresWritten && (currentChar == ',' || currentChar == ':')) {
ax25Frame = encodeAddressAX25(address) + ax25Frame;
dstAddresWritten = true;
} else {
ax25Frame += encodeAddressAX25(address);
}
address = "";
} else {
address += currentChar;
}
}
auto lastAddressChar = (uint8_t)ax25Frame.charAt(ax25Frame.length() - 1);
ax25Frame.setCharAt(ax25Frame.length() - 1, (char)(lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK));
ax25Frame += (char)APRS_CONTROL_FIELD;
ax25Frame += (char)APRS_INFORMATION_FIELD;
ax25Frame += frame.substring(frame.indexOf(':') + 1);
}
String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA);
return kissFrame;
}
String decodeKISS(const String& inputFrame, bool& dataFrame) {
String frame = "";
if (validateKISSFrame(inputFrame)) {
dataFrame = inputFrame.charAt(1) == CMD_DATA;
if (dataFrame) {
String ax25Frame = decapsulateKISS(inputFrame);
bool isLast = false;
String dstAddr = decodeAddressAX25(ax25Frame.substring(0, 7), isLast, false);
String srcAddr = decodeAddressAX25(ax25Frame.substring(7, 14), isLast, false);
frame = srcAddr + ">" + dstAddr;
int digiInfoIndex = 14;
while (!isLast && digiInfoIndex + 7 < ax25Frame.length()) {
String digiAddr = decodeAddressAX25(ax25Frame.substring(digiInfoIndex, digiInfoIndex + 7), isLast, true);
frame += ',' + digiAddr;
digiInfoIndex += 7;
}
frame += ':';
frame += ax25Frame.substring(digiInfoIndex + 2);
} else {
frame += inputFrame;
}
}
return frame;
}
+15
View File
@@ -0,0 +1,15 @@
#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
+117 -128
View File
@@ -1,227 +1,216 @@
#include <RadioLib.h>
#include <LoRa.h>
#include <WiFi.h>
#include "configuration.h"
#include "aprs_is_utils.h"
#include "syslog_utils.h"
#include "pins_config.h"
#include "display.h"
#include "utils.h"
extern Configuration Config;
#if defined(HELTEC_V3) || defined(TTGO_T_Beam_V1_2_SX1262)
extern std::vector<ReceivedPacket> receivedPackets;
bool transmissionFlag = true;
bool ignorePacket = false;
bool operationDone = true;
#ifdef HAS_SX1262
SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
bool transmissionFlag = true;
bool enableInterrupt = true;
#endif
#if defined(ESP32_DIY_1W_LoRa) || defined(TTGO_T_Beam_V1_0_SX1268)
#ifdef HAS_SX1268
SX1268 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
bool transmissionFlag = true;
bool enableInterrupt = true;
#endif
#ifdef HAS_SX1278
SX1278 radio = new Module(RADIO_CS_PIN, RADIO_BUSY_PIN, RADIO_RST_PIN);
#endif
int rssi, freqError;
float snr;
namespace LoRa_Utils {
void setFlag(void) {
#ifdef HAS_SX126X
transmissionFlag = true;
#endif
operationDone = true;
}
void setup() {
#ifdef HAS_SX127X
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
LoRa.setPins(LORA_CS, LORA_RST, LORA_IRQ);
long freq = Config.loramodule.rxFreq;
if (!LoRa.begin(freq)) {
Serial.println("Starting LoRa failed!");
show_display("ERROR", "Starting LoRa failed!");
while (true) {
delay(1000);
}
}
LoRa.setSpreadingFactor(Config.loramodule.spreadingFactor);
LoRa.setSignalBandwidth(Config.loramodule.signalBandwidth);
LoRa.setCodingRate4(Config.loramodule.codingRate4);
LoRa.enableCrc();
LoRa.setTxPower(Config.loramodule.power);
Serial.print("init : LoRa Module ... done!");
#endif
#ifdef HAS_SX126X
SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
float freq = (float)Config.loramodule.rxFreq/1000000;
float freq = (float)Config.loramodule.rxFreq / 1000000;
int state = radio.begin(freq);
if (state == RADIOLIB_ERR_NONE) {
Serial.print("Initializing SX126X LoRa Module");
Utils::println("Initializing LoRa Module");
} else {
Serial.println("Starting LoRa failed!");
while (true);
Utils::println("Starting LoRa failed!");
while (true);
}
radio.setDio1Action(setFlag);
#ifdef HAS_SX127X
radio.setDio0Action(setFlag, RISING);
#endif
#ifdef HAS_SX126X
if (!Config.lowPowerMode) {
radio.setDio1Action(setFlag);
} else {
radio.setDIOMapping(1, RADIOLIB_SX126X_IRQ_RX_DONE);
}
#endif
radio.setSpreadingFactor(Config.loramodule.spreadingFactor);
radio.setBandwidth(Config.loramodule.signalBandwidth);
float signalBandwidth = Config.loramodule.signalBandwidth/1000;
radio.setBandwidth(signalBandwidth);
radio.setCodingRate(Config.loramodule.codingRate4);
radio.setCRC(true);
#if defined(ESP32_DIY_1W_LoRa)
radio.setRfSwitchPins(RADIO_RXEN, RADIO_TXEN);
#endif
#if defined(HELTEC_V3) || defined(TTGO_T_Beam_V1_0_SX1268) || defined(TTGO_T_Beam_V1_2_SX1262)
#if defined(HAS_SX127X) || ESP32_DIY_1W_LoRa
state = radio.setOutputPower(Config.loramodule.power); // max value 20dB for 400M30S as it has Low Noise Amp
#endif
#if defined(HELTEC_V3) || defined(HELTEC_WS) || defined(TTGO_T_Beam_V1_0_SX1268) || defined(TTGO_T_Beam_V1_2_SX1262)
state = radio.setOutputPower(Config.loramodule.power + 2); // values available: 10, 17, 22 --> if 20 in tracker_conf.json it will be updated to 22.
#endif
#ifdef ESP32_DIY_1W_LoRa_GPS
state = radio.setOutputPower(Config.loramodule.power); // max value 20 (when 20dB in setup 30dB in output as 400M30S has Low Noise Amp)
#endif
if (state == RADIOLIB_ERR_NONE) {
Serial.println("init : LoRa Module ... done!");
Utils::println("init : LoRa Module ... done!");
} else {
Serial.println("Starting LoRa failed!");
Utils::println("Starting LoRa failed!");
while (true);
}
#endif
}
void changeFreqTx() {
delay(500);
#ifdef HAS_SX127X
LoRa.setFrequency(Config.loramodule.txFreq);
#endif
#ifdef HAS_SX126X
float freq = (float)Config.loramodule.txFreq/1000000;
float freq = (float)Config.loramodule.txFreq / 1000000;
radio.setFrequency(freq);
#endif
}
void changeFreqRx() {
delay(500);
#ifdef HAS_SX127X
LoRa.setFrequency(Config.loramodule.rxFreq);
#endif
#ifdef HAS_SX126X
float freq = (float)Config.loramodule.rxFreq/1000000;
float freq = (float)Config.loramodule.rxFreq / 1000000;
radio.setFrequency(freq);
#endif
}
void sendNewPacket(const String &typeOfMessage, const String &newPacket) {
void sendNewPacket(const String& typeOfMessage, const String& newPacket) {
if (!Config.loramodule.txActive) return;
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
changeFreqTx();
changeFreqTx();
}
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
digitalWrite(internalLedPin,HIGH);
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin, HIGH);
#endif
#ifdef HAS_SX127X
LoRa.beginPacket();
LoRa.write('<');
if (typeOfMessage == "APRS") {
LoRa.write(0xFF);
} else if (typeOfMessage == "LoRa") {
LoRa.write(0xF8);
}
LoRa.write(0x01);
LoRa.write((const uint8_t *)newPacket.c_str(), newPacket.length());
LoRa.endPacket();
#endif
#ifdef HAS_SX126X
int state = radio.transmit("\x3c\xff\x01" + newPacket);
transmissionFlag = true;
if (state == RADIOLIB_ERR_NONE) {
//Serial.println(F("success!"));
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
SYSLOG_Utils::log("Tx", newPacket, 0, 0, 0);
}
Utils::print("---> LoRa Packet Tx : ");
Utils::println(newPacket);
} else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) {
Serial.println(F("too long!"));
Utils::println(F("too long!"));
} else if (state == RADIOLIB_ERR_TX_TIMEOUT) {
Serial.println(F("timeout!"));
Utils::println(F("timeout!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
Utils::print(F("failed, code "));
Utils::println(String(state));
}
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin, LOW);
#endif
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
digitalWrite(internalLedPin,LOW);
#endif
SYSLOG_Utils::log("Tx", newPacket,0,0,0);
Serial.print("---> LoRa Packet Tx : ");
Serial.println(newPacket);
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
changeFreqRx();
}
//ignorePacket = true;
}
String generatePacket(String aprsisPacket) {
String firstPart, messagePart;
aprsisPacket.trim();
firstPart = aprsisPacket.substring(0, aprsisPacket.indexOf(","));
messagePart = aprsisPacket.substring(aprsisPacket.indexOf("::")+2);
messagePart = aprsisPacket.substring(aprsisPacket.indexOf("::") + 2);
return firstPart + ",TCPIP,WIDE1-1," + Config.callsign + "::" + messagePart;
}
String packetSanitization(String packet) {
Serial.println(packet);
if (packet.indexOf("\0")>0) {
packet.replace("\0","");
if (packet.indexOf("\0") > 0) {
packet.replace("\0", "");
}
if (packet.indexOf("\r")>0) {
packet.replace("\r","");
if (packet.indexOf("\r") > 0) {
packet.replace("\r", "");
}
if (packet.indexOf("\n")>0) {
packet.replace("\n","");
if (packet.indexOf("\n") > 0) {
packet.replace("\n", "");
}
return packet;
}
void startReceive() {
radio.startReceive();
}
String receivePacket() {
if(!operationDone && !Config.lowPowerMode) return "";
operationDone = false;
String loraPacket = "";
#ifdef HAS_SX127X
int packetSize = LoRa.parsePacket();
if (packetSize) {
while (LoRa.available()) {
int inChar = LoRa.read();
loraPacket += (char)inChar;
}
rssi = LoRa.packetRssi();
snr = LoRa.packetSnr();
freqError = LoRa.packetFrequencyError();
}
#endif
#ifdef HAS_SX126X
if (transmissionFlag) {
transmissionFlag = false;
if (transmissionFlag && !Config.lowPowerMode) {
radio.startReceive();
transmissionFlag = false;
} else {
int state = radio.readData(loraPacket);
if (state == RADIOLIB_ERR_NONE) {
Serial.println("LoRa Rx ---> " + loraPacket);
rssi = radio.getRSSI();
snr = radio.getSNR();
freqError = radio.getFrequencyError();
if (loraPacket != "" && !ignorePacket) {
rssi = radio.getRSSI();
snr = radio.getSNR();
freqError = radio.getFrequencyError();
Utils::println("<--- LoRa Packet Rx : " + loraPacket);
Utils::println("(RSSI:" + String(rssi) + " / SNR:" + String(snr) + " / FreqErr:" + String(freqError) + ")");
if (!Config.lowPowerMode) {
ReceivedPacket receivedPacket;
receivedPacket.millis = millis();
receivedPacket.packet = loraPacket.substring(3);
receivedPacket.RSSI = rssi;
receivedPacket.SNR = snr;
if (receivedPackets.size() >= 20) {
receivedPackets.erase(receivedPackets.begin());
}
receivedPackets.push_back(receivedPacket);
}
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
SYSLOG_Utils::log("Rx", loraPacket, rssi, snr, freqError);
}
return loraPacket;
}
} else if (state == RADIOLIB_ERR_RX_TIMEOUT) {
// timeout occurred while waiting for a packet
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
Serial.println(F("CRC error!"));
Utils::println(F("CRC error!"));
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
SYSLOG_Utils::log("Rx", "RADIOLIB_ERR_CRC_MISMATCH", 0,0,0);
}
loraPacket = "";
} else {
Serial.print(F("failed, code "));
Serial.println(state);
Utils::print(F("failed, code "));
Utils::println(String(state));
}
}
#endif
if ((loraPacket.indexOf("\0")!=-1) || (loraPacket.indexOf("\r")!=-1) || (loraPacket.indexOf("\n")!=-1)) {
loraPacket = packetSanitization(loraPacket);
}
if (ignorePacket) {
Utils::println("<--- LoRa Packet Rx : " + loraPacket);
Utils::println("Received own packet. Ignoring");
#ifndef TextSerialOutputForApp
if (loraPacket!="") {
Serial.println("(RSSI:" +String(rssi) + " / SNR:" + String(snr) + " / FreqErr:" + String(freqError) + ")");
}
#endif
if (Config.syslog.active && WiFi.status() == WL_CONNECTED && loraPacket != "") {
SYSLOG_Utils::log("Rx", loraPacket, rssi, snr, freqError);
ignorePacket = false;
return "";
}
}
return loraPacket;
}
+1
View File
@@ -13,6 +13,7 @@ namespace LoRa_Utils {
String receivePacket();
void changeFreqTx();
void changeFreqRx();
void startReceive();
}
+60 -30
View File
@@ -7,34 +7,18 @@
#undef OLED_SCL
#undef OLED_RST
#if defined(HELTEC_V3) || defined(ESP32_DIY_1W_LoRa) || defined(TTGO_T_Beam_V1_0_SX1268) || defined(TTGO_T_Beam_V1_2_SX1262)
#define HAS_SX126X
#endif
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(ESP32_DIY_LoRa) || defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_Beam_V1_2)
#define HAS_SX127X
#endif
#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_Beam_V1_0_SX1268)
#define HAS_AXP192
#endif
#if defined(TTGO_T_Beam_V1_2) || defined(TTGO_T_Beam_V1_2_SX1262)
#define HAS_AXP2101
#endif
// LORA MODULES
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(ESP32_DIY_LoRa)
#undef LORA_RST
#define LORA_SCK 5 // GPIO5 - SX1276 SCK
#define LORA_MISO 19 // GPIO19 - SX1276 MISO
#define LORA_MOSI 27 // GPIO27 - SX1276 MOSI
#define LORA_CS 18 // GPIO18 - SX1276 CS ---> NSS
#define LORA_RST 14 // GPIO14 - SX1276 RST
#define LORA_IRQ 26 // GPIO26 - SX1276 IRQ ---->DIO0
#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)
#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
#ifdef HELTEC_V3
#if defined(HELTEC_V3) || defined(HELTEC_WS)
#define RADIO_SCLK_PIN 9 // SX1262 SCK
#define RADIO_MISO_PIN 11 // SX1262 MISO
#define RADIO_MOSI_PIN 10 // SX1262 MOSI
@@ -67,32 +51,66 @@
#define RADIO_BUSY_PIN 32
#endif
#if defined(OE5HWN_MeshCom)
#define RADIO_SCLK_PIN 18
#define RADIO_MISO_PIN 19
#define RADIO_MOSI_PIN 23
#define RADIO_CS_PIN 5
#define RADIO_RST_PIN 27
#define RADIO_DIO1_PIN 33
#define RADIO_BUSY_PIN 26
#define RADIO_RXEN 14
#define RADIO_TXEN 13
#endif
#ifdef HELTEC_HTCT62
#define RADIO_SCLK_PIN 10 // SX1262 SCK
#define RADIO_MISO_PIN 6 // SX1262 MISO
#define RADIO_MOSI_PIN 7 // SX1262 MOSI
#define RADIO_CS_PIN 8 // SX1262 NSS
#define RADIO_RST_PIN 5 // SX1262 RST
#define RADIO_DIO1_PIN 3 // SX1262 DIO1
#define RADIO_BUSY_PIN 4 // SX1262 BUSY
#endif
// 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)
#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)
#define OLED_SDA 21
#define OLED_SCL 22
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#endif
#ifdef HELTEC_V2
#define OLED_SDA 4
#define OLED_SCL 15
#define OLED_RESET 16
#define OLED_RST 16
#endif
#ifdef HELTEC_V3
#if defined(HELTEC_V3) || defined(HELTEC_WS)
#define OLED_SDA 17
#define OLED_SCL 18
#define OLED_RESET 21
#define OLED_RST 21
#endif
#ifndef HELTEC_HTCT62
#define HAS_DISPLAY
#endif
// Leds and other stuff
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(HELTEC_WS) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
#define HAS_INTERNAL_LED
#endif
#ifdef HELTEC_HTCT62
#define batteryPin 1
#endif
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
#define internalLedPin 25 // Green Led
#define batteryPin 35
#endif
#ifdef HELTEC_V3
#if defined(HELTEC_V3) || defined(HELTEC_WS)
#define internalLedPin 35
#endif
#if defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
@@ -100,6 +118,18 @@
#endif
#ifdef ESP32_C3_DIY_LoRa
#define OLED_SDA 8
#define OLED_SCL 9
#define OLED_RST 10
#define RADIO_SCLK_PIN 4
#define RADIO_MISO_PIN 5
#define RADIO_MOSI_PIN 6
#define RADIO_CS 7
#define RADIO_RST_PIN 3
#define RADIO_IRQ_PIN 2
#endif
/* (Same pins for LILYGO LoRa32 and ESP32 Wroom Dev )
SX1278-------------------> ESP32 ttgo-lora32-v21 and ESP32 WROOM Dev
GND GND
+5 -1
View File
@@ -41,7 +41,11 @@ namespace QUERY_Utils {
if (queryOrigin == "APRSIS") {
return Config.callsign + ">APLRG1,TCPIP,qAC::" + station + ":" + answer;// + "\n";
} else { //} if (queryOrigin == "LoRa") {
return Config.callsign + ">APLRG1,RFONLY,WIDE1-1::" + station + ":" + answer;
if (Config.beacon.path == "") {
return Config.callsign + ">APLRG1,RFONLY::" + station + ":" + answer;
} else {
return Config.callsign + ">APLRG1,RFONLY," + Config.beacon.path + "::" + station + ":" + answer;
}
}
}
+29 -30
View File
@@ -1,6 +1,7 @@
#include "station_utils.h"
#include "aprs_is_utils.h"
#include "configuration.h"
#include "utils.h"
#include <vector>
extern Configuration Config;
@@ -14,15 +15,15 @@ extern String fourthLine;
namespace STATION_Utils {
void deleteNotHeard() {
for (int i=0; i<lastHeardStation.size(); i++) {
String deltaTimeString = lastHeardStation[i].substring(lastHeardStation[i].indexOf(",")+1);
for (int i = 0; i < lastHeardStation.size(); i++) {
String deltaTimeString = lastHeardStation[i].substring(lastHeardStation[i].indexOf(",") + 1);
uint32_t deltaTime = deltaTimeString.toInt();
if ((millis() - deltaTime) < Config.rememberStationTime*60*1000) {
if ((millis() - deltaTime) < Config.rememberStationTime * 60 * 1000) {
lastHeardStation_temp.push_back(lastHeardStation[i]);
}
}
lastHeardStation.clear();
for (int j=0; j<lastHeardStation_temp.size(); j++) {
for (int j = 0; j < lastHeardStation_temp.size(); j++) {
lastHeardStation.push_back(lastHeardStation_temp[j]);
}
lastHeardStation_temp.clear();
@@ -31,8 +32,8 @@ namespace STATION_Utils {
void updateLastHeard(String station) {
deleteNotHeard();
bool stationHeard = false;
for (int i=0; i<lastHeardStation.size(); i++) {
if (lastHeardStation[i].substring(0,lastHeardStation[i].indexOf(",")) == station) {
for (int i = 0; i < lastHeardStation.size(); i++) {
if (lastHeardStation[i].substring(0, lastHeardStation[i].indexOf(",")) == station) {
lastHeardStation[i] = station + "," + String(millis());
stationHeard = true;
}
@@ -47,55 +48,53 @@ namespace STATION_Utils {
}
fourthLine += String(lastHeardStation.size());
#ifndef TextSerialOutputForApp ////// This is just for debugging
Serial.print("Stations Near (last " + String(Config.rememberStationTime) + " minutes): ");
for (int k=0; k<lastHeardStation.size(); k++) {
Serial.print(lastHeardStation[k].substring(0,lastHeardStation[k].indexOf(","))); Serial.print(" ");
}
Serial.println("");
#endif
// DEBUG ONLY
// Serial.print("Stations Near (last " + String(Config.rememberStationTime) + " minutes): ");
// for (int k=0; k<lastHeardStation.size(); k++) {
// Serial.print(lastHeardStation[k].substring(0,lastHeardStation[k].indexOf(","))); Serial.print(" ");
// }
// Serial.println("");
}
bool wasHeard(String station) {
deleteNotHeard();
for (int i=0; i<lastHeardStation.size(); i++) {
if (lastHeardStation[i].substring(0,lastHeardStation[i].indexOf(",")) == station) {
Serial.println(" ---> Listened Station");
for (int i = 0; i < lastHeardStation.size(); i++) {
if (lastHeardStation[i].substring(0, lastHeardStation[i].indexOf(",")) == station) {
Utils::println(" ---> Listened Station");
return true;
}
}
}
Serial.println(" ---> Station not Heard for last 30 min (Not Tx)\n");
Utils::println(" ---> Station not Heard for last 30 min (Not Tx)\n");
return false;
}
void checkBuffer() {
for (int i=0; i<packetBuffer.size(); i++) {
String deltaTimeString = packetBuffer[i].substring(0,packetBuffer[i].indexOf(","));
for (int i = 0; i < packetBuffer.size(); i++) {
String deltaTimeString = packetBuffer[i].substring(0, packetBuffer[i].indexOf(","));
uint32_t deltaTime = deltaTimeString.toInt();
if ((millis() - deltaTime) < 60*1000) { // cambiar a 15 segundos?
if ((millis() - deltaTime) < 60 * 1000) { // cambiar a 15 segundos?
packetBuffer_temp.push_back(packetBuffer[i]);
}
}
packetBuffer.clear();
for (int j=0; j<packetBuffer_temp.size(); j++) {
for (int j = 0; j < packetBuffer_temp.size(); j++) {
packetBuffer.push_back(packetBuffer_temp[j]);
}
packetBuffer_temp.clear();
// BORRAR ESTO !!
for (int i=0; i<packetBuffer.size(); i++) {
Serial.println(packetBuffer[i]);
}
//
// DEBUG ONLY
// for (int i=0; i<packetBuffer.size(); i++) {
// Serial.println(packetBuffer[i]);
// }
}
void updatePacketBuffer(String packet) {
if ((packet.indexOf(":!") == -1) && (packet.indexOf(":=") == -1) && (packet.indexOf(":>") == -1) && (packet.indexOf(":`") == -1)) {
String sender = packet.substring(3,packet.indexOf(">"));
String sender = packet.substring(3, packet.indexOf(">"));
String tempAddressee = packet.substring(packet.indexOf("::") + 2);
String addressee = tempAddressee.substring(0,tempAddressee.indexOf(":"));
String addressee = tempAddressee.substring(0, tempAddressee.indexOf(":"));
addressee.trim();
String message = tempAddressee.substring(tempAddressee.indexOf(":")+1);
String message = tempAddressee.substring(tempAddressee.indexOf(":") + 1);
//Serial.println(String(millis()) + "," + sender + "," + addressee + "," + message);
packetBuffer.push_back(String(millis()) + "," + sender + "," + addressee + "," + message);
checkBuffer();
+1 -1
View File
@@ -12,7 +12,7 @@ WiFiUDP udpClient;
namespace SYSLOG_Utils {
void log(String type, String packet, int rssi, float snr, int freqError) {
String syslogPacket = "<165>1 - " + Config.callsign + " CA2RXU_LoRa_iGate_1.2" + " - - - "; //RFC5424 The Syslog Protocol
String syslogPacket = "<165>1 - " + Config.callsign + " CA2RXU_LoRa_iGate_1.3" + " - - - "; //RFC5424 The Syslog Protocol
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
if (type == "APRSIS Tx") {
if (packet.indexOf(":>") > 10) {
+155
View File
@@ -0,0 +1,155 @@
#include <WiFi.h>
#include "kiss_utils.h"
#include "kiss_protocol.h"
#include "lora_utils.h"
#include "configuration.h"
#include "utils.h"
extern Configuration Config;
#define MAX_CLIENTS 4
#define INPUT_BUFFER_SIZE (2 + MAX_CLIENTS)
#define TNC_PORT 8001
WiFiClient* clients[MAX_CLIENTS];
WiFiServer tncServer(TNC_PORT);
String inputServerBuffer[INPUT_BUFFER_SIZE];
String inputSerialBuffer = "";
namespace TNC_Utils {
void setup() {
if (Config.tnc.enableServer) {
tncServer.stop();
tncServer.begin();
}
}
void checkNewClients() {
WiFiClient new_client = tncServer.available();
if (new_client.connected()) {
for (int i = 0; i < MAX_CLIENTS; i++) {
WiFiClient* client = clients[i];
if (client == nullptr) {
clients[i] = new WiFiClient(new_client);
Utils::println("New TNC client connected");
break;
}
}
}
}
void handleInputData(char character, int bufferIndex) {
String* data;
if (bufferIndex == -1) {
data = &inputSerialBuffer;
} else {
data = &inputServerBuffer[bufferIndex];
}
if (data->length() == 0 && character != (char)FEND) {
return;
}
data->concat(character);
if (character == (char)FEND && data->length() > 3) {
bool isDataFrame = false;
const String& frame = decodeKISS(*data, isDataFrame);
if (isDataFrame) {
if (bufferIndex != -1) {
Utils::print("<--- Got from TNC : ");
Utils::println(frame);
}
String sender = frame.substring(0,frame.indexOf(">"));
if (Config.tnc.acceptOwn || sender != Config.callsign) {
LoRa_Utils::sendNewPacket("APRS", frame);
} else {
Utils::println("Ignored own frame from KISS");
}
}
data->clear();
}
if (data->length() > 255) {
data->clear();
}
}
void readFromClients() {
for (int i = 0; i < MAX_CLIENTS; i++) {
auto client = clients[i];
if (client != nullptr) {
if (client->connected()) {
while (client->available() > 0) {
char character = client->read();
handleInputData(character, 2 + i);
}
} else {
delete client;
clients[i] = nullptr;
}
}
}
}
void readFromSerial() {
while (Serial.available() > 0) {
char character = Serial.read();
handleInputData(character, -1);
}
}
void sendToClients(String packet) {
packet = packet.substring(3);
const String kissEncoded = encodeKISS(packet);
for (int i = 0; i < MAX_CLIENTS; i++) {
auto client = clients[i];
if (client != nullptr) {
if (client->connected()) {
client->print(kissEncoded);
client->flush();
} else {
delete client;
clients[i] = nullptr;
}
}
}
Utils::print("---> Sent to TNC : ");
Utils::println(packet);
}
void sendToSerial(String packet) {
packet = packet.substring(3);
Serial.print(encodeKISS(packet));
Serial.flush();
}
void loop() {
if (Config.tnc.enableServer) {
checkNewClients();
readFromClients();
}
if (Config.tnc.enableSerial) {
readFromSerial();
}
}
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef TNC_UTILS_H_
#define TNC_UTILS_H_
#include <Arduino.h>
namespace TNC_Utils {
void setup();
void loop();
void sendToClients(String packet);
void sendToSerial(String packet);
}
#endif
+46 -39
View File
@@ -43,20 +43,19 @@ namespace Utils {
void processStatus() {
String status = Config.callsign + ">APLRG1," + Config.beacon.path;
if (Config.aprs_is.active && Config.digi.mode == 0) { // If only IGate
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
delay(1000);
status += ",qAC:>https://github.com/richonguzman/LoRa_APRS_iGate " + versionDate;
APRS_IS_Utils::upload(status);
SYSLOG_Utils::log("APRSIS Tx", status,0,0,0);
statusAfterBoot = false;
}
if (statusAfterBoot && !Config.beacon.sendViaAPRSIS && Config.beacon.sendViaRF) {
delay(2000);
status += ":>https://github.com/richonguzman/LoRa_APRS_iGate " + versionDate;
LoRa_Utils::sendNewPacket("APRS", status);
statusAfterBoot = false;
}
// Comment this for now we will need this in the future
// } else if (Config.digi.mode == 2) {
// delay(5000);
// status += ":>https://github.com/richonguzman/LoRa_APRS_iGate " + versionDate;
// LoRa_Utils::sendNewPacket("APRS", status);
// }
statusAfterBoot = false;
}
String getLocalIP() {
@@ -69,12 +68,12 @@ namespace Utils {
void setupDisplay() {
setup_display();
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin,HIGH);
#endif
Serial.println("\nStarting Station: " + Config.callsign + " Version: " + versionDate);
show_display(" LoRa APRS", "", " ( iGATE & DIGI )", "", "", "Richonguzman / CA2RXU", " " + versionDate, 4000);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin,LOW);
#endif
firstLine = Config.callsign;
@@ -91,37 +90,41 @@ namespace Utils {
void checkBeaconInterval() {
uint32_t lastTx = millis() - lastBeaconTx;
String beaconPacket, secondaryBeaconPacket;
String beaconPacket = iGateBeaconPacket;
String secondaryBeaconPacket = iGateLoRaBeaconPacket;
if (lastTx >= Config.beacon.interval*60*1000) {
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
beaconUpdate = true;
}
if (beaconUpdate) {
display_toggle(true);
Serial.println("-- Sending Beacon to APRSIS --");
Utils::println("-- Sending Beacon to APRSIS --");
STATION_Utils::deleteNotHeard();
activeStations();
if (Config.bme.active) {
String sensorData = BME_Utils::readDataSensor();
beaconPacket = iGateBeaconPacket.substring(0,iGateBeaconPacket.indexOf(":=")+20) + "_" + sensorData + iGateBeaconPacket.substring(iGateBeaconPacket.indexOf(":=")+21);
secondaryBeaconPacket = iGateLoRaBeaconPacket + sensorData + Config.beacon.comment;
} else {
beaconPacket = iGateBeaconPacket;
secondaryBeaconPacket = iGateLoRaBeaconPacket + Config.beacon.comment;
beaconPacket += sensorData;
secondaryBeaconPacket += sensorData;
}
beaconPacket += Config.beacon.comment;
secondaryBeaconPacket += Config.beacon.comment;
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_HTCT62)
if (Config.sendBatteryVoltage) {
beaconPacket += " (Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V)";
beaconPacket += " Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V";
secondaryBeaconPacket += " Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V";
sixthLine = " (Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V)";
}
#endif
if (Config.externalVoltageMeasurement) {
beaconPacket += " (Ext V=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V)";
beaconPacket += " Ext=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V";
secondaryBeaconPacket += " Ext=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V";
sixthLine = " (Ext V=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V)";
}
@@ -185,29 +188,21 @@ namespace Utils {
void typeOfPacket(String packet, String packetType) {
String sender;
if (WiFi.status() == WL_CONNECTED) { // If mode 1 2 5
if (packetType == "LoRa-APRS") {
fifthLine = "LoRa Rx ----> APRS-IS";
} else if (packetType == "APRS-LoRa") {
fifthLine = "APRS-IS ----> LoRa Tx";
}
if (packetType == "LoRa-APRS") {
fifthLine = "LoRa Rx ----> APRS-IS";
sender = packet.substring(0,packet.indexOf(">"));
} else {
} else if (packetType == "APRS-LoRa") {
fifthLine = "APRS-IS ----> LoRa Tx";
sender = packet.substring(0,packet.indexOf(">"));
} else if (packetType == "Digi") {
fifthLine = "LoRa Rx ----> LoRa Tx";
sender = packet.substring(3,packet.indexOf(">"));
sender = packet.substring(0,packet.indexOf(">"));
}
for (int i=sender.length();i<9;i++) {
for (int i = sender.length(); i < 9; i++) {
sender += " ";
}
if (packet.indexOf("::") >= 10) {
if (packetType == "APRS-LoRa") {
String addresseeAndMessage = packet.substring(packet.indexOf("::")+2);
String addressee = addresseeAndMessage.substring(0, addresseeAndMessage.indexOf(":"));
addressee.trim();
sixthLine = sender + " > " + addressee;
} else {
sixthLine = sender + "> MESSAGE";
}
sixthLine = sender + "> MESSAGE";
seventhLine = "RSSI:" + String(rssi) + "dBm SNR: " + String(snr) + "dBm";
} else if (packet.indexOf(":>") >= 10) {
sixthLine = sender + "> NEW STATUS";
@@ -240,4 +235,16 @@ namespace Utils {
}
}
void print(String text) {
if (!Config.tnc.enableSerial) {
Serial.print(text);
}
}
void println(String text) {
if (!Config.tnc.enableSerial) {
Serial.println(text);
}
}
}
+9 -3
View File
@@ -3,6 +3,13 @@
#include <Arduino.h>
class ReceivedPacket {
public:
long millis;
String packet;
int RSSI;
float SNR;
};
namespace Utils {
@@ -15,9 +22,8 @@ namespace Utils {
void checkWiFiInterval();
void validateFreqs();
void typeOfPacket(String packet, String packetType);
void onOTAStart();
void onOTAProgress(size_t current, size_t final);
void onOTAEnd(bool success);
void print(String text);
void println(String text);
}
+50 -1
View File
@@ -1,8 +1,13 @@
#include <ArduinoJson.h>
#include "configuration.h"
#include "ota_utils.h"
#include "web_utils.h"
#include "utils.h"
extern Configuration Config;
extern Configuration Config;
extern uint32_t lastBeaconTx;
extern std::vector<ReceivedPacket> receivedPackets;
extern const char web_index_html[] asm("_binary_data_embed_index_html_gz_start");
extern const char web_index_html_end[] asm("_binary_data_embed_index_html_gz_end");
@@ -56,6 +61,23 @@ namespace WEB_Utils {
request->send(200, "application/json", fileContent);
}
void handleReceivedPackets(AsyncWebServerRequest *request) {
StaticJsonDocument<1536> data;
for (int i = 0; i < receivedPackets.size(); i++) {
data[i]["millis"] = receivedPackets[i].millis;
data[i]["packet"] = receivedPackets[i].packet;
data[i]["RSSI"] = receivedPackets[i].RSSI;
data[i]["SNR"] = receivedPackets[i].SNR;
}
String buffer;
serializeJson(data, buffer);
request->send(200, "application/json", buffer);
}
void handleWriteConfiguration(AsyncWebServerRequest *request) {
Serial.println("Got new config from www");
@@ -84,6 +106,10 @@ namespace WEB_Utils {
// Config.digi.latitude = request->getParam("digi.latitude", true)->value().toDouble();
// Config.digi.longitude = request->getParam("digi.longitude", true)->value().toDouble();
Config.tnc.enableServer = request->hasParam("tnc.enableServer", true);
Config.tnc.enableSerial = request->hasParam("tnc.enableSerial", true);
Config.tnc.acceptOwn = request->hasParam("tnc.acceptOwn", true);
Config.aprs_is.active = request->hasParam("aprs_is.active", true);
Config.aprs_is.passcode = request->getParam("aprs_is.passcode", true)->value();
Config.aprs_is.server = request->getParam("aprs_is.server", true)->value();
@@ -150,6 +176,13 @@ namespace WEB_Utils {
Config.externalVoltagePin = request->getParam("other.externalVoltagePin", true)->value().toInt();
}
Config.lowPowerMode = request->hasParam("other.lowPowerMode", true);
Config.lowVoltageCutOff = request->getParam("other.lowVoltageCutOff", true)->value().toDouble();
if (Config.bme.active) {
Config.beacon.symbol = "_";
}
Config.writeFile();
AsyncWebServerResponse *response = request->beginResponse(302, "text/html", "");
@@ -159,6 +192,20 @@ namespace WEB_Utils {
ESP.restart();
}
void handleAction(AsyncWebServerRequest *request) {
String type = request->getParam("type", false)->value();
if (type == "send-beacon") {
lastBeaconTx = 0;
request->send(200, "text/plain", "Beacon will be sent in a while");
} else if (type == "reboot") {
ESP.restart();
} else {
request->send(404, "text/plain", "Not Found");
}
}
void handleStyle(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/css", (const uint8_t*)web_style_css, web_style_css_len);
response->addHeader("Content-Encoding", "gzip");
@@ -188,8 +235,10 @@ namespace WEB_Utils {
void setup() {
server.on("/", HTTP_GET, handleHome);
server.on("/status", HTTP_GET, handleStatus);
server.on("/received-packets.json", HTTP_GET, handleReceivedPackets);
server.on("/configuration.json", HTTP_GET, handleReadConfiguration);
server.on("/configuration.json", HTTP_POST, handleWriteConfiguration);
server.on("/action", HTTP_POST, handleAction);
server.on("/style.css", HTTP_GET, handleStyle);
server.on("/script.js", HTTP_GET, handleScript);
server.on("/bootstrap.css", HTTP_GET, handleBootstrapStyle);
+6 -6
View File
@@ -7,7 +7,7 @@
extern Configuration Config;
extern WiFi_AP *currentWiFi;
extern int myWiFiAPIndex;
extern uint8_t myWiFiAPIndex;
extern int myWiFiAPSize;
extern uint32_t previousWiFiMillis;
extern bool WiFiConnected;
@@ -42,7 +42,7 @@ namespace WIFI_Utils {
if (currentWiFi->ssid == "") {
startAP = true;
} else {
int wifiCounter = 0;
uint8_t wifiCounter = 0;
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(500);
@@ -52,17 +52,17 @@ namespace WIFI_Utils {
WiFi.begin(currentWiFi->ssid.c_str(), currentWiFi->password.c_str());
while (WiFi.status() != WL_CONNECTED && wifiCounter<myWiFiAPSize) {
delay(500);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin,HIGH);
#endif
Serial.print('.');
delay(500);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin,LOW);
#endif
if ((millis() - start) > 10000){
delay(1000);
if(myWiFiAPIndex >= (myWiFiAPSize-1)) {
if(myWiFiAPIndex >= (myWiFiAPSize - 1)) {
myWiFiAPIndex = 0;
wifiCounter++;
} else {
@@ -78,7 +78,7 @@ namespace WIFI_Utils {
}
}
}
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2) || defined(HELTEC_V3) || defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_1W_LoRa)
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin,LOW);
#endif
if (WiFi.status() == WL_CONNECTED) {
+10 -1
View File
@@ -1,4 +1,7 @@
import gzip
import os
import datetime
Import("env")
files = [
'data_embed/index.html',
@@ -8,13 +11,19 @@ files = [
'data_embed/bootstrap.css',
]
for src in files:
out = src + ".gz"
with open(src, 'rb') as f:
content = f.read()
if src == 'data_embed/index.html':
env_vars = env["BOARD"] + "<br>" + ','.join(env["BUILD_FLAGS"]).replace('-Werror -Wall,', '').replace(',-DELEGANTOTA_USE_ASYNC_WEBSERVER=1', '')
current_date = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') + " UTC"
build_info = f'{env_vars}<br>Build date: {current_date}'.encode()
content = content.replace(b'%BUILD_INFO%', build_info)
with open(out, 'wb') as f:
f.write(gzip.compress(content, compresslevel=9))