Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1c61ed5
Update README.md
mattzzw Feb 27, 2026
9831fd1
Update README.md
mattzzw Feb 27, 2026
ac23a76
Merge branch 'meshcore-dev:main' into main
mattzzw Mar 3, 2026
821efdd
Merge branch 'meshcore-dev:main' into main
mattzzw Mar 6, 2026
c724f8c
Merge branch 'meshcore-dev:main' into main
mattzzw Mar 17, 2026
9dd7ddf
Merge branch 'meshcore-dev:main' into main
mattzzw Mar 29, 2026
d16f465
Update README.md
mattzzw Mar 29, 2026
1eb1419
Merge branch 'meshcore-dev:main' into main
mattzzw Apr 5, 2026
2fb2630
Merge branch 'meshcore-dev:main' into main
mattzzw Apr 20, 2026
06e0273
* CommonCLI: bounds check added to "unknown config:" replies
ripplebiz Apr 21, 2026
aa796e8
* CommonCLI: more reply bounds checking
ripplebiz Apr 21, 2026
321d5a7
Add Heltec V4 set adc.multiplier
Apr 19, 2026
1f066ad
Implement proper shutdown procedure for R1 neo
weebl2000 Apr 21, 2026
c33639e
Add sanity build for LR1110 and SX1276 too
weebl2000 Mar 13, 2026
b4f690b
Fix FEM/LNA enbaled by default for Heltec T096, Heltec Wireless Track…
weebl2000 Apr 20, 2026
46b5076
Fix RAK4631 SX1262 hardware pin config
weebl2000 Mar 12, 2026
8217a67
Fixes #1183 — T-Echo Lite incorrect battery voltage
pelgraine Apr 10, 2026
e902b8f
feat(techo-lite): add Non-Shell (screenless) companion BLE variant
jirogit Apr 21, 2026
1cb6c7a
Limit flood advert packet forwarding, implements #1223
mattzzw Jan 7, 2026
3955a96
Limit flood advert packet forwarding for roomservers as well
mattzzw Jan 9, 2026
fe7c388
Add cli config flood.advert.base
mattzzw Jan 13, 2026
d81626e
Update examples/simple_repeater/MyMesh.cpp
mattzzw Mar 5, 2026
2d2c047
Update examples/simple_room_server/MyMesh.cpp
mattzzw Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/pr-build-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ jobs:
- wio-e5-mini_repeater
# ESP32-C6
- LilyGo_Tlora_C6_repeater_
# LR1110 (nRF52)
- wio_wm1110_repeater
# SX1276 (ESP32)
- Tbeam_SX1276_repeater

steps:
- name: Clone Repo
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

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.
Expand Down
7 changes: 5 additions & 2 deletions boards/t-echo.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
]
],
"use_1200bps_touch": true,
"wait_for_upload_port": true
},
"url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/",
"vendor": "Nordic"
}
}
10 changes: 10 additions & 0 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,15 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
return false;
}
}
// Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1)
// https://github.com/meshcore-dev/MeshCore/issues/1223
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
double roll_dice = (double)rand() / RAND_MAX;
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
if (roll_dice > forw_prob)
return false;
}

return true;
}

Expand Down Expand Up @@ -885,6 +894,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 12; // 12 hours
_prefs.flood_advert_base = 0.308f;
_prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled

Expand Down
13 changes: 12 additions & 1 deletion examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,17 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {

bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;

// Limit flood advert packet forwarding using a probabilistic reduction defined by P(h) = base^(hops-1)
// https://github.com/meshcore-dev/MeshCore/issues/1223
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
double roll_dice = (double)rand() / RAND_MAX;
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
if (roll_dice > forw_prob)
return false;
}

return true;
}

Expand Down Expand Up @@ -642,6 +652,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.disable_fwd = 1;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 12; // 12 hours
_prefs.flood_advert_base = 0.308f;
_prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled
#ifdef ROOM_PASSWORD
Expand Down
30 changes: 23 additions & 7 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "CommonCLI.h"
#include "TxtDataHelpers.h"
#include "AdvertDataHelpers.h"
#include "TxtDataHelpers.h"
#include <RTClib.h>

