Compare commits

..

65 Commits

Author SHA1 Message Date
ripplebiz 3192684582 Merge pull request #2752 from Quency-D/heltec-tower-v2
Add Heltec tower v2
2026-06-23 11:51:03 +10:00
ripplebiz aa1cb1f663 Merge pull request #2765 from fizzyfuzzle/ESP32-Reset-Reason
Add ESP32 Reset Reason to existing pwrmgt.bootreason cli command
2026-06-23 11:41:57 +10:00
ripplebiz 6a63dfce1c Merge pull request #2771 from oltaco/nrf52-framework-buildfix
Point Heltec Mesh Solar and Mesh Pocket at our NRF52 framework fork
2026-06-23 11:37:03 +10:00
Liam Cottle c2cfba4af3 Merge pull request #2810 from IoTThinks/MCDev-Fixed-GPS-Time-for-T114-202606
Fixed GPS time sync for Heltec T114
2026-06-22 14:19:09 +12:00
Kevin Le 62849ef114 Fixed GPS time sync for Heltec T114 2026-06-22 09:13:36 +07:00
ripplebiz 60ea4a91bf Merge pull request #1727 from weebl2000/use-hardware-channel-activity-detection
Use hardware channel activity detection for checking interference
2026-06-15 19:51:59 +10:00
taco dd9b3b32c3 build fix for Heltec Mesh Solar and Mesh Pocket 2026-06-15 00:57:12 +10:00
Wessel Nieboer 7c8e092457 Have CAD be a separate toggle (set cad on/off) 2026-06-14 15:20:58 +02:00
Wessel Nieboer 099ef67348 Prevent packet errors from growing 2026-06-14 15:19:20 +02:00
Wessel Nieboer 04a6c7009a Just check for not channel free 2026-06-14 15:19:19 +02:00
Wessel Nieboer 813d108c7a Also return busy if preamble detected 2026-06-14 15:19:19 +02:00
Wessel Nieboer 4f9a091671 Use hardware channel activity detection for checking interference 2026-06-14 15:19:18 +02:00
Marco e7db7c5a94 Add ESP32 Reset Reason 2026-06-14 11:41:01 +02:00
Huw Duddy 29f0a7fc4f Merge pull request #2763 from meshcore-dev/anon-contacts-fix
* fix for anon contacts when full
2026-06-14 19:11:38 +10:00
Scott Powell 2e73fe948e * fix for anon lastmod 2026-06-14 18:54:51 +10:00
Scott Powell 538ac38e18 * fix for counter in RESP_CODE_CONTACTS_START 2026-06-14 18:39:34 +10:00
Scott Powell c2d223ff55 * now handle the case where onAdvertRecv() should _replace_ the anon contact slot 2026-06-14 18:12:08 +10:00
Scott Powell 07648e3344 * fix for anon contacts when full 2026-06-14 17:50:41 +10:00
ripplebiz a5cb0c25ca Merge pull request #2140 from Quency-D/cli-lna-command
Add CLI control to LoRa's fem LNA.
2026-06-13 22:00:08 +10:00
Quency-D 5300fa18c7 Restore companion NodePrefs file ending 2026-06-13 17:40:11 +08:00
Quency-D 3ee58fd2e3 Remove companion FEM RX gain command IDs 2026-06-13 17:31:54 +08:00
Scott Powell d5f74e93c5 * PAYLOAD_TYPE_PATH bad path_len now rejected 2026-06-13 18:19:41 +10:00
Quency-D 1c4c995a41 Fix low power consumption issues 2026-06-13 16:00:06 +08:00
Quency-D d2d3d44c9b Add maximum power limit 2026-06-12 18:05:27 +08:00
Quency-D fe56d01da2 add tower v2 PA control 2026-06-12 17:58:19 +08:00
Quency-D 190dbb5293 Merge pull request #13 from meshcore-dev/dev
merge Dev
2026-06-11 15:30:31 +08:00
Quency-D 9100a58671 Merge branch 'dev' into cli-lna-command 2026-06-10 11:30:04 +08:00
ripplebiz 5f3b7f25d0 Merge pull request #2722 from liamcottle/fix/free-packet-on-parse-failure
Fix: CMD_SEND_RAW_PACKET not freeing packet on parse failure
2026-06-08 22:47:06 +10:00
liamcottle ae0bb7ee95 use releasePacket instead of _mgr->free 2026-06-09 00:42:58 +12:00
liamcottle 07bfe90695 free packet on parse failure 2026-06-09 00:31:48 +12:00
Scott Powell 07a3ca9e05 Merge branch 'dev'
# Conflicts:
#	docs/faq.md
2026-06-06 21:12:43 +10:00
Scott Powell 8c0d5c5b24 * version 1.16.0 2026-06-06 21:09:38 +10:00
ripplebiz 74adda6316 Merge pull request #2702 from meshcore-dev/flood.max.advert
new CLI config: flood.max.advert
2026-06-06 21:05:22 +10:00
Liam Cottle b6454d6251 Merge pull request #2703 from IoTThinks/Fixed-powersaving--in-cli_commands.md-2026-06
Change default power saving setting to 'off'
2026-06-06 16:09:27 +12:00
IoTThinks 6a9a84e8a5 Change default power saving setting to 'off' 2026-06-06 11:04:36 +07:00
Liam Cottle add18d6866 Merge pull request #2641 from sefinek/ci/update-github-actions
ci: update GitHub Actions and Python version
2026-06-03 01:26:35 +12:00
Sefinek c67548347c ci: pin peaceiris/actions-gh-pages to v4.1.0 for Node.js 24 support 2026-05-28 16:10:46 +02:00
Sefinek bbd37f53a8 ci: update GitHub Actions and Python version, fix ruby-version typo 2026-05-28 16:01:59 +02:00
Quency-D 250f448c3f Merge branch 'dev' into cli-lna-command 2026-05-20 10:13:28 +08:00
ripplebiz c940ea5f30 Merge pull request #2567 from recrof/patch-4
Change MeshCore intro video link to The Comms Channel's MC intro playlist
2026-05-16 13:03:00 +10:00
Rastislav Vysoky d4c99dec65 Change MeshCore intro video link to The Comms Channel's MC intro playlist 2026-05-15 16:42:29 +02:00
ripplebiz 181a54e718 Merge pull request #2532 from swaits/fix/trace-offset-widening
fix(mesh): widen TRACE offset to uint16 to avoid narrowing
2026-05-15 13:17:03 +10:00
Liam Cottle 910b1bee5b Merge pull request #2541 from AI7NC/patch-2
Update cli_commands.md to include 'ver'
2026-05-13 14:43:10 +12:00
AI7NC 16cb6d518f Update cli_commands.md to include 'ver'
Include the 'ver' command for retrieving the firmware version
2026-05-12 12:42:33 -07:00
Quency-D 3a546a6395 add heltec tower v2 2026-05-12 14:53:17 +08:00
Stephen Waits 09a27a2591 fix(mesh): widen TRACE offset to uint16 to avoid narrowing 2026-05-11 19:32:56 -06:00
Quency-D 12e6899580 Merge branch 'dev' into cli-lna-command 2026-05-09 18:19:31 +08:00
ripplebiz 1a7b3614a8 Merge pull request #2388 from OhYou-0/lilygo-teth-elite-board-support
Add LilyGo T-ETH Elite SX1262 board support
2026-04-28 14:34:11 +10:00
Quency-D 444dcfb8fd Change write to read for radio_fem_rxgain 2026-04-27 14:06:21 +08:00
Liam Cottle 03a13aed30 Merge pull request #2413 from keithtweed/patch-1
Update script link in FAQ 4.7 to the repo of the fork
2026-04-27 14:17:19 +12:00
ripplebiz fed8d36d03 Merge pull request #2412 from LitBomb/patch-25
Removed links to outdated resources and links
2026-04-27 12:14:23 +10:00
Keith Tweed b948369d71 Update script link in FAQ 4.7 2026-04-26 19:51:33 -06:00
uncle lit 34db93150a Removed links to outdated resources and links
Removed links to outdated resources and links
2026-04-26 18:35:02 -07:00
Liam Cottle 528bf3f61e add FUNDING.yml 2026-04-26 00:24:40 +12:00
Quency-D 62b0d82682 Restore WiFi operation command scope comments 2026-04-25 16:12:40 +08:00
Quency-D 9d26953398 Remove unnecessary blank line in MyMesh.cpp 2026-04-25 16:10:57 +08:00
Quency-D c42f6db0eb Revise according to the review comments.
Co-authored-by: Copilot <copilot@github.com>
2026-04-25 16:01:26 +08:00
Quency-D 22f07b3e48 Revert "Merge branch 'meshcore-dev:main' into cli-lna-command"
This reverts commit ca047fe0c0, reversing
changes made to ddedb3c7a7.
2026-04-25 15:33:59 +08:00
Quency-D ca047fe0c0 Merge branch 'meshcore-dev:main' into cli-lna-command 2026-04-25 15:29:00 +08:00
Quency-D ddedb3c7a7 Merge branch 'dev' into cli-lna-command 2026-04-25 15:28:07 +08:00
OhYou-0 68360157ec Add LilyGo T-ETH Elite board support 2026-04-24 10:16:19 -07:00
Quency-D 9664305a87 Adapt LNA CLI control commands for heltec_t096. 2026-03-24 16:13:13 +08:00
Quency-D 2442e9a5bd Adapt LNA CLI control commands for heltec_tracker_v2. 2026-03-24 16:05:28 +08:00
Quency-D 65752fef72 Fix the memory leak issue in the strdup function. 2026-03-24 13:57:11 +08:00
Quency-D 8435464c84 Add v4 FEM LNA CLI control commands. 2026-03-24 10:48:15 +08:00
60 changed files with 1065 additions and 106 deletions
+1
View File
@@ -0,0 +1 @@
github: meshcore-dev
@@ -4,7 +4,7 @@ runs:
steps: steps:
- name: Init Cache - name: Init Cache
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: | path: |
~/.cache/pip ~/.cache/pip
@@ -12,9 +12,9 @@ runs:
key: ${{ runner.os }}-pio key: ${{ runner.os }}-pio
- name: Install Python - name: Install Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.11' python-version: '3.13'
- name: Install PlatformIO - name: Install PlatformIO
shell: bash shell: bash
@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Clone Repo - name: Clone Repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Build Environment - name: Setup Build Environment
uses: ./.github/actions/setup-build-environment uses: ./.github/actions/setup-build-environment
@@ -27,13 +27,13 @@ jobs:
run: /usr/bin/env bash build.sh build-companion-firmwares run: /usr/bin/env bash build.sh build-companion-firmwares
- name: Upload Workflow Artifacts - name: Upload Workflow Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: companion-firmwares name: companion-firmwares
path: out path: out
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
name: Companion Firmware ${{ env.GIT_TAG_VERSION }} name: Companion Firmware ${{ env.GIT_TAG_VERSION }}
@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Clone Repo - name: Clone Repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Build Environment - name: Setup Build Environment
uses: ./.github/actions/setup-build-environment uses: ./.github/actions/setup-build-environment
@@ -27,13 +27,13 @@ jobs:
run: /usr/bin/env bash build.sh build-repeater-firmwares run: /usr/bin/env bash build.sh build-repeater-firmwares
- name: Upload Workflow Artifacts - name: Upload Workflow Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: repeater-firmwares name: repeater-firmwares
path: out path: out
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
name: Repeater Firmware ${{ env.GIT_TAG_VERSION }} name: Repeater Firmware ${{ env.GIT_TAG_VERSION }}
@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Clone Repo - name: Clone Repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Build Environment - name: Setup Build Environment
uses: ./.github/actions/setup-build-environment uses: ./.github/actions/setup-build-environment
@@ -27,13 +27,13 @@ jobs:
run: /usr/bin/env bash build.sh build-room-server-firmwares run: /usr/bin/env bash build.sh build-room-server-firmwares
- name: Upload Workflow Artifacts - name: Upload Workflow Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: room-server-firmwares name: room-server-firmwares
path: out path: out
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
name: Room Server Firmware ${{ env.GIT_TAG_VERSION }} name: Room Server Firmware ${{ env.GIT_TAG_VERSION }}
+4 -4
View File
@@ -15,12 +15,12 @@ jobs:
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
ruby-version: 3.x python-version: '3.13'
- name: Build - name: Build
run: | run: |
@@ -28,7 +28,7 @@ jobs:
mkdocs build mkdocs build
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v4.1.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
cname: docs.meshcore.io cname: docs.meshcore.io
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
steps: steps:
- name: Clone Repo - name: Clone Repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Build Environment - name: Setup Build Environment
uses: ./.github/actions/setup-build-environment uses: ./.github/actions/setup-build-environment
+1 -2
View File
@@ -1,7 +1,6 @@
{ {
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [ "recommendations": [
"pioarduino.pioarduino-ide",
"platformio.platformio-ide" "platformio.platformio-ide"
], ],
"unwantedRecommendations": [ "unwantedRecommendations": [
+1 -1
View File
@@ -28,7 +28,7 @@ MeshCore provides the ability to create wireless mesh networks, similar to Mesht
## 🚀 How to Get Started ## 🚀 How to Get Started
- Watch the [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc) by Andy Kirby. - Watch the [MeshCore QuickStart Playlist](https://www.youtube.com/watch?v=iaFltojJrAc&list=PLshzThxhw4O4WU_iZo3NmNZOv6KMrUuF9) by The Comms Channel
- Watch the [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4) by Liam Cottle. - Watch the [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4) by Liam Cottle.
- Read through our [Frequently Asked Questions](./docs/faq.md) and [Documentation](https://docs.meshcore.io). - Read through our [Frequently Asked Questions](./docs/faq.md) and [Documentation](https://docs.meshcore.io).
- Flash the MeshCore firmware on a supported device. - Flash the MeshCore firmware on a supported device.
+61
View File
@@ -0,0 +1,61 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A","0x4405"],
["0x239A","0x0029"],
["0x239A","0x002A"],
["0x239A","0x0071"]
],
"usb_product": "HT-n5262",
"mcu": "nrf52840",
"variant": "heltec_tower_v2",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52.cfg"
},
"frameworks": [
"arduino"
],
"name": "Heltec Tower V2 Board",
"upload": {
"maximum_ram_size": 235520,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://heltec.org/",
"vendor": "Heltec"
}
+34 -1
View File
@@ -263,6 +263,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore
--- ---
#### View or change the LoRa FEM receive-path gain state on supported boards
**Usage:**
- `get radio.fem.rxgain`
- `set radio.fem.rxgain <state>`
**Parameters:**
- `state`: `on`|`off`
**Notes:**
- This controls the external LoRa FEM receive-path LNA where the board supports it.
- This is separate from `radio.rxgain`, which controls the radio chip receive gain mode.
---
### System ### System
#### View or change this node's name #### View or change this node's name
@@ -391,6 +405,11 @@ This document provides an overview of CLI commands that can be sent to MeshCore
--- ---
#### View this node's firmware version
**Usage:** `ver`
---
#### View this node's configured role #### View this node's configured role
**Usage:** `get role` **Usage:** `get role`
@@ -406,7 +425,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
- `on`: enable power saving - `on`: enable power saving
- `off`: disable power saving - `off`: disable power saving
**Default:** `on` **Default:** `off`
**Note:** When enabled, device enters sleep mode between radio transmissions **Note:** When enabled, device enters sleep mode between radio transmissions
@@ -559,6 +578,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore
--- ---
#### Enable or disable hardware Channel Activity Detection (CAD)
**Usage:**
- `get cad`
- `set cad <on|off>`
**Description:** When enabled, the radio performs a hardware Channel Activity Detection scan before transmitting and defers if the channel is busy. Runs independently of `int.thresh` — either, both, or none may be active.
**Parameters:**
- `on|off`: Enable or disable hardware CAD
**Default:** `off`
---
#### View or change the AGC Reset Interval #### View or change the AGC Reset Interval
**Usage:** **Usage:**
- `get agc.reset.interval` - `get agc.reset.interval`
+1 -9
View File
@@ -111,7 +111,6 @@ Anyone is able to build anything they like on top of MeshCore without paying any
- MeshCore Firmware on GitHub: [https://github.com/meshcore-dev/MeshCore](https://github.com/meshcore-dev/MeshCore) - MeshCore Firmware on GitHub: [https://github.com/meshcore-dev/MeshCore](https://github.com/meshcore-dev/MeshCore)
- MeshCore Companion Web App: [https://app.meshcore.nz](https://app.meshcore.nz) - MeshCore Companion Web App: [https://app.meshcore.nz](https://app.meshcore.nz)
- MeshCore Map: [https://map.meshcore.io](https://map.meshcore.io) - MeshCore Map: [https://map.meshcore.io](https://map.meshcore.io)
- Andy Kirby's [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc)
- Liam Cottle's [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4) - Liam Cottle's [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4)
You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server). You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server).
@@ -392,10 +391,7 @@ Another way to download map tiles is to use this Python script to get the tiles
<https://github.com/fistulareffigy/MTD-Script> <https://github.com/fistulareffigy/MTD-Script>
There is also a modified script that adds additional error handling and parallel downloads: There is also a modified script that adds additional error handling and parallel downloads:
<https://discord.com/channels/826570251612323860/1330643963501351004/1338775811548905572> <https://github.com/TheBestJohn/MTD-Script>
UK map tiles are available separately from Andy Kirby on his discord server:
<https://discord.com/channels/826570251612323860/1330643963501351004/1331346597367386224>
### 4.8. Q: Where do the map tiles go? ### 4.8. Q: Where do the map tiles go?
Once you have the tiles downloaded, copy the `\tiles` folder to the root of your T-Deck's SD card. Once you have the tiles downloaded, copy the `\tiles` folder to the root of your T-Deck's SD card.
@@ -553,10 +549,6 @@ pio run -e RAK_4631_Repeater
``` ```
then you'll find `firmware.zip` in `.pio/build/RAK_4631_Repeater` then you'll find `firmware.zip` in `.pio/build/RAK_4631_Repeater`
Andy also has a video on how to build using VS Code:
*How to build and flash Meshcore repeater firmware | Heltec V3*
<https://www.youtube.com/watch?v=WJvg6dt13hk> *(Link referenced in the Discord post)*
### 5.10. Q: Are there other MeshCore related open source projects? ### 5.10. Q: Are there other MeshCore related open source projects?
**A:** [Liam Cottle](https://liamcottle.net)'s MeshCore web client and MeshCore JavaScript library are open source under MIT license. **A:** [Liam Cottle](https://liamcottle.net)'s MeshCore web client and MeshCore JavaScript library are open source under MIT license.
+7 -10
View File
@@ -261,6 +261,9 @@ float MyMesh::getAirtimeBudgetFactor() const {
int MyMesh::getInterferenceThreshold() const { int MyMesh::getInterferenceThreshold() const {
return 0; // disabled for now, until currentRSSI() problem is resolved return 0; // disabled for now, until currentRSSI() problem is resolved
} }
bool MyMesh::getCADEnabled() const {
return true; // hardware CAD before TX (no CLI toggle on companion; enabled by default)
}
int MyMesh::calcRxDelay(float score, uint32_t air_time) const { int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
if (_prefs.rx_delay_base <= 0.0f) return 0; if (_prefs.rx_delay_base <= 0.0f) return 0;
@@ -854,7 +857,7 @@ void MyMesh::onSendTimeout() {}
MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui) MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui)
: BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables),
_serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui) { _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui), _iter(0) {
_iter_started = false; _iter_started = false;
_cli_rescue = false; _cli_rescue = false;
offline_queue_len = 0; offline_queue_len = 0;
@@ -1542,6 +1545,7 @@ void MyMesh::handleCmdFrame(size_t len) {
memcpy(anon.id.pub_key, pub_key, PUB_KEY_SIZE); memcpy(anon.id.pub_key, pub_key, PUB_KEY_SIZE);
anon.out_path_len = 0; // default to zero-hop direct anon.out_path_len = 0; // default to zero-hop direct
anon.type = ADV_TYPE_NONE; // unknown anon.type = ADV_TYPE_NONE; // unknown
anon.lastmod = getRTCClock()->getCurrentTime();
if (addContact(anon)) recipient = &anon; if (addContact(anon)) recipient = &anon;
} }
@@ -1981,6 +1985,7 @@ void MyMesh::handleCmdFrame(size_t len) {
sendPacket(pkt, priority, 0); sendPacket(pkt, priority, 0);
writeOKFrame(); writeOKFrame();
} else { } else {
releasePacket(pkt);
writeErrFrame(ERR_CODE_ILLEGAL_ARG); writeErrFrame(ERR_CODE_ILLEGAL_ARG);
} }
} else { } else {
@@ -2186,15 +2191,7 @@ void MyMesh::checkSerialInterface() {
&& !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly!
) { ) {
ContactInfo contact; ContactInfo contact;
bool found = false; if (_iter.hasNext(this, contact)) {
while (_iter.hasNext(this, contact)) {
if (contact.type != ADV_TYPE_NONE) {
found = true;
break;
}
}
if (found) {
if (contact.lastmod > _iter_filter_since) { // apply the 'since' filter if (contact.lastmod > _iter_filter_since) { // apply the 'since' filter
writeContactRespFrame(RESP_CODE_CONTACT, contact); writeContactRespFrame(RESP_CODE_CONTACT, contact);
if (contact.lastmod > _most_recent_lastmod) { if (contact.lastmod > _most_recent_lastmod) {
+3 -2
View File
@@ -8,11 +8,11 @@
#define FIRMWARE_VER_CODE 13 #define FIRMWARE_VER_CODE 13
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Apr 2026" #define FIRMWARE_BUILD_DATE "6 Jun 2026"
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.15.0" #define FIRMWARE_VERSION "v1.16.0"
#endif #endif
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
@@ -105,6 +105,7 @@ public:
protected: protected:
float getAirtimeBudgetFactor() const override; float getAirtimeBudgetFactor() const override;
int getInterferenceThreshold() const override; int getInterferenceThreshold() const override;
bool getCADEnabled() const override;
int calcRxDelay(float score, uint32_t air_time) const override; int calcRxDelay(float score, uint32_t air_time) const override;
uint32_t getRetransmitDelay(const mesh::Packet *packet) override; uint32_t getRetransmitDelay(const mesh::Packet *packet) override;
uint32_t getDirectRetransmitDelay(const mesh::Packet *packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet *packet) override;
+3
View File
@@ -893,6 +893,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.flood_max_unscoped = 64; _prefs.flood_max_unscoped = 64;
_prefs.flood_max_advert = 8; _prefs.flood_max_advert = 8;
_prefs.interference_threshold = 0; // disabled _prefs.interference_threshold = 0; // disabled
_prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on')
// bridge defaults // bridge defaults
_prefs.bridge_enabled = 1; // enabled _prefs.bridge_enabled = 1; // enabled
@@ -917,6 +918,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.rx_boosted_gain = 1; // enabled by default; _prefs.rx_boosted_gain = 1; // enabled by default;
#endif #endif
#endif #endif
_prefs.radio_fem_rxgain = 1;
pending_discover_tag = 0; pending_discover_tag = 0;
pending_discover_until = 0; pending_discover_until = 0;
@@ -965,6 +967,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
updateAdvertTimer(); updateAdvertTimer();
updateFloodAdvertTimer(); updateFloodAdvertTimer();
+5 -2
View File
@@ -69,11 +69,11 @@ struct NeighbourInfo {
}; };
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Apr 2026" #define FIRMWARE_BUILD_DATE "6 Jun 2026"
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.15.0" #define FIRMWARE_VERSION "v1.16.0"
#endif #endif
#define FIRMWARE_ROLE "repeater" #define FIRMWARE_ROLE "repeater"
@@ -150,6 +150,9 @@ protected:
int getInterferenceThreshold() const override { int getInterferenceThreshold() const override {
return _prefs.interference_threshold; return _prefs.interference_threshold;
} }
bool getCADEnabled() const override {
return _prefs.cad_enabled;
}
int getAGCResetInterval() const override { int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
} }
+2 -1
View File
@@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi
} }
// v1.2.3 (1 Jan 2025) // v1.2.3 (1 Jan 2025)
sprintf(_version_info, "%s (%s)", version, build_date); snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date);
free(version);
} }
void UITask::renderCurrScreen() { void UITask::renderCurrScreen() {
+3
View File
@@ -650,6 +650,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.flood_max_unscoped = 64; _prefs.flood_max_unscoped = 64;
_prefs.flood_max_advert = 8; _prefs.flood_max_advert = 8;
_prefs.interference_threshold = 0; // disabled _prefs.interference_threshold = 0; // disabled
_prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on')
#ifdef ROOM_PASSWORD #ifdef ROOM_PASSWORD
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
#endif #endif
@@ -658,6 +659,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.gps_enabled = 0; _prefs.gps_enabled = 0;
_prefs.gps_interval = 0; _prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS; _prefs.advert_loc_policy = ADVERT_LOC_PREFS;
_prefs.radio_fem_rxgain = 1;
next_post_idx = 0; next_post_idx = 0;
next_client_idx = 0; next_client_idx = 0;
@@ -699,6 +701,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setTxPower(_prefs.tx_power_dbm);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
updateAdvertTimer(); updateAdvertTimer();
updateFloodAdvertTimer(); updateFloodAdvertTimer();
+5 -2
View File
@@ -27,11 +27,11 @@
/* ------------------------------ Config -------------------------------- */ /* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Apr 2026" #define FIRMWARE_BUILD_DATE "6 Jun 2026"
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.15.0" #define FIRMWARE_VERSION "v1.16.0"
#endif #endif
#ifndef LORA_FREQ #ifndef LORA_FREQ
@@ -144,6 +144,9 @@ protected:
int getInterferenceThreshold() const override { int getInterferenceThreshold() const override {
return _prefs.interference_threshold; return _prefs.interference_threshold;
} }
bool getCADEnabled() const override {
return _prefs.cad_enabled;
}
int getAGCResetInterval() const override { int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
} }
+2 -1
View File
@@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi
} }
// v1.2.3 (1 Jan 2025) // v1.2.3 (1 Jan 2025)
sprintf(_version_info, "%s (%s)", version, build_date); snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date);
free(version);
} }
void UITask::renderCurrScreen() { void UITask::renderCurrScreen() {
+1 -1
View File
@@ -135,7 +135,7 @@ class MyMesh : public BaseChatMesh, ContactVisitor {
File file = _fs->open("/contacts", "w", true); File file = _fs->open("/contacts", "w", true);
#endif #endif
if (file) { if (file) {
ContactsIterator iter; ContactsIterator iter = startContactsIterator();
ContactInfo c; ContactInfo c;
uint8_t unused = 0; uint8_t unused = 0;
uint32_t reserved = 0; uint32_t reserved = 0;
+6
View File
@@ -323,6 +323,9 @@ uint32_t SensorMesh::getDirectRetransmitDelay(const mesh::Packet* packet) {
int SensorMesh::getInterferenceThreshold() const { int SensorMesh::getInterferenceThreshold() const {
return _prefs.interference_threshold; return _prefs.interference_threshold;
} }
bool SensorMesh::getCADEnabled() const {
return _prefs.cad_enabled;
}
int SensorMesh::getAGCResetInterval() const { int SensorMesh::getAGCResetInterval() const {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
} }
@@ -726,11 +729,13 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
_prefs.disable_fwd = true; _prefs.disable_fwd = true;
_prefs.flood_max = 64; _prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled _prefs.interference_threshold = 0; // disabled
_prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on')
// GPS defaults // GPS defaults
_prefs.gps_enabled = 0; _prefs.gps_enabled = 0;
_prefs.gps_interval = 0; _prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS; _prefs.advert_loc_policy = ADVERT_LOC_PREFS;
_prefs.radio_fem_rxgain = 1;
memset(default_scope.key, 0, sizeof(default_scope.key)); memset(default_scope.key, 0, sizeof(default_scope.key));
} }
@@ -766,6 +771,7 @@ void SensorMesh::begin(FILESYSTEM* fs) {
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setTxPower(_prefs.tx_power_dbm);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
updateAdvertTimer(); updateAdvertTimer();
updateFloodAdvertTimer(); updateFloodAdvertTimer();
+3 -2
View File
@@ -34,11 +34,11 @@
#define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts #define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Apr 2026" #define FIRMWARE_BUILD_DATE "6 Jun 2026"
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.15.0" #define FIRMWARE_VERSION "v1.16.0"
#endif #endif
#define FIRMWARE_ROLE "sensor" #define FIRMWARE_ROLE "sensor"
@@ -120,6 +120,7 @@ protected:
uint32_t getRetransmitDelay(const mesh::Packet* packet) override; uint32_t getRetransmitDelay(const mesh::Packet* packet) override;
uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override;
int getInterferenceThreshold() const override; int getInterferenceThreshold() const override;
bool getCADEnabled() const override;
int getAGCResetInterval() const override; int getAGCResetInterval() const override;
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
int searchPeersByHash(const uint8_t* hash) override; int searchPeersByHash(const uint8_t* hash) override;
+2 -1
View File
@@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi
} }
// v1.2.3 (1 Jan 2025) // v1.2.3 (1 Jan 2025)
sprintf(_version_info, "%s (%s)", version, build_date); snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date);
free(version);
} }
void UITask::renderCurrScreen() { void UITask::renderCurrScreen() {
+1
View File
@@ -66,6 +66,7 @@ uint32_t Dispatcher::getCADFailMaxDuration() const {
void Dispatcher::loop() { void Dispatcher::loop() {
if (millisHasNowPassed(next_floor_calib_time)) { if (millisHasNowPassed(next_floor_calib_time)) {
_radio->triggerNoiseFloorCalibrate(getInterferenceThreshold()); _radio->triggerNoiseFloorCalibrate(getInterferenceThreshold());
_radio->setCADEnabled(getCADEnabled());
next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL); next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL);
} }
_radio->loop(); _radio->loop();
+3
View File
@@ -65,6 +65,8 @@ public:
virtual void triggerNoiseFloorCalibrate(int threshold) { } virtual void triggerNoiseFloorCalibrate(int threshold) { }
virtual void setCADEnabled(bool enable) { }
virtual void resetAGC() { } virtual void resetAGC() { }
virtual bool isInRecvMode() const = 0; virtual bool isInRecvMode() const = 0;
@@ -166,6 +168,7 @@ protected:
virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailRetryDelay() const;
virtual uint32_t getCADFailMaxDuration() const; virtual uint32_t getCADFailMaxDuration() const;
virtual int getInterferenceThreshold() const { return 0; } // disabled by default virtual int getInterferenceThreshold() const { return 0; } // disabled by default
virtual bool getCADEnabled() const { return false; } // hardware CAD disabled by default
virtual int getAGCResetInterval() const { return 0; } // disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default
virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } virtual unsigned long getDutyCycleWindowMs() const { return 3600000; }
+7 -1
View File
@@ -50,7 +50,9 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
uint8_t path_sz = flags & 0x03; // NEW v1.11+: lower 2 bits is path hash size uint8_t path_sz = flags & 0x03; // NEW v1.11+: lower 2 bits is path hash size
uint8_t len = pkt->payload_len - i; uint8_t len = pkt->payload_len - i;
uint8_t offset = pkt->path_len << path_sz; // path_len*entry_size can exceed 255 (path_len up to 63, entry_size up to 8);
// a uint8_t offset would wrap and steer the isHashMatch() read to the wrong place.
uint16_t offset = (uint16_t)pkt->path_len << path_sz;
if (offset >= len) { // TRACE has reached end of given path if (offset >= len) { // TRACE has reached end of given path
onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len); onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len);
} else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) { } else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) {
@@ -153,6 +155,10 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH) { if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH) {
int k = 0; int k = 0;
uint8_t path_len = data[k++]; uint8_t path_len = data[k++];
if (!Packet::isValidPathLen(path_len)) {
MESH_DEBUG_PRINTLN("%s PAYLOAD_TYPE_PATH, bad path_len: %u", getLogDateTime(), (uint32_t)path_len);
break; // reject bad encoding
}
uint8_t hash_size = (path_len >> 6) + 1; uint8_t hash_size = (path_len >> 6) + 1;
uint8_t hash_count = path_len & 63; uint8_t hash_count = path_len & 63;
uint8_t* path = &data[k]; k += hash_size*hash_count; uint8_t* path = &data[k]; k += hash_size*hash_count;
+3
View File
@@ -64,6 +64,9 @@ public:
virtual uint8_t getStartupReason() const = 0; virtual uint8_t getStartupReason() const = 0;
virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; } virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; }
virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported
virtual bool setLoRaFemLnaEnabled(bool enable) { return false; }
virtual bool canControlLoRaFemLna() const { return false; }
virtual bool isLoRaFemLnaEnabled() const { return false; }
// Power management interface (boards with power management override these) // Power management interface (boards with power management override these)
virtual bool isExternalPowered() { return false; } virtual bool isExternalPowered() { return false; }
+32 -20
View File
@@ -68,29 +68,36 @@ void BaseChatMesh::bootstrapRTCfromContacts() {
} }
ContactInfo* BaseChatMesh::allocateContactSlot(bool transient_only) { ContactInfo* BaseChatMesh::allocateContactSlot(bool transient_only) {
if (num_contacts < MAX_CONTACTS) { int oldest_idx = -1;
return &contacts[num_contacts++]; uint32_t oldest_lastmod = 0xFFFFFFFF;
} else if (transient_only || shouldOverwriteWhenFull()) { if (transient_only) {
// Find oldest non-favourite contact by oldest lastmod timestamp // only allocate from first N
int oldest_idx = -1; for (int i = 0; i < MAX_ANON_CONTACTS; i++) {
uint32_t oldest_lastmod = 0xFFFFFFFF; if (contacts[i].type == ADV_TYPE_NONE && contacts[i].lastmod < oldest_lastmod) {
for (int i = 0; i < num_contacts; i++) { oldest_lastmod = contacts[i].lastmod;
if (transient_only) { oldest_idx = i;
if (contacts[i].type == ADV_TYPE_NONE && contacts[i].lastmod < oldest_lastmod) { }
oldest_lastmod = contacts[i].lastmod; }
oldest_idx = i; if (oldest_idx >= 0) {
} // NOTE: do NOT call onContactOverwrite()
} else { return &contacts[oldest_idx];
}
} else {
if (num_contacts < MAX_ANON_CONTACTS+MAX_CONTACTS) {
return &contacts[num_contacts++];
} else if (shouldOverwriteWhenFull()) {
// Find oldest non-favourite contact by oldest lastmod timestamp
for (int i = MAX_ANON_CONTACTS; i < num_contacts; i++) {
bool is_favourite = (contacts[i].flags & 0x01) != 0; bool is_favourite = (contacts[i].flags & 0x01) != 0;
if (!is_favourite && contacts[i].lastmod < oldest_lastmod && contacts[i].type != ADV_TYPE_NONE) { if (!is_favourite && contacts[i].lastmod < oldest_lastmod) {
oldest_lastmod = contacts[i].lastmod; oldest_lastmod = contacts[i].lastmod;
oldest_idx = i; oldest_idx = i;
} }
} }
} if (oldest_idx >= 0) {
if (oldest_idx >= 0) { onContactOverwrite(contacts[oldest_idx].id.pub_key);
onContactOverwrite(contacts[oldest_idx].id.pub_key); return &contacts[oldest_idx];
return &contacts[oldest_idx]; }
} }
} }
return NULL; // no space, no overwrite or all contacts are all favourites return NULL; // no space, no overwrite or all contacts are all favourites
@@ -139,6 +146,11 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
packet->header = save; packet->header = save;
} }
if (from && from->type == ADV_TYPE_NONE) { // already in contacts, but from a temporary ANON_REQ ?
memset(from, 0, sizeof(*from)); // clear the anon/temp slot
from = NULL; // do normal 'add' flow
}
bool is_new = false; // true = not in contacts[], false = exists in contacts[] bool is_new = false; // true = not in contacts[], false = exists in contacts[]
if (from == NULL) { if (from == NULL) {
if (!shouldAutoAddContactType(parser.getType())) { if (!shouldAutoAddContactType(parser.getType())) {
@@ -930,11 +942,11 @@ bool BaseChatMesh::getContactByIdx(uint32_t idx, ContactInfo& contact) {
} }
ContactsIterator BaseChatMesh::startContactsIterator() { ContactsIterator BaseChatMesh::startContactsIterator() {
return ContactsIterator(); return ContactsIterator(MAX_ANON_CONTACTS); // start at offset, skip the anon entries
} }
bool ContactsIterator::hasNext(const BaseChatMesh* mesh, ContactInfo& dest) { bool ContactsIterator::hasNext(const BaseChatMesh* mesh, ContactInfo& dest) {
if (next_idx >= mesh->getNumContacts()) return false; if (next_idx >= mesh->getTotalContactSlots()) return false;
dest = mesh->contacts[next_idx++]; dest = mesh->contacts[next_idx++];
return true; return true;
+12 -5
View File
@@ -28,8 +28,9 @@ public:
class BaseChatMesh; class BaseChatMesh;
class ContactsIterator { class ContactsIterator {
int next_idx = 0; int next_idx;
public: public:
ContactsIterator(int start) { next_idx = start; }
bool hasNext(const BaseChatMesh* mesh, ContactInfo& dest); bool hasNext(const BaseChatMesh* mesh, ContactInfo& dest);
}; };
@@ -79,8 +80,9 @@ class BaseChatMesh : public mesh::Mesh {
protected: protected:
BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables) BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables)
: mesh::Mesh(radio, ms, rng, rtc, mgr, tables) : mesh::Mesh(radio, ms, rng, rtc, mgr, tables)
{ {
num_contacts = 0; resetContacts();
#ifdef MAX_GROUP_CHANNELS #ifdef MAX_GROUP_CHANNELS
memset(channels, 0, sizeof(channels)); memset(channels, 0, sizeof(channels));
num_channels = 0; num_channels = 0;
@@ -91,7 +93,11 @@ protected:
} }
void bootstrapRTCfromContacts(); void bootstrapRTCfromContacts();
void resetContacts() { num_contacts = 0; }
void resetContacts() {
memset(contacts, 0, sizeof(contacts[0])*MAX_ANON_CONTACTS); // set all to have type = ADV_TYPE_NONE(0)
num_contacts = MAX_ANON_CONTACTS; // seed the first contacts for anon requests
}
void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp); void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp);
ContactInfo* allocateContactSlot(bool transient_only=false); // helper to find slot for new contact ContactInfo* allocateContactSlot(bool transient_only=false); // helper to find slot for new contact
@@ -166,7 +172,8 @@ public:
ContactInfo* lookupContactByPubKey(const uint8_t* pub_key, int prefix_len); ContactInfo* lookupContactByPubKey(const uint8_t* pub_key, int prefix_len);
bool removeContact(ContactInfo& contact); bool removeContact(ContactInfo& contact);
bool addContact(const ContactInfo& contact); bool addContact(const ContactInfo& contact);
int getNumContacts() const { return num_contacts; } int getTotalContactSlots() const { return num_contacts; }
int getNumContacts() const { return num_contacts - MAX_ANON_CONTACTS; } // don't include the reserved slots at start
bool getContactByIdx(uint32_t idx, ContactInfo& contact); bool getContactByIdx(uint32_t idx, ContactInfo& contact);
ContactsIterator startContactsIterator(); ContactsIterator startContactsIterator();
ChannelDetails* addChannel(const char* name, const char* psk_base64); ChannelDetails* addChannel(const char* name, const char* psk_base64);
+51 -15
View File
@@ -88,10 +88,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
file.read((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.read((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291
file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292
// next: 293 file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293
file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294
// next: 295
// sanitise bad pref values // sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -121,6 +123,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
// sanitise settings // sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
_prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean
_prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean
file.close(); file.close();
} }
@@ -181,10 +185,12 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291
file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292
// next: 293 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293
file.write((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294
// next: 295
file.close(); file.close();
} }
@@ -500,6 +506,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->interference_threshold = atoi(&config[11]); _prefs->interference_threshold = atoi(&config[11]);
savePrefs(); savePrefs();
strcpy(reply, "OK"); strcpy(reply, "OK");
} else if (memcmp(config, "cad ", 4) == 0) {
_prefs->cad_enabled = memcmp(&config[4], "on", 2) == 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "agc.reset.interval ", 19) == 0) { } else if (memcmp(config, "agc.reset.interval ", 19) == 0) {
_prefs->agc_reset_interval = atoi(&config[19]) / 4; _prefs->agc_reset_interval = atoi(&config[19]) / 4;
savePrefs(); savePrefs();
@@ -568,6 +578,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs(); savePrefs();
_callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain);
#endif #endif
} else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) {
if (!_board->canControlLoRaFemLna()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&config[17], "on", 2) == 0) {
if (_board->setLoRaFemLnaEnabled(true)) {
_prefs->radio_fem_rxgain = 1;
savePrefs();
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&config[17], "off", 3) == 0) {
if (_board->setLoRaFemLnaEnabled(false)) {
_prefs->radio_fem_rxgain = 0;
savePrefs();
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
} else if (memcmp(config, "radio ", 6) == 0) { } else if (memcmp(config, "radio ", 6) == 0) {
strcpy(tmp, &config[6]); strcpy(tmp, &config[6]);
const char *parts[4]; const char *parts[4];
@@ -757,7 +789,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} }
} else { } else {
_prefs->adc_multiplier = 0.0f; _prefs->adc_multiplier = 0.0f;
strcpy(reply, "Error: unsupported by this board"); strcpy(reply, "Error: unsupported");
}; };
} else { } else {
strcpy(reply, "unknown config: "); strcpy(reply, "unknown config: ");
@@ -776,6 +808,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor)); sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor));
} else if (memcmp(config, "int.thresh", 10) == 0) { } else if (memcmp(config, "int.thresh", 10) == 0) {
sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold);
} else if (memcmp(config, "cad", 3) == 0) {
sprintf(reply, "> %s", _prefs->cad_enabled ? "on" : "off");
} else if (memcmp(config, "agc.reset.interval", 18) == 0) { } else if (memcmp(config, "agc.reset.interval", 18) == 0) {
sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4);
} else if (memcmp(config, "multi.acks", 10) == 0) { } else if (memcmp(config, "multi.acks", 10) == 0) {
@@ -805,6 +839,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
} else if (memcmp(config, "radio.rxgain", 12) == 0) { } else if (memcmp(config, "radio.rxgain", 12) == 0) {
sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off"); sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off");
#endif #endif
} else if (memcmp(config, "radio.fem.rxgain", 16) == 0) {
if (!_board->canControlLoRaFemLna()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off");
}
} else if (memcmp(config, "radio", 5) == 0) { } else if (memcmp(config, "radio", 5) == 0) {
char freq[16], bw[16]; char freq[16], bw[16];
strcpy(freq, StrHelper::ftoa(_prefs->freq)); strcpy(freq, StrHelper::ftoa(_prefs->freq));
@@ -890,12 +930,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
strcpy(reply, "> unknown"); strcpy(reply, "> unknown");
} }
#else #else
strcpy(reply, "ERROR: unsupported"); strcpy(reply, "Error: unsupported");
#endif #endif
} else if (memcmp(config, "adc.multiplier", 14) == 0) { } else if (memcmp(config, "adc.multiplier", 14) == 0) {
float adc_mult = _board->getAdcMultiplier(); float adc_mult = _board->getAdcMultiplier();
if (adc_mult == 0.0f) { if (adc_mult == 0.0f) {
strcpy(reply, "Error: unsupported by this board"); strcpy(reply, "Error: unsupported");
} else { } else {
sprintf(reply, "> %.3f", adc_mult); sprintf(reply, "> %.3f", adc_mult);
} }
@@ -913,13 +953,9 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
strcpy(reply, "ERROR: Power management not supported"); strcpy(reply, "ERROR: Power management not supported");
#endif #endif
} else if (memcmp(config, "pwrmgt.bootreason", 17) == 0) { } else if (memcmp(config, "pwrmgt.bootreason", 17) == 0) {
#ifdef NRF52_POWER_MANAGEMENT
sprintf(reply, "> Reset: %s; Shutdown: %s", sprintf(reply, "> Reset: %s; Shutdown: %s",
_board->getResetReasonString(_board->getResetReason()), _board->getResetReasonString(_board->getResetReason()),
_board->getShutdownReasonString(_board->getShutdownReason())); _board->getShutdownReasonString(_board->getShutdownReason()));
#else
strcpy(reply, "ERROR: Power management not supported");
#endif
} else if (memcmp(config, "pwrmgt.bootmv", 13) == 0) { } else if (memcmp(config, "pwrmgt.bootmv", 13) == 0) {
#ifdef NRF52_POWER_MANAGEMENT #ifdef NRF52_POWER_MANAGEMENT
sprintf(reply, "> %u mV", _board->getBootVoltage()); sprintf(reply, "> %u mV", _board->getBootVoltage());
+2
View File
@@ -61,8 +61,10 @@ struct NodePrefs { // persisted to file
float adc_multiplier; float adc_multiplier;
char owner_info[120]; char owner_info[120];
uint8_t rx_boosted_gain; // power settings uint8_t rx_boosted_gain; // power settings
uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting
uint8_t path_hash_mode; // which path mode to use when sending uint8_t path_hash_mode; // which path mode to use when sending
uint8_t loop_detect; uint8_t loop_detect;
uint8_t cad_enabled; // hardware Channel Activity Detection before TX (boolean)
}; };
class CommonCLICallbacks { class CommonCLICallbacks {
+36
View File
@@ -155,6 +155,42 @@ public:
void setInhibitSleep(bool inhibit) { void setInhibitSleep(bool inhibit) {
inhibit_sleep = inhibit; inhibit_sleep = inhibit;
} }
uint32_t getResetReason() const override {
return esp_reset_reason();
}
// https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32/api-reference/system/system.html
const char* getResetReasonString(uint32_t reason) {
switch (reason) {
case ESP_RST_UNKNOWN:
return "Unknown or first boot";
case ESP_RST_POWERON:
return "Power-on reset";
case ESP_RST_EXT:
return "External reset";
case ESP_RST_SW:
return "Software reset";
case ESP_RST_PANIC:
return "Panic / exception reset";
case ESP_RST_INT_WDT:
return "Interrupt watchdog reset";
case ESP_RST_TASK_WDT:
return "Task watchdog reset";
case ESP_RST_WDT:
return "Other watchdog reset";
case ESP_RST_DEEPSLEEP:
return "Wake from deep sleep";
case ESP_RST_BROWNOUT:
return "Brownout (low voltage)";
case ESP_RST_SDIO:
return "SDIO reset";
default:
static char buf[40];
snprintf(buf, sizeof(buf), "Unknown reset reason (%d)", reason);
return buf;
}
}
}; };
class ESP32RTCClock : public mesh::RTCClock { class ESP32RTCClock : public mesh::RTCClock {
+20 -3
View File
@@ -36,6 +36,7 @@ void RadioLibWrapper::begin() {
_noise_floor = 0; _noise_floor = 0;
_threshold = 0; _threshold = 0;
_cad_enabled = false;
// start average out some samples // start average out some samples
_num_floor_samples = 0; _num_floor_samples = 0;
@@ -178,10 +179,26 @@ void RadioLibWrapper::onSendFinished() {
state = STATE_IDLE; state = STATE_IDLE;
} }
int16_t RadioLibWrapper::performChannelScan() {
return _radio->scanChannel();
}
bool RadioLibWrapper::isChannelActive() { bool RadioLibWrapper::isChannelActive() {
return _threshold == 0 // int.thresh: RSSI-based interference detection (relative to noise floor)
? false // interference check is disabled if (_threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true;
: getCurrentRSSI() > _noise_floor + _threshold;
// cad: hardware channel activity detection
if (_cad_enabled) {
int16_t result = performChannelScan();
// scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY
// via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't
// try to read a non-existent packet and count a spurious recv error.
state = STATE_IDLE;
startRecv();
if (result != RADIOLIB_CHANNEL_FREE) return true;
}
return false;
} }
float RadioLibWrapper::getLastRSSI() const { float RadioLibWrapper::getLastRSSI() const {
+4 -1
View File
@@ -9,6 +9,7 @@ protected:
mesh::MainBoard* _board; mesh::MainBoard* _board;
uint32_t n_recv, n_sent, n_recv_errors; uint32_t n_recv, n_sent, n_recv_errors;
int16_t _noise_floor, _threshold; int16_t _noise_floor, _threshold;
bool _cad_enabled;
uint16_t _num_floor_samples; uint16_t _num_floor_samples;
int32_t _floor_sample_sum; int32_t _floor_sample_sum;
uint8_t _preamble_sf; uint8_t _preamble_sf;
@@ -32,7 +33,7 @@ public:
bool isInRecvMode() const override; bool isInRecvMode() const override;
bool isChannelActive(); bool isChannelActive();
bool isReceiving() override { bool isReceiving() override {
if (isReceivingPacket()) return true; if (isReceivingPacket()) return true;
return isChannelActive(); return isChannelActive();
@@ -46,9 +47,11 @@ public:
virtual uint8_t getSpreadingFactor() const { return LORA_SF; } virtual uint8_t getSpreadingFactor() const { return LORA_SF; }
static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; } static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; }
void updatePreamble(uint8_t sf) { _preamble_sf = sf; _radio->setPreambleLength(preambleLengthForSF(sf)); } void updatePreamble(uint8_t sf) { _preamble_sf = sf; _radio->setPreambleLength(preambleLengthForSF(sf)); }
virtual int16_t performChannelScan();
int getNoiseFloor() const override { return _noise_floor; } int getNoiseFloor() const override { return _noise_floor; }
void triggerNoiseFloorCalibrate(int threshold) override; void triggerNoiseFloorCalibrate(int threshold) override;
void setCADEnabled(bool enable) override { _cad_enabled = enable; }
void resetAGC() override; void resetAGC() override;
void loop() override; void loop() override;
@@ -1,7 +1,6 @@
[Heltec_mesh_solar] [Heltec_mesh_solar]
extends = nrf52_base extends = nrf52_base
board = heltec_mesh_solar board = heltec_mesh_solar
platform_packages = framework-arduinoadafruitnrf52
board_build.ldscript = boards/nrf52840_s140_v6.ld board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52_base.build_flags} build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52 -I src/helpers/nrf52
+2 -1
View File
@@ -12,8 +12,9 @@ class LoRaFEMControl
void setRxModeEnable(void); void setRxModeEnable(void);
void setRxModeEnableWhenMCUSleep(void); void setRxModeEnableWhenMCUSleep(void);
void setLNAEnable(bool enabled); void setLNAEnable(bool enabled);
bool isLnaCanControl(void) { return lna_can_control; } bool isLnaCanControl(void) const { return lna_can_control; }
void setLnaCanControl(bool can_control) { lna_can_control = can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
bool isLNAEnabled(void) const { return lna_enabled; }
private: private:
bool lna_enabled = false; bool lna_enabled = false;
+19 -1
View File
@@ -123,4 +123,22 @@ void T096Board::powerOff() {
const char* T096Board::getManufacturerName() const { const char* T096Board::getManufacturerName() const {
return "Heltec T096"; return "Heltec T096";
} }
bool T096Board::setLoRaFemLnaEnabled(bool enable) {
if (!loRaFEMControl.isLnaCanControl()) {
return false;
}
loRaFEMControl.setLNAEnable(enable);
loRaFEMControl.setRxModeEnable();
return true;
}
bool T096Board::canControlLoRaFemLna() const {
return loRaFEMControl.isLnaCanControl();
}
bool T096Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
+3
View File
@@ -25,4 +25,7 @@ public:
uint16_t getBattMilliVolts() override; uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ; const char* getManufacturerName() const override ;
void powerOff() override; void powerOff() override;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
}; };
+1 -1
View File
@@ -22,7 +22,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock);
#if ENV_INCLUDE_GPS #if ENV_INCLUDE_GPS
#include <helpers/sensors/MicroNMEALocationProvider.h> #include <helpers/sensors/MicroNMEALocationProvider.h>
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock);
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
#else #else
EnvironmentSensorManager sensors; EnvironmentSensorManager sensors;
@@ -0,0 +1,103 @@
#include "HeltecTowerV2Board.h"
#include <Arduino.h>
#include <Wire.h>
extern void variant_shutdown();
#ifdef NRF52_POWER_MANAGEMENT
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
};
void HeltecTowerV2Board::initiateShutdown(uint8_t reason) {
pinMode(PIN_GPS_EN, OUTPUT);
digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE);
pinMode(PIN_GPS_STANDBY, OUTPUT);
digitalWrite(PIN_GPS_STANDBY, LOW);
pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE);
loRaFEMControl.setSleepModeEnable();
bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
reason == SHUTDOWN_REASON_BOOT_PROTECT);
pinMode(PIN_BAT_CTL, OUTPUT);
digitalWrite(PIN_BAT_CTL, enable_lpcomp ? HIGH : LOW);
if (enable_lpcomp) {
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
}
variant_shutdown();
enterSystemOff(reason);
}
#endif
void HeltecTowerV2Board::begin() {
NRF52Board::begin();
pinMode(P_LORA_TX_LED, OUTPUT);
digitalWrite(P_LORA_TX_LED, !LED_STATE_ON);
pinMode(PIN_BAT_CTL, OUTPUT);
digitalWrite(PIN_BAT_CTL, LOW);
#ifdef NRF52_POWER_MANAGEMENT
checkBootVoltage(&power_config);
#endif
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
Wire.begin();
pinMode(PIN_GPS_EN, OUTPUT);
digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE);
pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE);
pinMode(PIN_GPS_STANDBY, OUTPUT);
digitalWrite(PIN_GPS_STANDBY, HIGH);
loRaFEMControl.init();
}
void HeltecTowerV2Board::onBeforeTransmit() {
digitalWrite(P_LORA_TX_LED, LED_STATE_ON);
loRaFEMControl.setTxModeEnable();
}
void HeltecTowerV2Board::onAfterTransmit() {
digitalWrite(P_LORA_TX_LED, !LED_STATE_ON);
loRaFEMControl.setRxModeEnable();
}
uint16_t HeltecTowerV2Board::getBattMilliVolts() {
analogReadResolution(12);
analogReference(VBAT_AR_INTERNAL);
pinMode(PIN_VBAT_READ, INPUT);
pinMode(PIN_BAT_CTL, OUTPUT);
digitalWrite(PIN_BAT_CTL, HIGH);
delay(10);
int adcvalue = analogRead(PIN_VBAT_READ);
digitalWrite(PIN_BAT_CTL, LOW);
return (uint16_t)((float)adcvalue * MV_LSB * ADC_MULTIPLIER);
}
const char* HeltecTowerV2Board::getManufacturerName() const {
return "Heltec Tower V2";
}
void HeltecTowerV2Board::powerOff() {
pinMode(PIN_GPS_EN, OUTPUT);
digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE);
pinMode(PIN_GPS_STANDBY, OUTPUT);
digitalWrite(PIN_GPS_STANDBY, LOW);
pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE);
loRaFEMControl.setSleepModeEnable();
pinMode(PIN_BAT_CTL, OUTPUT);
digitalWrite(PIN_BAT_CTL, LOW);
variant_shutdown();
sd_power_system_off();
}
@@ -0,0 +1,24 @@
#pragma once
#include <Arduino.h>
#include <MeshCore.h>
#include <helpers/NRF52Board.h>
#include "LoRaFEMControl.h"
class HeltecTowerV2Board : public NRF52BoardDCDC {
protected:
#ifdef NRF52_POWER_MANAGEMENT
void initiateShutdown(uint8_t reason) override;
#endif
public:
LoRaFEMControl loRaFEMControl;
HeltecTowerV2Board() : NRF52Board("TOWER_V2_OTA") {}
void begin();
void onBeforeTransmit() override;
void onAfterTransmit() override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override;
void powerOff() override;
};
@@ -0,0 +1,40 @@
#include "LoRaFEMControl.h"
#include <Arduino.h>
#include "variant.h"
static void enableFEMPower() {
bool wasOff = digitalRead(LORA_KCT8103L_EN) != HIGH;
digitalWrite(LORA_KCT8103L_EN, HIGH);
if (wasOff) {
delay(5);
}
}
void LoRaFEMControl::init() {
pinMode(LORA_KCT8103L_EN, OUTPUT);
digitalWrite(LORA_KCT8103L_EN, HIGH);
delay(1);
pinMode(LORA_KCT8103L_TX_RX, OUTPUT);
digitalWrite(LORA_KCT8103L_TX_RX, LOW);
}
void LoRaFEMControl::setSleepModeEnable() {
pinMode(LORA_KCT8103L_EN, OUTPUT);
digitalWrite(LORA_KCT8103L_EN, LOW);
}
void LoRaFEMControl::setTxModeEnable() {
enableFEMPower();
digitalWrite(LORA_KCT8103L_TX_RX, HIGH);
}
void LoRaFEMControl::setRxModeEnable() {
enableFEMPower();
digitalWrite(LORA_KCT8103L_TX_RX, LOW);
}
void LoRaFEMControl::setRxModeEnableWhenMCUSleep() {
enableFEMPower();
digitalWrite(LORA_KCT8103L_TX_RX, LOW);
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
class LoRaFEMControl {
public:
LoRaFEMControl() {}
virtual ~LoRaFEMControl() {}
void init();
void setSleepModeEnable();
void setTxModeEnable();
void setRxModeEnable();
void setRxModeEnableWhenMCUSleep();
};
+104
View File
@@ -0,0 +1,104 @@
[Heltec_tower_v2]
extends = nrf52_base
board = heltec_tower_v2
board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52_base.build_flags}
-D ENV_INCLUDE_GPS=1
-I lib/nrf52/s140_nrf52_6.1.1_API/include
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
-I variants/heltec_tower_v2
-D HELTEC_TOWER_V2
-D NRF52_POWER_MANAGEMENT
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=12
-D MAX_LORA_TX_POWER=22 ; Max SX1262 output -> ~29dBm at antenna
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52_base.build_src_filter}
+<helpers/*.cpp>
+<helpers/sensors>
+<../variants/heltec_tower_v2>
lib_deps =
${nrf52_base.lib_deps}
stevemarple/MicroNMEA @ ^2.0.6
debug_tool = jlink
upload_protocol = nrfutil
[env:Heltec_tower_v2_repeater]
extends = Heltec_tower_v2
build_src_filter = ${Heltec_tower_v2.build_src_filter}
+<../examples/simple_repeater>
build_flags =
${Heltec_tower_v2.build_flags}
-D ADVERT_NAME='"Heltec_Tower_V2 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_tower_v2_room_server]
extends = Heltec_tower_v2
build_src_filter = ${Heltec_tower_v2.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Heltec_tower_v2.build_flags}
-D ADVERT_NAME='"Heltec_Tower_V2 Room"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_tower_v2_companion_radio_ble]
extends = Heltec_tower_v2
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
board_upload.maximum_size = 712704
build_flags =
${Heltec_tower_v2.build_flags}
-I examples/companion_radio/ui-new
-D DISPLAY_CLASS=NullDisplayDriver
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
-D OFFLINE_QUEUE_SIZE=256
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_tower_v2.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
${Heltec_tower_v2.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_tower_v2_companion_radio_usb]
extends = Heltec_tower_v2
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
board_upload.maximum_size = 712704
build_flags =
${Heltec_tower_v2.build_flags}
-I examples/companion_radio/ui-new
-D DISPLAY_CLASS=NullDisplayDriver
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
; -D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_tower_v2.build_src_filter}
+<helpers/nrf52/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
${Heltec_tower_v2.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_tower_v2_kiss_modem]
extends = Heltec_tower_v2
build_src_filter = ${Heltec_tower_v2.build_src_filter}
+<../examples/kiss_modem/>
+31
View File
@@ -0,0 +1,31 @@
#include "target.h"
#include <Arduino.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/sensors/MicroNMEALocationProvider.h>
HeltecTowerV2Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock);
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display;
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
#endif
bool radio_init() {
rtc_clock.begin(Wire);
return radio.std_init(&SPI);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng);
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <HeltecTowerV2Board.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/radiolib/CustomSX1262Wrapper.h>
#include <helpers/radiolib/RadioLibWrappers.h>
#include <helpers/sensors/EnvironmentSensorManager.h>
#include <helpers/sensors/LocationProvider.h>
#ifdef DISPLAY_CLASS
#include <helpers/ui/MomentaryButton.h>
#include "helpers/ui/NullDisplayDriver.h"
#endif
extern HeltecTowerV2Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern EnvironmentSensorManager sensors;
#ifdef DISPLAY_CLASS
extern DISPLAY_CLASS display;
extern MomentaryButton user_btn;
#endif
bool radio_init();
mesh::LocalIdentity radio_new_identity();
+42
View File
@@ -0,0 +1,42 @@
#include "variant.h"
#include "Arduino.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47
};
void initVariant()
{
}
void variant_shutdown()
{
nrf_gpio_cfg_default(PIN_GPS_EN);
nrf_gpio_cfg_default(PIN_GPS_PPS);
nrf_gpio_cfg_default(PIN_GPS_RESET);
nrf_gpio_cfg_default(PIN_GPS_STANDBY);
nrf_gpio_cfg_default(GPS_RX_PIN);
nrf_gpio_cfg_default(GPS_TX_PIN);
pinMode(LORA_KCT8103L_EN, OUTPUT);
digitalWrite(LORA_KCT8103L_EN, LOW);
nrf_gpio_cfg_default(LORA_KCT8103L_TX_RX);
nrf_gpio_cfg_default(RF_PA_DETECT_PIN);
nrf_gpio_cfg_default(SX126X_CS);
nrf_gpio_cfg_default(SX126X_DIO1);
nrf_gpio_cfg_default(SX126X_BUSY);
nrf_gpio_cfg_default(SX126X_RESET);
nrf_gpio_cfg_default(PIN_SPI_MISO);
nrf_gpio_cfg_default(PIN_SPI_MOSI);
nrf_gpio_cfg_default(PIN_SPI_SCK);
nrf_gpio_cfg_default(PIN_LED);
detachInterrupt(PIN_GPS_PPS);
detachInterrupt(PIN_BUTTON1);
}
+108
View File
@@ -0,0 +1,108 @@
#pragma once
#include "WVariant.h"
#define USE_LFXO
#define VARIANT_MCK (64000000ul)
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (1)
#define NUM_ANALOG_OUTPUTS (0)
#define WIRE_INTERFACES_COUNT (1)
#define PIN_WIRE_SDA (0 + 30)
#define PIN_WIRE_SCL (0 + 5)
#define PIN_BOARD_SDA PIN_WIRE_SDA
#define PIN_BOARD_SCL PIN_WIRE_SCL
#define SPI_INTERFACES_COUNT (1)
#define PIN_SPI_MISO (0 + 23)
#define PIN_SPI_MOSI (0 + 22)
#define PIN_SPI_SCK (0 + 19)
#define PIN_SPI_NSS LORA_CS
#define LED_BUILTIN (32 + 15)
#define PIN_LED LED_BUILTIN
#define LED_RED (-1)
#define LED_GREEN (-1)
#define LED_BLUE (-1)
#define LED_PIN (-1)
#define P_LORA_TX_LED LED_BUILTIN
#define LED_STATE_ON LOW
#define PIN_BUTTON1 (32 + 10)
#define BUTTON_PIN PIN_BUTTON1
#define PIN_USER_BTN BUTTON_PIN
#define USE_SX1262
#define SX126X_CS (0 + 24)
#define LORA_CS SX126X_CS
#define SX126X_DIO1 (0 + 20)
#define SX126X_BUSY (0 + 17)
#define SX126X_RESET (0 + 25)
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define P_LORA_NSS LORA_CS
#define P_LORA_DIO_1 SX126X_DIO1
#define P_LORA_BUSY SX126X_BUSY
#define P_LORA_RESET SX126X_RESET
#define P_LORA_MISO PIN_SPI_MISO
#define P_LORA_MOSI PIN_SPI_MOSI
#define P_LORA_SCLK PIN_SPI_SCK
#define USE_KCT8103L_PA_ONLY
#define LORA_KCT8103L_EN (0 + 15)
#define LORA_KCT8103L_TX_RX (0 + 16)
#define LORA_PA_POWER LORA_KCT8103L_EN
#define RF_PA_DETECT_PIN (0 + 13)
#define RF_PA_HIGH_POWER_VALUE HIGH
#define GPS_L76K
#define GPS_RESET_MODE LOW
#define PIN_GPS_RESET (32 + 6)
#define PIN_GPS_RESET_ACTIVE GPS_RESET_MODE
#define PIN_GPS_EN (0 + 7)
#define PIN_GPS_EN_ACTIVE LOW
#define GPS_EN_ACTIVE PIN_GPS_EN_ACTIVE
#define PIN_GPS_STANDBY (32 + 2)
#define PIN_GPS_PPS (32 + 4)
#define GPS_BAUD_RATE 9600
// Upstream names are from the GPS perspective. MeshCore's PIN_GPS_TX is the
// CPU RX pin because EnvironmentSensorManager passes it as Serial1 RX.
#define GPS_TX_PIN (32 + 7)
#define GPS_RX_PIN (32 + 5)
#define PIN_GPS_TX GPS_RX_PIN
#define PIN_GPS_RX GPS_TX_PIN
#define PIN_SERIAL1_RX PIN_GPS_TX
#define PIN_SERIAL1_TX PIN_GPS_RX
#define PIN_SERIAL2_RX (-1)
#define PIN_SERIAL2_TX (-1)
#define HAS_HARDWARE_WATCHDOG
#define HARDWARE_WATCHDOG_DONE (0 + 9)
#define HARDWARE_WATCHDOG_WAKE (0 + 10)
#define HARDWARE_WATCHDOG_TIMEOUT_MS (8 * 60 * 1000)
#define SERIAL_PRINT_PORT 0
#define PIN_BAT_CTL (0 + 21)
#define ADC_CTRL PIN_BAT_CTL
#define ADC_CTRL_ENABLED HIGH
#define BATTERY_PIN (0 + 4)
#define PIN_VBAT_READ BATTERY_PIN
#define ADC_RESOLUTION 14
#define BATTERY_SENSE_RESOLUTION_BITS 12
#define BATTERY_SENSE_RESOLUTION 4096.0
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (4.916F)
#define MV_LSB (3000.0F / 4096.0F)
#define NRF52_POWER_MANAGEMENT
#define PWRMGT_VOLTAGE_BOOTLOCK 3100
#define PWRMGT_LPCOMP_AIN 2
#define PWRMGT_LPCOMP_REFSEL 1
@@ -82,3 +82,21 @@ void HeltecTrackerV2Board::begin() {
const char* HeltecTrackerV2Board::getManufacturerName() const { const char* HeltecTrackerV2Board::getManufacturerName() const {
return "Heltec Tracker V2"; return "Heltec Tracker V2";
} }
bool HeltecTrackerV2Board::setLoRaFemLnaEnabled(bool enable) {
if (!loRaFEMControl.isLnaCanControl()) {
return false;
}
loRaFEMControl.setLNAEnable(enable);
loRaFEMControl.setRxModeEnable();
return true;
}
bool HeltecTrackerV2Board::canControlLoRaFemLna() const {
return loRaFEMControl.isLnaCanControl();
}
bool HeltecTrackerV2Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
@@ -21,5 +21,8 @@ public:
void powerOff() override; void powerOff() override;
uint16_t getBattMilliVolts() override; uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ; const char* getManufacturerName() const override ;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
}; };
+2 -1
View File
@@ -12,8 +12,9 @@ class LoRaFEMControl
void setRxModeEnable(void); void setRxModeEnable(void);
void setRxModeEnableWhenMCUSleep(void); void setRxModeEnableWhenMCUSleep(void);
void setLNAEnable(bool enabled); void setLNAEnable(bool enabled);
bool isLnaCanControl(void) { return lna_can_control; } bool isLnaCanControl(void) const { return lna_can_control; }
void setLnaCanControl(bool can_control) { lna_can_control = can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
bool isLNAEnabled(void) const { return lna_enabled; }
private: private:
bool lna_enabled = false; bool lna_enabled = false;
+18
View File
@@ -83,3 +83,21 @@ void HeltecV4Board::begin() {
return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED"; return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED";
#endif #endif
} }
bool HeltecV4Board::setLoRaFemLnaEnabled(bool enable) {
if (!loRaFEMControl.isLnaCanControl()) {
return false;
}
loRaFEMControl.setLNAEnable(enable);
loRaFEMControl.setRxModeEnable();
return true;
}
bool HeltecV4Board::canControlLoRaFemLna() const {
return loRaFEMControl.isLnaCanControl();
}
bool HeltecV4Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
+3
View File
@@ -25,6 +25,9 @@ public:
void onAfterTransmit(void) override; void onAfterTransmit(void) override;
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
void powerOff() override; void powerOff() override;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
uint16_t getBattMilliVolts() override; uint16_t getBattMilliVolts() override;
bool setAdcMultiplier(float multiplier) override { bool setAdcMultiplier(float multiplier) override {
if (multiplier == 0.0f) { if (multiplier == 0.0f) {
+2 -1
View File
@@ -18,8 +18,9 @@ class LoRaFEMControl
void setRxModeEnable(void); void setRxModeEnable(void);
void setRxModeEnableWhenMCUSleep(void); void setRxModeEnableWhenMCUSleep(void);
void setLNAEnable(bool enabled); void setLNAEnable(bool enabled);
bool isLnaCanControl(void) { return lna_can_control; } bool isLnaCanControl(void) const { return lna_can_control; }
void setLnaCanControl(bool can_control) { lna_can_control = can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
bool isLNAEnabled(void) const { return lna_enabled; }
LoRaFEMType getFEMType(void) const { return fem_type; } LoRaFEMType getFEMType(void) const { return fem_type; }
private: private:
LoRaFEMType fem_type=OTHER_FEM_TYPES; LoRaFEMType fem_type=OTHER_FEM_TYPES;
@@ -0,0 +1,10 @@
#pragma once
#include <helpers/ESP32Board.h>
class TETHEliteBoard : public ESP32Board {
public:
const char* getManufacturerName() const override {
return "LilyGO T-ETH Elite";
}
};
+99
View File
@@ -0,0 +1,99 @@
[LilyGo_TETH_Elite_sx1262]
extends = esp32_base
board = esp32s3box
board_build.partitions = default_16MB.csv
board_upload.flash_size = 16MB
build_flags =
${esp32_base.build_flags}
-I variants/lilygo_teth_elite
-D BOARD_HAS_PSRAM
-D LILYGO_TETH_ELITE
-D LILYGO_T_ETH_ELITE_ESP32S3
-D ARDUINO_USB_CDC_ON_BOOT=1
-D P_LORA_DIO_1=8
-D P_LORA_NSS=40
-D P_LORA_RESET=46
-D P_LORA_BUSY=16
-D P_LORA_SCLK=10
-D P_LORA_MISO=9
-D P_LORA_MOSI=11
-D P_LORA_TX_LED=38
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140
-D USE_SX1262
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=8
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/lilygo_teth_elite>
lib_deps =
${esp32_base.lib_deps}
[env:LilyGo_TETH_Elite_sx1262_repeater]
extends = LilyGo_TETH_Elite_sx1262
build_flags =
${LilyGo_TETH_Elite_sx1262.build_flags}
-D ADVERT_NAME='"T-ETH Elite Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter}
+<../examples/simple_repeater>
lib_deps =
${LilyGo_TETH_Elite_sx1262.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_TETH_Elite_sx1262_room_server]
extends = LilyGo_TETH_Elite_sx1262
build_flags =
${LilyGo_TETH_Elite_sx1262.build_flags}
-D ADVERT_NAME='"T-ETH Elite Room"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter}
+<../examples/simple_room_server>
lib_deps =
${LilyGo_TETH_Elite_sx1262.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_TETH_Elite_sx1262_companion_radio_usb]
extends = LilyGo_TETH_Elite_sx1262
build_flags =
${LilyGo_TETH_Elite_sx1262.build_flags}
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter}
+<../examples/companion_radio/*.cpp>
lib_deps =
${LilyGo_TETH_Elite_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TETH_Elite_sx1262_companion_radio_ble]
extends = LilyGo_TETH_Elite_sx1262
build_flags =
${LilyGo_TETH_Elite_sx1262.build_flags}
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
-D OFFLINE_QUEUE_SIZE=256
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps =
${LilyGo_TETH_Elite_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
+43
View File
@@ -0,0 +1,43 @@
#include <Arduino.h>
#include "target.h"
TETHEliteBoard board;
static SPIClass spi(HSPI);
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
SensorManager sensors;
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
return radio.std_init(&spi);
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(int8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng);
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/radiolib/RadioLibWrappers.h>
#include <helpers/radiolib/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/SensorManager.h>
#include "TETHEliteBoard.h"
extern TETHEliteBoard board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern SensorManager sensors;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(int8_t dbm);
mesh::LocalIdentity radio_new_identity();
-3
View File
@@ -1,7 +1,6 @@
[Mesh_pocket] [Mesh_pocket]
extends = nrf52_base extends = nrf52_base
board = heltec_mesh_pocket board = heltec_mesh_pocket
platform_packages = framework-arduinoadafruitnrf52
board_build.ldscript = boards/nrf52840_s140_v6.ld board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52_base.build_flags} build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52 -I src/helpers/nrf52
@@ -32,7 +31,6 @@ lib_deps =
stevemarple/MicroNMEA @ ^2.0.6 stevemarple/MicroNMEA @ ^2.0.6
zinggjm/GxEPD2 @ 1.6.2 zinggjm/GxEPD2 @ 1.6.2
bakercp/CRC32 @ ^2.0.0 bakercp/CRC32 @ ^2.0.0
debug_tool = jlink debug_tool = jlink
upload_protocol = nrfutil upload_protocol = nrfutil
@@ -40,7 +38,6 @@ upload_protocol = nrfutil
extends = Mesh_pocket extends = Mesh_pocket
build_src_filter = ${Mesh_pocket.build_src_filter} build_src_filter = ${Mesh_pocket.build_src_filter}
+<../examples/simple_repeater> +<../examples/simple_repeater>
build_flags = build_flags =
${Mesh_pocket.build_flags} ${Mesh_pocket.build_flags}
-D ADVERT_NAME='"Heltec_Mesh_Pocket Repeater"' -D ADVERT_NAME='"Heltec_Mesh_Pocket Repeater"'