Compare commits

...

35 Commits

Author SHA1 Message Date
Matthias Wientapper
b7a3fb122c pr1810, modify to not completely block flood adverts 2026-04-24 21:35:55 +02:00
Matthias Wientapper
c0a107cb23 platformio.ini: set LORA_CR to 8 2026-04-24 21:35:55 +02:00
Matthias Wientapper
e9e98f1c87 rak4631, Xiao NRf, T114: set lockout voltage to 0V 2026-04-24 21:35:55 +02:00
Matthias Wientapper
c41309601d set default: disable flood adverts 2026-04-24 21:35:55 +02:00
Matthias Wientapper
30d4059a8c Integration of upstrem PR #1810 2026-04-24 21:35:55 +02:00
Matthias Wientapper
08b25cf724 Integration of upstrem PR #1727 2026-04-24 21:35:53 +02:00
Matthias Wientapper
f7f6dbc22d Integration of upstrem PR #1338 2026-04-24 21:35:51 +02:00
mattzzw
2d2c0475ac Update examples/simple_room_server/MyMesh.cpp
Fix pow() and rand() are calculated for every forwarded packet, but we only need it for flood advert. Also fixes 'paket' typo.

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

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

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

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

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

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

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

Tested on T-Echo Lite Non-Shell (USB-C, no display). Battery readings match
Heltec V4 reference within 10mV.
2026-04-23 13:48:03 +02:00
Wessel Nieboer
46b5076082 Fix RAK4631 SX1262 hardware pin config 2026-04-23 13:48:03 +02:00
Wessel Nieboer
b4f690bfeb Fix FEM/LNA enbaled by default for Heltec T096, Heltec Wireless Tracker v2
Should only really be disabled when it causes issues.
2026-04-23 13:48:03 +02:00
Wessel Nieboer
c33639ee3c Add sanity build for LR1110 and SX1276 too 2026-04-23 13:48:03 +02:00
Wessel Nieboer
1f066ad9c4 Implement proper shutdown procedure for R1 neo
fixes #2361
2026-04-23 13:48:03 +02:00
Marco
321d5a7e98 Add Heltec V4 set adc.multiplier 2026-04-23 13:48:03 +02:00
Scott Powell
aa796e8623 * CommonCLI: more reply bounds checking 2026-04-23 13:48:03 +02:00
Scott Powell
06e0273e1f * CommonCLI: bounds check added to "unknown config:" replies 2026-04-23 13:48:03 +02:00
mattzzw
2fb2630f40 Merge branch 'meshcore-dev:main' into main 2026-04-20 12:02:54 +02:00
mattzzw
1eb14190d1 Merge branch 'meshcore-dev:main' into main 2026-04-05 17:11:27 +02:00
Wessel Nieboer
dc9e6ae893 Prevent packet errors from growing 2026-04-04 13:18:42 +02:00
Wessel Nieboer
de97baba4f Just check for not channel free 2026-04-04 13:18:42 +02:00
Wessel Nieboer
8744213945 Also return busy if preamble detected 2026-04-04 13:18:42 +02:00
Wessel Nieboer
952d12c8b9 Use hardware channel activity detection for checking interference 2026-04-04 13:18:41 +02:00
mattzzw
d16f465363 Update README.md 2026-03-29 12:14:47 +02:00
mattzzw
9dd7ddf6cb Merge branch 'meshcore-dev:main' into main 2026-03-29 10:29:48 +02:00
mattzzw
c724f8cc9f Merge branch 'meshcore-dev:main' into main 2026-03-17 10:22:56 +01:00
mattzzw
821efddbe9 Merge branch 'meshcore-dev:main' into main 2026-03-06 19:44:51 +01:00
mattzzw
ac23a76f97 Merge branch 'meshcore-dev:main' into main 2026-03-03 21:01:59 +01:00
mattzzw
9831fd1470 Update README.md
Add MeshCore-Evo description
2026-02-27 09:11:52 +01:00
mattzzw
1c61ed57a6 Update README.md
Add MeshCore-Evo description
2026-02-27 09:06:32 +01:00
Stefan Berthold
f864c5f547 allow direct message paths when denyf * is set 2026-02-23 23:33:41 +01:00
13 changed files with 81 additions and 18 deletions

View File