#ifndef BRIDGE_MAX_BAUD
Expand Down Expand Up @@ -87,8 +88,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.read((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294

// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
Expand Down Expand Up @@ -118,6 +119,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {

// sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
_prefs->flood_advert_base = constrain(_prefs->flood_advert_base, 0, 1);

file.close();
}
Expand Down Expand Up @@ -178,8 +180,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.write((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294

file.close();
}
Expand Down Expand Up @@ -285,7 +287,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
// change admin password
StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password));
savePrefs();
sprintf(reply, "password now: %s", _prefs->password); // echo back just to let admin know for sure!!
sprintf(reply, "password now: ");
StrHelper::strncpy(&reply[14], _prefs->password, 160-15); // echo back just to let admin know for sure!!
} else if (memcmp(command, "clear stats", 11) == 0) {
_callbacks->clearStats();
strcpy(reply, "(OK - stats reset)");
Expand Down Expand Up @@ -605,6 +608,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
strcpy(reply, "Error, max 64");
}
} else if (memcmp(config, "flood.advert.base ", 18) == 0) {
float f = atof(&config[18]);
if (f >= 0.0f && f <= 1.0f) {
_prefs->flood_advert_base = f;
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error: base must be between 0 and 1");
}
} else if (memcmp(config, "direct.txdelay ", 15) == 0) {
float f = atof(&config[15]);
if (f >= 0) {
Expand Down Expand Up @@ -726,7 +738,8 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
strcpy(reply, "Error: unsupported by this board");
};
} else {
sprintf(reply, "unknown config: %s", config);
strcpy(reply, "unknown config: ");
StrHelper::strncpy(&reply[16], config, 160-17);
}
}

Expand Down Expand Up @@ -781,13 +794,16 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor));
} else if (memcmp(config, "flood.max", 9) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max);
} else if (memcmp(config, "flood.advert.base", 17) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->flood_advert_base));
} else if (memcmp(config, "direct.txdelay", 14) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
} else if (memcmp(config, "owner.info", 10) == 0) {
auto start = reply;
*reply++ = '>';
*reply++ = ' ';
const char* sp = _prefs->owner_info;
while (*sp) {
while (*sp && reply - start < 159) {
*reply++ = (*sp == '\n') ? '|' : *sp; // translate newline back to orig '|'
sp++;
}
Expand Down
1 change: 1 addition & 0 deletions src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct NodePrefs { // persisted to file
uint8_t flood_max;
uint8_t interference_threshold;
uint8_t agc_reset_interval; // secs / 4
float flood_advert_base;
// Bridge settings
uint8_t bridge_enabled; // boolean
uint16_t bridge_delay; // milliseconds (default 500 ms)
Expand Down
2 changes: 1 addition & 1 deletion variants/heltec_t096/LoRaFEMControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ void LoRaFEMControl::init(void)
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
setLnaCanControl(true);
}

Expand Down
2 changes: 1 addition & 1 deletion variants/heltec_t096/LoRaFEMControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class LoRaFEMControl
void setLnaCanControl(bool can_control) { lna_can_control = can_control; }

private:
bool lna_enabled = false;
bool lna_enabled = true;
bool lna_can_control = false;
};
2 changes: 1 addition & 1 deletion variants/heltec_tracker_v2/LoRaFEMControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void LoRaFEMControl::init(void)
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT);
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
setLnaCanControl(true);
}

Expand Down
2 changes: 1 addition & 1 deletion variants/heltec_tracker_v2/LoRaFEMControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class LoRaFEMControl
void setLnaCanControl(bool can_control) { lna_can_control = can_control; }

private:
bool lna_enabled = false;
bool lna_enabled = true;
bool lna_can_control = false;
};
2 changes: 1 addition & 1 deletion variants/heltec_v4/HeltecV4Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ void HeltecV4Board::begin() {

digitalWrite(PIN_ADC_CTRL, LOW);

return (5.42 * (3.3 / 1024.0) * raw) * 1000;
return (adc_mult * (3.3 / 1024.0) * raw) * 1000;
}

