Compare commits

...

83 Commits

Author SHA1 Message Date
Florent fec88e1300 fix tenstar c3 repeater build 2026-07-06 12:04:42 -04:00
Florent 3ee2f77877 restore display and radio poweroff in ui 2026-07-06 08:46:57 -04:00
fdlamotte 9e66a2e246 Merge pull request #2895 from IoTThinks/MCdev-Fixed-poweroff-to-uA-202607
Fixed hibernate / poweroff for BLE companions and repeaters to stay at uA
2026-07-06 08:02:23 -04:00
Kevin Le 35f654ced3 Fixed hibernate/powerOff for ESP32 boards to stay at uA 2026-07-05 23:47:27 +07:00
Kevin Le 6b205da4e9 Added missing driver/rtc_io.h 2026-07-05 23:33:56 +07:00
Kevin Le eec75eab31 Cleanup Poweroff for NRF52 boards 2026-07-05 22:10:47 +07:00
Kevin Le 7d7de88707 board->powerOff() is sufficient to power off radio, display, GPS and components 2026-07-05 21:22:13 +07:00
Kevin Le 73b7367e45 Put powerOff to NRF52Board 2026-07-05 21:20:39 +07:00
Kevin Le a92046b0eb Put powerOff and enterDeepSleep to ESP32Board 2026-07-05 21:20:13 +07:00
liamcottle b40968a0f0 stop tracking vscode extensions.json 2026-07-05 12:23:22 +12:00
ripplebiz 61e21462aa Merge pull request #2867 from entr0p1/feature/sx1262-ldo-support
SX1262 LDO Support
2026-07-04 12:35:03 +10:00
Liam Cottle acdf4e5bdc Merge pull request #2372 from jirogit/fix/region-save-return-value
fix(RegionMap): save() returns actual write success instead of hardcoded true
2026-07-03 12:40:22 +12:00
ripplebiz 33fdf67e4d Merge pull request #2874 from neilalexander/neil/replyregion
Fix `recv_pkt_region` incorrect usage
2026-07-02 22:20:30 +10:00
Neil Alexander 0d7379520f Fix recv_pkt_region incorrect usage
The `recv_pkt_region` is set when processing a flood packet in `filterRecvFloodPacket`
but direct/non-flood packets would never pass through that function, so the pointer was
not cleared for them.

`sendFloodReply` would then later use it blindly, which meant that the response would
either inherit the region from the last flood packet, or refer to a non-initialised pointer
if no region floods had been received yet.
2026-07-02 09:28:41 +01:00
ripplebiz affe61dc3b Merge pull request #2880 from ckoehler/push-sonzsorqmwqz
refactor: split MeshTables::hasSeen into pure query + markSeen
2026-07-02 13:58:48 +10:00
Christoph Koehler 4f701b7aec refactor: split MeshTables::hasSeen into pure query + markSeen
hasSeen() was simultaneously a predicate and a mutator — it inserted the
packet hash on every miss, making five call sites that only wanted to mark
a packet as sent call it with the return value discarded.

Split into:
- wasSeen()   — pure predicate, no side effects
- markSeen()  — explicit insert

All query sites now call markSeen() immediately after wasSeen() returns
false, preserving identical runtime behaviour. The five mark-only send
sites (sendFlood, sendDirect, sendZeroHop x2) now call markSeen directly.

Also fixes three bridge sites (BridgeBase, ESPNowBridge, RS232Bridge)
that had the same query+implicit-insert pattern.

