Compare commits

..

44 Commits

Author SHA1 Message Date
Matthias Wientapper
b7a3fb122c pr1810, modify to not completely block flood adverts 2026-04-24 21:35:55 +02:00
Matthias Wientapper
c0a107cb23 platformio.ini: set LORA_CR to 8 2026-04-24 21:35:55 +02:00
Matthias Wientapper
e9e98f1c87 rak4631, Xiao NRf, T114: set lockout voltage to 0V 2026-04-24 21:35:55 +02:00
Matthias Wientapper
c41309601d set default: disable flood adverts 2026-04-24 21:35:55 +02:00
Matthias Wientapper
30d4059a8c Integration of upstrem PR #1810 2026-04-24 21:35:55 +02:00
Matthias Wientapper
08b25cf724 Integration of upstrem PR #1727 2026-04-24 21:35:53 +02:00
Matthias Wientapper
f7f6dbc22d Integration of upstrem PR #1338 2026-04-24 21:35:51 +02:00
Liam Cottle
9a1be5386d Merge pull request #2326 from IoTThinks/MCdev-Added-HeltecV4-Expansion-Kit-2026-04-16
Added support for Expansion Kit to Heltec V4 OLED repeaters
2026-04-25 00:14:12 +12:00
mattzzw
2d2c0475ac Update examples/simple_room_server/MyMesh.cpp
Fix pow() and rand() are calculated for every forwarded packet, but we only need it for flood advert. Also fixes 'paket' typo.

Co-authored-by: Wessel <wessel@weebl.me>
2026-04-24 09:27:36 +02:00
mattzzw
d81626ece5 Update examples/simple_repeater/MyMesh.cpp
Fix pow() and rand() are calculated for every forwarded packet, but we only need it for flood advert. Also fixes 'paket' typo.

Co-authored-by: Wessel <wessel@weebl.me>
2026-04-24 09:27:36 +02:00
Matthias Wientapper
fe7c388fdb Add cli config flood.advert.base
0 = forwarding flood adverts off
1 = forwarding flood adverts on (unrestricted)
0.308 (default) = prob. forwarding according to #1338
2026-04-24 09:26:43 +02:00
Matthias Wientapper
3955a96df7 Limit flood advert packet forwarding for roomservers as well 2026-04-24 09:22:35 +02:00
Matthias Wientapper
1cb6c7a900 Limit flood advert packet forwarding, implements #1223 2026-04-24 09:22:35 +02:00
me
e902b8f121 feat(techo-lite): add Non-Shell (screenless) companion BLE variant
T-Echo Lite Non-Shell has no ePaper display, but the existing
companion BLE env inherits DISPLAY_CLASS=GxEPDDisplay from the base
env. This causes display.begin() to run on non-existent hardware,
corrupting BLE initialization and generating a random PIN that
cannot be displayed, making BLE connection impossible.

Add LilyGo_T-Echo-Lite_non_shell_companion_radio_ble env that:
- excludes DISPLAY_CLASS and ePaper-related build flags
- removes GxEPDDisplay.cpp and ui-new from build sources
- uses static BLE_PIN_CODE=123456 (avoids random PIN generation)

Also fix boards/t-echo.json:
- add nrfutil to upload protocols list
- add use_1200bps_touch=true and wait_for_upload_port=true
  to enable reliable flashing via PlatformIO upload button
2026-04-23 13:48:03 +02:00
pelgraine
8217a67f0c Fixes #1183 — T-Echo Lite incorrect battery voltage
Battery (TechoBoard.h/cpp): Added PIN_VBAT_MEAS_EN (P0.31) — the T-Echo
Lite has a gated voltage divider that must be enabled before reading. Also
added pinMode(PIN_VBAT_READ, INPUT) before each ADC read to reclaim P0.02
from other peripherals. Pin definitions hardcoded from LilyGo's
t_echo_lite_config.h.

I2C (variant.h): Corrected SDA/SCL from P0.04/P0.02 to P1.04/P1.02 per
LilyGo's IIC_1_SDA/IIC_1_SCL. The old P0.02 mapping conflicted with the
battery ADC pin.

GPS (variant.h): Corrected all five GPS pin assignments to match LilyGo's
config — UART TX/RX, wake, PPS, and power enable were all scrambled.

SPI (variant.h): Fixed SPI_INTERFACES_COUNT from _PINNUM(0, 2) to (2).

Tested on T-Echo Lite Non-Shell (USB-C, no display). Battery readings match
Heltec V4 reference within 10mV.
2026-04-23 13:48:03 +02:00
Wessel Nieboer
46b5076082 Fix RAK4631 SX1262 hardware pin config 2026-04-23 13:48:03 +02:00
Wessel Nieboer
b4f690bfeb Fix FEM/LNA enbaled by default for Heltec T096, Heltec Wireless Tracker v2
Should only really be disabled when it causes issues.
2026-04-23 13:48:03 +02:00
Wessel Nieboer
c33639ee3c Add sanity build for LR1110 and SX1276 too 2026-04-23 13:48:03 +02:00
Wessel Nieboer
1f066ad9c4 Implement proper shutdown procedure for R1 neo
fixes #2361
2026-04-23 13:48:03 +02:00
Marco
321d5a7e98 Add Heltec V4 set adc.multiplier 2026-04-23 13:48:03 +02:00
Scott Powell
aa796e8623 * CommonCLI: more reply bounds checking 2026-04-23 13:48:03 +02:00
Scott Powell
06e0273e1f * CommonCLI: bounds check added to "unknown config:" replies 2026-04-23 13:48:03 +02:00
Liam Cottle
5372388e6e Merge pull request #2353 from jirogit/fix/techo-lite-non-shell-ble-clean
feat(techo-lite): add Non-Shell companion BLE variant
2026-04-22 20:25:32 +12:00
Liam Cottle
dc548578ff Merge pull request #2287 from pelgraine/fix/techo-lite-battery
Fixes #1183 — T-Echo Lite incorrect battery voltage
2026-04-22 20:23:21 +12:00
Liam Cottle
d3b457681a Merge pull request #2008 from weebl2000/p_lora_reset_rak4631
Fix RAK4631 SX1262 hardware pin config
2026-04-22 19:45:06 +12:00
Liam Cottle
f1f1361fd5 Merge pull request #2340 from weebl2000/fem-enabled-def
Fix FEM/LNA enbaled by default for Heltec T096, Heltec Wireless Tracker v2
2026-04-22 19:33:29 +12:00
Liam Cottle
bdae60498c Merge pull request #2016 from weebl2000/add-lr1110-sx1276
Add sanity build for LR1110 and SX1276 too
2026-04-22 19:30:12 +12:00
Wessel Nieboer
66fe9ac2db Fix FEM/LNA enbaled by default for Heltec T096, Heltec Wireless Tracker v2
Should only really be disabled when it causes issues.
2026-04-22 09:30:00 +02:00
Liam Cottle
cc6139bc23 Merge pull request #2371 from weebl2000/r1-neo-shutdown
Implement proper shutdown procedure for R1 neo
2026-04-22 19:22:46 +12:00
pelgraine
d516cd8a9e Fixes #1183 — T-Echo Lite incorrect battery voltage
Battery (TechoBoard.h/cpp): Added PIN_VBAT_MEAS_EN (P0.31) — the T-Echo
Lite has a gated voltage divider that must be enabled before reading. Also
added pinMode(PIN_VBAT_READ, INPUT) before each ADC read to reclaim P0.02
from other peripherals. Pin definitions hardcoded from LilyGo's
t_echo_lite_config.h.