@@ -1,3 +1,18 @@
## About MeshCore-Evo
This is a friendly fork of the MeshCore project.
Its aim is to provide repeater firmwares with a few additional pending upstream PRs/improvements (i.e. PRs/improvements that are available to the MeshCore project but have not yet been merged in the upstream repo or PRs that might never be merged upstream for some reason).
These changes are intended to help mitigating challenges in big or dense meshes, e.g.:
- Dealing with flood advert traffic
- improving `denyf *` handling
This list might change any time.
Refer to the [release notes](https://github.com/mattzzw/MeshCore-Evo/releases) for an up-to-date list of the applied changes.
## About MeshCore ## About MeshCore
MeshCore is a lightweight, portable C++ library that enables multi-hop packet routing for embedded projects using LoRa and other packet radios. It is designed for developers who want to create resilient, decentralized communication networks that work without the internet. MeshCore is a lightweight, portable C++ library that enables multi-hop packet routing for embedded projects using LoRa and other packet radios. It is designed for developers who want to create resilient, decentralized communication networks that work without the internet.

View File

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

View File

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

View File

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

View File

@@ -725,7 +725,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
_prefs.flood_advert_interval = 0; // disabled _prefs.flood_advert_interval = 0; // disabled
_prefs.disable_fwd = true; _prefs.disable_fwd = true;
_prefs.flood_max = 64; _prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled _prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
// GPS defaults // GPS defaults
_prefs.gps_enabled = 0; _prefs.gps_enabled = 0;

View File

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

View File

@@ -88,8 +88,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.read((uint8_t *)&_prefs->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->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
// next: 291 file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294
// 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);
@@ -119,6 +119,7 @@ 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->flood_advert_base = constrain(_prefs->flood_advert_base, 0, 1);
file.close(); file.close();
} }
@@ -179,8 +180,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.write((uint8_t *)&_prefs->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->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
// next: 291 file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294
file.close(); file.close();
} }
@@ -607,6 +608,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else { } else {
strcpy(reply, "Error, max 64"); strcpy(reply, "Error, max 64");
} }
} else if (memcmp(config, "flood.advert.base ", 18) == 0) {
float f = atof(&config[18]);
if (f >= 0.0f && f <= 1.0f) {
_prefs->flood_advert_base = f;
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error: base must be between 0 and 1");
}
} else if (memcmp(config, "direct.txdelay ", 15) == 0) { } else if (memcmp(config, "direct.txdelay ", 15) == 0) {
float f = atof(&config[15]); float f = atof(&config[15]);
if (f >= 0) { if (f >= 0) {
@@ -784,6 +794,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor));
} else if (memcmp(config, "flood.max", 9) == 0) { } else if (memcmp(config, "flood.max", 9) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); sprintf(reply, "> %d", (uint32_t)_prefs->flood_max);
} else if (memcmp(config, "flood.advert.base", 17) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->flood_advert_base));
} else if (memcmp(config, "direct.txdelay", 14) == 0) { } else if (memcmp(config, "direct.txdelay", 14) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
} else if (memcmp(config, "owner.info", 10) == 0) { } else if (memcmp(config, "owner.info", 10) == 0) {

View File

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

View File

@@ -168,10 +168,20 @@ 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 if (_threshold == 0) return false; // interference check is disabled
? false // interference check is disabled
: getCurrentRSSI() > _noise_floor + _threshold; int16_t result = performChannelScan();
// scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY
// via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't
// try to read a non-existent packet and count a spurious recv error.
state = STATE_IDLE;
startRecv();
return result != RADIOLIB_CHANNEL_FREE;
} }
float RadioLibWrapper::getLastRSSI() const { float RadioLibWrapper::getLastRSSI() const {

View File

@@ -38,6 +38,7 @@ public:
} }
virtual float getCurrentRSSI() =0; virtual float getCurrentRSSI() =0;
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;

View File

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

View File

@@ -106,7 +106,7 @@ extern "C"
// Power management boot protection threshold (millivolts) // Power management boot protection threshold (millivolts)
// Set to 0 to disable boot protection // Set to 0 to disable boot protection
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV) #define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage (mV)
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF) // LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ // AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ
#define PWRMGT_LPCOMP_AIN 3 #define PWRMGT_LPCOMP_AIN 3

View File

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