Tests: add test/test_mesh_tables/ covering wasSeen purity, markSeen,
dup stats, and clear. Update SHA256 mock to produce deterministic output
(previously finalize() was a no-op). Add Packet.cpp to native build filter.
2026-07-01 21:51:38 -06:00
Liam Cottle dd87371574 Merge pull request #2873 from ckoehler/fix/nix-devshell
build: add gcc, gtest to nix shell, fix native test framework
2026-07-02 11:32:21 +12:00
Christoph Koehler 4f7c1cb88f build: add gcc, gtest to nix shell, fix native test framework 2026-07-01 08:19:27 -06:00
entr0p1 2cb3a29fa8 SX1262 LDO Support
- Added build flag "SX126X_USE_REGULATOR_LDO" (set to 1 to enable) to use LDO for the SX1262 radio instead of DC-DC when the DCDC pin isn't wired up (e.g. t-echo lite)
- Added additional debug mode diagnostic messages during SX1262 init to better highlight faults
2026-06-30 16:55:16 +10:00
Liam Cottle 1869b57d62 Merge pull request #2853 from HDDen/dev
Add Data-Type to number_allocations.md
2026-06-29 21:49:19 +12:00
HDDen 58b0f7df9c Update number_allocations.md 2026-06-29 12:41:44 +03:00
HDDen bc35208682 Update number_allocations.md
Added Data-Type range for MeshCore Images codec (range is for small reserve to future versions)
2026-06-28 00:32:22 +03:00
Scott Powell 4f8cb8db78 * ACK packets being 'clipped' (in Dispatcher send). Needed to extend max_airtime timeout calc for short packets 2026-06-27 21:06:01 +10:00
Liam Cottle 5ff39c6243 Merge pull request #2570 from IoTThinks/MCdev-Fixed-flashmode-XiaoC6-202605
Added flash_mode=dio to avoid boot loop when flashing using merge.bin
2026-06-27 11:32:10 +12:00
Liam Cottle dc200cf22d Merge pull request #2639 from sefinek/fix/typos-in-comments
fix: fix typos in source code comments
2026-06-27 11:26:54 +12:00
Liam Cottle 62b95b4e48 Merge pull request #2844 from oltaco/fix-rx-boosted-gain
Fix for Rx boosted gain on LR1110
2026-06-27 11:22:47 +12:00
taco f3d4d8cd5e always save boosted gain setting 2026-06-26 22:36:49 +10:00
Liam Cottle 3d4eec4a6a Merge pull request #1194 from ViezeVingertjes/nibble_zero_connect
Added support for the nibble zero connect
2026-06-26 13:59:59 +12:00
taco b07aba7937 fix: report error when rxgain setting is unsupported or rejected 2026-06-26 08:45:03 +10:00
taco 1f9bb67400 return bool when setting rx boost 2026-06-26 07:44:01 +10:00
ViezeVingertjes b8f1fad654 Add trailing underscore to nibble_screen_connect env names to exclude from automatic builds 2026-06-25 23:27:07 +02:00
ViezeVingertjes eb938a9b78 Address review comments and sync with dev 2026-06-25 23:26:55 +02:00
ViezeVingertjes 69df5c7a95 Remove NeoPixel configuration as its unused. 2026-06-25 23:26:55 +02:00
ViezeVingertjes 9c7d71b9be Added support for the nibble zero connect 2026-06-25 23:26:55 +02:00
taco 5d0ff7fe6e remove guard on MyMesh::setRxBoostedGain() 2026-06-26 07:06:37 +10:00
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
Sefinek 4bf391f5c3 fix: fix typos in source code comments 2026-05-28 16:38:39 +02:00
Sefinek 00d445a269 fix: fix typos in source code comments 2026-05-28 15:44:56 +02:00
Quency-D 250f448c3f Merge branch 'dev' into cli-lna-command 2026-05-20 10:13:28 +08:00
Kevin Le f29cae602f Added flash_mode=dio to avoid boot loop when flashing using merge.bin 2026-05-16 09:30:49 +07:00
Quency-D 3a546a6395 add heltec tower v2 2026-05-12 14:53:17 +08:00
Quency-D 12e6899580 Merge branch 'dev' into cli-lna-command 2026-05-09 18:19:31 +08:00
jirogit 8e787f6ee4 fix(RegionMap): save() returns actual write success instead of hardcoded true 2026-05-01 00:06:46 -07:00
Quency-D 444dcfb8fd Change write to read for radio_fem_rxgain 2026-04-27 14:06:21 +08: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
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
125 changed files with 1489 additions and 662 deletions
-9
View File
@@ -1,9 +0,0 @@
{
"recommendations": [
"pioarduino.pioarduino-ide",
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}
+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"
}
+4 -3
View File
@@ -1,11 +1,12 @@
{ pkgs ? import <nixpkgs> {} }: {pkgs ? import <nixpkgs> {}}: let
let
in in
pkgs.mkShell { pkgs.mkShell {
buildInputs = [ buildInputs = [
pkgs.platformio pkgs.platformio
pkgs.python3 pkgs.python3
pkgs.gcc
pkgs.gtest
# optional: needed as a programmer i.e. for esp32 # optional: needed as a programmer i.e. for esp32
pkgs.avrdude pkgs.avrdude
]; ];
} }
+28
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
@@ -564,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
View File
@@ -17,6 +17,7 @@ Once you have a working app/project, you need to be able to demonstrate it exist
| 0000 - 00FF | -reserved for internal use- | | | 0000 - 00FF | -reserved for internal use- | |
| 0100 | MeshCore Open | zsylvester@monitormx.com — https://github.com/zjs81/meshcore-open | | 0100 | MeshCore Open | zsylvester@monitormx.com — https://github.com/zjs81/meshcore-open |
| 0110 - 011F | Ripple | ripple_biz@protonmail.com — https://buymeacoffee.com/ripplebiz | | 0110 - 011F | Ripple | ripple_biz@protonmail.com — https://buymeacoffee.com/ripplebiz |
| 0120 | MCO Advanced | most.original.address@gmail.com — https://hdden.ru/MCOa/ |
| FF00 - FFFF | -reserved for testing/dev- | | | FF00 - FFFF | -reserved for testing/dev- | |
(add rows, inside the range 0100 - FEFF for custom apps) (add rows, inside the range 0100 - FEFF for custom apps)
+1 -1
View File
@@ -545,7 +545,7 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src
uint32_t pos = 0, found_pos = 0; uint32_t pos = 0, found_pos = 0;
uint32_t min_timestamp = 0xFFFFFFFF; uint32_t min_timestamp = 0xFFFFFFFF;
// search for matching key OR evict by oldest timestmap // search for matching key OR evict by oldest timestamp
BlobRec tmp; BlobRec tmp;
file.seek(0); file.seek(0);
while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) {
+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) {
+1
View File
@@ -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;
@@ -697,8 +697,10 @@ void UITask::shutdown(bool restart){
if (restart) { if (restart) {
_board->reboot(); _board->reboot();
} else { } else {
// still necessary until all boards are refactored to use poweroff
_display->turnOff(); _display->turnOff();
radio_driver.powerOff(); radio_driver.powerOff();
// Power off board including radio, display, GPS and components
_board->powerOff(); _board->powerOff();
} }
} }
@@ -307,7 +307,9 @@ void UITask::shutdown(bool restart){
if (restart) { if (restart) {
_board->reboot(); _board->reboot();
} else { } else {
_display->turnOff();
radio_driver.powerOff(); radio_driver.powerOff();
// Power off board including radio, display, GPS and components
_board->powerOff(); _board->powerOff();
} }
} }
@@ -568,6 +568,7 @@ void UITask::shutdown(bool restart){
} else { } else {
_display->turnOff(); _display->turnOff();
radio_driver.powerOff(); radio_driver.powerOff();
// Power off board including radio, display, GPS and components
_board->powerOff(); _board->powerOff();
} }
} }
+8 -8
View File
@@ -549,8 +549,7 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
return getRNG()->nextInt(0, 5*t + 1); return getRNG()->nextInt(0, 5*t + 1);
} }
bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) {
// just try to determine region for packet (apply later in allowPacketForward())
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD); recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
@@ -562,8 +561,7 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
} else { } else {
recv_pkt_region = NULL; recv_pkt_region = NULL;
} }
// do normal processing return Mesh::onRecvPacket(pkt);
return false;
} }
void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender,
@@ -867,6 +865,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
set_radio_at = revert_radio_at = 0; set_radio_at = revert_radio_at = 0;
_logging = false; _logging = false;
region_load_active = false; region_load_active = false;
recv_pkt_region = NULL;
#if MAX_NEIGHBOURS #if MAX_NEIGHBOURS
memset(neighbours, 0, sizeof(neighbours)); memset(neighbours, 0, sizeof(neighbours));
@@ -893,6 +892,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 +917,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 +966,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();
@@ -1059,11 +1061,9 @@ void MyMesh::setTxPower(int8_t power_dbm) {
radio_driver.setTxPower(power_dbm); radio_driver.setTxPower(power_dbm);
} }
#if defined(USE_SX1262) || defined(USE_SX1268) bool MyMesh::setRxBoostedGain(bool enable) {
void MyMesh::setRxBoostedGain(bool enable) { return radio_driver.setRxBoostedGainMode(enable);
radio_driver.setRxBoostedGainMode(enable);
} }
#endif
void MyMesh::formatNeighborsReply(char *reply) { void MyMesh::formatNeighborsReply(char *reply) {
char *dp = reply; char *dp = reply;
+6 -4
View File
@@ -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
} }
@@ -163,7 +166,7 @@ protected:
} }
#endif #endif
bool filterRecvFloodPacket(mesh::Packet* pkt) override; mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) 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;
@@ -249,7 +252,6 @@ public:
// To check if there is pending work // To check if there is pending work
bool hasPendingWork() const; bool hasPendingWork() const;
#if defined(USE_SX1262) || defined(USE_SX1268) bool setRxBoostedGain(bool enable) override;
void setRxBoostedGain(bool enable) override;
#endif
}; };
+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() {
+6 -4
View File
@@ -290,8 +290,7 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
return true; return true;
} }
bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) {
// just try to determine region for packet (apply later in allowPacketForward())
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD); recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
@@ -303,8 +302,7 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
} else { } else {
recv_pkt_region = NULL; recv_pkt_region = NULL;
} }
// do normal processing return Mesh::onRecvPacket(pkt);
return false;
} }
void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender,
@@ -627,6 +625,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_logging = false; _logging = false;
region_load_active = false; region_load_active = false;
set_radio_at = revert_radio_at = 0; set_radio_at = revert_radio_at = 0;
recv_pkt_region = NULL;
// defaults // defaults
memset(&_prefs, 0, sizeof(_prefs)); memset(&_prefs, 0, sizeof(_prefs));
@@ -650,6 +649,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 +658,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 +700,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();
+4 -1
View File
@@ -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
} }
@@ -151,7 +154,7 @@ protected:
return _prefs.multi_acks; return _prefs.multi_acks;
} }
bool filterRecvFloodPacket(mesh::Packet* pkt) override; mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) override;
bool allowPacketForward(const mesh::Packet* packet) override; bool allowPacketForward(const mesh::Packet* packet) 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;
+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();
+1
View File
@@ -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() {
+2
View File
@@ -157,6 +157,7 @@ lib_deps =
[env:native] [env:native]
platform = native platform = native
test_framework = googletest
build_flags = -std=c++17 build_flags = -std=c++17
-I src -I src
-I test/mocks -I test/mocks
@@ -164,5 +165,6 @@ test_build_src = yes
build_src_filter = build_src_filter =
-<*> -<*>
+<../src/Utils.cpp> +<../src/Utils.cpp>
+<../src/Packet.cpp>
lib_deps = lib_deps =
google/googletest @ 1.17.0 google/googletest @ 1.17.0
+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; }
+31 -16
View File
@@ -55,7 +55,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
uint16_t offset = (uint16_t)pkt->path_len << path_sz; 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->wasSeen(pkt)) {
_tables->markSeen(pkt);
// append SNR (Not hash!) // append SNR (Not hash!)
pkt->path[pkt->path_len++] = (int8_t) (pkt->getSNR()*4); pkt->path[pkt->path_len++] = (int8_t) (pkt->getSNR()*4);
@@ -89,14 +90,16 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) {
return forwardMultipartDirect(pkt); return forwardMultipartDirect(pkt);
} else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) { } else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) {
if (!_tables->hasSeen(pkt)) { // don't retransmit! if (!_tables->wasSeen(pkt)) { // don't retransmit!
_tables->markSeen(pkt);
removeSelfFromPath(pkt); removeSelfFromPath(pkt);
routeDirectRecvAcks(pkt, 0); routeDirectRecvAcks(pkt, 0);
} }
return ACTION_RELEASE; return ACTION_RELEASE;
} }
if (!_tables->hasSeen(pkt)) { if (!_tables->wasSeen(pkt)) {
_tables->markSeen(pkt);
removeSelfFromPath(pkt); removeSelfFromPath(pkt);
uint32_t d = getDirectRetransmitDelay(pkt); uint32_t d = getDirectRetransmitDelay(pkt);
@@ -117,7 +120,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
memcpy(&ack_crc, &pkt->payload[i], 4); i += 4; memcpy(&ack_crc, &pkt->payload[i], 4); i += 4;
if (i > pkt->payload_len) { if (i > pkt->payload_len) {
MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete ACK packet", getLogDateTime()); MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete ACK packet", getLogDateTime());
} else if (!_tables->hasSeen(pkt)) { } else if (!_tables->wasSeen(pkt)) {
_tables->markSeen(pkt);
onAckRecv(pkt, ack_crc); onAckRecv(pkt, ack_crc);
action = routeRecvPacket(pkt); action = routeRecvPacket(pkt);
} }
@@ -134,7 +138,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data
if (i + CIPHER_MAC_SIZE >= pkt->payload_len) { if (i + CIPHER_MAC_SIZE >= pkt->payload_len) {
MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime()); MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime());
} else if (!_tables->hasSeen(pkt)) { } else if (!_tables->wasSeen(pkt)) {
_tables->markSeen(pkt);
// NOTE: this is a 'first packet wins' impl. When receiving from multiple paths, the first to arrive wins. // NOTE: this is a 'first packet wins' impl. When receiving from multiple paths, the first to arrive wins.
// For flood mode, the path may not be the 'best' in terms of hops. // For flood mode, the path may not be the 'best' in terms of hops.
// FUTURE: could send back multiple paths, using createPathReturn(), and let sender choose which to use(?) // FUTURE: could send back multiple paths, using createPathReturn(), and let sender choose which to use(?)
@@ -155,6 +160,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;
@@ -193,7 +202,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data
if (i + 2 >= pkt->payload_len) { if (i + 2 >= pkt->payload_len) {
MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime()); MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime());
} else if (!_tables->hasSeen(pkt)) { } else if (!_tables->wasSeen(pkt)) {
_tables->markSeen(pkt);
if (self_id.isHashMatch(&dest_hash)) { if (self_id.isHashMatch(&dest_hash)) {
Identity sender(sender_pub_key); Identity sender(sender_pub_key);
@@ -220,7 +230,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data
if (i + 2 >= pkt->payload_len) { if (i + 2 >= pkt->payload_len) {
MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime()); MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime());
} else if (!_tables->hasSeen(pkt)) { } else if (!_tables->wasSeen(pkt)) {
_tables->markSeen(pkt);
// scan channels DB, for all matching hashes of 'channel_hash' (max 4 matches supported ATM) // scan channels DB, for all matching hashes of 'channel_hash' (max 4 matches supported ATM)
GroupChannel channels[4]; GroupChannel channels[4];
int num = searchChannelsByHash(&channel_hash, channels, 4); int num = searchChannelsByHash(&channel_hash, channels, 4);
@@ -251,7 +262,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete advertisement packet", getLogDateTime()); MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete advertisement packet", getLogDateTime());
} else if (self_id.matches(id.pub_key)) { } else if (self_id.matches(id.pub_key)) {
MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): receiving SELF advert packet", getLogDateTime()); MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): receiving SELF advert packet", getLogDateTime());
} else if (!_tables->hasSeen(pkt)) { } else if (!_tables->wasSeen(pkt)) {
_tables->markSeen(pkt);
uint8_t* app_data = &pkt->payload[i]; uint8_t* app_data = &pkt->payload[i];
int app_data_len = pkt->payload_len - i; int app_data_len = pkt->payload_len - i;
if (app_data_len > MAX_ADVERT_DATA_SIZE) { app_data_len = MAX_ADVERT_DATA_SIZE; } if (app_data_len > MAX_ADVERT_DATA_SIZE) { app_data_len = MAX_ADVERT_DATA_SIZE; }
@@ -278,7 +290,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
break; break;
} }
case PAYLOAD_TYPE_RAW_CUSTOM: { case PAYLOAD_TYPE_RAW_CUSTOM: {
if (pkt->isRouteDirect() && !_tables->hasSeen(pkt)) { if (pkt->isRouteDirect() && !_tables->wasSeen(pkt)) {
_tables->markSeen(pkt);
onRawDataRecv(pkt); onRawDataRecv(pkt);
//action = routeRecvPacket(pkt); don't flood route these (yet) //action = routeRecvPacket(pkt); don't flood route these (yet)
} }
@@ -296,7 +309,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
tmp.payload_len = pkt->payload_len - 1; tmp.payload_len = pkt->payload_len - 1;
memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len);
if (!_tables->hasSeen(&tmp)) { if (!_tables->wasSeen(&tmp)) {
_tables->markSeen(&tmp);
uint32_t ack_crc; uint32_t ack_crc;
memcpy(&ack_crc, tmp.payload, 4); memcpy(&ack_crc, tmp.payload, 4);
@@ -353,7 +367,8 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) {
tmp.payload_len = pkt->payload_len - 1; tmp.payload_len = pkt->payload_len - 1;
memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len);
if (!_tables->hasSeen(&tmp)) { // don't retransmit! if (!_tables->wasSeen(&tmp)) { // don't retransmit!
_tables->markSeen(&tmp);
removeSelfFromPath(&tmp); removeSelfFromPath(&tmp);
routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2) routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2)
} }
@@ -633,7 +648,7 @@ void Mesh::sendFlood(Packet* packet, uint32_t delay_millis, uint8_t path_hash_si
packet->header |= ROUTE_TYPE_FLOOD; packet->header |= ROUTE_TYPE_FLOOD;
packet->setPathHashSizeAndCount(path_hash_size, 0); packet->setPathHashSizeAndCount(path_hash_size, 0);
_tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us
uint8_t pri; uint8_t pri;
if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) {
@@ -662,7 +677,7 @@ void Mesh::sendFlood(Packet* packet, uint16_t* transport_codes, uint32_t delay_m
packet->transport_codes[1] = transport_codes[1]; packet->transport_codes[1] = transport_codes[1];
packet->setPathHashSizeAndCount(path_hash_size, 0); packet->setPathHashSizeAndCount(path_hash_size, 0);
_tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us
uint8_t pri; uint8_t pri;
if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) {
@@ -695,7 +710,7 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin
pri = 0; pri = 0;
} }
} }
_tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us
sendPacket(packet, pri, delay_millis); sendPacket(packet, pri, delay_millis);
} }
@@ -705,7 +720,7 @@ void Mesh::sendZeroHop(Packet* packet, uint32_t delay_millis) {
packet->path_len = 0; // path_len of zero means Zero Hop packet->path_len = 0; // path_len of zero means Zero Hop
_tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us
sendPacket(packet, 0, delay_millis); sendPacket(packet, 0, delay_millis);
} }
@@ -718,7 +733,7 @@ void Mesh::sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay
packet->path_len = 0; // path_len of zero means Zero Hop packet->path_len = 0; // path_len of zero means Zero Hop
_tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us
sendPacket(packet, 0, delay_millis); sendPacket(packet, 0, delay_millis);
} }
+4 -3
View File
@@ -15,8 +15,9 @@ public:
*/ */
class MeshTables { class MeshTables {
public: public:
virtual bool hasSeen(const Packet* packet) = 0; virtual bool wasSeen(const Packet* packet) = 0;
virtual void clear(const Packet* packet) = 0; // remove this packet hash from table virtual void markSeen(const Packet* packet) = 0;
virtual void clear(const Packet* packet) = 0; // remove this packet hash from table
}; };
/** /**
@@ -100,7 +101,7 @@ protected:
* \param auth_code a code to authenticate the packet * \param auth_code a code to authenticate the packet
* \param flags zero for now * \param flags zero for now
* \param path_snrs single byte SNR*4 for each hop in the path * \param path_snrs single byte SNR*4 for each hop in the path
* \param path_hashes hashes if each repeater in the path * \param path_hashes hashes of each repeater in the path
* \param path_len length of the path_snrs[] and path_hashes[] arrays * \param path_len length of the path_snrs[] and path_hashes[] arrays
*/ */
virtual void onTraceRecv(Packet* packet, uint32_t tag, uint32_t auth_code, uint8_t flags, const uint8_t* path_snrs, const uint8_t* path_hashes, uint8_t path_len) { } virtual void onTraceRecv(Packet* packet, uint32_t tag, uint32_t auth_code, uint8_t flags, const uint8_t* path_snrs, const uint8_t* path_hashes, uint8_t path_len) { }
+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; }
+1 -1
View File
@@ -25,7 +25,7 @@ namespace mesh {
#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type(uint16), data_len, blob) #define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type(uint16), data_len, blob)
#define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...) #define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
#define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra) #define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
#define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop #define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNR for each hop
#define PAYLOAD_TYPE_MULTIPART 0x0A // packet is one of a set of packets #define PAYLOAD_TYPE_MULTIPART 0x0A // packet is one of a set of packets
#define PAYLOAD_TYPE_CONTROL 0x0B // a control/discovery packet #define PAYLOAD_TYPE_CONTROL 0x0B // a control/discovery packet
//... //...
+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);
+58 -22
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();
@@ -561,13 +571,37 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0;
savePrefs(); savePrefs();
strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON");
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110)
} else if (memcmp(config, "radio.rxgain ", 13) == 0) { } else if (memcmp(config, "radio.rxgain ", 13) == 0) {
_prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0; bool enabled = memcmp(&config[13], "on", 2) == 0;
strcpy(reply, "OK"); _prefs->rx_boosted_gain = enabled;
savePrefs(); savePrefs();
_callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); if (_callbacks->setRxBoostedGain(enabled)) {
#endif strcpy(reply, "OK");
} else {
strcpy(reply, "Error: unsupported");
}
} 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 +791,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 +810,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) {
@@ -801,10 +837,14 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lat)); sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lat));
} else if (memcmp(config, "lon", 3) == 0) { } else if (memcmp(config, "lon", 3) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lon)); sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lon));
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110)
} 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 } 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());
+4 -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 {
@@ -109,8 +111,8 @@ public:
// no op by default // no op by default
}; };
virtual void setRxBoostedGain(bool enable) { virtual bool setRxBoostedGain(bool enable) {
// no op by default return false; // CommonCLI reports unsupported if not overridden by wrapper
}; };
}; };
+42
View File
@@ -1,6 +1,7 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "ESP32Board.h" #include "ESP32Board.h"
#include <target.h>
#if defined(ADMIN_PASSWORD) && !defined(DISABLE_WIFI_OTA) // Repeater or Room Server only #if defined(ADMIN_PASSWORD) && !defined(DISABLE_WIFI_OTA) // Repeater or Room Server only
#include <WiFi.h> #include <WiFi.h>
@@ -44,4 +45,45 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
} }
#endif #endif
void ESP32Board::powerOff() {
enterDeepSleep(0); // Do not wakeup
}
void ESP32Board::enterDeepSleep(uint32_t secs) {
// Power off the display if any
#ifdef DISPLAY_CLASS
display.turnOff();
#endif
// Power off LoRa
radio_driver.powerOff();
// Keep LoRa inactive during deepsleep
digitalWrite(P_LORA_NSS, HIGH);
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6)
gpio_hold_en((gpio_num_t)P_LORA_NSS);
#else
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
#endif
// Power off GPS if any
if (sensors.getLocationProvider() != NULL) {
sensors.getLocationProvider()->stop();
}
// Flush serial buffers
Serial.flush();
delay(100);
// Clear stale wakeup sources to avoid ghost wakeup
// This is required when Power Management and automatic lightsleep are enabled
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000ULL);
}
// Finally set ESP32 into deepsleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
#endif #endif
+40
View File
@@ -14,6 +14,7 @@
#include <Wire.h> #include <Wire.h>
#include "soc/rtc.h" #include "soc/rtc.h"
#include "esp_system.h" #include "esp_system.h"
#include <driver/rtc_io.h>
class ESP32Board : public mesh::MainBoard { class ESP32Board : public mesh::MainBoard {
protected: protected:
@@ -62,6 +63,9 @@ public:
return raw / 4; return raw / 4;
} }
virtual void powerOff() override;
void enterDeepSleep(uint32_t secs);
uint32_t getIRQGpio() override { uint32_t getIRQGpio() override {
return P_LORA_DIO_1; // default for SX1262 return P_LORA_DIO_1; // default for SX1262
} }
@@ -155,6 +159,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 {
-30
View File
@@ -15,8 +15,6 @@
#include "ESP32Board.h" #include "ESP32Board.h"
#include <driver/rtc_io.h>
class MeshadventurerBoard : public ESP32Board { class MeshadventurerBoard : public ESP32Board {
public: public:
@@ -35,34 +33,6 @@ public:
} }
} }
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are held on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void powerOff() override {
// TODO: re-enable this when there is a definite wake-up source pin:
// enterDeepSleep(0);
}
uint16_t getBattMilliVolts() override { uint16_t getBattMilliVolts() override {
analogReadResolution(12); analogReadResolution(12);
+32
View File
@@ -1,5 +1,6 @@
#if defined(NRF52_PLATFORM) #if defined(NRF52_PLATFORM)
#include "NRF52Board.h" #include "NRF52Board.h"
#include <target.h>
#include <bluefruit.h> #include <bluefruit.h>
#include <nrf_soc.h> #include <nrf_soc.h>
@@ -297,6 +298,37 @@ float NRF52Board::getMCUTemperature() {
return temp * 0.25f; // Convert to *C return temp * 0.25f; // Convert to *C
} }
void NRF52Board::powerOff() {
// Power off the display if any
#ifdef DISPLAY_CLASS
display.turnOff();
#endif
// Power off LoRa
radio_driver.powerOff();
// Keep LoRa inactive during deepsleep
digitalWrite(P_LORA_NSS, HIGH);
// Power off GPS if any
if(sensors.getLocationProvider() != NULL) {
sensors.getLocationProvider()->stop();
}
// Flush serial buffers
Serial.flush();
delay(100);
// Enter SYSTEMOFF
uint8_t sd_enabled = 0;
sd_softdevice_is_enabled(&sd_enabled);
if (sd_enabled) { // SoftDevice is enabled
sd_power_system_off();
} else { // SoftDevice is not enable
NRF_POWER->SYSTEMOFF = POWER_SYSTEMOFF_SYSTEMOFF_Enter;
}
}
bool NRF52Board::getBootloaderVersion(char* out, size_t max_len) { bool NRF52Board::getBootloaderVersion(char* out, size_t max_len) {
static const char BOOTLOADER_MARKER[] = "UF2 Bootloader "; static const char BOOTLOADER_MARKER[] = "UF2 Bootloader ";
const uint8_t* flash = (const uint8_t*)0x000FB000; // earliest known info.txt location is 0xFB90B, latest is 0xFCC4B const uint8_t* flash = (const uint8_t*)0x000FB000; // earliest known info.txt location is 0xFB90B, latest is 0xFCC4B
+1
View File
@@ -50,6 +50,7 @@ public:
virtual uint8_t getStartupReason() const override { return startup_reason; } virtual uint8_t getStartupReason() const override { return startup_reason; }
virtual float getMCUTemperature() override; virtual float getMCUTemperature() override;
virtual void reboot() override { NVIC_SystemReset(); } virtual void reboot() override { NVIC_SystemReset(); }
virtual void powerOff() override;
virtual bool getBootloaderVersion(char* version, size_t max_len) override; virtual bool getBootloaderVersion(char* version, size_t max_len) override;
virtual bool startOTAUpdate(const char *id, char reply[]) override; virtual bool startOTAUpdate(const char *id, char reply[]) override;
virtual void sleep(uint32_t secs) override; virtual void sleep(uint32_t secs) override;
+1 -1
View File
@@ -139,7 +139,7 @@ bool RegionMap::save(FILESYSTEM* _fs, const char* path) {
} }
} }
file.close(); file.close();
return true; return success;
} }
return false; // failed return false; // failed
} }
+10 -6
View File
@@ -31,27 +31,31 @@ public:
} }
#endif #endif
bool hasSeen(const mesh::Packet* packet) override { bool wasSeen(const mesh::Packet* packet) override {
uint8_t hash[MAX_HASH_SIZE]; uint8_t hash[MAX_HASH_SIZE];
packet->calculatePacketHash(hash); packet->calculatePacketHash(hash);
const uint8_t* sp = _hashes; const uint8_t* sp = _hashes;
for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) {
if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) {
if (packet->isRouteDirect()) { if (packet->isRouteDirect()) {
_direct_dups++; // keep some stats _direct_dups++;
} else { } else {
_flood_dups++; _flood_dups++;
} }
return true; return true;
} }
} }
memcpy(&_hashes[_next_idx*MAX_HASH_SIZE], hash, MAX_HASH_SIZE);
_next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; // cyclic table
return false; return false;
} }
void markSeen(const mesh::Packet* packet) override {
uint8_t hash[MAX_HASH_SIZE];
packet->calculatePacketHash(hash);
memcpy(&_hashes[_next_idx * MAX_HASH_SIZE], hash, MAX_HASH_SIZE);
_next_idx = (_next_idx + 1) % MAX_PACKET_HASHES;
}
void clear(const mesh::Packet* packet) override { void clear(const mesh::Packet* packet) override {
uint8_t hash[MAX_HASH_SIZE]; uint8_t hash[MAX_HASH_SIZE];
packet->calculatePacketHash(hash); packet->calculatePacketHash(hash);
+2 -1
View File
@@ -39,7 +39,8 @@ void BridgeBase::handleReceivedPacket(mesh::Packet *packet) {
return; return;
} }
if (!_seen_packets.hasSeen(packet)) { if (!_seen_packets.wasSeen(packet)) {
_seen_packets.markSeen(packet);
// bridge_delay provides a buffer to prevent immediate processing conflicts in the mesh network. // bridge_delay provides a buffer to prevent immediate processing conflicts in the mesh network.
_mgr->queueInbound(packet, millis() + _prefs->bridge_delay); _mgr->queueInbound(packet, millis() + _prefs->bridge_delay);
} else { } else {
+1 -1
View File
@@ -110,7 +110,7 @@ protected:
* @brief Common packet handling for received packets * @brief Common packet handling for received packets
* *
* Implements the standard pattern used by all bridges: * Implements the standard pattern used by all bridges:
* - Check if packet was seen before using _seen_packets.hasSeen() * - Check if packet was seen before using _seen_packets.wasSeen()
* - Queue packet for mesh processing if not seen before * - Queue packet for mesh processing if not seen before
* - Free packet if already seen to prevent duplicates * - Free packet if already seen to prevent duplicates
* *
+3 -2
View File
@@ -32,7 +32,7 @@ void ESPNowBridge::begin() {
// Initialize WiFi in station mode // Initialize WiFi in station mode
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
// Set wifi channel // Set Wi-Fi channel
if (esp_wifi_set_channel(_prefs->bridge_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) { if (esp_wifi_set_channel(_prefs->bridge_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) {
BRIDGE_DEBUG_PRINTLN("Error setting WIFI channel to %d\n", _prefs->bridge_channel); BRIDGE_DEBUG_PRINTLN("Error setting WIFI channel to %d\n", _prefs->bridge_channel);
return; return;
@@ -167,7 +167,8 @@ void ESPNowBridge::sendPacket(mesh::Packet *packet) {
return; return;
} }
if (!_seen_packets.hasSeen(packet)) { if (!_seen_packets.wasSeen(packet)) {
_seen_packets.markSeen(packet);
// Create a temporary buffer just for size calculation and reuse for actual writing // Create a temporary buffer just for size calculation and reuse for actual writing
uint8_t sizingBuffer[MAX_PAYLOAD_SIZE]; uint8_t sizingBuffer[MAX_PAYLOAD_SIZE];
uint16_t meshPacketLen = packet->writeTo(sizingBuffer); uint16_t meshPacketLen = packet->writeTo(sizingBuffer);
+2 -1
View File
@@ -115,7 +115,8 @@ void RS232Bridge::sendPacket(mesh::Packet *packet) {
return; return;
} }
if (!_seen_packets.hasSeen(packet)) { if (!_seen_packets.wasSeen(packet)) {
_seen_packets.markSeen(packet);
uint8_t buffer[MAX_SERIAL_PACKET_SIZE]; uint8_t buffer[MAX_SERIAL_PACKET_SIZE];
uint16_t len = packet->writeTo(buffer + 4); uint16_t len = packet->writeTo(buffer + 4);
+1 -1
View File
@@ -38,7 +38,7 @@ public:
* These two functions do nothing for ESP-NOW, but are needed for the * These two functions do nothing for ESP-NOW, but are needed for the
* Radio interface. * Radio interface.
*/ */
virtual void setRxBoostedGainMode(bool) { } virtual bool setRxBoostedGainMode(bool) { }
virtual bool getRxBoostedGainMode() const { return false; } virtual bool getRxBoostedGainMode() const { return false; }
uint32_t intID(); uint32_t intID();
+2 -2
View File
@@ -33,8 +33,8 @@ public:
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
void setRxBoostedGainMode(bool en) override { bool setRxBoostedGainMode(bool en) override {
((CustomLLCC68 *)_radio)->setRxBoostedGainMode(en); return ((CustomLLCC68 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
} }
bool getRxBoostedGainMode() const override { bool getRxBoostedGainMode() const override {
return ((CustomLLCC68 *)_radio)->getRxBoostedGainMode(); return ((CustomLLCC68 *)_radio)->getRxBoostedGainMode();
+7 -2
View File
@@ -26,6 +26,11 @@ public:
return rssi; return rssi;
} }
uint32_t getEstAirtimeFor(int len_bytes) override {
auto airtime = RadioLibWrapper::getEstAirtimeFor(len_bytes);
return airtime < 200 ? 200 : airtime; // at least 200 millis
}
void onSendFinished() override { void onSendFinished() override {
RadioLibWrapper::onSendFinished(); RadioLibWrapper::onSendFinished();
_radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts _radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts
@@ -36,8 +41,8 @@ public:
uint8_t getSpreadingFactor() const override { return ((CustomLR1110 *)_radio)->getSpreadingFactor(); } uint8_t getSpreadingFactor() const override { return ((CustomLR1110 *)_radio)->getSpreadingFactor(); }
void setRxBoostedGainMode(bool en) override { bool setRxBoostedGainMode(bool en) override {
((CustomLR1110 *)_radio)->setRxBoostedGainMode(en); return ((CustomLR1110 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
} }
bool getRxBoostedGainMode() const override { bool getRxBoostedGainMode() const override {
return ((CustomLR1110 *)_radio)->getRxBoostedGainMode(); return ((CustomLR1110 *)_radio)->getRxBoostedGainMode();
+14 -2
View File
@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <RadioLib.h> #include <RadioLib.h>
#include "MeshCore.h"
#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received #define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received
#define SX126X_IRQ_PREAMBLE_DETECTED 0x04 #define SX126X_IRQ_PREAMBLE_DETECTED 0x04
@@ -27,6 +28,14 @@ class CustomSX1262 : public SX1262 {
uint8_t cr = 5; uint8_t cr = 5;
#endif #endif
#ifdef SX126X_USE_REGULATOR_LDO
constexpr bool useRegulatorLDO = SX126X_USE_REGULATOR_LDO;
#else
constexpr bool useRegulatorLDO = false;
#endif
MESH_DEBUG_PRINTLN("SX1262 regulator requested: %s", useRegulatorLDO ? "LDO" : "DC-DC");
#if defined(P_LORA_SCLK) #if defined(P_LORA_SCLK)
#ifdef NRF52_PLATFORM #ifdef NRF52_PLATFORM
if (spi) { spi->setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); spi->begin(); } if (spi) { spi->setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); spi->begin(); }
@@ -42,11 +51,12 @@ class CustomSX1262 : public SX1262 {
if (spi) spi->begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); if (spi) spi->begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif #endif
#endif #endif
int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo, useRegulatorLDO);
// if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f // if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
MESH_DEBUG_PRINTLN("SX1262 init failed with error %d, retrying with TCXO at 0.0V", status);
tcxo = 0.0f; tcxo = 0.0f;
status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo, useRegulatorLDO);
} }
if (status != RADIOLIB_ERR_NONE) { if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: "); Serial.print("ERROR: radio init failed: ");
@@ -83,6 +93,8 @@ class CustomSX1262 : public SX1262 {
writeRegister(0x8B5, &r_data, 1); writeRegister(0x8B5, &r_data, 1);
#endif #endif
MESH_DEBUG_PRINTLN("SX1262 status=0x%02X device_errors=0x%04X", getStatus(), getDeviceErrors());
return true; // success return true; // success
} }
+2 -2
View File
@@ -40,8 +40,8 @@ public:
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
void setRxBoostedGainMode(bool en) override { bool setRxBoostedGainMode(bool en) override {
((CustomSX1262 *)_radio)->setRxBoostedGainMode(en); return ((CustomSX1262 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
} }
bool getRxBoostedGainMode() const override { bool getRxBoostedGainMode() const override {
return ((CustomSX1262 *)_radio)->getRxBoostedGainMode(); return ((CustomSX1262 *)_radio)->getRxBoostedGainMode();
+2 -2
View File
@@ -37,8 +37,8 @@ public:
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
void setRxBoostedGainMode(bool en) override { bool setRxBoostedGainMode(bool en) override {
((CustomSX1268 *)_radio)->setRxBoostedGainMode(en); return ((CustomSX1268 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
} }
bool getRxBoostedGainMode() const override { bool getRxBoostedGainMode() const override {
return ((CustomSX1268 *)_radio)->getRxBoostedGainMode(); return ((CustomSX1268 *)_radio)->getRxBoostedGainMode();
+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 {
+5 -2
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;
@@ -63,7 +66,7 @@ public:
float packetScore(float snr, int packet_len) override { return packetScoreInt(snr, 10, packet_len); } // assume sf=10 float packetScore(float snr, int packet_len) override { return packetScoreInt(snr, 10, packet_len); } // assume sf=10
virtual void setRxBoostedGainMode(bool) { } virtual bool setRxBoostedGainMode(bool) { return false; }
virtual bool getRxBoostedGainMode() const { return false; } virtual bool getRxBoostedGainMode() const { return false; }
}; };
+1 -1
View File
@@ -142,7 +142,7 @@ public:
case LPP_GPS: case LPP_GPS:
_pos += 9; break; _pos += 9; break;
case LPP_POLYLINE: case LPP_POLYLINE:
_pos += 8; break; // TODO: this is MINIMIUM _pos += 8; break; // TODO: this is MINIMUM
case LPP_GYROMETER: case LPP_GYROMETER:
case LPP_ACCELEROMETER: case LPP_ACCELEROMETER:
_pos += 6; break; _pos += 6; break;
+3 -3
View File
@@ -37,7 +37,7 @@ static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, l
} }
// Program a region in a block. The block must have previously // Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user. // been erased. Negative error codes are propagated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad. // May return LFS_ERR_CORRUPT if the block should be considered bad.
static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
{ {
@@ -62,7 +62,7 @@ static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, l
// Erase a block. A block must be erased before being programmed. // Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes // The state of an erased block is undefined. Negative error codes
// are propogated to the user. // are propagated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad. // May return LFS_ERR_CORRUPT if the block should be considered bad.
static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block) static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block)
{ {
@@ -87,7 +87,7 @@ static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block)
} }
// Sync the state of the underlying block device. Negative error codes // Sync the state of the underlying block device. Negative error codes
// are propogated to the user. // are propagated to the user.
static int _internal_flash_sync(const struct lfs_config *c) static int _internal_flash_sync(const struct lfs_config *c)
{ {
return LFS_ERR_OK; // don't need sync return LFS_ERR_OK; // don't need sync
+1 -1
View File
@@ -1166,7 +1166,7 @@ char DefaultFontTableLookup(const uint8_t ch) {
uint8_t last = LASTCHAR; // get last char uint8_t last = LASTCHAR; // get last char
LASTCHAR = ch; LASTCHAR = ch;
switch (last) { // conversion depnding on first UTF8-character switch (last) { // conversion depending on first UTF8-character
case 0xC2: return (uint8_t) ch; case 0xC2: return (uint8_t) ch;
case 0xC3: return (uint8_t) (ch | 0xC0); case 0xC3: return (uint8_t) (ch | 0xC0);
case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol
+25 -4
View File
@@ -3,12 +3,33 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
// Mock SHA256 class for testing // Mock SHA256 for native testing — deterministic but not cryptographic.
// Provides minimal interface to allow Utils.cpp to compile // finalize() writes real (non-garbage) output so calculatePacketHash() produces
// distinguishable results for packets with different payloads.
#include <string.h>
class SHA256 { class SHA256 {
uint8_t _state[32];
size_t _len;
public: public:
void update(const uint8_t* data, size_t len) {} SHA256() : _len(0) { memset(_state, 0, sizeof(_state)); }
void finalize(uint8_t* hash, size_t hashLen) {}
void update(const void* data, size_t len) {
const uint8_t* bytes = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < len; i++) {
uint8_t b = bytes[i];
_state[_len % 32] ^= b;
_state[(_len + 1) % 32] += (uint8_t)((b >> 1) | (b << 7));
_len++;
}
}
void finalize(uint8_t* hash, size_t hashLen) {
for (size_t i = 0; i < hashLen; i++) {
hash[i] = _state[i % 32];
}
}
void resetHMAC(const uint8_t* key, size_t keyLen) {} void resetHMAC(const uint8_t* key, size_t keyLen) {}
void finalizeHMAC(const uint8_t* key, size_t keyLen, uint8_t* hash, size_t hashLen) {} void finalizeHMAC(const uint8_t* key, size_t keyLen, uint8_t* hash, size_t hashLen) {}
}; };
@@ -0,0 +1,103 @@
#include <gtest/gtest.h>
#include "helpers/SimpleMeshTables.h"
using namespace mesh;
// Build a packet that calculatePacketHash() distinguishes by payload content.
// header selects ROUTE_TYPE_FLOOD so isRouteDirect() returns false.
static Packet makeFloodPacket(uint8_t seed) {
Packet p;
p.header = ROUTE_TYPE_FLOOD | (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT);
p.payload[0] = seed;
p.payload_len = 1;
p.path_len = 0;
return p;
}
static Packet makeDirectPacket(uint8_t seed) {
Packet p;
p.header = ROUTE_TYPE_DIRECT | (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT);
p.payload[0] = seed;
p.payload_len = 1;
p.path_len = 0;
return p;
}
// ── wasSeen: pure query ───────────────────────────────────────────────────────
TEST(SimpleMeshTables, WasSeen_ReturnsFalseForUnseen) {
SimpleMeshTables t;
Packet p = makeFloodPacket(0x01);
EXPECT_FALSE(t.wasSeen(&p));
}
// wasSeen shouldn't change state
TEST(SimpleMeshTables, WasSeen_IsPureQuery_DoesNotInsert) {
SimpleMeshTables t;
Packet p = makeFloodPacket(0x01);
EXPECT_FALSE(t.wasSeen(&p));
EXPECT_FALSE(t.wasSeen(&p));
}
// ── markSeen + wasSeen ───────────────────────────────────────────────────────
TEST(SimpleMeshTables, MarkSeen_MakesWasSeenReturnTrue) {
SimpleMeshTables t;
Packet p = makeFloodPacket(0x01);
t.markSeen(&p);
EXPECT_TRUE(t.wasSeen(&p));
}
TEST(SimpleMeshTables, MarkSeen_DoesNotAffectOtherPackets) {
SimpleMeshTables t;
Packet p1 = makeFloodPacket(0x01);
Packet p2 = makeFloodPacket(0x02);
t.markSeen(&p1);
EXPECT_FALSE(t.wasSeen(&p2));
}
// Canonical pattern used at every onRecvPacket call site:
// if (!wasSeen(pkt)) { markSeen(pkt); process(pkt); }
TEST(SimpleMeshTables, QueryThenMark_WorksCorrectly) {
SimpleMeshTables t;
Packet p = makeFloodPacket(0x01);
EXPECT_FALSE(t.wasSeen(&p));
t.markSeen(&p);
EXPECT_TRUE(t.wasSeen(&p));
}
// ── dup stats ────────────────────────────────────────────────────────────────
TEST(SimpleMeshTables, WasSeen_IncrementsFloodDupStat) {
SimpleMeshTables t;
Packet p = makeFloodPacket(0x01);
t.markSeen(&p);
t.wasSeen(&p);
EXPECT_EQ(1u, t.getNumFloodDups());
EXPECT_EQ(0u, t.getNumDirectDups());
}
TEST(SimpleMeshTables, WasSeen_IncrementsDirectDupStat) {
SimpleMeshTables t;
Packet p = makeDirectPacket(0x01);
t.markSeen(&p);
t.wasSeen(&p);
EXPECT_EQ(0u, t.getNumFloodDups());
EXPECT_EQ(1u, t.getNumDirectDups());
}
// ── clear ────────────────────────────────────────────────────────────────────
TEST(SimpleMeshTables, Clear_RemovesSeenPacket) {
SimpleMeshTables t;
Packet p = makeFloodPacket(0x01);
t.markSeen(&p);
ASSERT_TRUE(t.wasSeen(&p));
t.clear(&p);
EXPECT_FALSE(t.wasSeen(&p));
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -47,7 +47,7 @@ public:
uint32_t button_pin = PIN_BUTTON1; uint32_t button_pin = PIN_BUTTON1;
nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW);
sd_power_system_off(); NRF52Board::powerOff();
} }
}; };
@@ -47,7 +47,7 @@ public:
uint32_t button_pin = PIN_BUTTON1; uint32_t button_pin = PIN_BUTTON1;
nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW);
sd_power_system_off(); NRF52Board::powerOff();
} }
}; };
@@ -47,7 +47,7 @@ public:
uint32_t button_pin = PIN_BUTTON1; uint32_t button_pin = PIN_BUTTON1;
nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW);
sd_power_system_off(); NRF52Board::powerOff();
} }
}; };
@@ -38,7 +38,7 @@ public:
uint32_t button_pin = PIN_BUTTON1; uint32_t button_pin = PIN_BUTTON1;
nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW);
sd_power_system_off(); NRF52Board::powerOff();
} }
}; };
-27
View File
@@ -20,33 +20,6 @@ void HeltecE213Board::begin() {
} }
} }
void HeltecE213Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void HeltecE213Board::powerOff() {
enterDeepSleep(0);
}
uint16_t HeltecE213Board::getBattMilliVolts() { uint16_t HeltecE213Board::getBattMilliVolts() {
analogReadResolution(10); analogReadResolution(10);
digitalWrite(PIN_ADC_CTRL, HIGH); digitalWrite(PIN_ADC_CTRL, HIGH);
-3
View File
@@ -3,7 +3,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <helpers/RefCountedDigitalPin.h> #include <helpers/RefCountedDigitalPin.h>
#include <helpers/ESP32Board.h> #include <helpers/ESP32Board.h>
#include <driver/rtc_io.h>
class HeltecE213Board : public ESP32Board { class HeltecE213Board : public ESP32Board {
@@ -13,8 +12,6 @@ public:
HeltecE213Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } HeltecE213Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { }
void begin(); void begin();
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
void powerOff() override;
uint16_t getBattMilliVolts() override; uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ; const char* getManufacturerName() const override ;
}; };
-27
View File
@@ -20,33 +20,6 @@ void HeltecE290Board::begin() {
} }
} }
void HeltecE290Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void HeltecE290Board::powerOff() {
enterDeepSleep(0);
}
uint16_t HeltecE290Board::getBattMilliVolts() { uint16_t HeltecE290Board::getBattMilliVolts() {
analogReadResolution(10); analogReadResolution(10);
digitalWrite(PIN_ADC_CTRL, HIGH); digitalWrite(PIN_ADC_CTRL, HIGH);
-3
View File
@@ -3,7 +3,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <helpers/RefCountedDigitalPin.h> #include <helpers/RefCountedDigitalPin.h>
#include <helpers/ESP32Board.h> #include <helpers/ESP32Board.h>
#include <driver/rtc_io.h>
class HeltecE290Board : public ESP32Board { class HeltecE290Board : public ESP32Board {
@@ -13,8 +12,6 @@ public:
HeltecE290Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { } HeltecE290Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { }
void begin(); void begin();
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
void powerOff() override;
uint16_t getBattMilliVolts() override; uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ; const char* getManufacturerName() const 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;
+22 -8
View File
@@ -112,15 +112,29 @@ void T096Board::variant_shutdown() {
} }
void T096Board::powerOff() { void T096Board::powerOff() {
#if ENV_INCLUDE_GPS == 1 loRaFEMControl.setSleepModeEnable();
pinMode(PIN_GPS_EN, OUTPUT); nrf_gpio_cfg_default(PIN_GPS_EN); // 363uA down to 39uA
digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); NRF52Board::powerOff();
#endif
loRaFEMControl.setSleepModeEnable();
variant_shutdown();
sd_power_system_off();
} }
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 -29
View File
@@ -81,34 +81,6 @@ uint16_t T1Board::getBattMilliVolts() {
} }
void T1Board::variant_shutdown() { void T1Board::variant_shutdown() {
nrf_gpio_cfg_default(PIN_TFT_CS);
nrf_gpio_cfg_default(PIN_TFT_DC);
nrf_gpio_cfg_default(PIN_TFT_SDA);
nrf_gpio_cfg_default(PIN_TFT_SCL);
nrf_gpio_cfg_default(PIN_TFT_RST);
nrf_gpio_cfg_default(PIN_TFT_LEDA_CTL);
nrf_gpio_cfg_default(PIN_TFT_VDD_CTL);
nrf_gpio_cfg_default(PIN_WIRE_SDA);
nrf_gpio_cfg_default(PIN_WIRE_SCL);
nrf_gpio_cfg_default(LORA_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_SPI1_MOSI);
nrf_gpio_cfg_default(PIN_SPI1_SCK);
nrf_gpio_cfg_default(PIN_GPS_RESET);
nrf_gpio_cfg_default(PIN_GPS_EN);
nrf_gpio_cfg_default(PIN_GPS_PPS);
nrf_gpio_cfg_default(PIN_GPS_RX);
nrf_gpio_cfg_default(PIN_GPS_TX);
nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_1); nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_1);
nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_2); nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_2);
@@ -127,7 +99,7 @@ void T1Board::variant_shutdown() {
void T1Board::powerOff() { void T1Board::powerOff() {
variant_shutdown(); variant_shutdown();
sd_power_system_off(); NRF52Board::powerOff();
} }
const char* T1Board::getManufacturerName() const { const char* T1Board::getManufacturerName() const {
+2 -5
View File
@@ -50,10 +50,7 @@ public:
#ifdef LED_PIN #ifdef LED_PIN
digitalWrite(LED_PIN, HIGH); digitalWrite(LED_PIN, HIGH);
#endif #endif
#if ENV_INCLUDE_GPS == 1
pinMode(GPS_EN, OUTPUT); NRF52Board::powerOff();
digitalWrite(GPS_EN, LOW);
#endif
sd_power_system_off();
} }
}; };
+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;
-27
View File
@@ -20,33 +20,6 @@ void HeltecT190Board::begin() {
} }
} }
void HeltecT190Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void HeltecT190Board::powerOff() {
enterDeepSleep(0);
}
uint16_t HeltecT190Board::getBattMilliVolts() { uint16_t HeltecT190Board::getBattMilliVolts() {
analogReadResolution(10); analogReadResolution(10);
digitalWrite(PIN_ADC_CTRL, HIGH); digitalWrite(PIN_ADC_CTRL, HIGH);
-3
View File
@@ -3,7 +3,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <helpers/RefCountedDigitalPin.h> #include <helpers/RefCountedDigitalPin.h>
#include <helpers/ESP32Board.h> #include <helpers/ESP32Board.h>
#include <driver/rtc_io.h>
class HeltecT190Board : public ESP32Board { class HeltecT190Board : public ESP32Board {
@@ -13,8 +12,6 @@ public:
HeltecT190Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } HeltecT190Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { }
void begin(); void begin();
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
void powerOff() override;
uint16_t getBattMilliVolts() override; uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ; const char* getManufacturerName() const override ;
@@ -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
@@ -35,33 +35,12 @@ void HeltecTrackerV2Board::begin() {
loRaFEMControl.setRxModeEnable(); loRaFEMControl.setRxModeEnable();
} }
void HeltecTrackerV2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { void HeltecTrackerV2Board::powerOff() {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Turn off PA
digitalWrite(P_LORA_PA_POWER, LOW);
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep ESP32Board::powerOff();
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void HeltecTrackerV2Board::powerOff() {
enterDeepSleep(0);
} }
uint16_t HeltecTrackerV2Board::getBattMilliVolts() { uint16_t HeltecTrackerV2Board::getBattMilliVolts() {
@@ -82,3 +61,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();
}
@@ -3,7 +3,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <helpers/RefCountedDigitalPin.h> #include <helpers/RefCountedDigitalPin.h>
#include <helpers/ESP32Board.h> #include <helpers/ESP32Board.h>
#include <driver/rtc_io.h>
#include "LoRaFEMControl.h" #include "LoRaFEMControl.h"
class HeltecTrackerV2Board : public ESP32Board { class HeltecTrackerV2Board : public ESP32Board {
@@ -17,9 +16,11 @@ public:
void begin(); void begin();
void onBeforeTransmit(void) override; void onBeforeTransmit(void) override;
void onAfterTransmit(void) override; void onAfterTransmit(void) override;
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
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;
-25
View File
@@ -7,8 +7,6 @@
#define PIN_VBAT_READ 37 #define PIN_VBAT_READ 37
#define PIN_LED_BUILTIN 25 #define PIN_LED_BUILTIN 25
#include <driver/rtc_io.h>
class HeltecV2Board : public ESP32Board { class HeltecV2Board : public ESP32Board {
public: public:
void begin() { void begin() {
@@ -26,29 +24,6 @@ public:
} }
} }
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
uint16_t getBattMilliVolts() override { uint16_t getBattMilliVolts() override {
analogReadResolution(10); analogReadResolution(10);
-29
View File
@@ -17,8 +17,6 @@
#define PIN_ADC_CTRL_ACTIVE LOW #define PIN_ADC_CTRL_ACTIVE LOW
#define PIN_ADC_CTRL_INACTIVE HIGH #define PIN_ADC_CTRL_INACTIVE HIGH
#include <driver/rtc_io.h>
class HeltecV3Board : public ESP32Board { class HeltecV3Board : public ESP32Board {
private: private:
bool adc_active_state; bool adc_active_state;
@@ -52,33 +50,6 @@ public:
} }
} }
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void powerOff() override {
enterDeepSleep(0);
}
uint16_t getBattMilliVolts() override { uint16_t getBattMilliVolts() override {
analogReadResolution(10); analogReadResolution(10);
digitalWrite(PIN_ADC_CTRL, adc_active_state); digitalWrite(PIN_ADC_CTRL, adc_active_state);
+23 -26
View File
@@ -32,33 +32,12 @@ void HeltecV4Board::begin() {
loRaFEMControl.setRxModeEnable(); loRaFEMControl.setRxModeEnable();
} }
void HeltecV4Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { void HeltecV4Board::powerOff() {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Turn off PA
digitalWrite(P_LORA_PA_POWER, LOW);
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep ESP32Board::powerOff();
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void HeltecV4Board::powerOff() {
enterDeepSleep(0);
} }
uint16_t HeltecV4Board::getBattMilliVolts() { uint16_t HeltecV4Board::getBattMilliVolts() {
@@ -83,3 +62,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 -2
View File
@@ -3,7 +3,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <helpers/RefCountedDigitalPin.h> #include <helpers/RefCountedDigitalPin.h>
#include <helpers/ESP32Board.h> #include <helpers/ESP32Board.h>
#include <driver/rtc_io.h>
#include "LoRaFEMControl.h" #include "LoRaFEMControl.h"
#ifndef ADC_MULTIPLIER #ifndef ADC_MULTIPLIER
@@ -23,8 +22,10 @@ public:
void begin(); void begin();
void onBeforeTransmit(void) override; void onBeforeTransmit(void) override;
void onAfterTransmit(void) override; void onAfterTransmit(void) override;
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;
-4
View File
@@ -37,8 +37,4 @@ public:
digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off
} }
#endif #endif
void powerOff() override {
sd_power_system_off();
}
}; };
@@ -48,12 +48,10 @@ public:
} }
void powerOff() override { void powerOff() override {
// power off system
NRF52Board::powerOff();
// turn off 3.3v // turn off 3.3v
digitalWrite(RT9080_EN, LOW); digitalWrite(RT9080_EN, LOW);
// power off system
sd_power_system_off();
} }
}; };
-24
View File
@@ -3,7 +3,6 @@
#include <Wire.h> #include <Wire.h>
#include <Arduino.h> #include <Arduino.h>
#include "helpers/ESP32Board.h" #include "helpers/ESP32Board.h"
#include <driver/rtc_io.h>
#define PIN_VBAT_READ 4 #define PIN_VBAT_READ 4
#define BATTERY_SAMPLES 8 #define BATTERY_SAMPLES 8
@@ -23,29 +22,6 @@ public:
} }
#endif #endif
void enterDeepSleep(uint32_t secs, int pin_wake_btn) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
uint16_t getBattMilliVolts() { uint16_t getBattMilliVolts() {
#if defined(PIN_VBAT_READ) && defined(ADC_MULTIPLIER) #if defined(PIN_VBAT_READ) && defined(ADC_MULTIPLIER)
analogReadResolution(12); analogReadResolution(12);
+1 -1
View File
@@ -24,6 +24,7 @@ public:
} }
void powerOff() override { void powerOff() override {
NRF52Board::powerOff();
#ifdef LED_RED #ifdef LED_RED
digitalWrite(LED_RED, HIGH); digitalWrite(LED_RED, HIGH);
#endif #endif
@@ -39,6 +40,5 @@ public:
#ifdef PIN_PWR_EN #ifdef PIN_PWR_EN
digitalWrite(PIN_PWR_EN, LOW); digitalWrite(PIN_PWR_EN, LOW);
#endif #endif
sd_power_system_off();
} }
}; };
@@ -91,7 +91,7 @@ void TechoCardBoard::powerOff() {
nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
turnOffLeds(); turnOffLeds();
digitalWrite(PIN_PWR_EN, LOW); digitalWrite(PIN_PWR_EN, LOW);
sd_power_system_off(); NRF52Board::powerOff();
} }
#endif #endif
+2 -1
View File
@@ -22,6 +22,8 @@ public:
} }
void powerOff() override { void powerOff() override {
NRF52Board::powerOff();
digitalWrite(PIN_VBAT_MEAS_EN, LOW); digitalWrite(PIN_VBAT_MEAS_EN, LOW);
#ifdef LED_RED #ifdef LED_RED
digitalWrite(LED_RED, LOW); digitalWrite(LED_RED, LOW);
@@ -38,6 +40,5 @@ public:
#ifdef PIN_PWR_EN #ifdef PIN_PWR_EN
digitalWrite(PIN_PWR_EN, LOW); digitalWrite(PIN_PWR_EN, LOW);
#endif #endif
sd_power_system_off();
} }
}; };
-4
View File
@@ -32,8 +32,4 @@ public:
const char* getManufacturerName() const override { const char* getManufacturerName() const override {
return "Heltec MeshPocket"; return "Heltec MeshPocket";
} }
void powerOff() override {
sd_power_system_off();
}
}; };
-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"'
+1 -1
View File
@@ -60,7 +60,7 @@ public:
nrf_gpio_cfg_sense_input(g_ADigitalPinMap[PIN_USER_BTN], NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); nrf_gpio_cfg_sense_input(g_ADigitalPinMap[PIN_USER_BTN], NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
#endif #endif
sd_power_system_off(); NRF52Board::powerOff();
} }
}; };
@@ -65,7 +65,7 @@ public:
#ifdef BUTTON_PIN #ifdef BUTTON_PIN
nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
#endif #endif
sd_power_system_off(); NRF52Board::powerOff();
} }
#if defined(P_LORA_TX_LED) #if defined(P_LORA_TX_LED)

Some files were not shown because too many files have changed in this diff Show More