I2C (variant.h): Corrected SDA/SCL from P0.04/P0.02 to P1.04/P1.02 per
LilyGo's IIC_1_SDA/IIC_1_SCL. The old P0.02 mapping conflicted with the
battery ADC pin.

GPS (variant.h): Corrected all five GPS pin assignments to match LilyGo's
config — UART TX/RX, wake, PPS, and power enable were all scrambled.

SPI (variant.h): Fixed SPI_INTERFACES_COUNT from _PINNUM(0, 2) to (2).

Tested on T-Echo Lite Non-Shell (USB-C, no display). Battery readings match
Heltec V4 reference within 10mV.
2026-04-22 12:56:15 +10:00
Wessel Nieboer
07671cf1fe Implement proper shutdown procedure for R1 neo
fixes #2361
2026-04-21 21:29:18 +02:00
Liam Cottle
d532481f34 Merge pull request #2335 from fizzyfuzzle/v4_adc_mult
Add Heltec V4 set adc.multiplier functionality
2026-04-21 18:06:42 +12:00
me
28b1586fd7 feat(techo-lite): add Non-Shell (screenless) companion BLE variant
T-Echo Lite Non-Shell has no ePaper display, but the existing
companion BLE env inherits DISPLAY_CLASS=GxEPDDisplay from the base
env. This causes display.begin() to run on non-existent hardware,
corrupting BLE initialization and generating a random PIN that
cannot be displayed, making BLE connection impossible.

Add LilyGo_T-Echo-Lite_non_shell_companion_radio_ble env that:
- excludes DISPLAY_CLASS and ePaper-related build flags
- removes GxEPDDisplay.cpp and ui-new from build sources
- uses static BLE_PIN_CODE=123456 (avoids random PIN generation)

Also fix boards/t-echo.json:
- add nrfutil to upload protocols list
- add use_1200bps_touch=true and wait_for_upload_port=true
  to enable reliable flashing via PlatformIO upload button
2026-04-20 21:08:08 -07:00
Scott Powell
3d982711a6 * CommonCLI: more reply bounds checking 2026-04-21 12:12:47 +10:00
Scott Powell
db7baa7bd7 * CommonCLI: bounds check added to "unknown config:" replies 2026-04-21 12:07:39 +10:00
Marco
e8907a3108 Add Heltec V4 set adc.multiplier 2026-04-19 15:29:43 +02:00
Kevin Le
992dddeee5 Added support for Expansion Kit to Heltec V4 OLED repeaters 2026-04-18 09:24:53 +07:00
Wessel Nieboer
dc9e6ae893 Prevent packet errors from growing 2026-04-04 13:18:42 +02:00
Wessel Nieboer
de97baba4f Just check for not channel free 2026-04-04 13:18:42 +02:00
Wessel Nieboer
8744213945 Also return busy if preamble detected 2026-04-04 13:18:42 +02:00
Wessel Nieboer
952d12c8b9 Use hardware channel activity detection for checking interference 2026-04-04 13:18:41 +02:00
Wessel Nieboer
a5ae89aac0 Fix RAK4631 SX1262 hardware pin config 2026-04-04 13:18:17 +02:00
Wessel Nieboer
c0252ab82c Add sanity build for LR1110 and SX1276 too 2026-04-04 13:18:15 +02:00
Stefan Berthold
f864c5f547 allow direct message paths when denyf * is set 2026-02-23 23:33:41 +01:00
38 changed files with 376 additions and 447 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
github: meshcore-dev

View File

@@ -39,6 +39,10 @@ jobs:
- wio-e5-mini_repeater
# ESP32-C6
- LilyGo_Tlora_C6_repeater_
# LR1110 (nRF52)
- wio_wm1110_repeater
# SX1276 (ESP32)
- Tbeam_SX1276_repeater
steps:
- name: Clone Repo

150
README.md
View File