const char* HeltecV4Board::getManufacturerName() const {
Expand Down
20 changes: 18 additions & 2 deletions variants/heltec_v4/HeltecV4Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@
#include <helpers/ESP32Board.h>
#include <driver/rtc_io.h>
#include "LoRaFEMControl.h"

#ifndef ADC_MULTIPLIER
#define ADC_MULTIPLIER 5.42
#endif

class HeltecV4Board : public ESP32Board {

protected:
float adc_mult = ADC_MULTIPLIER;

public:
RefCountedDigitalPin periph_power;
LoRaFEMControl loRaFEMControl;
Expand All @@ -18,6 +26,14 @@ class HeltecV4Board : public ESP32Board {
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
void powerOff() override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ;

bool setAdcMultiplier(float multiplier) override {
if (multiplier == 0.0f) {
adc_mult = ADC_MULTIPLIER;
} else {
adc_mult = multiplier;
}
return true;
}
float getAdcMultiplier() const override { return adc_mult; }
const char* getManufacturerName() const override;
};
43 changes: 33 additions & 10 deletions variants/lilygo_techo_lite/TechoBoard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,47 @@
void TechoBoard::begin() {
NRF52Board::begin();

// Configure battery measurement control BEFORE Wire.begin()
// to ensure P0.02 is not claimed by another peripheral
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
pinMode(PIN_VBAT_READ, INPUT);

Wire.begin();

pinMode(SX126X_POWER_EN, OUTPUT);
digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up
delay(10);
}

uint16_t TechoBoard::getBattMilliVolts() {
int adcvalue = 0;

// Use LilyGo's exact ADC configuration
analogReference(AR_INTERNAL_3_0);
analogReadResolution(12);
delay(10);

// ADC range is 0..3000mV and resolution is 12-bit (0..4095)
adcvalue = analogRead(PIN_VBAT_READ);
// Convert the raw value to compensated mv, taking the resistor-
// divider into account (providing the actual LIPO voltage)
return (uint16_t)((float)adcvalue * REAL_VBAT_MV_PER_LSB);
// Enable battery voltage divider (MOSFET gate on P0.31)
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
digitalWrite(PIN_VBAT_MEAS_EN, HIGH);

// Reclaim P0.02 for analog input (in case another peripheral touched it)
pinMode(PIN_VBAT_READ, INPUT);
delay(10); // let divider + ADC settle

// Read and average (matching LilyGo's approach)
uint32_t sum = 0;
for (int i = 0; i < 8; i++) {
sum += analogRead(PIN_VBAT_READ);
delayMicroseconds(100);
}
uint16_t adc = sum / 8;

// Disable divider to save power
digitalWrite(PIN_VBAT_MEAS_EN, LOW);

// LilyGo's exact formula: adc * (3000.0 / 4096.0) * 2.0
// = adc * 0.73242188 * 2.0 = adc * 1.46484375
uint16_t millivolts = (uint16_t)((float)adc * (3000.0f / 4096.0f) * 2.0f);

return millivolts;
}
#endif
#endif
19 changes: 9 additions & 10 deletions variants/lilygo_techo_lite/TechoBoard.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
#include <Arduino.h>
#include <helpers/NRF52Board.h>

// built-ins
#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096

#define VBAT_DIVIDER (0.5F) // 150K + 150K voltage divider on VBAT
#define VBAT_DIVIDER_COMP (2.0F) // Compensation factor for the VBAT divider

#define PIN_VBAT_READ (4)
#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
// ============================================================
// T-Echo Lite battery pins — hardcoded from LilyGo t_echo_lite_config.h
// NOT using any defines from variant.h for battery measurement
// ============================================================
#define PIN_VBAT_READ _PINNUM(0, 2) // BATTERY_ADC_DATA
#define PIN_VBAT_MEAS_EN _PINNUM(0, 31) // BATTERY_MEASUREMENT_CONTROL

class TechoBoard : public NRF52BoardDCDC {
public:
Expand All @@ -20,10 +18,11 @@ class TechoBoard : public NRF52BoardDCDC {
uint16_t getBattMilliVolts() override;

const char* getManufacturerName() const override {
return "LilyGo T-Echo";
return "LilyGo T-Echo Lite";
}

void powerOff() override {
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
#ifdef LED_RED
digitalWrite(LED_RED, LOW);
#endif
Expand All @@ -41,4 +40,4 @@ class TechoBoard : public NRF52BoardDCDC {
#endif
sd_power_system_off();
}
};
};
Loading