mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-24 19:50:35 +00:00
Compare commits
11 Commits
TinyFontTe
...
v2.7.14.e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4df6627ab1 | ||
|
|
e9590003f4 | ||
|
|
1c0c6b2736 | ||
|
|
602945f66b | ||
|
|
b86827967e | ||
|
|
b707001873 | ||
|
|
85afd706fd | ||
|
|
e76013fb60 | ||
|
|
b25797e1b3 | ||
|
|
bdb3fb1477 | ||
|
|
7eca061f01 |
@@ -8,15 +8,15 @@ plugins:
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.489
|
||||
- renovate@41.169.1
|
||||
- checkov@3.2.492
|
||||
- renovate@42.5.4
|
||||
- prettier@3.6.2
|
||||
- trufflehog@3.90.12
|
||||
- trufflehog@3.90.13
|
||||
- yamllint@1.37.1
|
||||
- bandit@1.8.6
|
||||
- trivy@0.67.2
|
||||
- taplo@0.10.0
|
||||
- ruff@0.14.3
|
||||
- ruff@0.14.4
|
||||
- isort@7.0.0
|
||||
- markdownlint@0.45.0
|
||||
- oxipng@9.1.5
|
||||
@@ -26,9 +26,9 @@ lint:
|
||||
- hadolint@2.14.0
|
||||
- shfmt@3.6.0
|
||||
- shellcheck@0.11.0
|
||||
- black@25.9.0
|
||||
- black@25.11.0
|
||||
- git-diff-check
|
||||
- gitleaks@8.28.0
|
||||
- gitleaks@8.29.0
|
||||
- clang-format@16.0.3
|
||||
ignore:
|
||||
- linters: [ALL]
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.14" date="2025-11-03">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.14</url>
|
||||
</release>
|
||||
<release version="2.7.13" date="2025-10-11">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13</url>
|
||||
</release>
|
||||
|
||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
||||
meshtasticd (2.7.14.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.14
|
||||
|
||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Mon, 03 Nov 2025 16:11:31 +0000
|
||||
|
||||
meshtasticd (2.7.13.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.13
|
||||
|
||||
@@ -120,7 +120,7 @@ lib_deps =
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/19b7855e9a1d9deff37391659ca7194e4ef57c43.zip
|
||||
https://github.com/meshtastic/device-ui/archive/28167c67dfd13015a0b5eef1828f95fe8e3ab7c3.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
|
||||
@@ -250,6 +250,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// -----------------------------------------------------------------------------
|
||||
#define FT6336U_ADDR 0x48
|
||||
#define CST328_ADDR 0x1A
|
||||
#define CHSC6X_ADDR 0x2E
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
|
||||
|
||||
@@ -82,7 +82,10 @@ class ScanI2C
|
||||
BHI260AP,
|
||||
BMM150,
|
||||
TSL2561,
|
||||
DRV2605
|
||||
DRV2605,
|
||||
BH1750,
|
||||
DA217,
|
||||
CHSC6X
|
||||
} DeviceType;
|
||||
|
||||
// typedef uint8_t DeviceAddress;
|
||||
|
||||
@@ -485,7 +485,26 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(CST328_ADDR, CST328, "CST328", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(LTR553ALS_ADDR, LTR553ALS, "LTR553ALS", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(CHSC6X_ADDR, CHSC6X, "CHSC6X", (uint8_t)addr.address);
|
||||
case LTR553ALS_ADDR:
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x86), 1); // Part ID register
|
||||
if (registerValue == 0x92) { // LTR553ALS Part ID
|
||||
type = LTR553ALS;
|
||||
logFoundDevice("LTR553ALS", (uint8_t)addr.address);
|
||||
} else {
|
||||
// Test BH1750 - send power on command
|
||||
i2cBus->beginTransmission(addr.address);
|
||||
i2cBus->write(0x01); // Power On command
|
||||
uint8_t bh1750_error = i2cBus->endTransmission();
|
||||
if (bh1750_error == 0) {
|
||||
type = BH1750;
|
||||
logFoundDevice("BH1750", (uint8_t)addr.address);
|
||||
} else {
|
||||
LOG_INFO("Device found at address 0x%x was not able to be enumerated", (uint8_t)addr.address);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
SCAN_SIMPLE_CASE(BHI260AP_ADDR, BHI260AP, "BHI260AP", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
|
||||
|
||||
@@ -422,7 +422,57 @@ static LGFX *tft = nullptr;
|
||||
|
||||
#elif defined(ST7789_CS)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
#ifdef HELTEC_V4_TFT
|
||||
#include "chsc6x.h"
|
||||
#include "lgfx/v1/Touch.hpp"
|
||||
namespace lgfx
|
||||
{
|
||||
inline namespace v1
|
||||
{
|
||||
class TOUCH_CHSC6X : public ITouch
|
||||
{
|
||||
public:
|
||||
TOUCH_CHSC6X(void)
|
||||
{
|
||||
_cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
|
||||
_cfg.x_min = 0;
|
||||
_cfg.x_max = 240;
|
||||
_cfg.y_min = 0;
|
||||
_cfg.y_max = 320;
|
||||
};
|
||||
|
||||
bool init(void) override
|
||||
{
|
||||
if (chsc6xTouch == nullptr) {
|
||||
chsc6xTouch = new chsc6x(&Wire1, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN);
|
||||
}
|
||||
chsc6xTouch->chsc6x_init();
|
||||
return true;
|
||||
};
|
||||
|
||||
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override
|
||||
{
|
||||
uint16_t raw_x, raw_y;
|
||||
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y) == 0) {
|
||||
tp[0].x = 320 - 1 - raw_y;
|
||||
tp[0].y = 240 - 1 - raw_x;
|
||||
tp[0].size = 1;
|
||||
tp[0].id = 1;
|
||||
return 1;
|
||||
}
|
||||
tp[0].size = 0;
|
||||
return 0;
|
||||
};
|
||||
|
||||
void wakeup(void) override{};
|
||||
void sleep(void) override{};
|
||||
|
||||
private:
|
||||
chsc6x *chsc6xTouch = nullptr;
|
||||
};
|
||||
} // namespace v1
|
||||
} // namespace lgfx
|
||||
#endif
|
||||
class LGFX : public lgfx::LGFX_Device
|
||||
{
|
||||
lgfx::Panel_ST7789 _panel_instance;
|
||||
@@ -431,6 +481,8 @@ class LGFX : public lgfx::LGFX_Device
|
||||
#if HAS_TOUCHSCREEN
|
||||
#if defined(T_WATCH_S3) || defined(ELECROW)
|
||||
lgfx::Touch_FT5x06 _touch_instance;
|
||||
#elif defined(HELTEC_V4_TFT)
|
||||
lgfx::TOUCH_CHSC6X _touch_instance;
|
||||
#else
|
||||
lgfx::Touch_GT911 _touch_instance;
|
||||
#endif
|
||||
@@ -464,9 +516,9 @@ class LGFX : public lgfx::LGFX_Device
|
||||
{ // Set the display panel control.
|
||||
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
|
||||
|
||||
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
|
||||
cfg.pin_rst = -1; // Pin number where RST is connected (-1 = disable)
|
||||
cfg.pin_busy = -1; // Pin number where BUSY is connected (-1 = disable)
|
||||
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
|
||||
cfg.pin_rst = ST7789_RESET; // Pin number where RST is connected (-1 = disable)
|
||||
cfg.pin_busy = ST7789_BUSY; // Pin number where BUSY is connected (-1 = disable)
|
||||
|
||||
// The following setting values are general initial values for each panel, so please comment out any
|
||||
// unknown items and try them.
|
||||
|
||||
@@ -936,7 +936,9 @@ void menuHandler::BluetoothToggleMenu()
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 3;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 1 || selected == 2) {
|
||||
if (selected == 0)
|
||||
return;
|
||||
else if (selected != (config.bluetooth.enabled ? 1 : 2)) {
|
||||
InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
|
||||
inputBroker->injectInputEvent(&event);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ int InputBroker::handleInputEvent(const InputEvent *event)
|
||||
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
||||
|
||||
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
|
||||
moduleConfig.external_notification.enabled) {
|
||||
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
|
||||
externalNotificationModule->stopNow();
|
||||
}
|
||||
|
||||
|
||||
@@ -653,7 +653,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
|
||||
|
||||
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \
|
||||
defined(ELECROW_PANEL)) && \
|
||||
defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT)) && \
|
||||
HAS_TFT
|
||||
// switch BT off by default; use TFT programming mode or hotkey to enable
|
||||
config.bluetooth.enabled = false;
|
||||
@@ -978,12 +978,25 @@ void NodeDB::installDefaultChannels()
|
||||
channelFile.version = DEVICESTATE_CUR_VER;
|
||||
}
|
||||
|
||||
void NodeDB::resetNodes()
|
||||
void NodeDB::resetNodes(bool keepFavorites)
|
||||
{
|
||||
if (!config.position.fixed_position)
|
||||
clearLocalPosition();
|
||||
numMeshNodes = 1;
|
||||
std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), meshtastic_NodeInfoLite());
|
||||
if (keepFavorites) {
|
||||
LOG_INFO("Clearing node database - preserving favorites");
|
||||
for (size_t i = 0; i < meshNodes->size(); i++) {
|
||||
meshtastic_NodeInfoLite &node = meshNodes->at(i);
|
||||
if (i > 0 && !node.is_favorite) {
|
||||
node = meshtastic_NodeInfoLite();
|
||||
} else {
|
||||
numMeshNodes += 1;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
LOG_INFO("Clearing node database - removing favorites");
|
||||
std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), meshtastic_NodeInfoLite());
|
||||
}
|
||||
devicestate.has_rx_text_message = false;
|
||||
devicestate.has_rx_waypoint = false;
|
||||
saveNodeDatabaseToDisk();
|
||||
|
||||
@@ -229,7 +229,8 @@ class NodeDB
|
||||
*/
|
||||
size_t getNumOnlineMeshNodes(bool localOnly = false);
|
||||
|
||||
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(NodeNum nodeNum);
|
||||
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(bool keepFavorites = false),
|
||||
removeNodeByNum(NodeNum nodeNum);
|
||||
|
||||
bool factoryReset(bool eraseBleBonds = false);
|
||||
|
||||
|
||||
@@ -289,7 +289,12 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
case meshtastic_AdminMessage_nodedb_reset_tag: {
|
||||
disableBluetooth();
|
||||
LOG_INFO("Initiate node-db reset");
|
||||
nodeDB->resetNodes();
|
||||
// CLIENT_BASE, ROUTER and ROUTER_LATE are able to preserve the remaining hop count when relaying a packet via a
|
||||
// favorited node, so ensure that their favorites are kept on reset
|
||||
bool rolePreference =
|
||||
isOneOf(config.device.role, meshtastic_Config_DeviceConfig_Role_CLIENT_BASE,
|
||||
meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_ROUTER_LATE);
|
||||
nodeDB->resetNodes(rolePreference ? rolePreference : r->nodedb_reset);
|
||||
reboot(DEFAULT_REBOOT_SECONDS);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,113 @@ extern graphics::Screen *screen;
|
||||
|
||||
TraceRouteModule *traceRouteModule;
|
||||
|
||||
void TraceRouteModule::setResultText(const String &text)
|
||||
{
|
||||
resultText = text;
|
||||
resultLines.clear();
|
||||
resultLinesDirty = true;
|
||||
}
|
||||
|
||||
void TraceRouteModule::clearResultLines()
|
||||
{
|
||||
resultLines.clear();
|
||||
resultLinesDirty = false;
|
||||
}
|
||||
#if HAS_SCREEN
|
||||
void TraceRouteModule::rebuildResultLines(OLEDDisplay *display)
|
||||
{
|
||||
if (!display) {
|
||||
resultLinesDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
resultLines.clear();
|
||||
|
||||
if (resultText.length() == 0) {
|
||||
resultLinesDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int maxWidth = display->getWidth() - 4;
|
||||
if (maxWidth <= 0) {
|
||||
resultLinesDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int textLength = resultText.length();
|
||||
|
||||
while (start <= textLength) {
|
||||
int newlinePos = resultText.indexOf('\n', start);
|
||||
String segment;
|
||||
|
||||
if (newlinePos != -1) {
|
||||
segment = resultText.substring(start, newlinePos);
|
||||
start = newlinePos + 1;
|
||||
} else {
|
||||
segment = resultText.substring(start);
|
||||
start = textLength + 1;
|
||||
}
|
||||
|
||||
if (segment.length() == 0) {
|
||||
resultLines.push_back("");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (display->getStringWidth(segment) <= maxWidth) {
|
||||
resultLines.push_back(segment);
|
||||
continue;
|
||||
}
|
||||
|
||||
String remaining = segment;
|
||||
|
||||
while (remaining.length() > 0) {
|
||||
String tempLine = "";
|
||||
int lastGoodBreak = -1;
|
||||
bool lineComplete = false;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
|
||||
char ch = remaining.charAt(i);
|
||||
String testLine = tempLine + ch;
|
||||
|
||||
if (display->getStringWidth(testLine) > maxWidth) {
|
||||
if (lastGoodBreak >= 0) {
|
||||
resultLines.push_back(remaining.substring(0, lastGoodBreak + 1));
|
||||
remaining = remaining.substring(lastGoodBreak + 1);
|
||||
lineComplete = true;
|
||||
break;
|
||||
} else if (tempLine.length() > 0) {
|
||||
resultLines.push_back(tempLine);
|
||||
remaining = remaining.substring(i);
|
||||
lineComplete = true;
|
||||
break;
|
||||
} else {
|
||||
resultLines.push_back(String(ch));
|
||||
remaining = remaining.substring(i + 1);
|
||||
lineComplete = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tempLine = testLine;
|
||||
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')' || ch == ',') {
|
||||
lastGoodBreak = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lineComplete) {
|
||||
if (tempLine.length() > 0) {
|
||||
resultLines.push_back(tempLine);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultLinesDirty = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
|
||||
{
|
||||
// We only alter the packet in alterReceivedProtobuf()
|
||||
@@ -406,7 +513,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
||||
if (node == 0 || node == NODENUM_BROADCAST) {
|
||||
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Invalid node";
|
||||
setResultText("Invalid node");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
|
||||
@@ -420,7 +527,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
||||
if (node == nodeDB->getNodeNum()) {
|
||||
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Cannot trace self";
|
||||
setResultText("Cannot trace self");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
|
||||
@@ -447,6 +554,8 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
||||
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
||||
bannerText = String("Wait for ") + String(wait) + String("s");
|
||||
runState = TRACEROUTE_STATE_COOLDOWN;
|
||||
resultText = "";
|
||||
clearResultLines();
|
||||
|
||||
requestFocus();
|
||||
UIFrameEvent e;
|
||||
@@ -459,6 +568,8 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
||||
tracingNode = node;
|
||||
lastTraceRouteTime = now;
|
||||
runState = TRACEROUTE_STATE_TRACKING;
|
||||
resultText = "";
|
||||
clearResultLines();
|
||||
bannerText = String("Tracing ") + getNodeName(node);
|
||||
|
||||
LOG_INFO("TraceRoute UI: Starting trace route to node 0x%08x, requesting focus", node);
|
||||
@@ -501,7 +612,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
||||
} else {
|
||||
LOG_ERROR("MeshService is NULL!");
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Service unavailable";
|
||||
setResultText("Service unavailable");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
|
||||
@@ -514,7 +625,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
||||
} else {
|
||||
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Failed to send";
|
||||
setResultText("Failed to send");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
|
||||
@@ -532,7 +643,7 @@ void TraceRouteModule::launch(NodeNum node)
|
||||
if (node == 0 || node == NODENUM_BROADCAST) {
|
||||
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Invalid node";
|
||||
setResultText("Invalid node");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
|
||||
@@ -546,7 +657,7 @@ void TraceRouteModule::launch(NodeNum node)
|
||||
if (node == nodeDB->getNodeNum()) {
|
||||
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Cannot trace self";
|
||||
setResultText("Cannot trace self");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
|
||||
@@ -568,6 +679,8 @@ void TraceRouteModule::launch(NodeNum node)
|
||||
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
||||
bannerText = String("Wait for ") + String(wait) + String("s");
|
||||
runState = TRACEROUTE_STATE_COOLDOWN;
|
||||
resultText = "";
|
||||
clearResultLines();
|
||||
|
||||
requestFocus();
|
||||
UIFrameEvent e;
|
||||
@@ -580,6 +693,8 @@ void TraceRouteModule::launch(NodeNum node)
|
||||
runState = TRACEROUTE_STATE_TRACKING;
|
||||
tracingNode = node;
|
||||
lastTraceRouteTime = now;
|
||||
resultText = "";
|
||||
clearResultLines();
|
||||
bannerText = String("Tracing ") + getNodeName(node);
|
||||
|
||||
requestFocus();
|
||||
@@ -614,14 +729,14 @@ void TraceRouteModule::launch(NodeNum node)
|
||||
} else {
|
||||
LOG_ERROR("MeshService is NULL!");
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Service unavailable";
|
||||
setResultText("Service unavailable");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "Failed to send";
|
||||
setResultText("Failed to send");
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
}
|
||||
@@ -629,7 +744,7 @@ void TraceRouteModule::launch(NodeNum node)
|
||||
|
||||
void TraceRouteModule::handleTraceRouteResult(const String &result)
|
||||
{
|
||||
resultText = result;
|
||||
setResultText(result);
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultShowTime = millis();
|
||||
tracingNode = 0;
|
||||
@@ -679,83 +794,15 @@ void TraceRouteModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
if (resultText.length() > 0) {
|
||||
std::vector<String> lines;
|
||||
String currentLine = "";
|
||||
int maxWidth = display->getWidth() - 4;
|
||||
|
||||
int start = 0;
|
||||
int newlinePos = resultText.indexOf('\n', start);
|
||||
|
||||
while (newlinePos != -1 || start < static_cast<int>(resultText.length())) {
|
||||
String segment;
|
||||
if (newlinePos != -1) {
|
||||
segment = resultText.substring(start, newlinePos);
|
||||
start = newlinePos + 1;
|
||||
newlinePos = resultText.indexOf('\n', start);
|
||||
} else {
|
||||
segment = resultText.substring(start);
|
||||
start = resultText.length();
|
||||
}
|
||||
|
||||
if (display->getStringWidth(segment) <= maxWidth) {
|
||||
lines.push_back(segment);
|
||||
} else {
|
||||
// Try to break at better positions (space, >, <, -)
|
||||
String remaining = segment;
|
||||
|
||||
while (remaining.length() > 0) {
|
||||
String tempLine = "";
|
||||
int lastGoodBreak = -1;
|
||||
bool lineComplete = false;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
|
||||
char ch = remaining.charAt(i);
|
||||
String testLine = tempLine + ch;
|
||||
|
||||
if (display->getStringWidth(testLine) > maxWidth) {
|
||||
if (lastGoodBreak >= 0) {
|
||||
// Break at the last good position
|
||||
lines.push_back(remaining.substring(0, lastGoodBreak + 1));
|
||||
remaining = remaining.substring(lastGoodBreak + 1);
|
||||
lineComplete = true;
|
||||
break;
|
||||
} else if (tempLine.length() > 0) {
|
||||
lines.push_back(tempLine);
|
||||
remaining = remaining.substring(i);
|
||||
lineComplete = true;
|
||||
break;
|
||||
} else {
|
||||
// Single character exceeds width
|
||||
lines.push_back(String(ch));
|
||||
remaining = remaining.substring(i + 1);
|
||||
lineComplete = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tempLine = testLine;
|
||||
// Mark good break positions
|
||||
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')') {
|
||||
lastGoodBreak = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lineComplete) {
|
||||
// Reached end of remaining text
|
||||
if (tempLine.length() > 0) {
|
||||
lines.push_back(tempLine);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (resultLinesDirty) {
|
||||
rebuildResultLines(display);
|
||||
}
|
||||
|
||||
int lineHeight = FONT_HEIGHT_SMALL + 1; // Use proper font height with 1px spacing
|
||||
for (size_t i = 0; i < lines.size(); i++) {
|
||||
for (size_t i = 0; i < resultLines.size(); i++) {
|
||||
int lineY = contentStartY + (i * lineHeight);
|
||||
if (lineY + FONT_HEIGHT_SMALL <= display->getHeight()) {
|
||||
display->drawString(x + 2, lineY, lines[i]);
|
||||
display->drawString(x + 2, lineY, resultLines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -779,7 +826,7 @@ int32_t TraceRouteModule::runOnce()
|
||||
if (runState == TRACEROUTE_STATE_TRACKING && now - lastTraceRouteTime > trackingTimeoutMs) {
|
||||
LOG_INFO("TraceRoute timeout, no response received");
|
||||
runState = TRACEROUTE_STATE_RESULT;
|
||||
resultText = "No response received";
|
||||
setResultText("No response received");
|
||||
resultShowTime = now;
|
||||
tracingNode = 0;
|
||||
|
||||
@@ -815,6 +862,8 @@ int32_t TraceRouteModule::runOnce()
|
||||
// Cooldown finished
|
||||
LOG_INFO("TraceRoute cooldown finished, returning to IDLE");
|
||||
runState = TRACEROUTE_STATE_IDLE;
|
||||
resultText = "";
|
||||
clearResultLines();
|
||||
bannerText = "";
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
@@ -828,6 +877,7 @@ int32_t TraceRouteModule::runOnce()
|
||||
LOG_INFO("TraceRoute result display timeout, returning to IDLE");
|
||||
runState = TRACEROUTE_STATE_IDLE;
|
||||
resultText = "";
|
||||
clearResultLines();
|
||||
bannerText = "";
|
||||
tracingNode = 0;
|
||||
UIFrameEvent e;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#if HAS_SCREEN
|
||||
#include "OLEDDisplayUi.h"
|
||||
#endif
|
||||
#include <vector>
|
||||
|
||||
#define ROUTE_SIZE sizeof(((meshtastic_RouteDiscovery *)0)->route) / sizeof(((meshtastic_RouteDiscovery *)0)->route[0])
|
||||
|
||||
@@ -49,6 +50,11 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
void setResultText(const String &text);
|
||||
void clearResultLines();
|
||||
#if HAS_SCREEN
|
||||
void rebuildResultLines(OLEDDisplay *display);
|
||||
#endif
|
||||
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
|
||||
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
|
||||
|
||||
@@ -74,6 +80,8 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
|
||||
unsigned long trackingTimeoutMs = 10000;
|
||||
String bannerText;
|
||||
String resultText;
|
||||
std::vector<String> resultLines;
|
||||
bool resultLinesDirty = false;
|
||||
NodeNum tracingNode = 0;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
@@ -59,7 +59,27 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length);
|
||||
return;
|
||||
}
|
||||
|
||||
const meshtastic_Channel &ch = channels.getByName(e.channel_id);
|
||||
// Find channel by channel_id and check downlink_enabled
|
||||
if (!(strcmp(e.channel_id, "PKI") == 0 ||
|
||||
(strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && ch.settings.downlink_enabled))) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool anyChannelHasDownlink = false;
|
||||
size_t numChan = channels.getNumChannels();
|
||||
for (size_t i = 0; i < numChan; ++i) {
|
||||
const auto &c = channels.getByIndex(i);
|
||||
if (c.settings.downlink_enabled) {
|
||||
anyChannelHasDownlink = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(e.channel_id, "PKI") == 0 && !anyChannelHasDownlink) {
|
||||
return;
|
||||
}
|
||||
// Generate node ID from nodenum for comparison
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
if (strcmp(e.gateway_id, nodeId.c_str()) == 0) {
|
||||
@@ -77,11 +97,6 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
return;
|
||||
}
|
||||
|
||||
// Find channel by channel_id and check downlink_enabled
|
||||
if (!(strcmp(e.channel_id, "PKI") == 0 ||
|
||||
(strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && ch.settings.downlink_enabled))) {
|
||||
return;
|
||||
}
|
||||
LOG_INFO("Received MQTT topic %s, len=%u", topic, length);
|
||||
if (e.packet->hop_limit > HOP_MAX || e.packet->hop_start > HOP_MAX) {
|
||||
LOG_INFO("Invalid hop_limit(%u) or hop_start(%u)", e.packet->hop_limit, e.packet->hop_start);
|
||||
|
||||
@@ -13,8 +13,8 @@ static const uint8_t LED_BUILTIN = 35;
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 3;
|
||||
static const uint8_t SCL = 4;
|
||||
static const uint8_t SDA = 4;
|
||||
static const uint8_t SCL = 3;
|
||||
|
||||
static const uint8_t SS = 8;
|
||||
static const uint8_t MOSI = 10;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[env:heltec-v4]
|
||||
[heltec_v4_base]
|
||||
extends = esp32s3_base
|
||||
board = heltec_v4
|
||||
board_check = true
|
||||
@@ -7,3 +7,106 @@ build_flags =
|
||||
${esp32s3_base.build_flags}
|
||||
-D HELTEC_V4
|
||||
-I variants/esp32s3/heltec_v4
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
|
||||
|
||||
[env:heltec-v4]
|
||||
extends = heltec_v4_base
|
||||
build_flags =
|
||||
${heltec_v4_base.build_flags}
|
||||
-D HELTEC_V4_OLED
|
||||
-D USE_SSD1306 ; Heltec_v4 has an SSD1315 display (compatible with SSD1306 driver)
|
||||
-D LED_PIN=35
|
||||
-D RESET_OLED=21
|
||||
-D I2C_SDA=17
|
||||
-D I2C_SCL=18
|
||||
-D I2C_SDA1=4
|
||||
-D I2C_SCL1=3
|
||||
lib_deps =
|
||||
${heltec_v4_base.lib_deps}
|
||||
|
||||
[env:heltec-v4-tft]
|
||||
extends = heltec_v4_base
|
||||
build_flags =
|
||||
${heltec_v4_base.build_flags} ;-Os
|
||||
-D HELTEC_V4_TFT
|
||||
-D I2C_SDA=4
|
||||
-D I2C_SCL=3
|
||||
-D I2C_SDA1=47
|
||||
-D I2C_SCL1=48
|
||||
-D PIN_BUTTON2=35
|
||||
-D PIN_BUZZER=6
|
||||
-D USE_PIN_BUZZER=PIN_BUZZER
|
||||
-D CONFIG_ARDUHAL_LOG_COLORS
|
||||
-D RADIOLIB_DEBUG_SPI=0
|
||||
-D RADIOLIB_DEBUG_PROTOCOL=0
|
||||
-D RADIOLIB_DEBUG_BASIC=0
|
||||
-D RADIOLIB_VERBOSE_ASSERT=0
|
||||
-D RADIOLIB_SPI_PARANOID=0
|
||||
-D CONFIG_DISABLE_HAL_LOCKS=1
|
||||
-D INPUTDRIVER_BUTTON_TYPE=0
|
||||
-D HAS_SCREEN=1
|
||||
-D HAS_TFT=1
|
||||
-D RAM_SIZE=1560
|
||||
-D LV_LVGL_H_INCLUDE_SIMPLE
|
||||
-D LV_CONF_INCLUDE_SIMPLE
|
||||
-D LV_COMP_CONF_INCLUDE_SIMPLE
|
||||
-D LV_USE_SYSMON=0
|
||||
-D LV_USE_PROFILER=0
|
||||
-D LV_USE_PERF_MONITOR=0
|
||||
-D LV_USE_MEM_MONITOR=0
|
||||
-D LV_USE_LOG=0
|
||||
-D LV_BUILD_TEST=0
|
||||
-D USE_LOG_DEBUG
|
||||
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
|
||||
-D USE_PACKET_API
|
||||
-D LGFX_DRIVER=LGFX_HELTEC_V4_TFT
|
||||
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_HELTEC_V4_TFT.h\"
|
||||
-D VIEW_320x240
|
||||
-D MAP_FULL_REDRAW
|
||||
-D DISPLAY_SIZE=320x240 ; landscape mode
|
||||
-D LGFX_PIN_SCK=17
|
||||
-D LGFX_PIN_MOSI=33
|
||||
-D LGFX_PIN_DC=16
|
||||
-D LGFX_PIN_CS=15
|
||||
-D LGFX_PIN_BL=21
|
||||
-D LGFX_PIN_RST=18
|
||||
-D CUSTOM_TOUCH_DRIVER
|
||||
-D TOUCH_SDA_PIN=I2C_SDA1
|
||||
-D TOUCH_SCL_PIN=I2C_SCL1
|
||||
-D TOUCH_INT_PIN=-1 ;45
|
||||
-D TOUCH_RST_PIN=44
|
||||
;base UI
|
||||
-D TFT_CS=LGFX_PIN_CS
|
||||
-D ST7789_CS=TFT_CS
|
||||
-D ST7789_RS=LGFX_PIN_DC
|
||||
-D ST7789_SDA=LGFX_PIN_MOSI
|
||||
-D ST7789_SCK=LGFX_PIN_SCK
|
||||
-D ST7789_RESET=LGFX_PIN_RST
|
||||
-D ST7789_MISO=-1
|
||||
-D ST7789_BUSY=-1
|
||||
-D ST7789_BL=LGFX_PIN_BL
|
||||
-D ST7789_SPI_HOST=SPI3_HOST
|
||||
-D TFT_BL=ST7789_BL
|
||||
-D SPI_FREQUENCY=40000000
|
||||
-D SPI_READ_FREQUENCY=4000000
|
||||
-D TFT_HEIGHT=320
|
||||
-D TFT_WIDTH=240
|
||||
-D TFT_OFFSET_X=0
|
||||
-D TFT_OFFSET_Y=0
|
||||
-D TFT_OFFSET_ROTATION=0
|
||||
-D SCREEN_ROTATE
|
||||
-D SCREEN_TRANSITION_FRAMERATE=5
|
||||
-D BRIGHTNESS_DEFAULT=130 ; Medium Low Brightness
|
||||
-D HAS_TOUCHSCREEN=1
|
||||
-D TOUCH_I2C_PORT=0
|
||||
-D TOUCH_SLAVE_ADDRESS=0x2E
|
||||
-D SCREEN_TOUCH_INT=TOUCH_INT_PIN
|
||||
-D SCREEN_TOUCH_RST=TOUCH_RST_PIN
|
||||
|
||||
lib_deps = ${heltec_v4_base.lib_deps}
|
||||
; ${device-ui_base.lib_deps}
|
||||
lovyan03/LovyanGFX@1.2.0
|
||||
https://github.com/Quency-D/chsc6x/archive/5cbead829d6b432a8d621ed1aafd4eb474fd4f27.zip
|
||||
https://github.com/Quency-D/device-ui/archive/7c9870b8016641190b059bdd90fe16c1012a39eb.zip
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
#define LED_PIN 35
|
||||
|
||||
#define USE_SSD1306 // Heltec_v4 has an SSD1315 display (compatible with SSD1306 driver)
|
||||
|
||||
#define RESET_OLED 21
|
||||
#define I2C_SDA 17 // I2C pins for this board
|
||||
#define I2C_SCL 18
|
||||
|
||||
#define VEXT_ENABLE 36 // active low, powers the oled display and the lora antenna boost
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 2
|
||||
minor = 7
|
||||
build = 13
|
||||
build = 14
|
||||
|
||||
Reference in New Issue
Block a user