@@ -1,39 +1,135 @@
# Meshcore EVO - AOI Edition
Diese Firmware wurde verändert:
## About MeshCore-Evo
* Modifikation der Sensor-Firmware
* Gestestet mit Heltec V4
* Sollte theoretisch mit allen ESP-Boards funktionieren
* GPIO15 und GPIO16 sind jeweils als Pullup-Eingang definiert.
This is a friendly fork of the MeshCore project.
Its aim is to provide repeater firmwares with a few additional pending upstream PRs/improvements (i.e. PRs/improvements that are available to the MeshCore project but have not yet been merged in the upstream repo or PRs that might never be merged upstream for some reason).
These changes are intended to help mitigating challenges in big or dense meshes, e.g.:
### Neue Befehle:
- Dealing with flood advert traffic
- improving `denyf *` handling
* "io" gibt den zustand aller 32 GPIO-Pins als Hexcode aus. (Die originale FW gibt eine festcodierte 0 aus.)
* "bin" gibt den zustand aller 32 GPIO-Pins als Binärcode aus
* "readpin <pin>" gibt den zustand eines Pins als ON/OFF aus
This list might change any time.
Refer to the [release notes](https://github.com/mattzzw/MeshCore-Evo/releases) for an up-to-date list of the applied changes.
### Verwendung:
* GPIO 15 oder 16 können z.B. mit einm Schalter oder Relais verbunden werden.
* Der standardmäßige zustand ist "ON", da intern 3,3V anliegen (Pull-Up Widerstand)
* Wird ein GPIO nach Masse geschaltet, wechselt der Zustand nach "OFF"
* Mit "readpin 0" kann als Test der User Button ausgelesen werden.
#### Beispiel:
## About MeshCore
Eine laufende Anlage hält ein Überwachungsrelais ständig angezogen, und schaltet GPIO15
nach Masse. Der Zustand ist daher "OFF" (das könnte als Alarm: OFF gelesen werden).
MeshCore is a lightweight, portable C++ library that enables multi-hop packet routing for embedded projects using LoRa and other packet radios. It is designed for developers who want to create resilient, decentralized communication networks that work without the internet.
Fällt die Stromversorgung der Anlage aus, fällt das Relais ab. Der Zustand von GPIO15
wechselt daher auf "ON" (das könnte wiederum als Alarm: ON gelesen werden)
## 🔍 What is MeshCore?
### Hardware:
MeshCore now supports a range of LoRa devices, allowing for easy flashing without the need to compile firmware manually. Users can flash a pre-built binary using tools like Adafruit ESPTool and interact with the network through a serial console.
MeshCore provides the ability to create wireless mesh networks, similar to Meshtastic and Reticulum but with a focus on lightweight multi-hop packet routing for embedded projects. Unlike Meshtastic, which is tailored for casual LoRa communication, or Reticulum, which offers advanced networking, MeshCore balances simplicity with scalability, making it ideal for custom embedded solutions., where devices (nodes) can communicate over long distances by relaying messages through intermediate nodes. This is especially useful in off-grid, emergency, or tactical situations where traditional communication infrastructure is unavailable.
#### Heltec V4:
* Firmware kompiliert fehlerfrei und funktioniert
* Der USR-Button kann mit "readpin 0" gelesen werden
## ⚡ Key Features
#### Seeed XIAO S3 WIO
* Firmware kompiliert fehlerfrei und funktioniert
* Der USR-Button auf der LoRa-Platine kann mit "readpin 21" ausgelesen werden
* Multi-Hop Packet Routing
* Devices can forward messages across multiple nodes, extending range beyond a single radio's reach.
* Supports up to a configurable number of hops to balance network efficiency and prevent excessive traffic.
* Nodes use fixed roles where "Companion" nodes are not repeating messages at all to prevent adverse routing paths from being used.
* Supports LoRa Radios Works with Heltec, RAK Wireless, and other LoRa-based hardware.
* Decentralized & Resilient No central server or internet required; the network is self-healing.
* Low Power Consumption Ideal for battery-powered or solar-powered devices.
* Simple to Deploy Pre-built example applications make it easy to get started.
## 🎯 What Can You Use MeshCore For?
* Off-Grid Communication: Stay connected even in remote areas.
* Emergency Response & Disaster Recovery: Set up instant networks where infrastructure is down.
* Outdoor Activities: Hiking, camping, and adventure racing communication.
* Tactical & Security Applications: Military, law enforcement, and private security use cases.
* IoT & Sensor Networks: Collect data from remote sensors and relay it back to a central location.
## 🚀 How to Get Started
- Watch the [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc) by Andy Kirby.
- 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).
- Flash the MeshCore firmware on a supported device.
- Connect with a supported client.
For developers;
- Install [PlatformIO](https://docs.platformio.org) in [Visual Studio Code](https://code.visualstudio.com).
- Clone and open the MeshCore repository in Visual Studio Code.
- See the example applications you can modify and run:
- [Companion Radio](./examples/companion_radio) - For use with an external chat app, over BLE, USB or WiFi.
- [KISS Modem](./examples/kiss_modem) - Serial KISS protocol bridge for host applications. ([protocol docs](./docs/kiss_modem_protocol.md))
- [Simple Repeater](./examples/simple_repeater) - Extends network coverage by relaying messages.
- [Simple Room Server](./examples/simple_room_server) - A simple BBS server for shared Posts.
- [Simple Secure Chat](./examples/simple_secure_chat) - Secure terminal based text communication between devices.
- [Simple Sensor](./examples/simple_sensor) - Remote sensor node with telemetry and alerting.
The Simple Secure Chat example can be interacted with through the Serial Monitor in Visual Studio Code, or with a Serial USB Terminal on Android.
## ⚡️ MeshCore Flasher
We have prebuilt firmware ready to flash on supported devices.
- Launch https://meshcore.io/flasher
- Select a supported device
- Flash one of the firmware types:
- Companion, Repeater or Room Server
- Once flashing is complete, you can connect with one of the MeshCore clients below.
## 📱 MeshCore Clients
**Companion Firmware**
The companion firmware can be connected to via BLE, USB or WiFi depending on the firmware type you flashed.
- Web: https://app.meshcore.nz
- Android: https://play.google.com/store/apps/details?id=com.liamcottle.meshcore.android
- iOS: https://apps.apple.com/us/app/meshcore/id6742354151?platform=iphone
- NodeJS: https://github.com/liamcottle/meshcore.js
- Python: https://github.com/fdlamotte/meshcore-cli
**Repeater and Room Server Firmware**
The repeater and room server firmwares can be setup via USB in the web config tool.
- https://config.meshcore.io
They can also be managed via LoRa in the mobile app by using the Remote Management feature.
## 🛠 Hardware Compatibility
MeshCore is designed for devices listed in the [MeshCore Flasher](https://meshcore.io/flasher)
## 📜 License
MeshCore is open-source software released under the MIT License. You are free to use, modify, and distribute it for personal and commercial projects.
## Contributing
Please submit PR's using 'dev' as the base branch!
For minor changes just submit your PR and we'll try to review it, but for anything more 'impactful' please open an Issue first and start a discussion. Is better to sound out what it is you want to achieve first, and try to come to a consensus on what the best approach is, especially when it impacts the structure or architecture of this codebase.
Here are some general principals you should try to adhere to:
* Keep it simple. Please, don't think like a high-level lang programmer. Think embedded, and keep code concise, without any unnecessary layers.
* No dynamic memory allocation, except during setup/begin functions.
* Use the same brace and indenting style that's in the core source modules. (A .clang-format is prob going to be added soon, but please do NOT retroactively re-format existing code. This just creates unnecessary diffs that make finding problems harder)
Help us prioritize! Please react with thumbs-up to issues/PRs you care about most. We look at reaction counts when planning work.
## Road-Map / To-Do
There are a number of fairly major features in the pipeline, with no particular time-frames attached yet. In very rough chronological order:
- [X] Companion radio: UI redesign
- [X] Repeater + Room Server: add ACL's (like Sensor Node has)
- [X] Standardise Bridge mode for repeaters
- [ ] Repeater/Bridge: Standardise the Transport Codes for zoning/filtering
- [X] Core + Repeater: enhanced zero-hop neighbour discovery
- [ ] Core: round-trip manual path support
- [ ] Companion + Apps: support for multiple sub-meshes (and 'off-grid' client repeat mode)
- [ ] Core + Apps: support for LZW message compression
- [ ] Core: dynamic CR (Coding Rate) for weak vs strong hops
- [ ] Core: new framework for hosting multiple virtual nodes on one physical device
- [ ] V2 protocol spec: discussion and consensus around V2 packet protocol, including path hashes, new encryption specs, etc
## 📞 Get Support
- Report bugs and request features on the [GitHub Issues](https://github.com/ripplebiz/MeshCore/issues) page.
- Find additional guides and components on [my site](https://buymeacoffee.com/ripplebiz).
- Join [MeshCore Discord](https://discord.gg/BMwCtwHj5V) to chat with the developers and get help from the community.

View File

@@ -1,135 +0,0 @@
## About MeshCore-Evo
This is a friendly fork of the MeshCore project.
Its aim is to provide repeater firmwares with a few additional pending upstream PRs/improvements (i.e. PRs/improvements that are available to the MeshCore project but have not yet been merged in the upstream repo or PRs that might never be merged upstream for some reason).
These changes are intended to help mitigating challenges in big or dense meshes, e.g.:
- Dealing with flood advert traffic
- improving `denyf *` handling
This list might change any time.
Refer to the [release notes](https://github.com/mattzzw/MeshCore-Evo/releases) for an up-to-date list of the applied changes.
## About MeshCore
MeshCore is a lightweight, portable C++ library that enables multi-hop packet routing for embedded projects using LoRa and other packet radios. It is designed for developers who want to create resilient, decentralized communication networks that work without the internet.
## 🔍 What is MeshCore?
MeshCore now supports a range of LoRa devices, allowing for easy flashing without the need to compile firmware manually. Users can flash a pre-built binary using tools like Adafruit ESPTool and interact with the network through a serial console.
MeshCore provides the ability to create wireless mesh networks, similar to Meshtastic and Reticulum but with a focus on lightweight multi-hop packet routing for embedded projects. Unlike Meshtastic, which is tailored for casual LoRa communication, or Reticulum, which offers advanced networking, MeshCore balances simplicity with scalability, making it ideal for custom embedded solutions., where devices (nodes) can communicate over long distances by relaying messages through intermediate nodes. This is especially useful in off-grid, emergency, or tactical situations where traditional communication infrastructure is unavailable.
## ⚡ Key Features
* Multi-Hop Packet Routing
* Devices can forward messages across multiple nodes, extending range beyond a single radio's reach.
* Supports up to a configurable number of hops to balance network efficiency and prevent excessive traffic.
* Nodes use fixed roles where "Companion" nodes are not repeating messages at all to prevent adverse routing paths from being used.
* Supports LoRa Radios Works with Heltec, RAK Wireless, and other LoRa-based hardware.
* Decentralized & Resilient No central server or internet required; the network is self-healing.
* Low Power Consumption Ideal for battery-powered or solar-powered devices.
* Simple to Deploy Pre-built example applications make it easy to get started.
## 🎯 What Can You Use MeshCore For?
* Off-Grid Communication: Stay connected even in remote areas.
* Emergency Response & Disaster Recovery: Set up instant networks where infrastructure is down.
* Outdoor Activities: Hiking, camping, and adventure racing communication.
* Tactical & Security Applications: Military, law enforcement, and private security use cases.
* IoT & Sensor Networks: Collect data from remote sensors and relay it back to a central location.
## 🚀 How to Get Started
- Watch the [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc) by Andy Kirby.
- 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).
- Flash the MeshCore firmware on a supported device.
- Connect with a supported client.
For developers;
- Install [PlatformIO](https://docs.platformio.org) in [Visual Studio Code](https://code.visualstudio.com).
- Clone and open the MeshCore repository in Visual Studio Code.
- See the example applications you can modify and run:
- [Companion Radio](./examples/companion_radio) - For use with an external chat app, over BLE, USB or WiFi.
- [KISS Modem](./examples/kiss_modem) - Serial KISS protocol bridge for host applications. ([protocol docs](./docs/kiss_modem_protocol.md))
- [Simple Repeater](./examples/simple_repeater) - Extends network coverage by relaying messages.
- [Simple Room Server](./examples/simple_room_server) - A simple BBS server for shared Posts.
- [Simple Secure Chat](./examples/simple_secure_chat) - Secure terminal based text communication between devices.
- [Simple Sensor](./examples/simple_sensor) - Remote sensor node with telemetry and alerting.
The Simple Secure Chat example can be interacted with through the Serial Monitor in Visual Studio Code, or with a Serial USB Terminal on Android.
## ⚡️ MeshCore Flasher
We have prebuilt firmware ready to flash on supported devices.
- Launch https://meshcore.io/flasher
- Select a supported device
- Flash one of the firmware types:
- Companion, Repeater or Room Server
- Once flashing is complete, you can connect with one of the MeshCore clients below.
## 📱 MeshCore Clients
**Companion Firmware**
The companion firmware can be connected to via BLE, USB or WiFi depending on the firmware type you flashed.
- Web: https://app.meshcore.nz
- Android: https://play.google.com/store/apps/details?id=com.liamcottle.meshcore.android
- iOS: https://apps.apple.com/us/app/meshcore/id6742354151?platform=iphone
- NodeJS: https://github.com/liamcottle/meshcore.js
- Python: https://github.com/fdlamotte/meshcore-cli
**Repeater and Room Server Firmware**
The repeater and room server firmwares can be setup via USB in the web config tool.
- https://config.meshcore.io
They can also be managed via LoRa in the mobile app by using the Remote Management feature.
## 🛠 Hardware Compatibility
MeshCore is designed for devices listed in the [MeshCore Flasher](https://meshcore.io/flasher)
## 📜 License
MeshCore is open-source software released under the MIT License. You are free to use, modify, and distribute it for personal and commercial projects.
## Contributing
Please submit PR's using 'dev' as the base branch!
For minor changes just submit your PR and we'll try to review it, but for anything more 'impactful' please open an Issue first and start a discussion. Is better to sound out what it is you want to achieve first, and try to come to a consensus on what the best approach is, especially when it impacts the structure or architecture of this codebase.
Here are some general principals you should try to adhere to:
* Keep it simple. Please, don't think like a high-level lang programmer. Think embedded, and keep code concise, without any unnecessary layers.
* No dynamic memory allocation, except during setup/begin functions.
* Use the same brace and indenting style that's in the core source modules. (A .clang-format is prob going to be added soon, but please do NOT retroactively re-format existing code. This just creates unnecessary diffs that make finding problems harder)
Help us prioritize! Please react with thumbs-up to issues/PRs you care about most. We look at reaction counts when planning work.
## Road-Map / To-Do
There are a number of fairly major features in the pipeline, with no particular time-frames attached yet. In very rough chronological order:
- [X] Companion radio: UI redesign
- [X] Repeater + Room Server: add ACL's (like Sensor Node has)
- [X] Standardise Bridge mode for repeaters
- [ ] Repeater/Bridge: Standardise the Transport Codes for zoning/filtering
- [X] Core + Repeater: enhanced zero-hop neighbour discovery
- [ ] Core: round-trip manual path support
- [ ] Companion + Apps: support for multiple sub-meshes (and 'off-grid' client repeat mode)
- [ ] Core + Apps: support for LZW message compression
- [ ] Core: dynamic CR (Coding Rate) for weak vs strong hops
- [ ] Core: new framework for hosting multiple virtual nodes on one physical device
- [ ] V2 protocol spec: discussion and consensus around V2 packet protocol, including path hashes, new encryption specs, etc
## 📞 Get Support
- Report bugs and request features on the [GitHub Issues](https://github.com/ripplebiz/MeshCore/issues) page.
- Find additional guides and components on [my site](https://buymeacoffee.com/ripplebiz).
- Join [MeshCore Discord](https://meshcore.gg) to chat with the developers and get help from the community.

View File

@@ -53,11 +53,14 @@
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
]
],
"use_1200bps_touch": true,
"wait_for_upload_port": true
},
"url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/",
"vendor": "Nordic"
}
}

View File

@@ -111,6 +111,7 @@ 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 Companion Web App: [https://app.meshcore.nz](https://app.meshcore.nz)
- 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)
You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server).
@@ -193,7 +194,7 @@ Recently, as of October 2025, many regions have moved to the "narrow" setting, a
After extensive testing, many regions have switched or about to switch over to BW62.5 and SF7, 8, or 9. Narrower bandwidth setting and lower SF setting allow MeshCore's radio signals to fit between interference in the ISM band, provide for a lower noise floor, better SNR, and faster transmissions.
If you have consensus from your community in your region to update your region's preset recommendation, please post your update request on the [#meshcore-app](https://discord.com/channels/1343693475589263471/1391681655911088241) channel on the [MeshCore Discord server ](https://meshcore.gg) to let Liam Cottle know.
If you have consensus from your community in your region to update your region's preset recommendation, please post your update request on the [#meshcore-app](https://discord.com/channels/1343693475589263471/1391681655911088241) channel on the [MeshCore Discord server ](https://discord.gg/cYtQNYCCRK) to let Liam Cottle know.
@@ -401,7 +402,10 @@ Another way to download map tiles is to use this Python script to get the tiles
<https://github.com/fistulareffigy/MTD-Script>
There is also a modified script that adds additional error handling and parallel downloads:
<https://github.com/TheBestJohn/MTD-Script>
<https://discord.com/channels/826570251612323860/1330643963501351004/1338775811548905572>
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?
Once you have the tiles downloaded, copy the `\tiles` folder to the root of your T-Deck's SD card.
@@ -522,7 +526,7 @@ The third character is the capital letter 'O', not zero `0`
- Firmware repo: https://github.com/meshcore-dev/MeshCore
### 5.8. Q: How can I support MeshCore?
**A:** Provide your honest feedback on GitHub and on [MeshCore Discord server](https://meshcore.gg). Spread the word of MeshCore to your friends and communities; help them get started with MeshCore. Support Scott's MeshCore development at <https://buymeacoffee.com/ripplebiz>.
**A:** Provide your honest feedback on GitHub and on [MeshCore Discord server](https://discord.gg/BMwCtwHj5V). Spread the word of MeshCore to your friends and communities; help them get started with MeshCore. Support Scott's MeshCore development at <https://buymeacoffee.com/ripplebiz>.
Support Liam Cottle's smartphone client development by unlocking the server administration wait gate with in-app purchase
@@ -559,6 +563,10 @@ pio run -e 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?
**A:** [Liam Cottle](https://liamcottle.net)'s MeshCore web client and MeshCore Javascript library are open source under MIT license.

View File

@@ -258,7 +258,7 @@ float MyMesh::getAirtimeBudgetFactor() const {
}
int MyMesh::getInterferenceThreshold() const {
return 0; // disabled for now, until currentRSSI() problem is resolved
return 1; // non-zero enables hardware CAD (Channel Activity Detection) before TX
}
int MyMesh::calcRxDelay(float score, uint32_t air_time) const {

View File

@@ -447,6 +447,15 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
return false;
}
}
// Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1)
// https://github.com/meshcore-dev/MeshCore/issues/1223
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
double roll_dice = (double)rand() / RAND_MAX;
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
if (roll_dice > forw_prob)
return false;
}
return true;
}
@@ -550,7 +559,9 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
if (region_map.getWildcard().flags & REGION_DENY_FLOOD) {
if ((pkt->getPayloadType() == PAYLOAD_TYPE_GRP_TXT ||
pkt->getPayloadType() == PAYLOAD_TYPE_GRP_DATA) &&
region_map.getWildcard().flags & REGION_DENY_FLOOD) {
recv_pkt_region = NULL;
} else {
recv_pkt_region = &region_map.getWildcard();
@@ -884,9 +895,10 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.cr = LORA_CR;
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 12; // 12 hours
_prefs.flood_advert_interval = 0; // disabled
_prefs.flood_advert_base = 0.308f;
_prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
// bridge defaults
_prefs.bridge_enabled = 1; // enabled

View File

@@ -282,7 +282,17 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
// Limit flood advert packet forwarding using a probabilistic reduction defined by P(h) = base^(hops-1)
// https://github.com/meshcore-dev/MeshCore/issues/1223
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
double roll_dice = (double)rand() / RAND_MAX;
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
if (roll_dice > forw_prob)
return false;
}
return true;
}
@@ -642,8 +652,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.disable_fwd = 1;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 12; // 12 hours
_prefs.flood_advert_base = 0.308f;
_prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
#ifdef ROOM_PASSWORD
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
#endif

View File

@@ -3,16 +3,16 @@
/* ------------------------------ Config -------------------------------- */
#ifndef LORA_FREQ
#define LORA_FREQ 869.618
#define LORA_FREQ 915.0
#endif
#ifndef LORA_BW
#define LORA_BW 62.5
#define LORA_BW 250
#endif
#ifndef LORA_SF
#define LORA_SF 8
#define LORA_SF 10
#endif
#ifndef LORA_CR
#define LORA_CR 8
#define LORA_CR 5
#endif
#ifndef LORA_TX_POWER
#define LORA_TX_POWER 20
@@ -443,24 +443,7 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
board.setGpio(val);
}
sprintf(reply, "%x", board.getGpio());
} else if (memcmp(command, "readpin ", 8) == 0) {
uint8_t pin = atoi(&command[8]); // Pin-Nummer extrahieren
if (pin < 32) {
bool pin_state = board.readPin(pin); // Zustand des Pins auslesen
sprintf(reply, "%s", pin_state ? "ON" : "OFF");
} else {
strcpy(reply, "Err - invalid pin");
}
} else if (memcmp(command, "bin", 3) == 0) {
uint32_t gpio_state = board.getGpio(); // 32-Bit-GPIO-Zustand auslesen
// Initialisiere reply mit Nullen, um sicherzustellen, dass es sauber ist
memset(reply, 0, 33); // 32 Zeichen + Null-Terminator
// Binärstring erstellen (Bit 31 bis Bit 0)
for (int i = 0; i < 32; i++) {
reply[31 - i] = (gpio_state & (1 << i)) ? '1' : '0';
}
reply[32] = '\0'; // Null-Terminator explizit setzen
} else {
} else{
_cli.handleCommand(sender_timestamp, command, reply); // common CLI commands
}
}
@@ -742,7 +725,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
_prefs.flood_advert_interval = 0; // disabled
_prefs.disable_fwd = true;
_prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
// GPS defaults
_prefs.gps_enabled = 0;

View File

@@ -38,7 +38,7 @@
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.15.1"
#define FIRMWARE_VERSION "v1.14.1"
#endif
#define FIRMWARE_ROLE "sensor"

View File

@@ -61,7 +61,7 @@ void setup() {
#ifdef DISPLAY_CLASS
if (display.begin()) {
display.startFrame();
display.print("AOI FW! Bitte warten...");
display.print("Please wait...");
display.endFrame();
}
#endif
@@ -102,10 +102,6 @@ void setup() {
command[0] = 0;
// Konfiguriere GPIO 15 als Eingang mit Pull-Up
pinMode(15, INPUT_PULLUP);
pinMode(16, INPUT_PULLUP);
sensors.begin();
the_mesh.begin(fs);

View File

@@ -28,6 +28,7 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
-D LORA_FREQ=869.618
-D LORA_BW=62.5
-D LORA_SF=8
-D LORA_CR=8
-D ENABLE_ADVERT_ON_BOOT=1
-D ENABLE_PRIVATE_KEY_IMPORT=1 ; NOTE: comment these out for more secure firmware
-D ENABLE_PRIVATE_KEY_EXPORT=1

View File

@@ -2,6 +2,7 @@
#include "CommonCLI.h"
#include "TxtDataHelpers.h"
#include "AdvertDataHelpers.h"
#include "TxtDataHelpers.h"
#include <RTClib.h>
#ifndef BRIDGE_MAX_BAUD
@@ -87,8 +88,8 @@ 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->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
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
// next: 291
file.read((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294
// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -118,6 +119,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
// sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
_prefs->flood_advert_base = constrain(_prefs->flood_advert_base, 0, 1);
file.close();
}
@@ -178,8 +180,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
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->owner_info, sizeof(_prefs->owner_info)); // 170
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.write((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294
file.close();
}
@@ -285,7 +287,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
// change admin password
StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password));
savePrefs();
sprintf(reply, "password now: %s", _prefs->password); // echo back just to let admin know for sure!!
sprintf(reply, "password now: ");
StrHelper::strncpy(&reply[14], _prefs->password, 160-15); // echo back just to let admin know for sure!!
} else if (memcmp(command, "clear stats", 11) == 0) {
_callbacks->clearStats();
strcpy(reply, "(OK - stats reset)");
@@ -605,6 +608,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
strcpy(reply, "Error, max 64");
}
} else if (memcmp(config, "flood.advert.base ", 18) == 0) {
float f = atof(&config[18]);
if (f >= 0.0f && f <= 1.0f) {
_prefs->flood_advert_base = f;
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error: base must be between 0 and 1");
}
} else if (memcmp(config, "direct.txdelay ", 15) == 0) {
float f = atof(&config[15]);
if (f >= 0) {
@@ -726,7 +738,8 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
strcpy(reply, "Error: unsupported by this board");
};
} else {
sprintf(reply, "unknown config: %s", config);
strcpy(reply, "unknown config: ");
StrHelper::strncpy(&reply[16], config, 160-17);
}
}
@@ -781,13 +794,16 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor));
} else if (memcmp(config, "flood.max", 9) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max);
} else if (memcmp(config, "flood.advert.base", 17) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->flood_advert_base));
} else if (memcmp(config, "direct.txdelay", 14) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
} else if (memcmp(config, "owner.info", 10) == 0) {
auto start = reply;
*reply++ = '>';
*reply++ = ' ';
const char* sp = _prefs->owner_info;
while (*sp) {
while (*sp && reply - start < 159) {
*reply++ = (*sp == '\n') ? '|' : *sp; // translate newline back to orig '|'
sp++;
}

View File

@@ -42,6 +42,7 @@ struct NodePrefs { // persisted to file
uint8_t flood_max;
uint8_t interference_threshold;
uint8_t agc_reset_interval; // secs / 4
float flood_advert_base;
// Bridge settings
uint8_t bridge_enabled; // boolean
uint16_t bridge_delay; // milliseconds (default 500 ms)

View File

@@ -48,25 +48,6 @@ public:
#endif
}
// Implement an actual getGpio for ESP32 boards
uint32_t getGpio() override {
uint32_t state = 0;
for (int pin = 0; pin < 32; pin++) {
if (digitalRead(pin) == HIGH) {
state |= (1 << pin);
}
}
return state;
}
// Read the state of a single pin
public:
// Liest den Zustand eines einzelnen Pins (0-31)
bool readPin(uint8_t pin) {
if (pin >= 32) return false; // Ungültiger Pin
return digitalRead(pin) == HIGH;
}
// Temperature from ESP32 MCU
float getMCUTemperature() override {
uint32_t raw = 0;

View File

@@ -168,10 +168,20 @@ void RadioLibWrapper::onSendFinished() {
state = STATE_IDLE;
}
int16_t RadioLibWrapper::performChannelScan() {
return _radio->scanChannel();
}
bool RadioLibWrapper::isChannelActive() {
return _threshold == 0
? false // interference check is disabled
: getCurrentRSSI() > _noise_floor + _threshold;
if (_threshold == 0) return false; // interference check is disabled
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();
return result != RADIOLIB_CHANNEL_FREE;
}
float RadioLibWrapper::getLastRSSI() const {

View File

@@ -31,13 +31,14 @@ public:
bool isInRecvMode() const override;
bool isChannelActive();
bool isReceiving() override {
bool isReceiving() override {
if (isReceivingPacket()) return true;
return isChannelActive();
}
virtual float getCurrentRSSI() =0;
virtual int16_t performChannelScan();
int getNoiseFloor() const override { return _noise_floor; }
void triggerNoiseFloorCalibrate(int threshold) override;

View File

@@ -9,7 +9,7 @@ void LoRaFEMControl::init(void)
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
setLnaCanControl(true);
}

View File

@@ -16,6 +16,6 @@ class LoRaFEMControl
void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
private:
bool lna_enabled = false;
bool lna_enabled = true;
bool lna_can_control = false;
};

View File

@@ -32,7 +32,7 @@
// Power management boot protection threshold (millivolts)
// Set to 0 to disable boot protection
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV)
#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage (mV)
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
// AIN2 = P0.04 = BATTERY_PIN / PIN_VBAT_READ
#define PWRMGT_LPCOMP_AIN 2

View File

@@ -14,7 +14,7 @@ void LoRaFEMControl::init(void)
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
setLnaCanControl(true);
}

View File

@@ -16,6 +16,6 @@ class LoRaFEMControl
void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
private:
bool lna_enabled = false;
bool lna_enabled = true;
bool lna_can_control = false;
};

