Compare commits

..

90 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
Ricardo Guzman (Richonguzman) 5334260a30 Merge pull request #43 from SQ2CPA/main
Changes before release
2024-03-08 15:03:49 -03:00
SQ2CPA 0868448a8c improvement: required fields 2024-03-08 18:03:42 +01:00
SQ2CPA 849d5d7c34 fix: digi mode 2024-03-08 18:03:42 +01:00
SQ2CPA 6f426edaf4 fix: av false positive 2024-03-08 18:03:42 +01:00
richonguzman d50f0dd300 date change 2024-03-07 22:48:05 -03:00
Ricardo Guzman (Richonguzman) 5dd6108350 Merge pull request #42 from SQ2CPA/main
web support footer
2024-03-07 14:56:59 -03:00
SQ2CPA c2b4c612df feat: web support footer 2024-03-07 18:55:35 +01:00
Ricardo Guzman (Richonguzman) 32fc475a1b Merge pull request #41 from SQ2CPA/main
stationMode removed and more features
2024-03-07 13:58:43 -03:00
SQ2CPA eafd7d4e81 fix: default aprsis filter 2024-03-07 17:50:47 +01:00
SQ2CPA 43ea66e304 refactor: remove old firmware bin 2024-03-07 17:47:24 +01:00
SQ2CPA a46a97691d feat: stationMode removed 2024-03-07 17:46:50 +01:00
SQ2CPA 8a7d91a7f6 improvement: workaround for windows via lnk 2024-03-07 17:46:11 +01:00
SQ2CPA 7af7ca5fba feat: enable esp prog debugger 2024-03-07 17:45:41 +01:00
richonguzman ca995821c8 BME fixes 2024-03-06 09:01:50 -03:00
richonguzman e8c903d20c BME fix 2024-03-06 09:01:17 -03:00
58 changed files with 2536 additions and 1289 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
+30 -29
View File
@@ -1,46 +1,48 @@
{
"callsign": "NOCALL-10",
"stationMode": 2,
"iGateComment": "LoRa_APRS_iGate",
"wifi": {
"autoAP": {
"password": "1234567890",
"powerOff": 10
},
"AP": [
{
"ssid": "WIFI_1",
"password": "WIFI_1_password",
"latitude": 0.0,
"longitude": 0.0
},
{
"ssid": "WIFI_2",
"password": "WIFI_2_password",
"latitude": 0.0,
"longitude": 0.0
}
]
"AP": []
},
"beacon": {
"latitude": 0.0,
"longitude": 0.0,
"comment": "LoRa APRS",
"interval": 15,
"overlay": "L",
"symbol": "a",
"path": "WIDE1-1",
"sendViaAPRSIS": false,
"sendViaRF": false
},
"digi": {
"comment": "LoRa_APRS_Digirepeater",
"latitude": 0.0,
"longitude": 0.0
"mode": 0
},
"tnc": {
"enableServer": false,
"enableSerial": false,
"acceptOwn": false
},
"aprs_is": {
"active": false,
"passcode": "XYZVW",
"server": "rotate.aprs2.net",
"port": 14580,
"reportingDistance": 30
"filter": "m/10",
"toRF": false
},
"lora": {
"iGateFreq": 433775000,
"digirepeaterTxFreq": 433775000,
"digirepeaterRxFreq": 433900000,
"txFreq": 433775000,
"rxFreq": 433775000,
"spreadingFactor": 12,
"signalBandwidth": 125000,
"codingRate4": 5,
"power": 20
"power": 20,
"txActive": false,
"rxActive": true
},
"display": {
"alwaysOn": true,
@@ -60,12 +62,11 @@
"password": ""
},
"other": {
"beaconInterval": 15,
"igateSendsLoRaBeacons": false,
"igateRepeatsLoRaPackets": false,
"rememberStationTime": 30,
"sendBatteryVoltage": false,
"externalVoltageMeasurement": false,
"externalVoltagePin": 34
"externalVoltagePin": 34,
"lowPowerMode": false,
"lowVoltageCutOff": 0
}
}
}
+633 -244
View File
File diff suppressed because one or more lines are too long
+226 -101
View File
@@ -75,14 +75,18 @@ function loadSettings(settings) {
currentSettings = settings;
// General
document.getElementById("callsign").value = settings.callsign;
document.getElementById("stationMode").value = settings.stationMode;
// document.getElementById("stationMode").value = settings.stationMode;
document.getElementById("bme.active").checked = settings.bme.active;
document.getElementById("iGateComment").value = settings.iGateComment;
document.getElementById("beacon.comment").value = settings.beacon.comment;
document.getElementById("beacon.symbol").value = settings.beacon.symbol;
document.getElementById("beacon.overlay").value = settings.beacon.overlay;
document.getElementById("action.symbol").value = settings.beacon.overlay + settings.beacon.symbol;
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;
@@ -94,23 +98,15 @@ function loadSettings(settings) {
// Increment the name, id, and for attributes
const attributeName = `wifi.AP.${networkCount}`;
networkElement.innerHTML = `
<div class="form-floating col-6 col-md-3 px-1 mb-2">
<div class="form-floating col-5 px-1 mb-2">
<input type="text" class="form-control form-control-sm" name="${attributeName}.ssid" id="${attributeName}.ssid" value="${network.ssid}">
<label for="${attributeName}.ssid">SSID</label>
</div>
<div class="form-floating col-6 col-md-3 px-1 mb-2">
<input type="text" class="form-control form-control-sm" name="${attributeName}.password" id="${attributeName}.password" value="${network.password}">
<div class="form-floating col-5 px-1 mb-2">
<input type="password" class="form-control form-control-sm" name="${attributeName}.password" id="${attributeName}.password" value="${network.password}">
<label for="${attributeName}.password">Passphrase</label>
</div>
<div class="col-4 col-md-2 form-floating px-1 mb-2">
<input type="text" class="form-control form-control-sm latitude" name="${attributeName}.latitude" id="${attributeName}.latitude" value="${network.latitude}">
<label for="${attributeName}.latitude">Latitude</label>
</div>
<div class="col-4 col-md-2 form-floating px-1 mb-2">
<input type="text" class="form-control form-control-sm longitude" name="${attributeName}.longitude" id="${attributeName}.longitude" value="${network.longitude}">
<label for="${attributeName}.longitude">Longitude</label>
</div>
<div class="col-4 col-md-2 d-flex align-items-center justify-content-end">
<div class="col-2 d-flex align-items-center justify-content-end">
<div class="btn-group" role="group">
<button type="button" class="btn btn-sm btn-danger" title="Delete" onclick="return this.parentNode.parentNode.parentNode.remove();"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06m6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528M8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5"/>
@@ -123,10 +119,11 @@ function loadSettings(settings) {
});
// APRS-IS
document.getElementById("aprs_is.active").checked = settings.aprs_is.active;
document.getElementById("aprs_is.toRF").checked = settings.aprs_is.toRF;
document.getElementById("aprs_is.server").value = settings.aprs_is.server;
document.getElementById("aprs_is.port").value = settings.aprs_is.port;
document.getElementById("aprs_is.reportingDistance").value =
settings.aprs_is.reportingDistance;
document.getElementById("aprs_is.filter").value = settings.aprs_is.filter;
document.getElementById("aprs_is.passcode").value =
settings.aprs_is.passcode;
@@ -148,29 +145,45 @@ function loadSettings(settings) {
settings.wifi.autoAP.powerOff;
// Digi
document.getElementById("digi.comment").value = settings.digi.comment;
document.getElementById("digi.latitude").value = settings.digi.latitude;
document.getElementById("digi.longitude").value = settings.digi.longitude;
// document.getElementById("digi.comment").value = settings.digi.comment;
// document.getElementById("digi.latitude").value = settings.digi.latitude;
// 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;
// Beacon
document.getElementById("other.beaconInterval").value =
settings.other.beaconInterval;
document.getElementById("beacon.interval").value = settings.beacon.interval;
document.getElementById("other.rememberStationTime").value =
settings.other.rememberStationTime;
document.getElementById("other.sendBatteryVoltage").checked =
settings.other.sendBatteryVoltage;
document.getElementById("other.externalVoltageMeasurement").checked =
settings.other.externalVoltageMeasurement;
document.getElementById("other.externalVoltagePin").value =
settings.other.externalVoltagePin;
document.getElementById("other.igateSendsLoRaBeacon").value =
settings.other.igateSendsLoRaBeacon;
document.getElementById("other.igateRepeatLoRaPackets").value =
settings.other.igateRepeatLoRaPackets;
// document.getElementById("beacon.igateSendsLoRaBeacon").value =
// settings.beacon.igateSendsLoRaBeacon;
// document.getElementById("beacon.igateRepeatLoRaPackets").value =
// settings.beacon.igateRepeatLoRaPackets;
document.getElementById("beacon.path").value = settings.beacon.path;
document.getElementById("beacon.latitude").value = settings.beacon.latitude;
document.getElementById("beacon.longitude").value =
settings.beacon.longitude;
document.getElementById("beacon.sendViaAPRSIS").checked =
settings.beacon.sendViaAPRSIS;
document.getElementById("beacon.sendViaRF").checked =
settings.beacon.sendViaRF;
// Syslog
document.getElementById("syslog.active").checked = settings.syslog.active;
@@ -183,11 +196,15 @@ function loadSettings(settings) {
}
// LoRa
document.getElementById("lora.digirepeaterTxFreq").value =
settings.lora.digirepeaterTxFreq;
document.getElementById("lora.iGateFreq").value = settings.lora.iGateFreq;
document.getElementById("lora.digirepeaterRxFreq").value =
settings.lora.digirepeaterRxFreq;
// document.getElementById("lora.digirepeaterTxFreq").value =
// settings.lora.digirepeaterTxFreq;
// document.getElementById("lora.iGateFreq").value = settings.lora.iGateFreq;
// document.getElementById("lora.digirepeaterRxFreq").value =
// settings.lora.digirepeaterRxFreq;
document.getElementById("lora.txFreq").value = settings.lora.txFreq;
document.getElementById("lora.rxFreq").value = settings.lora.rxFreq;
document.getElementById("lora.txActive").checked = settings.lora.txActive;
document.getElementById("lora.rxActive").checked = settings.lora.rxActive;
document.getElementById("lora.spreadingFactor").value =
settings.lora.spreadingFactor;
document.getElementById("lora.signalBandwidth").value =
@@ -196,70 +213,69 @@ 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']");
const digirepeaterTxFreqInput = document.querySelector(
"input[name='lora.digirepeaterRxFreq']"
);
function updateImage() {
const value = document.getElementById("beacon.overlay").value + document.getElementById("beacon.symbol").value;
const image = document.querySelector("img");
const isChecked = bmeCheckbox.checked;
if (isChecked) {
image.src =
"data:image/png;base64,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";
} else {
const selectedValue = stationModeSelect.value;
if (selectedValue === "1") {
if (bmeCheckbox.checked) {
image.src =
"data:image/png;base64,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";
} else {
image.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==";
}
digirepeaterTxFreqInput.disabled = true;
} else if (selectedValue === "2") {
if (bmeCheckbox.checked) {
image.src =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAlUSURBVHgB7Z1LiBxFGMe/nt1s1nd8Pw4uERVfCJqDB8XsSUHER1DiTYweRBCTgBdBETyIh6AQDx6SoAc1EjRCCOJBkg1CDoaYBSGHRDQhia+8dJdN9jVtfTXduz09X01XdVd3V/V+P/g2mZqame7+d1V99VV1FQDDMAzjKAE0lnCt+HNz9OI+YbcTmfYL+yv6/wFxOX6EhtEAgaWQLwgbEbZS2FVQ7LzmhE0KG4fODbC7icI7DAoa7hR2TlhYkc0KOyJsi7ARYGwjRT0YXejQAZuKbrJHgMkLlhR5EaccEVVlp4S9D4wuWCpkddh2XFiqGt8JXIWrWBDWF0FVhjfmXhZ6AVkVN0FYSugtsLSRVZpvVbGpoQ+xHpYW0it23XmybdgLGIHmI9snX0SxbbNVl+YKI1ny7j0kbAUwY+LSj0IFtKASZDjxNzAWNyzBnGA1dCJxI1AyFQgsPcntYFxbOCNGWeDNfjS6+Uuj5CoaHQt4AHJRlsDOja/giW4Ux/URlECJZ4t9W3KITvfzUA7ODqBtFcf2ClimpCq6qLhLkpehhMBICbdzXnFDzbQ26BEAfXq6abVhtSRbPrO84rYVaVT6LOiBldMAkT5IpKluhtqwJrLFKhoDGFwtW2JdAe+6lTRLAsu2YzUwtsDq5EuwMKHAQr0k77TtUAiuohVcEHa3OLZjBp/pKrQFz0pGYkRnnbxqBrDAfRDXN7jDIL9VgU/B4tRU3c8QaXNE+sXI0vwNelwWWRoqWoqiO+1dmzhdLeULM3As11RcJifriPa4pWM5BZY/9jQwVYFVyS7IQd4SvAsa/VSEk6yIak0jcggsp4jymG49PG06xGg6hGfBa6acrBlh86m0icjSHAU9rokszS1E2nKg73UnK6nYq1YVzq50U6E+g8JdIop56BUYu4D/LbwaHb0C9uzB89ILlgXBbqAP9UYizauxZ3EBpkXsYfkO+u3n83rRsvQ+CkwNpGelLPtEkbGVmdAHLL3sWLmB8IHm39DJqCkwl173CN7RyaVbgksuvdTkOAxTziVs3vA7Z1Kfj83ZiXimiFJ8IXMwQtdhehisoRrETwuIYcrJxOtlYMZZ6DwLnmZe8fvpe92H1mhom/hzZ3faP6ZetOz3luA5M8WQBeU2gOMrAW6NR5taq1ZNGHvRLwLjKqKauemtfhkyBJbOFQ8oOM3Amn7vZpXgt4FxHeFsTSh9pCyBn4BKoDxbdIZmEzYHZlxMfT62xnjRCYY3qN7p4zxx9VwPJjfcQt6HIBr/PXPmzFAyR78S/CownhDcpHqnn8CPA+MRk69Tqf0EvgsYjxh8ikzt84lLoHYW26MwzOMMNcGB0mXwfjKVzuzeoiGBceSw6eL29AAwLtuanp7u0lRVRa8GxiG0Vi8QReDwI+12u5U0lcBG834YV7i252F7lcArgfGQ4Z7hQ5XAy4HxkODqdIrKi3bAg2bMGbhd9Da63FEe5/UWKo7evgQdq2QKUUXzKqlNgmqDnwXGUwaG0ikWl3Bg6icYTqc0uA1WjfXqPmxuskqPivon7rGT5QWqJabCnrTTp0NbD4AzPsACN4qwZ14TJfABYDylPZVOIdpg3MatCUNteDPPEOnUwi6D0HspVE7WgGaaqnKs1vFqsJOlEniaSFMJ3FLkTbNMkbeOh8r1nCzTOaqME0z/kk5RCTwJTCNQCXwaGA+Z2J1OUbXBJ8H7lWOx/b1ApFOVE0ax0pciXkssjWqofIB4XXUka6pnGUCVwN+C9/OysBKiBLqUSMPhb0ogyju+mki7DnqFx7AwJXCZjtc93wBcc1nWryHGC24xdTP/b+ffs+2kKQSWy9eyJ+0VbXLJ4X6hyt+B8YipH6jUfgKPAeMRP22jUvtFsj4FudVLFVBhQXRwkguvmAbdTgC9tvQJIg2/O32v428PEXmpGcX4GNeVqTR0xignzXQxGR3aIv765HEgLlKfqyZj0tjP4BmWTpIsEPM4QBTPWujynbKKxffCngEP2bPnXQiC3hYoDHUHBlSx6O5ZMePjZ2H9+vNQL2eVG2plCbwJPBV4dPReqI46BcYx4JGvVe9mDPhjNU2GgxhnmPmu37s6Mzq+gNKJq8OkoTMynLAhYChObO73ro7A74HVGQABYdR+EizwItQ1Q+b/FOHJ/YmM6GR1PVOqIbCMav0KjGOgyFMfwqL3TJrupLuXgHEMdK6u+zgrl6bA0tnS3SyBqYSZz3VymUybLbEUU+0w9uCWJ6yMCJANsF8d+whJi8eDqbazMKL0Xq61jplB/E9GtrAUlzARAA8j7cddAUlRz58fhLGxWRGo6J0VGQRtkd5OpeVdmWfhG8R3BIpjXWR8HPPcAPR4cFlzGuc26eY03VYHlwjYB4VuRd29C7H7TU1xpWLJk9A7uolrXZrsFp5GtbmlyYD/MJF3Wcbvqx5TWUBEVQauB/ruwYPWXsqQOhBsi/cBUyMXXjPJbSgwEowCTwaoifYh0fbuMPlEDoElbwJTNaJQ/fEcGFKkLT0o/jwAVjDZIJp6MgHb295HKYuT7WR1oJ5sUM3KzCpTqjZ4dqNo0zdnHEhPG1xEYFzL4zBYGS9mgfuDVfPAKsg+kJ4fzVtFQxTCxL7xUlrxsw5Ed2JiDeSkgMBI8JX4sw2YssDC8xjAimOQk4ICI3JveQ5jlsMHUdc0NxafrQjPQe6No1WOBdUGq3Yuq6qlMHk+uNAiLlujwhN/WRqtNtimwBadLvl9JeUtSiWr7MSbQMeovLVML86iwIhNkZeswGlxERN33JYXTSE967uB53HlhRK3EJYFRljknFgXFylBYMSGyJW0dSVidEw/lyEuUpLAyILIBbpQgYG1KjLd49EGveUHwW/CvTj8ztZlom9XaHcb1R04mLKqwJORJ+WTCGXZFHQmTxRBS+CKGy/ZjRKB87wBkUYwFo2pF6WOblIW2C7LjSNwDZAQlhY4SWKDJXF9AKuo8JQnVWpR2wv2ca0NViHb5lmPxDKxUxbaWlPSgrtCuKVBQp+D+vZ/dFXgGK+FrlPYGNcFjpFVtw9tNHb9DkL1VbEKXwSOkc7YTuj0HV0SFm++LeAevgmcZEHsOko2ltQjHVGd3jzMZ4HTyGp8byS47RJ+LiHoWvAUF4dhCiLFuBk6i6nGDxLdCfQkBFyx9GT0fxwUwQW1DxSdB8UwDMMwDMM0nP8Bl3lxJTtDbOQAAAAASUVORK5CYII=";
} else {
image.src =
"data:image/png;base64,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";
}
digirepeaterTxFreqInput.disabled = true;
} else if (selectedValue === "3" || selectedValue === "4") {
switch (value) {
case "L&":
image.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==";
break;
case "L#":
image.src =
"data:image/png;base64,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";
digirepeaterTxFreqInput.disabled = selectedValue === "3";
} else if (selectedValue === "5") {
if (bmeCheckbox.checked) {
image.src =
"data:image/png;base64,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";
} else {
image.src =
"data:image/png;base64,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";
}
digirepeaterTxFreqInput.disabled = true;
}
break;
case "L_":
image.src =
"data:image/png;base64,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";
break;
case "La":
image.src =
"data:image/png;base64,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";
break;
}
}
stationModeSelect.addEventListener("change", updateImage);
bmeCheckbox.addEventListener("change", updateImage);
function toggleFields() {
const sendBatteryVoltageCheckbox = document.querySelector(
'input[name="other.sendBatteryVoltage"]'
);
const externalVoltageMeasurementCheckbox = document.querySelector(
'input[name="other.externalVoltageMeasurement"]'
);
@@ -267,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"]'
);
@@ -283,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 () {
@@ -301,22 +310,14 @@ document.querySelector(".new button").addEventListener("click", function () {
// Increment the name, id, and for attributes
const attributeName = `wifi.AP.${networkCount}`;
networkElement.innerHTML = `
<div class="form-floating col-6 col-md-3 px-1 mb-2">
<div class="form-floating col-6 col-md-5 px-1 mb-2">
<input type="text" class="form-control form-control-sm" name="${attributeName}.ssid" id="${attributeName}.ssid" placeholder="" >
<label for="${attributeName}.ssid">SSID</label>
</div>
<div class="form-floating col-6 col-md-3 px-1 mb-2">
<input type="text" class="form-control form-control-sm" name="${attributeName}.password" id="${attributeName}.password" placeholder="">
<div class="form-floating col-6 col-md-5 px-1 mb-2">
<input type="password" class="form-control form-control-sm" name="${attributeName}.password" id="${attributeName}.password" placeholder="">
<label for="${attributeName}.password">Passphrase</label>
</div>
<div class="col-4 col-md-2 form-floating px-1 mb-2">
<input type="text" class="form-control form-control-sm latitude" name="${attributeName}.latitude" id="${attributeName}.latitude" placeholder="">
<label for="${attributeName}.latitude">Latitude</label>
</div>
<div class="col-4 col-md-2 form-floating px-1 mb-2">
<input type="text" class="form-control form-control-sm longitude" name="${attributeName}.longitude" id="${attributeName}.longitude" placeholder="">
<label for="${attributeName}.longitude">Longitude</label>
</div>
<div class="col-4 col-md-2 d-flex align-items-center justify-content-end">
<div class="btn-group" role="group">
<button type="button" class="btn btn-sm btn-danger" title="Delete" onclick="return this.parentNode.parentNode.parentNode.remove();"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
@@ -333,7 +334,76 @@ document.querySelector(".new button").addEventListener("click", function () {
document.querySelector(".new").before(networkElement);
});
toggleFields();
document
.getElementById("action.symbol")
.addEventListener("change", function () {
const value = document.getElementById("action.symbol").value;
document.getElementById("beacon.overlay").value = value[0];
document.getElementById("beacon.symbol").value = value[1];
updateImage();
});
const speedStandards = {
300: [125, 5, 12],
244: [125, 6, 12],
209: [125, 7, 12],
183: [125, 8, 12],
610: [125, 8, 10],
1200: [125, 7, 9],
};
function refreshSpeedStandard() {
const bw = Number(document.getElementById("lora.signalBandwidth").value);
const cr4 = Number(document.getElementById("lora.codingRate4").value);
const sf = Number(document.getElementById("lora.spreadingFactor").value);
let found = false;
for (const speed in speedStandards) {
const standard = speedStandards[speed];
if (standard[0] !== bw / 1000) continue;
if (standard[1] !== cr4) continue;
if (standard[2] !== sf) continue;
document.getElementById("action.speed").value = speed;
found = true;
break;
}
if (!found) {
document.getElementById("action.speed").value = "";
}
}
document
.getElementById("lora.signalBandwidth")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.codingRate4")
.addEventListener("focusout", refreshSpeedStandard);
document
.getElementById("lora.spreadingFactor")
.addEventListener("focusout", refreshSpeedStandard);
document.getElementById("action.speed").addEventListener("change", function () {
const speed = document.getElementById("action.speed").value;
if (speed !== "") {
const value = speedStandards[Number(speed)];
const bw = value[0];
const cr4 = value[1];
const sf = value[2];
document.getElementById("lora.signalBandwidth").value = bw * 1000;
document.getElementById("lora.codingRate4").value = cr4;
document.getElementById("lora.spreadingFactor").value = sf;
}
});
const form = document.querySelector("form");
@@ -382,7 +452,62 @@ form.addEventListener("submit", async (event) => {
saveModal.show();
setTimeout(checkConnection, 0);
setTimeout(checkConnection, 2000);
});
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.
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

+7 -1
View File
@@ -4,6 +4,12 @@ echo "IF THIS IS YOUR FIRST FLASH ON THIS BOARD PLEASE USE install_with_factory_
read -p "Enter COM port (for example /dev/ttyS5): " port
python3 ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin
PYTHON_CMD=python
if command -v python3 &> /dev/null
then
PYTHON_CMD=python3
fi
PYTHON_CMD ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin
echo "Firmware flashed"
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
echo "Your firmware will be upgraded without factory reset."
echo "IF THIS IS YOUR FIRST FLASH ON THIS BOARD PLEASE USE install_with_factory_reset.sh INSTEAD!"
read -p "Enter COM port (for example /dev/ttyS5): " port
PYTHON_CMD=python
if command -v python3 &> /dev/null
then
PYTHON_CMD=python3
fi
$PYTHON_CMD ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 2686976 firmware/spiffs.bin
$PYTHON_CMD ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin
echo "Firmware flashed"
+86 -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
@@ -40,46 +39,119 @@ lib_deps =
extra_scripts =
pre:tools/compress.py
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
+140 -34
View File
@@ -14,19 +14,21 @@
#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.02.25";
int myWiFiAPIndex = 0;
String versionDate = "2024.04.13";
uint8_t myWiFiAPIndex = 0;
int myWiFiAPSize = Config.wifiAPs.size();
WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex];
int stationMode = Config.stationMode;
bool isUpdatingOTA = false;
bool statusAfterBoot = true;
bool beaconUpdate = true;
uint32_t lastBeaconTx = 0;
@@ -36,11 +38,14 @@ uint32_t lastScreenOn = millis();
uint32_t lastWiFiCheck = 0;
bool WiFiConnect = true;
bool WiFiConnected = false;
int lastStationModeState = 1;
bool WiFiAutoAPStarted = false;
long WiFiAutoAPTime = false;
uint32_t lastBatteryCheck = 0;
uint32_t bmeLastReading = -60000;
String batteryVoltage;
std::vector<String> lastHeardStation;
@@ -48,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) {
@@ -67,47 +74,146 @@ void setup() {
#endif
delay(1000);
Utils::setupDisplay();
WIFI_Utils::setup();
Config.check();
LoRa_Utils::setup();
Utils::validateDigiFreqs();
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() {
WEB_Utils::loop();
WIFI_Utils::checkIfAutoAPShouldPowerOff();
if (stationMode==1 || stationMode==2 ) { // iGate (1 Only Rx / 2 Rx+Tx)
if (!WiFiConnected) {
thirdLine = Utils::getLocalIP();
if (isUpdatingOTA) {
ElegantOTA.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
// Utils::checkWiFiInterval();
if (Config.aprs_is.active && !espClient.connected()) {
APRS_IS_Utils::connect();
}
TNC_Utils::loop();
Utils::checkDisplayInterval();
Utils::checkBeaconInterval();
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
String packet = "";
if (Config.loramodule.rxActive) {
packet = LoRa_Utils::receivePacket(); // We need to fetch LoRa packet above APRSIS and Digi
}
if (packet != "") {
if (Config.aprs_is.active) { // If APRSIS enabled
APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS
}
WIFI_Utils::checkWiFi();
if (!espClient.connected()) {
APRS_IS_Utils::connect();
if (Config.digi.mode == 2) { // If Digi enabled
DIGI_Utils::loop(packet); // Send received packet to Digi
}
APRS_IS_Utils::loop();
} else if (stationMode==3 || stationMode==4) { // DigiRepeater (3 RxFreq=TxFreq / 4 RxFreq!=TxFreq)
DIGI_Utils::loop();
} else if (stationMode==5) { // iGate when WiFi and APRS available , DigiRepeater when not (RxFreq=TxFreq)
Utils::checkWiFiInterval();
thirdLine = Utils::getLocalIP();
if (WiFi.status() == WL_CONNECTED) { // iGate Mode
if (!espClient.connected()) {
APRS_IS_Utils::connect();
}
if (lastStationModeState == 1) {
iGateBeaconPacket = GPS_Utils::generateBeacon();
lastStationModeState = 0;
}
APRS_IS_Utils::loop();
} else { // DigiRepeater Mode
DIGI_Utils::loop();
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.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);
}
+109 -110
View File
@@ -1,4 +1,3 @@
#include <ElegantOTA.h>
#include <WiFi.h>
#include "configuration.h"
#include "aprs_is_utils.h"
@@ -12,9 +11,7 @@
extern Configuration Config;
extern WiFiClient espClient;
extern int internalLedPin;
extern uint32_t lastScreenOn;
extern int stationMode;
extern String firstLine;
extern String secondLine;
extern String thirdLine;
@@ -30,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) {
@@ -46,9 +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) +")");
aprsauth = "user " + Config.callsign + " pass " + Config.aprs_is.passcode + " vers CA2RXU_LoRa_iGate 1.3 filter t/m/" + Config.callsign + "/" + (String)Config.aprs_is.reportingDistance;// + "\r\n";
}
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;
upload(aprsauth);
delay(200);
}
@@ -57,136 +58,141 @@ 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) {
display_toggle(true);
}
lastScreenOn = millis();
}
if (espClient.connected()) {
aprsisState = "OK";
} else {
if (!Config.aprs_is.active) {
aprsisState = "OFF";
}
else if (espClient.connected()) {
aprsisState = "OK";
}
else {
aprsisState = "--";
if (!Config.display.alwaysOn) {
display_toggle(true);
}
lastScreenOn = millis();
}
secondLine = "WiFi: " + wifiState + " APRS-IS: " + aprsisState;
secondLine = "WiFi: " + wifiState + " APRS-IS: " + aprsisState;
}
String createPacket(String packet) {
if (stationMode>1) {
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";
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(":"));
}
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 (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 (stationMode!=1 && Config.igateRepeatsLoRaPackets && Addressee != Config.callsign) { // if its not for me
String digiRepeatedPacket = DIGI_Utils::generateDigiRepeatedPacket(packet.substring(3), Config.callsign);
if (digiRepeatedPacket != "X") {
delay(500);
LoRa_Utils::sendNewPacket("APRS", digiRepeatedPacket);
if (packet.indexOf("::") > 10 && Addressee == Config.callsign) { // its a message for me!
queryMessage = processReceivedLoRaMessage(Sender, AddresseeAndMessage);
}
}
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 (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) {
@@ -195,20 +201,19 @@ 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
if ((stationMode==2 || stationMode==5) && STATION_Utils::wasHeard(Addressee)) {
Utils::print("Received from APRS-IS : " + packet);
if (Config.aprs_is.toRF && STATION_Utils::wasHeard(Addressee)) {
LoRa_Utils::sendNewPacket("APRS", LoRa_Utils::generatePacket(packet));
display_toggle(true);
lastScreenOn = millis();
@@ -216,23 +221,17 @@ namespace APRS_IS_Utils {
}
}
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
}
}
}
}
void loop() {
checkStatus();
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
while (espClient.connected()) {
Utils::checkDisplayInterval();
Utils::checkBeaconInterval();
processLoRaPacket(LoRa_Utils::receivePacket());
void listenAPRSIS() {
if (espClient.connected()) {
if (espClient.available()) {
String aprsisPacket;
aprsisPacket.concat(espClient.readStringUntil('\r'));
String aprsisPacket = espClient.readStringUntil('\r');
// Serial.println(aprsisPacket);
processAPRSISPacket(aprsisPacket);
}
ElegantOTA.loop();
}
}
+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();
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();
}
+47 -18
View File
@@ -7,8 +7,11 @@
#define HEIGHT_CORRECTION 0 // in meters
#define CORRECTION_FACTOR (8.2296) // for meters
extern Configuration Config;
extern String fifthLine;
extern Configuration Config;
extern String fifthLine;
extern uint32_t bmeLastReading;
float newHum, newTemp, newPress, newGas;
namespace BME_Utils {
@@ -33,14 +36,29 @@ namespace BME_Utils {
while (1); // sacar esto para que quede pegado si no encuentra BME280
} else {
#ifdef BME280Sensor
bme.setSampling(Adafruit_BME280::MODE_FORCED,
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::FILTER_OFF
);
Serial.println("init : BME280 Module ... done!");
#endif
#ifdef BMP280Sensor
bme.setSampling(Adafruit_BMP280::MODE_FORCED,
Adafruit_BMP280::SAMPLING_X1,
Adafruit_BMP280::SAMPLING_X1,
Adafruit_BMP280::FILTER_OFF
);
Serial.println("init : BMP280 Module ... done!");
#endif
#ifdef BME680Sensor
bme.setTemperatureOversampling(BME680_OS_1X);
bme.setHumidityOversampling(BME680_OS_1X);
bme.setPressureOversampling(BME680_OS_1X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_0);
Serial.println("init : BME680 Module ... done!");
#endif
#endif
}
} else {
Serial.println("(BME/BMP sensor not 'active' in 'igate_conf.json')");
@@ -113,22 +131,33 @@ namespace BME_Utils {
String readDataSensor() {
String wx, tempStr, humStr, presStr;
float newHum;
float newTemp = bme.readTemperature();
#if defined(BME280Sensor) || defined(BME680Sensor)
newHum = bme.readHumidity();
#endif
#ifdef BMP280Sensor
newHum = 0;
#endif
float newPress = (bme.readPressure() / 100.0F);
#ifdef BME680Sensor
float newGas = bme.gas_resistance / 1000.0; // in Kilo ohms
#endif
//bme.readAltitude(SEALEVELPRESSURE_HPA) // this is for approximate Altitude Calculation.
uint32_t lastReading = millis() - bmeLastReading;
if (lastReading > 60*1000) {
#if defined(BME280Sensor) || defined(BMP280Sensor)
bme.takeForcedMeasurement();
newTemp = bme.readTemperature();
newPress = (bme.readPressure() / 100.0F);
#ifdef BME280Sensor
newHum = bme.readHumidity();
#endif
#ifdef BMP280Sensor
newHum = 0;
#endif
#endif
#ifdef BME680Sensor
bme.performReading();
delay(50);
if (bme.endReading()) {
newTemp = bme.temperature;
newPress = (bme.pressure / 100.0F);
newHum = bme.humidity;
newGas = bme.gas_resistance / 1000.0; // in Kilo ohms
}
#endif
bmeLastReading = millis();
}
if (isnan(newTemp) || isnan(newHum) || isnan(newPress)) {
Serial.println("BME/BMP Module data failed");
+181 -49
View File
@@ -3,19 +3,28 @@
#include "configuration.h"
#include "display.h"
void Configuration::check() {
if (reload) {
show_display("------- UPDATE ------", "config is old", "device will update", "and then reboot", 1000);
writeFile();
ESP.restart();
}
}
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
for (int i = 0; i < wifiAPs.size(); i++) {
data["wifi"]["AP"][i]["ssid"] = wifiAPs[i].ssid;
data["wifi"]["AP"][i]["password"] = wifiAPs[i].password;
data["wifi"]["AP"][i]["latitude"] = wifiAPs[i].latitude;
data["wifi"]["AP"][i]["longitude"] = wifiAPs[i].longitude;
// data["wifi"]["AP"][i]["latitude"] = wifiAPs[i].latitude;
// data["wifi"]["AP"][i]["longitude"] = wifiAPs[i].longitude;
}
}
@@ -23,33 +32,56 @@ void Configuration::writeFile() {
data["wifi"]["autoAP"]["powerOff"] = wifiAutoAP.powerOff;
data["callsign"] = callsign;
data["stationMode"] = stationMode;
data["iGateComment"] = iGateComment;
// data["stationMode"] = stationMode; // only check for config version
// data["iGateComment"] = iGateComment;
data["other"]["beaconInterval"] = beaconInterval;
data["other"]["igateSendsLoRaBeacons"] = igateSendsLoRaBeacons;
data["other"]["igateRepeatsLoRaPackets"] = igateRepeatsLoRaPackets;
// data["other"]["beaconInterval"] = beaconInterval;
// data["other"]["igateSendsLoRaBeacons"] = igateSendsLoRaBeacons;
// data["other"]["igateRepeatsLoRaPackets"] = igateRepeatsLoRaPackets;
data["other"]["rememberStationTime"] = rememberStationTime;
data["other"]["sendBatteryVoltage"] = sendBatteryVoltage;
data["other"]["externalVoltageMeasurement"] = externalVoltageMeasurement;
data["other"]["externalVoltagePin"] = externalVoltagePin;
data["digi"]["comment"] = digi.comment;
data["digi"]["latitude"] = digi.latitude;
data["digi"]["longitude"] = digi.longitude;
data["digi"]["mode"] = digi.mode;
// data["digi"]["comment"] = digi.comment;
// 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;
data["aprs_is"]["port"] = aprs_is.port;
data["aprs_is"]["reportingDistance"] = aprs_is.reportingDistance;
data["aprs_is"]["filter"] = aprs_is.filter;
data["aprs_is"]["toRF"] = aprs_is.toRF;
data["lora"]["iGateFreq"] = loramodule.iGateFreq;
data["lora"]["digirepeaterTxFreq"] = loramodule.digirepeaterTxFreq;
data["lora"]["digirepeaterRxFreq"] = loramodule.digirepeaterRxFreq;
data["beacon"]["comment"] = beacon.comment;
// data["beacon"]["igateRepeatsLoRaPackets"] = beacon.igateRepeatsLoRaPackets;
// data["beacon"]["igateSendsLoRaBeacons"] = beacon.igateSendsLoRaBeacons;
data["beacon"]["interval"] = beacon.interval;
data["beacon"]["latitude"] = beacon.latitude;
data["beacon"]["longitude"] = beacon.longitude;
data["beacon"]["overlay"] = beacon.overlay;
data["beacon"]["symbol"] = beacon.symbol;
data["beacon"]["sendViaAPRSIS"] = beacon.sendViaAPRSIS;
data["beacon"]["sendViaRF"] = beacon.sendViaRF;
data["beacon"]["path"] = beacon.path;
// data["lora"]["iGateFreq"] = loramodule.iGateFreq;
// data["lora"]["digirepeaterTxFreq"] = loramodule.digirepeaterTxFreq;
// data["lora"]["digirepeaterRxFreq"] = loramodule.digirepeaterRxFreq;
data["lora"]["rxFreq"] = loramodule.rxFreq;
data["lora"]["txFreq"] = loramodule.txFreq;
data["lora"]["spreadingFactor"] = loramodule.spreadingFactor;
data["lora"]["signalBandwidth"] = loramodule.signalBandwidth;
data["lora"]["codingRate4"] = loramodule.codingRate4;
data["lora"]["power"] = loramodule.power;
data["lora"]["txActive"] = loramodule.txActive;
data["lora"]["rxActive"] = loramodule.rxActive;
data["display"]["alwaysOn"] = display.alwaysOn;
data["display"]["timeout"] = display.timeout;
@@ -63,7 +95,10 @@ 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();
@@ -77,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) {
@@ -89,8 +124,6 @@ bool Configuration::readFile() {
WiFi_AP wifiap;
wifiap.ssid = WiFiArray[i]["ssid"].as<String>();
wifiap.password = WiFiArray[i]["password"].as<String>();
wifiap.latitude = WiFiArray[i]["latitude"].as<double>();
wifiap.longitude = WiFiArray[i]["longitude"].as<double>();
wifiAPs.push_back(wifiap);
}
@@ -99,28 +132,15 @@ bool Configuration::readFile() {
wifiAutoAP.powerOff = data["wifi"]["autoAP"]["powerOff"].as<int>();
callsign = data["callsign"].as<String>();
stationMode = data["stationMode"].as<int>();
iGateComment = data["iGateComment"].as<String>();
beaconInterval = data["other"]["beaconInterval"].as<int>();
igateSendsLoRaBeacons = data["other"]["igateSendsLoRaBeacons"].as<bool>();
igateRepeatsLoRaPackets = data["other"]["igateRepeatsLoRaPackets"].as<bool>();
rememberStationTime = data["other"]["rememberStationTime"].as<int>();
sendBatteryVoltage = data["other"]["sendBatteryVoltage"].as<bool>();
externalVoltageMeasurement = data["other"]["externalVoltageMeasurement"].as<bool>();
externalVoltagePin = data["other"]["externalVoltagePin"].as<int>();
digi.comment = data["digi"]["comment"].as<String>();
digi.latitude = data["digi"]["latitude"].as<double>();
digi.longitude = data["digi"]["longitude"].as<double>();
aprs_is.passcode = data["aprs_is"]["passcode"].as<String>();
aprs_is.server = data["aprs_is"]["server"].as<String>();
aprs_is.port = data["aprs_is"]["port"].as<int>();
aprs_is.reportingDistance = data["aprs_is"]["reportingDistance"].as<int>();
loramodule.iGateFreq = data["lora"]["iGateFreq"].as<long>();
loramodule.digirepeaterTxFreq = data["lora"]["digirepeaterTxFreq"].as<long>();
loramodule.digirepeaterRxFreq = data["lora"]["digirepeaterRxFreq"].as<long>();
loramodule.spreadingFactor = data["lora"]["spreadingFactor"].as<int>();
loramodule.signalBandwidth = data["lora"]["signalBandwidth"].as<long>();
loramodule.codingRate4 = data["lora"]["codingRate4"].as<int>();
@@ -139,12 +159,94 @@ 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) {
// Load new settings
beacon.latitude = data["beacon"]["latitude"].as<double>();
beacon.longitude = data["beacon"]["longitude"].as<double>();
beacon.comment = data["beacon"]["comment"].as<String>();
beacon.overlay = data["beacon"]["overlay"].as<String>();
beacon.symbol = data["beacon"]["symbol"].as<String>();
beacon.interval = data["beacon"]["interval"].as<int>();
// beacon.igateSendsLoRaBeacons = data["beacon"]["igateSendsLoRaBeacons"].as<bool>();
// beacon.igateRepeatsLoRaPackets = data["beacon"]["igateRepeatsLoRaPackets"].as<bool>();
beacon.sendViaAPRSIS = data["beacon"]["sendViaAPRSIS"].as<bool>();
beacon.sendViaRF = data["beacon"]["sendViaRF"].as<bool>();
beacon.path = data["beacon"]["path"].as<String>();
digi.mode = data["digi"]["mode"].as<int>();
aprs_is.active = data["aprs_is"]["active"].as<bool>();
aprs_is.filter = data["aprs_is"]["filter"].as<String>();
aprs_is.toRF = data["aprs_is"]["toRF"].as<bool>();
loramodule.txFreq = data["lora"]["txFreq"].as<long>();
loramodule.rxFreq = data["lora"]["rxFreq"].as<long>();
loramodule.txActive = data["lora"]["txActive"].as<bool>();
loramodule.rxActive = data["lora"]["rxActive"].as<bool>();
} else {
// Load old settings and put into new variables not actual config
String iGateComment = data["iGateComment"].as<String>();
int beaconInterval = data["other"]["beaconInterval"].as<int>();
// bool igateSendsLoRaBeacons = data["other"]["igateSendsLoRaBeacons"].as<bool>();
// bool igateRepeatsLoRaPackets = data["other"]["igateRepeatsLoRaPackets"].as<bool>();
long iGateFreq = data["lora"]["iGateFreq"].as<long>();
long digirepeaterTxFreq = data["lora"]["digirepeaterTxFreq"].as<long>();
long digirepeaterRxFreq = data["lora"]["digirepeaterRxFreq"].as<long>();
String digiComment = data["digi"]["comment"].as<String>();
double digiLatitude = data["digi"]["latitude"].as<double>();
double digiLongitude = data["digi"]["longitude"].as<double>();
beacon.latitude = digiLatitude;
beacon.longitude = digiLongitude;
beacon.interval = beaconInterval;
// beacon.igateSendsLoRaBeacons = igateSendsLoRaBeacons;
// beacon.igateRepeatsLoRaPackets = igateRepeatsLoRaPackets;
loramodule.txFreq = digirepeaterTxFreq;
loramodule.rxFreq = digirepeaterRxFreq;
loramodule.rxActive = true;
beacon.sendViaAPRSIS = true;
beacon.sendViaRF = false;
switch (stationMode) {
case 1: // IGate only
// aprs_is.active = true; // better don't do that automatically
beacon.comment = iGateComment;
loramodule.rxFreq = iGateFreq;
break;
case 5: // Digi + IGate
case 2: // Digi + IGate
// aprs_is.active = true; // better don't do that automatically
// digi.mode = 2; // better don't do that automatically
beacon.comment = digiComment;
loramodule.rxFreq = iGateFreq;
break;
case 3: // Digi
case 4: // Digi
// digi.mode = 2; // better don't do that automatically
beacon.comment = digiComment;
break;
}
reload = true;
}
if (wifiAPs.size() == 0) { // If we don't have any WiFi's from config we need to add "empty" SSID for AUTO AP
WiFi_AP wifiap;
wifiap.ssid = "";
wifiap.password = "";
wifiap.latitude = 0.0;
wifiap.longitude = 0.0;
wifiAPs.push_back(wifiap);
}
@@ -158,36 +260,61 @@ bool Configuration::readFile() {
}
void Configuration::init() {
reload = false;
WiFi_AP wifiap;
wifiap.ssid = "";
wifiap.password = "";
wifiap.latitude = 0.0;
wifiap.longitude = 0.0;
// wifiap.latitude = 0.0; // deprecated
// wifiap.longitude = 0.0; // deprecated
wifiAPs.push_back(wifiap);
wifiAutoAP.password = "1234567890";
wifiAutoAP.powerOff = 15;
callsign = "N0CALL";
stationMode = 1;
iGateComment = "LoRa_APRS_iGate Development";
// stationMode = 1; // deprecated
// iGateComment = "LoRa_APRS_iGate Development"; // deprecated
digi.comment = "LoRa_APRS_iGate Development";
digi.latitude = 0.0;
digi.longitude = 0.0;
beacon.comment = "LoRa APRS"; // new
beacon.latitude = 0.0; // new
beacon.longitude = 0.0; // new
beacon.interval = 15; // new
// beacon.igateRepeatsLoRaPackets = false; // new
// beacon.igateSendsLoRaBeacons = false; // new
beacon.overlay = "L"; // new
beacon.symbol = "#"; // new
beacon.sendViaAPRSIS = true; // new
beacon.sendViaRF = false; // new
beacon.path = "WIDE1-1"; // new
digi.mode = 0; // new
// digi.comment = "LoRa_APRS_iGate Development"; // deprecated
// 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";
aprs_is.port = 14580;
aprs_is.reportingDistance = 30;
aprs_is.filter = "m/10"; // new
aprs_is.toRF = false; // new
loramodule.iGateFreq = 433775000;
loramodule.digirepeaterTxFreq = 433775000;
loramodule.digirepeaterRxFreq = 433900000;
// loramodule.iGateFreq = 433775000; // deprecated
// loramodule.digirepeaterTxFreq = 433775000; // deprecated
// loramodule.digirepeaterRxFreq = 433900000; // deprecated
loramodule.txFreq = 433775000; // new
loramodule.rxFreq = 433775000; // new
loramodule.spreadingFactor = 12;
loramodule.signalBandwidth = 125000;
loramodule.codingRate4 = 5;
loramodule.power = 20;
loramodule.txActive = false; // new
loramodule.rxActive = true; // new
display.alwaysOn = true;
display.timeout = 4;
@@ -202,14 +329,17 @@ void Configuration::init() {
ota.username = "";
ota.password = "";
beaconInterval = 15;
igateSendsLoRaBeacons = false;
igateRepeatsLoRaPackets = false;
// beaconInterval = 15; // deprecated
// igateSendsLoRaBeacons = false; // deprecated
// igateRepeatsLoRaPackets = false; // deprecated
rememberStationTime = 30;
sendBatteryVoltage = false;
externalVoltageMeasurement = false;
externalVoltagePin = 34;
lowPowerMode = false;
lowVoltageCutOff = 0;
Serial.println("todo escrito");
}
@@ -218,13 +348,15 @@ Configuration::Configuration() {
Serial.println("SPIFFS Mount Failed");
return;
} else {
Serial.println("montado");
Serial.println("SPIFFS Mounted");
}
bool exists = SPIFFS.exists("/igate_conf.json");
if (!exists) {
init();
writeFile();
ESP.restart();
}
readFile();
}
+47 -14
View File
@@ -9,8 +9,8 @@ class WiFi_AP {
public:
String ssid;
String password;
double latitude;
double longitude;
// double latitude; // deprecated
// double longitude; // deprecated
};
class WiFi_Auto_AP {
@@ -19,27 +19,47 @@ public:
int powerOff;
};
class Beacon {
public:
double latitude; // new
double longitude; // new
String comment; // new
String overlay; // new
String symbol; // new
int interval; // new
bool sendViaRF; // new
bool sendViaAPRSIS; // new
String path; // new
};
class DIGI {
public:
String comment;
double latitude;
double longitude;
int mode; // new
// String comment; // deprecated
// double latitude; // deprecated
// double longitude; // deprecated
};
class APRS_IS {
public:
bool active; // new
String passcode;
String server;
int port;
int reportingDistance;
// int reportingDistance; // deprecated
String filter; // new
bool toRF; // new
};
class LoraModule {
public:
long iGateFreq;
long digirepeaterTxFreq;
long digirepeaterRxFreq;
// long iGateFreq; // deprecated
// long digirepeaterTxFreq; // deprecated
// long digirepeaterRxFreq; // deprecated
long txFreq; // new
long rxFreq; // new
bool txActive; // new
bool rxActive; // new
int spreadingFactor;
long signalBandwidth;
int codingRate4;
@@ -53,6 +73,13 @@ public:
bool turn180;
};
class TNC {
public:
bool enableServer;
bool enableSerial;
bool acceptOwn;
};
class SYSLOG {
public:
bool active;
@@ -76,19 +103,24 @@ public:
class Configuration {
public:
bool reload;
String callsign;
int stationMode;
String iGateComment;
int beaconInterval;
bool igateSendsLoRaBeacons;
bool igateRepeatsLoRaPackets;
// int stationMode; // deprecated
// String iGateComment; // deprecated
// int beaconInterval; // deprecated
// bool igateSendsLoRaBeacons; // deprecated
// bool igateRepeatsLoRaPackets; // deprecated
int rememberStationTime;
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;
@@ -98,6 +130,7 @@ public:
void init();
void writeFile();
void check();
Configuration();
private:
+43 -65
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"
@@ -10,9 +11,7 @@
#include "utils.h"
extern Configuration Config;
extern int stationMode;
extern uint32_t lastScreenOn;
extern int lastStationModeState;
extern String iGateBeaconPacket;
extern String firstLine;
extern String secondLine;
@@ -27,89 +26,68 @@ 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 {
return "X";
}
} else {
return "X";
else {
return "";
}
}
} else {
return "X";
else {
return "";
}
}
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, "Digi");
if ((stationMode==3 || stationMode==5) && (packet.indexOf("WIDE1-") > 10)) {
loraPacket = generateDigiRepeatedPacket(packet.substring(3), Config.callsign);
if (loraPacket != "X") {
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);
}
} else if (stationMode==4){
if (packet.indexOf("WIDE1-") == -1) {
loraPacket = packet.substring(3,packet.indexOf(":")) + "," + Config.callsign + "*" + packet.substring(packet.indexOf(":"));
} else {
loraPacket = packet.substring(3,packet.indexOf(",")+1) + Config.callsign + "*" + packet.substring(packet.indexOf(","));
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();
}
}
delay(500);
if (stationMode==4) {
LoRa_Utils::changeFreqTx();
}
LoRa_Utils::sendNewPacket("APRS", loraPacket);
if (stationMode==4) {
LoRa_Utils::changeFreqRx();
}
display_toggle(true);
lastScreenOn = millis();
}
} else {
Serial.println(" ---> LoRa Packet Ignored (first 3 bytes or NOGATE)\n");
}
}
}
void loop() {
if (stationMode==3 || stationMode==4 || stationMode==5) {
if (lastStationModeState==0 && stationMode==5) {
iGateBeaconPacket = GPS_Utils::generateBeacon();
lastStationModeState = 1;
String Tx = String(Config.loramodule.digirepeaterTxFreq);
secondLine = "Rx:" + String(Tx.substring(0,3)) + "." + String(Tx.substring(3,6));
secondLine += " Tx:" + String(Tx.substring(0,3)) + "." + String(Tx.substring(3,6));
thirdLine = "<< DigiRepeater >>";
}
Utils::checkDisplayInterval();
Utils::checkBeaconInterval();
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
processPacket(LoRa_Utils::receivePacket());
}
void loop(String packet) {
processPacket(packet);
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ namespace DIGI_Utils {
String generateDigiRepeatedPacket(String packet, String callsign);
void processPacket(String packet);
void loop();
void loop(String packet);
}
+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
}
+37 -98
View File
@@ -4,125 +4,64 @@
#include "gps_utils.h"
extern Configuration Config;
extern WiFi_AP *currentWiFi;
extern WiFiClient espClient;
extern int stationMode;
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, stationLongitude, beaconPacket;
if (stationMode==1 || stationMode==2 || (stationMode==5 && WiFi.status()==WL_CONNECTED && espClient.connected())) {
stationLatitude = processLatitudeAPRS(currentWiFi->latitude);
stationLongitude = processLongitudeAPRS(currentWiFi->longitude);
beaconPacket = Config.callsign + ">APLRG1,WIDE1-1";
if (stationMode!=6) {
beaconPacket += ",qAC";
}
beaconPacket += ":=" + stationLatitude + "L" + stationLongitude;
if (stationMode==1) {
beaconPacket += "&";
} else {
beaconPacket += "a";
}
beaconPacket += Config.iGateComment;
} else { //stationMode 3, 4 and 5
if (stationMode==5) {
stationLatitude = processLatitudeAPRS(currentWiFi->latitude);
stationLongitude = processLongitudeAPRS(currentWiFi->longitude);
} else {
stationLatitude = processLatitudeAPRS(Config.digi.latitude);
stationLongitude = processLongitudeAPRS(Config.digi.longitude);
}
beaconPacket = Config.callsign + ">APLRG1,WIDE1-1:=" + stationLatitude + "L" + stationLongitude + "#" + Config.digi.comment;
}
return beaconPacket;
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";
}
return beaconPacket + ":!" + encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);;
}
String generateiGateLoRaBeacon() {
String stationLatitude, stationLongitude, beaconPacket;
stationLatitude = processLatitudeAPRS(currentWiFi->latitude);
stationLongitude = processLongitudeAPRS(currentWiFi->longitude);
beaconPacket = Config.callsign + ">APLRG1,RFONLY:=" + stationLatitude + "L" + stationLongitude;
if (Config.bme.active) {
beaconPacket += "_";
} else {
beaconPacket += "a";
}
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) {
return TinyGPSPlus::distanceBetween(currentWiFi->latitude,currentWiFi->longitude, latitude, longitude) / 1000.0;
return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0;
}
String decodeEncodedGPS(String packet) {
@@ -178,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
+133 -140
View File
@@ -1,225 +1,218 @@
#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;
extern int stationMode;
#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;
if (stationMode==1 || stationMode==2) {
freq = Config.loramodule.iGateFreq;
} else {
freq = Config.loramodule.digirepeaterTxFreq;
}
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.iGateFreq/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 sendNewPacket(const String &typeOfMessage, const String &newPacket) {
#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);
#endif
#ifdef HAS_SX127X
LoRa.beginPacket();
LoRa.write('<');
if (typeOfMessage == "APRS") {
LoRa.write(0xFF);
} else if (typeOfMessage == "LoRa") {
LoRa.write(0xF8);
void changeFreqTx() {
delay(500);
float freq = (float)Config.loramodule.txFreq / 1000000;
radio.setFrequency(freq);
}
void changeFreqRx() {
delay(500);
float freq = (float)Config.loramodule.rxFreq / 1000000;
radio.setFrequency(freq);
}
void sendNewPacket(const String& typeOfMessage, const String& newPacket) {
if (!Config.loramodule.txActive) return;
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
changeFreqTx();
}
LoRa.write(0x01);
LoRa.write((const uint8_t *)newPacket.c_str(), newPacket.length());
LoRa.endPacket();
#ifdef HAS_INTERNAL_LED
digitalWrite(internalLedPin, HIGH);
#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 && (stationMode==1 || stationMode==2 || (stationMode==5 && WiFi.status()==WL_CONNECTED)) && loraPacket!="") {
SYSLOG_Utils::log("Rx", loraPacket, rssi, snr, freqError);
ignorePacket = false;
return "";
}
}
return loraPacket;
}
void changeFreqTx() {
delay(500);
#ifdef HAS_SX127X
LoRa.setFrequency(Config.loramodule.digirepeaterTxFreq);
#endif
#ifdef HAS_SX126X
float freq = (float)Config.loramodule.digirepeaterTxFreq/1000000;
radio.setFrequency(freq);
#endif
}
void changeFreqRx() {
delay(500);
#ifdef HAS_SX127X
LoRa.setFrequency(Config.loramodule.digirepeaterRxFreq);
#endif
#ifdef HAS_SX126X
float freq = (float)Config.loramodule.digirepeaterRxFreq/1000000;
radio.setFrequency(freq);
#endif
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace LoRa_Utils {
String receivePacket();
void changeFreqTx();
void changeFreqRx();
void startReceive();
}
+3
View File
@@ -7,6 +7,7 @@
extern Configuration Config;
extern uint32_t lastScreenOn;
extern bool isUpdatingOTA;
unsigned long ota_progress_millis = 0;
@@ -31,6 +32,7 @@ namespace OTA_Utils {
display_toggle(true);
lastScreenOn = millis();
show_display("", "", "", " OTA update started!", "", "", "", 1000);
isUpdatingOTA = true;
}
void onOTAProgress(size_t current, size_t final) {
@@ -53,6 +55,7 @@ namespace OTA_Utils {
Serial.println("There was an error during OTA update!");
show_display("", "", " OTA update fail!", "", "", "", "", 4000);
}
isUpdatingOTA = false;
}
}
+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
-10
View File
@@ -127,14 +127,4 @@ namespace POWER_Utils {
#endif
}
/*void lowerCpuFrequency() {
#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_Beam_V1_0_SX1268) || defined(TTGO_T_Beam_V1_2) || defined(ESP32_DIY_LoRa_GPS) || defined(TTGO_T_LORA32_V2_1_GPS) || defined(TTGO_T_LORA32_V2_1_TNC) || defined(ESP32_DIY_1W_LoRa_GPS) || defined(TTGO_T_Beam_V1_2_SX1262)
if (setCpuFrequencyMhz(80)) {
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "CPU frequency set to 80MHz");
} else {
logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, "Main", "CPU frequency unchanged");
}
#endif
}*/
}
-1
View File
@@ -12,7 +12,6 @@ namespace POWER_Utils {
void deactivateLoRa();
bool begin(TwoWire &port);
void setup();
//void lowerCpuFrequency();
}
+7 -5
View File
@@ -2,11 +2,9 @@
#include "query_utils.h"
extern Configuration Config;
extern WiFi_AP *currentWiFi;
extern std::vector<String> lastHeardStation;
extern std::vector<String> lastHeardStation_temp;
extern String versionDate;
extern int stationMode;
namespace QUERY_Utils {
@@ -16,9 +14,9 @@ namespace QUERY_Utils {
if (query=="?APRS?" || query=="?aprs?" || query=="?Aprs?" || query=="H" || query=="h" || query=="HELP" || query=="Help" || query=="help" || query=="?") {
answer = "?APRSV ?APRSP ?APRSL ?APRSH ?WHERE callsign";
} else if (query=="?APRSV" || query=="?aprsv" || query=="?Aprsv") {
answer = "CA2RXU_LoRa_iGate 1.3 v" + versionDate + " sM" + String(stationMode);
answer = "CA2RXU_LoRa_iGate 1.3 v" + versionDate;
} else if (query=="?APRSP" || query=="?aprsp" || query=="?Aprsp") {
answer = "iGate QTH: " + String(currentWiFi->latitude,2) + " " + String(currentWiFi->longitude,2);
answer = "iGate QTH: " + String(Config.beacon.latitude,2) + " " + String(Config.beacon.longitude,2);
} else if (query=="?APRSL" || query=="?aprsl" || query=="?Aprsl") {
if (lastHeardStation.size() == 0) {
answer = "No Station Listened in the last " + String(Config.rememberStationTime) + "min.";
@@ -43,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();
+3 -4
View File
@@ -5,7 +5,6 @@
#include "gps_utils.h"
extern Configuration Config;
extern int stationMode;
WiFiUDP udpClient;
@@ -13,8 +12,8 @@ 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
if (Config.syslog.active && (stationMode==1 || stationMode==2 || (stationMode==5 && WiFi.status()==WL_CONNECTED))) {
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) {
syslogPacket += type + " / StartUp_Status / " + packet.substring(packet.indexOf(":>")+2);
@@ -67,7 +66,7 @@ namespace SYSLOG_Utils {
}
void setup() {
if (Config.syslog.active && (stationMode==1 || stationMode==2 || (stationMode==5 && WiFi.status()==WL_CONNECTED))) {
if (Config.syslog.active && WiFi.status() == WL_CONNECTED) {
udpClient.begin(WiFi.localIP(), 0);
Serial.println("init : Syslog Server ... done! (at " + Config.syslog.server + ")");
}
+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
+78 -141
View File
@@ -26,7 +26,6 @@ extern String seventhLine;
extern uint32_t lastBeaconTx;
extern uint32_t lastScreenOn;
extern bool beaconUpdate;
extern int stationMode;
extern String iGateBeaconPacket;
extern String iGateLoRaBeaconPacket;
extern std::vector<String> lastHeardStation;
@@ -42,24 +41,21 @@ extern bool WiFiConnected;
namespace Utils {
void processStatus() {
String status = Config.callsign + ">APLRG1,WIDE1-1";
if (stationMode==1 || stationMode==2 || (stationMode==5 && WiFi.status() == WL_CONNECTED)) {
String status = Config.callsign + ">APLRG1," + Config.beacon.path;
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);
} else {
delay(5000);
status += ":>https://github.com/richonguzman/LoRa_APRS_iGate " + versionDate;
if (stationMode==4) {
LoRa_Utils::changeFreqTx();
}
LoRa_Utils::sendNewPacket("APRS", status);
if (stationMode==4) {
LoRa_Utils::changeFreqRx();
}
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;
}
statusAfterBoot = false;
}
String getLocalIP() {
@@ -72,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;
@@ -94,126 +90,65 @@ namespace Utils {
void checkBeaconInterval() {
uint32_t lastTx = millis() - lastBeaconTx;
String beaconPacket, secondaryBeaconPacket;
if (lastTx >= Config.beaconInterval*60*1000) {
String beaconPacket = iGateBeaconPacket;
String secondaryBeaconPacket = iGateLoRaBeaconPacket;
if (lastBeaconTx == 0 || lastTx >= Config.beacon.interval * 60 * 1000) {
beaconUpdate = true;
}
if (beaconUpdate) {
display_toggle(true);
Serial.println("---- Sending iGate Beacon ----");
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) + " + WX";
if (Config.igateSendsLoRaBeacons && stationMode!=1) {
secondaryBeaconPacket = iGateLoRaBeaconPacket + sensorData + Config.iGateComment + " + WX";
}
} else {
beaconPacket = iGateBeaconPacket;
if (Config.igateSendsLoRaBeacons && stationMode!=1) {
secondaryBeaconPacket = iGateLoRaBeaconPacket + Config.iGateComment;
}
beaconPacket += sensorData;
secondaryBeaconPacket += sensorData;
}
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
beaconPacket += Config.beacon.comment;
secondaryBeaconPacket += Config.beacon.comment;
#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)";
}
if (stationMode==1 || stationMode==2) {
thirdLine = getLocalIP();
if (!Config.bme.active) {
fifthLine = "";
}
sixthLine = "";
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING iGate BEACON", 1000);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
if (Config.sendBatteryVoltage) {
sixthLine = " (Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V)";
}
#endif
if (Config.externalVoltageMeasurement) {
sixthLine = " (Ext V=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V)";
}
if (Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING IGATE BEACON", 0);
seventhLine = " listening...";
APRS_IS_Utils::upload(beaconPacket);
if (Config.igateSendsLoRaBeacons && stationMode==2) {
LoRa_Utils::sendNewPacket("APRS", secondaryBeaconPacket);
}
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
} else if (stationMode==3 || stationMode==4) {
String Rx = String(Config.loramodule.digirepeaterRxFreq);
String Tx = String(Config.loramodule.digirepeaterTxFreq);
if (stationMode==3) {
secondLine = "Rx:" + String(Tx.substring(0,3)) + "." + String(Tx.substring(3,6));
} else {
secondLine = "Rx:" + String(Rx.substring(0,3)) + "." + String(Rx.substring(3,6));
}
secondLine += " Tx:" + String(Tx.substring(0,3)) + "." + String(Tx.substring(3,6));
thirdLine = "<< DigiRepeater >>";
fifthLine = "";
sixthLine = "";
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING iGate BEACON", 0);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
if (Config.sendBatteryVoltage) {
sixthLine = " (Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V)";
}
#endif
if (Config.externalVoltageMeasurement) {
sixthLine = " (Ext V=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V)";
}
seventhLine = " listening...";
if (stationMode==4) {
LoRa_Utils::changeFreqTx();
}
LoRa_Utils::sendNewPacket("APRS", beaconPacket);
if (stationMode==4) {
LoRa_Utils::changeFreqRx();
}
} else if (stationMode==5) {
if (!Config.bme.active) {
fifthLine = "";
}
sixthLine = "";
if (WiFi.status() == WL_CONNECTED && espClient.connected()) {
APRS_IS_Utils::checkStatus();
thirdLine = getLocalIP();
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING iGate BEACON", 1000);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
if (Config.sendBatteryVoltage) {
sixthLine = " (Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V)";
}
#endif
if (Config.externalVoltageMeasurement) {
sixthLine = " (Ext V=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V)";
}
seventhLine = " listening...";
APRS_IS_Utils::upload(beaconPacket);
if (Config.igateSendsLoRaBeacons) {
LoRa_Utils::sendNewPacket("APRS", secondaryBeaconPacket);
}
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
} else {
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING iGate BEACON", 0);
#if defined(TTGO_T_LORA32_V2_1) || defined(HELTEC_V2)
if (Config.sendBatteryVoltage) {
sixthLine = " (Batt=" + String(BATTERY_Utils::checkBattery(),2) + "V)";
}
#endif
if (Config.externalVoltageMeasurement) {
sixthLine = " (Ext V=" + String(BATTERY_Utils::checkExternalVoltage(),2) + "V)";
}
seventhLine = " listening...";
LoRa_Utils::sendNewPacket("APRS", beaconPacket);
}
}
if (Config.beacon.sendViaRF) {
show_display(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING DIGI BEACON", 0);
seventhLine = " listening...";
LoRa_Utils::sendNewPacket("APRS", secondaryBeaconPacket);
}
lastBeaconTx = millis();
lastScreenOn = millis();
beaconUpdate = false;
}
if (statusAfterBoot) {
processStatus();
}
@@ -241,43 +176,33 @@ namespace Utils {
}
}
void validateDigiFreqs() {
if (stationMode==4) {
if (abs(Config.loramodule.digirepeaterTxFreq - Config.loramodule.digirepeaterRxFreq) < 125000) {
Serial.println("Tx Freq less than 125kHz from Rx Freq ---> NOT VALID");
show_display("Tx Freq is less than ", "125kHz from Rx Freq", "device will autofix", "and then reboot", 1000);
Config.loramodule.digirepeaterTxFreq = Config.loramodule.digirepeaterRxFreq; // Inform about that but then change the digirepeaterTxFreq to digirepeaterRxFreq and reset the device
Config.writeFile();
ESP.restart();
}
void validateFreqs() {
if (Config.loramodule.txFreq != Config.loramodule.rxFreq && abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) < 125000) {
Serial.println("Tx Freq less than 125kHz from Rx Freq ---> NOT VALID");
show_display("Tx Freq is less than ", "125kHz from Rx Freq", "device will autofix", "and then reboot", 1000);
Config.loramodule.txFreq = Config.loramodule.rxFreq; // Inform about that but then change the TX QRG to RX QRG and reset the device
Config.writeFile();
ESP.restart();
}
}
void typeOfPacket(String packet, String packetType) {
String sender;
if (stationMode==1 || stationMode==2 || (stationMode==5 && WiFi.status()==WL_CONNECTED)) {
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";
@@ -310,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);
}
}
}
+10 -4
View File
@@ -3,6 +3,13 @@
#include <Arduino.h>
class ReceivedPacket {
public:
long millis;
String packet;
int RSSI;
float SNR;
};
namespace Utils {
@@ -13,11 +20,10 @@ namespace Utils {
void checkBeaconInterval();
void checkDisplayInterval();
void checkWiFiInterval();
void validateDigiFreqs();
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);
}
+86 -26
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");
@@ -29,10 +34,6 @@ namespace WEB_Utils {
AsyncWebServer server(80);
void loop() {
}
void handleNotFound(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", "Not found");
response->addHeader("Cache-Control", "max-age=3600");
@@ -60,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");
@@ -71,42 +89,51 @@ namespace WEB_Utils {
WiFi_AP wifiap;
wifiap.ssid = request->getParam("wifi.AP." + String(i) + ".ssid", true)->value();
wifiap.password = request->getParam("wifi.AP." + String(i) + ".password", true)->value();
wifiap.latitude = request->getParam("wifi.AP." + String(i) + ".latitude", true)->value().toDouble();
wifiap.longitude = request->getParam("wifi.AP." + String(i) + ".longitude", true)->value().toDouble();
// wifiap.latitude = request->getParam("wifi.AP." + String(i) + ".latitude", true)->value().toDouble();
// wifiap.longitude = request->getParam("wifi.AP." + String(i) + ".longitude", true)->value().toDouble();
Config.wifiAPs.push_back(wifiap);
}
Config.callsign = request->getParam("callsign", true)->value();
Config.stationMode = request->getParam("stationMode", true)->value().toInt();
Config.iGateComment = request->getParam("iGateComment", true)->value();
// Config.iGateComment = request->getParam("iGateComment", true)->value();
Config.wifiAutoAP.password = request->getParam("wifi.autoAP.password", true)->value();
Config.wifiAutoAP.powerOff = request->getParam("wifi.autoAP.powerOff", true)->value().toInt();
Config.digi.comment = request->getParam("digi.comment", true)->value();
Config.digi.latitude = request->getParam("digi.latitude", true)->value().toDouble();
Config.digi.longitude = request->getParam("digi.longitude", true)->value().toDouble();
Config.digi.mode = request->getParam("digi.mode", true)->value().toInt();
// Config.digi.comment = request->getParam("digi.comment", true)->value();
// 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();
Config.aprs_is.port = request->getParam("aprs_is.port", true)->value().toInt();
Config.aprs_is.reportingDistance = request->getParam("aprs_is.reportingDistance", true)->value().toInt();
Config.loramodule.iGateFreq = request->getParam("lora.iGateFreq", true)->value().toInt();
if (request->hasParam("lora.digirepeaterTxFreq", true)) {
Config.loramodule.digirepeaterTxFreq = request->getParam("lora.digirepeaterTxFreq", true)->value().toInt();
}
if (request->hasParam("lora.digirepeaterRxFreq", true)) {
Config.loramodule.digirepeaterRxFreq = request->getParam("lora.digirepeaterRxFreq", true)->value().toInt();
}
// Config.aprs_is.reportingDistance = request->getParam("aprs_is.reportingDistance", true)->value().toInt();
Config.aprs_is.filter = request->getParam("aprs_is.filter", true)->value();
Config.aprs_is.toRF = request->hasParam("aprs_is.toRF", true);
// Config.loramodule.iGateFreq = request->getParam("lora.iGateFreq", true)->value().toInt();
// if (request->hasParam("lora.digirepeaterTxFreq", true)) {
// Config.loramodule.digirepeaterTxFreq = request->getParam("lora.digirepeaterTxFreq", true)->value().toInt();
// }
// if (request->hasParam("lora.digirepeaterRxFreq", true)) {
// Config.loramodule.digirepeaterRxFreq = request->getParam("lora.digirepeaterRxFreq", true)->value().toInt();
// }
Config.loramodule.txFreq = request->getParam("lora.txFreq", true)->value().toInt();
Config.loramodule.rxFreq = request->getParam("lora.rxFreq", true)->value().toInt();
Config.loramodule.spreadingFactor = request->getParam("lora.spreadingFactor", true)->value().toInt();
Config.loramodule.signalBandwidth = request->getParam("lora.signalBandwidth", true)->value().toInt();
Config.loramodule.codingRate4 = request->getParam("lora.codingRate4", true)->value().toInt();
Config.loramodule.power = request->getParam("lora.power", true)->value().toInt();
Config.loramodule.txActive = request->hasParam("lora.txActive", true);
Config.loramodule.rxActive = request->hasParam("lora.rxActive", true);
Config.display.alwaysOn = request->hasParam("display.alwaysOn", true);
@@ -128,9 +155,19 @@ namespace WEB_Utils {
Config.ota.username = request->getParam("ota.username", true)->value();
Config.ota.password = request->getParam("ota.password", true)->value();
Config.beaconInterval = request->getParam("other.beaconInterval", true)->value().toInt();
Config.igateSendsLoRaBeacons = request->hasParam("other.igateSendsLoRaBeacons", true);
Config.igateRepeatsLoRaPackets = request->hasParam("other.igateRepeatsLoRaPackets", true);
Config.beacon.interval = request->getParam("beacon.interval", true)->value().toInt();
Config.beacon.sendViaAPRSIS = request->hasParam("beacon.sendViaAPRSIS", true);
Config.beacon.sendViaRF = request->hasParam("beacon.sendViaRF", true);
Config.beacon.latitude = request->getParam("beacon.latitude", true)->value().toDouble();
Config.beacon.longitude = request->getParam("beacon.longitude", true)->value().toDouble();
Config.beacon.comment = request->getParam("beacon.comment", true)->value();
Config.beacon.overlay = request->getParam("beacon.overlay", true)->value();
Config.beacon.symbol = request->getParam("beacon.symbol", true)->value();
Config.beacon.path = request->getParam("beacon.path", true)->value();
// Config.beaconInterval = request->getParam("other.beaconInterval", true)->value().toInt();
// Config.igateSendsLoRaBeacons = request->hasParam("other.igateSendsLoRaBeacons", true);
// Config.igateRepeatsLoRaPackets = request->hasParam("other.igateRepeatsLoRaPackets", true);
Config.rememberStationTime = request->getParam("other.rememberStationTime", true)->value().toInt();
Config.sendBatteryVoltage = request->hasParam("other.sendBatteryVoltage", true);
Config.externalVoltageMeasurement = request->hasParam("other.externalVoltageMeasurement", true);
@@ -139,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", "");
@@ -148,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");
@@ -177,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);
-2
View File
@@ -9,8 +9,6 @@
namespace WEB_Utils {
void loop();
void handleNotFound(AsyncWebServerRequest *request);
void handleStatus(AsyncWebServerRequest *request);
+10 -41
View File
@@ -7,9 +7,8 @@
extern Configuration Config;
extern WiFi_AP *currentWiFi;
extern int myWiFiAPIndex;
extern uint8_t myWiFiAPIndex;
extern int myWiFiAPSize;
extern int stationMode;
extern uint32_t previousWiFiMillis;
extern bool WiFiConnected;
extern long WiFiAutoAPTime;
@@ -32,7 +31,7 @@ namespace WIFI_Utils {
WiFi.mode(WIFI_MODE_NULL);
WiFi.mode(WIFI_AP);
WiFi.softAP(Config.callsign + " AP", "1234567890");
WiFi.softAP(Config.callsign + " AP", Config.wifiAutoAP.password);
WiFiAutoAPTime = millis();
WiFiAutoAPStarted = true;
@@ -43,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);
@@ -53,21 +52,19 @@ 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;
if (stationMode==5) {
wifiCounter++;
}
wifiCounter++;
} else {
myWiFiAPIndex++;
}
@@ -81,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) {
@@ -123,36 +120,8 @@ namespace WIFI_Utils {
}
void setup() {
if (stationMode==1 || stationMode==2) {
if (stationMode==1) {
Serial.println("stationMode ---> iGate (only Rx)");
} else {
Serial.println("stationMode ---> iGate (Rx + Tx)");
}
startWiFi();
btStop();
} else if (stationMode==3 || stationMode==4) {
if (stationMode==3) {
Serial.println("stationMode ---> DigiRepeater (Rx freq == Tx freq)");
} else {
Serial.println("stationMode ---> DigiRepeater (Rx freq != Tx freq)");
}
startAutoAP();
btStop();
} else if (stationMode==5) {
Serial.println("stationMode ---> iGate when Wifi/APRS available (DigiRepeater when not)");
startWiFi();
btStop();
} else {
Serial.println("stationMode ---> NOT VALID");
show_display("------- ERROR -------", "stationMode Not Valid", "device will autofix", "and then reboot", 1000);
Config.stationMode = 1; // Inform about that but then change the station mode to 1 and reset the device
Config.writeFile();
ESP.restart();
}
startWiFi();
btStop();
}
}
+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))