View File

@@ -73,7 +73,7 @@ void HeltecV4Board::begin() {
digitalWrite(PIN_ADC_CTRL, LOW);
return (5.42 * (3.3 / 1024.0) * raw) * 1000;
return (adc_mult * (3.3 / 1024.0) * raw) * 1000;
}
const char* HeltecV4Board::getManufacturerName() const {

View File

@@ -5,8 +5,16 @@
#include <helpers/ESP32Board.h>
#include <driver/rtc_io.h>
#include "LoRaFEMControl.h"
#ifndef ADC_MULTIPLIER
#define ADC_MULTIPLIER 5.42
#endif
class HeltecV4Board : public ESP32Board {
protected:
float adc_mult = ADC_MULTIPLIER;
public:
RefCountedDigitalPin periph_power;
LoRaFEMControl loRaFEMControl;
@@ -18,6 +26,14 @@ public:
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
void powerOff() override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ;
bool setAdcMultiplier(float multiplier) override {
if (multiplier == 0.0f) {
adc_mult = ADC_MULTIPLIER;
} else {
adc_mult = multiplier;
}
return true;
}
float getAdcMultiplier() const override { return adc_mult; }
const char* getManufacturerName() const override;
};

View File

@@ -102,6 +102,28 @@ lib_deps =
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:heltec_v4_expansionkit_repeater]
extends = heltec_v4_oled
build_flags =
${heltec_v4_oled.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec 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
-D ENV_PIN_SDA=4
-D ENV_PIN_SCL=3
build_src_filter = ${heltec_v4_oled.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
lib_deps =
${heltec_v4_oled.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:heltec_v4_repeater_bridge_espnow]
extends = heltec_v4_oled
build_flags =

View File

@@ -8,24 +8,47 @@
void TechoBoard::begin() {
NRF52Board::begin();
// Configure battery measurement control BEFORE Wire.begin()
// to ensure P0.02 is not claimed by another peripheral
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
pinMode(PIN_VBAT_READ, INPUT);
Wire.begin();
pinMode(SX126X_POWER_EN, OUTPUT);
digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up
delay(10);
}
uint16_t TechoBoard::getBattMilliVolts() {
int adcvalue = 0;
// Use LilyGo's exact ADC configuration
analogReference(AR_INTERNAL_3_0);
analogReadResolution(12);
delay(10);
// ADC range is 0..3000mV and resolution is 12-bit (0..4095)
adcvalue = analogRead(PIN_VBAT_READ);
// Convert the raw value to compensated mv, taking the resistor-
// divider into account (providing the actual LIPO voltage)
return (uint16_t)((float)adcvalue * REAL_VBAT_MV_PER_LSB);
// Enable battery voltage divider (MOSFET gate on P0.31)
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
digitalWrite(PIN_VBAT_MEAS_EN, HIGH);
// Reclaim P0.02 for analog input (in case another peripheral touched it)
pinMode(PIN_VBAT_READ, INPUT);
delay(10); // let divider + ADC settle
// Read and average (matching LilyGo's approach)
uint32_t sum = 0;
for (int i = 0; i < 8; i++) {
sum += analogRead(PIN_VBAT_READ);
delayMicroseconds(100);
}
uint16_t adc = sum / 8;
// Disable divider to save power
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
// LilyGo's exact formula: adc * (3000.0 / 4096.0) * 2.0
// = adc * 0.73242188 * 2.0 = adc * 1.46484375
uint16_t millivolts = (uint16_t)((float)adc * (3000.0f / 4096.0f) * 2.0f);
return millivolts;
}
#endif
#endif

View File

@@ -4,14 +4,12 @@
#include <Arduino.h>
#include <helpers/NRF52Board.h>
// built-ins
#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
#define VBAT_DIVIDER (0.5F) // 150K + 150K voltage divider on VBAT
#define VBAT_DIVIDER_COMP (2.0F) // Compensation factor for the VBAT divider
#define PIN_VBAT_READ (4)
#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
// ============================================================
// T-Echo Lite battery pins — hardcoded from LilyGo t_echo_lite_config.h
// NOT using any defines from variant.h for battery measurement
// ============================================================
#define PIN_VBAT_READ _PINNUM(0, 2) // BATTERY_ADC_DATA
#define PIN_VBAT_MEAS_EN _PINNUM(0, 31) // BATTERY_MEASUREMENT_CONTROL
class TechoBoard : public NRF52BoardDCDC {
public:
@@ -20,10 +18,11 @@ public:
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override {
return "LilyGo T-Echo";
return "LilyGo T-Echo Lite";
}
void powerOff() override {
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
#ifdef LED_RED
digitalWrite(LED_RED, LOW);
#endif
@@ -41,4 +40,4 @@ public:
#endif
sd_power_system_off();
}
};
};

View File

@@ -96,3 +96,47 @@ build_src_filter = ${LilyGo_T-Echo-Lite.build_src_filter}
lib_deps =
${LilyGo_T-Echo-Lite.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_T-Echo-Lite_non_shell_companion_radio_ble]
extends = LilyGo_T-Echo-Lite
upload_protocol = nrfutil
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
board_upload.maximum_size = 712704
build_flags =
${nrf52_base.build_flags}
-I variants/lilygo_techo_lite
-I src/helpers/nrf52
-I lib/nrf52/s140_nrf52_6.1.1_API/include
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
-I src/helpers/ui
-I examples/companion_radio/ui-new
-D LILYGO_TECHO
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_POWER_EN=30
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D P_LORA_TX_LED=LED_GREEN
-D DISABLE_DIAGNOSTIC_OUTPUT
-D ENV_INCLUDE_GPS=1
-D GPS_BAUD_RATE=9600
-D PIN_GPS_EN=GPS_EN
-D AUTO_OFF_MILLIS=0
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456
-D OFFLINE_QUEUE_SIZE=256
-D UI_RECENT_LIST_SIZE=9
-D AUTO_SHUTDOWN_MILLIVOLTS=3300
build_src_filter = ${nrf52_base.build_src_filter}
+<helpers/*.cpp>
+<TechoBoard.cpp>
+<helpers/sensors/EnvironmentSensorManager.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../variants/lilygo_techo_lite>
+<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps =
${LilyGo_T-Echo-Lite.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -24,7 +24,7 @@
#define PIN_PWR_EN _PINNUM(0, 30) // RT9080_EN
#define BATTERY_PIN _PINNUM(0, 2)
#define ADC_MULTIPLIER (4.90F)
#define ADC_MULTIPLIER (2.0F)
#define ADC_RESOLUTION (14)
#define BATTERY_SENSE_RES (12)
@@ -47,13 +47,13 @@
////////////////////////////////////////////////////////////////////////////////
// I2C pin definition
#define PIN_WIRE_SDA _PINNUM(0, 4) // (SDA)
#define PIN_WIRE_SCL _PINNUM(0, 2) // (SCL)
#define PIN_WIRE_SDA _PINNUM(1, 4) // (SDA) - per LilyGo IIC_1_SDA
#define PIN_WIRE_SCL _PINNUM(1, 2) // (SCL) - per LilyGo IIC_1_SCL
////////////////////////////////////////////////////////////////////////////////
// SPI pin definition
#define SPI_INTERFACES_COUNT _PINNUM(0, 2)
#define SPI_INTERFACES_COUNT (2)
#define PIN_SPI_MISO _PINNUM(0, 17) // (MISO)
#define PIN_SPI_MOSI _PINNUM(0, 15) // (MOSI)
@@ -149,10 +149,11 @@ extern const int SCK;
#define PIN_DISPLAY_BUSY DISP_BUSY
////////////////////////////////////////////////////////////////////////////////
// GPS
// GPS — per LilyGo t_echo_lite_config.h
// PIN_GPS_TX/RX named from GPS module's perspective
#define PIN_GPS_RX _PINNUM(1, 13) // RXD
#define PIN_GPS_TX _PINNUM(1, 15) // TXD
#define GPS_EN _PINNUM(1, 11) // POWER_RT9080_EN
#define PIN_GPS_STANDBY _PINNUM(1, 10)
#define PIN_GPS_PPS _PINNUM(0, 29) // 1PPS
#define PIN_GPS_TX _PINNUM(0, 29) // GPS UART TX → MCU RX
#define PIN_GPS_RX _PINNUM(1, 10) // GPS UART RX ← MCU TX
#define GPS_EN _PINNUM(1, 11) // GPS RT9080 power enable
#define PIN_GPS_STANDBY _PINNUM(1, 13) // GPS wake-up
#define PIN_GPS_PPS _PINNUM(1, 15) // GPS 1PPS

View File

@@ -1,10 +0,0 @@
#pragma once
#include <helpers/ESP32Board.h>
class TETHEliteBoard : public ESP32Board {
public:
const char* getManufacturerName() const override {
return "LilyGO T-ETH Elite";
}
};

View File

@@ -1,99 +0,0 @@
[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

View File

@@ -1,43 +0,0 @@
#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);
}

View File

@@ -1,20 +0,0 @@
#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();

View File

@@ -14,9 +14,12 @@ const PowerMgtConfig power_config = {
void R1NeoBoard::initiateShutdown(uint8_t reason) {
// Disable LoRa module power before shutdown
MESH_DEBUG_PRINTLN("R1Neo: shutting down");
digitalWrite(SX126X_POWER_EN, LOW);
// Signal IO controller that MCU is off, then release DCDC latch
digitalWrite(PIN_SOFT_SHUTDOWN, LOW);
digitalWrite(PIN_DCDC_EN_MCU_HOLD, LOW);
if (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
reason == SHUTDOWN_REASON_BOOT_PROTECT) {
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);

View File

@@ -18,6 +18,12 @@ public:
R1NeoBoard() : NRF52Board("R1NEO_OTA") {}
void begin();
#ifdef NRF52_POWER_MANAGEMENT
void powerOff() override {
initiateShutdown(SHUTDOWN_REASON_USER);
}
#endif
#if defined(P_LORA_TX_LED)
void onBeforeTransmit() override {
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on

View File

@@ -106,7 +106,7 @@ extern "C"
// Power management boot protection threshold (millivolts)
// Set to 0 to disable boot protection
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV)
#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage (mV)
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ
#define PWRMGT_LPCOMP_AIN 3
@@ -147,7 +147,7 @@ extern "C"
// LoRa radio module pins for RAK4631
#define P_LORA_DIO_1 (47)
#define P_LORA_NSS (42)
#define P_LORA_RESET (-1)
#define P_LORA_RESET (38)
#define P_LORA_BUSY (46)
#define P_LORA_SCLK (43)
#define P_LORA_MISO (45)

View File

@@ -77,7 +77,7 @@ static const uint8_t D10 = 10;
// Power management boot protection threshold (millivolts)
// Set to 0 to disable boot protection
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage
#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
#define PWRMGT_LPCOMP_AIN 7 // AIN7 = P0.31 = PIN_VBAT