Merge branch 'master' into develop-to-master
This commit is contained in:
@@ -143,7 +143,7 @@ jobs:
|
|||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Test Report
|
- name: Test Report
|
||||||
uses: dorny/test-reporter@v2.1.1
|
uses: dorny/test-reporter@v2.2.0
|
||||||
with:
|
with:
|
||||||
name: PlatformIO Tests
|
name: PlatformIO Tests
|
||||||
path: testreport.xml
|
path: testreport.xml
|
||||||
|
|||||||
+6
-6
@@ -8,15 +8,15 @@ plugins:
|
|||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
lint:
|
lint:
|
||||||
enabled:
|
enabled:
|
||||||
- checkov@3.2.489
|
- checkov@3.2.492
|
||||||
- renovate@41.169.1
|
- renovate@42.5.4
|
||||||
- prettier@3.6.2
|
- prettier@3.6.2
|
||||||
- trufflehog@3.90.12
|
- trufflehog@3.90.13
|
||||||
- yamllint@1.37.1
|
- yamllint@1.37.1
|
||||||
- bandit@1.8.6
|
- bandit@1.8.6
|
||||||
- trivy@0.67.2
|
- trivy@0.67.2
|
||||||
- taplo@0.10.0
|
- taplo@0.10.0
|
||||||
- ruff@0.14.3
|
- ruff@0.14.4
|
||||||
- isort@7.0.0
|
- isort@7.0.0
|
||||||
- markdownlint@0.45.0
|
- markdownlint@0.45.0
|
||||||
- oxipng@9.1.5
|
- oxipng@9.1.5
|
||||||
@@ -26,9 +26,9 @@ lint:
|
|||||||
- hadolint@2.14.0
|
- hadolint@2.14.0
|
||||||
- shfmt@3.6.0
|
- shfmt@3.6.0
|
||||||
- shellcheck@0.11.0
|
- shellcheck@0.11.0
|
||||||
- black@25.9.0
|
- black@25.11.0
|
||||||
- git-diff-check
|
- git-diff-check
|
||||||
- gitleaks@8.28.0
|
- gitleaks@8.29.0
|
||||||
- clang-format@16.0.3
|
- clang-format@16.0.3
|
||||||
ignore:
|
ignore:
|
||||||
- linters: [ALL]
|
- linters: [ALL]
|
||||||
|
|||||||
Vendored
+1
@@ -4,6 +4,7 @@ meshtasticd (2.7.16.0) unstable; urgency=medium
|
|||||||
|
|
||||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 19 Nov 2025 16:12:32 +0000
|
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 19 Nov 2025 16:12:32 +0000
|
||||||
|
|
||||||
|
|
||||||
meshtasticd (2.7.15.0) unstable; urgency=medium
|
meshtasticd (2.7.15.0) unstable; urgency=medium
|
||||||
|
|
||||||
* Version 2.7.15
|
* Version 2.7.15
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ BuildRequires: pkgconfig(x11)
|
|||||||
BuildRequires: pkgconfig(libinput)
|
BuildRequires: pkgconfig(libinput)
|
||||||
BuildRequires: pkgconfig(xkbcommon-x11)
|
BuildRequires: pkgconfig(xkbcommon-x11)
|
||||||
|
|
||||||
|
# libbsd is needed on older Fedora/RHEL to provide 'strlcpy'
|
||||||
|
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
|
||||||
|
BuildRequires: glibc-devel >= 2.38
|
||||||
|
%else
|
||||||
|
BuildRequires: pkgconfig(libbsd-overlay)
|
||||||
|
%endif
|
||||||
|
|
||||||
Requires: systemd-udev
|
Requires: systemd-udev
|
||||||
|
|
||||||
%description
|
%description
|
||||||
|
|||||||
+2
-2
@@ -62,7 +62,7 @@ monitor_speed = 115200
|
|||||||
monitor_filters = direct
|
monitor_filters = direct
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||||
@@ -121,7 +121,7 @@ lib_deps =
|
|||||||
[device-ui_base]
|
[device-ui_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
# 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
|
; Common libs for environmental measurements in telemetry module
|
||||||
[environmental_base]
|
[environmental_base]
|
||||||
|
|||||||
@@ -935,7 +935,9 @@ void menuHandler::BluetoothToggleMenu()
|
|||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 3;
|
bannerOptions.optionsCount = 3;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
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};
|
InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
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
|
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
||||||
|
|
||||||
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
|
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
|
||||||
moduleConfig.external_notification.enabled) {
|
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
|
||||||
externalNotificationModule->stopNow();
|
externalNotificationModule->stopNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,10 +85,8 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
|||||||
switch (event->inputEvent) {
|
switch (event->inputEvent) {
|
||||||
// GPS
|
// GPS
|
||||||
case INPUT_BROKER_GPS_TOGGLE:
|
case INPUT_BROKER_GPS_TOGGLE:
|
||||||
LOG_WARN("GPS Toggle");
|
|
||||||
#if !MESHTASTIC_EXCLUDE_GPS
|
#if !MESHTASTIC_EXCLUDE_GPS
|
||||||
if (gps) {
|
if (gps) {
|
||||||
LOG_WARN("GPS Toggle2");
|
|
||||||
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED &&
|
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED &&
|
||||||
config.position.fixed_position == false) {
|
config.position.fixed_position == false) {
|
||||||
nodeDB->clearLocalPosition();
|
nodeDB->clearLocalPosition();
|
||||||
|
|||||||
@@ -11,6 +11,113 @@ extern graphics::Screen *screen;
|
|||||||
|
|
||||||
TraceRouteModule *traceRouteModule;
|
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)
|
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
|
||||||
{
|
{
|
||||||
// We only alter the packet in alterReceivedProtobuf()
|
// We only alter the packet in alterReceivedProtobuf()
|
||||||
@@ -406,7 +513,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
if (node == 0 || node == NODENUM_BROADCAST) {
|
if (node == 0 || node == NODENUM_BROADCAST) {
|
||||||
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Invalid node";
|
setResultText("Invalid node");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@@ -420,7 +527,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
if (node == nodeDB->getNodeNum()) {
|
if (node == nodeDB->getNodeNum()) {
|
||||||
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Cannot trace self";
|
setResultText("Cannot trace self");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@@ -447,6 +554,8 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
||||||
bannerText = String("Wait for ") + String(wait) + String("s");
|
bannerText = String("Wait for ") + String(wait) + String("s");
|
||||||
runState = TRACEROUTE_STATE_COOLDOWN;
|
runState = TRACEROUTE_STATE_COOLDOWN;
|
||||||
|
resultText = "";
|
||||||
|
clearResultLines();
|
||||||
|
|
||||||
requestFocus();
|
requestFocus();
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
@@ -459,6 +568,8 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
tracingNode = node;
|
tracingNode = node;
|
||||||
lastTraceRouteTime = now;
|
lastTraceRouteTime = now;
|
||||||
runState = TRACEROUTE_STATE_TRACKING;
|
runState = TRACEROUTE_STATE_TRACKING;
|
||||||
|
resultText = "";
|
||||||
|
clearResultLines();
|
||||||
bannerText = String("Tracing ") + getNodeName(node);
|
bannerText = String("Tracing ") + getNodeName(node);
|
||||||
|
|
||||||
LOG_INFO("TraceRoute UI: Starting trace route to node 0x%08x, requesting focus", 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 {
|
} else {
|
||||||
LOG_ERROR("MeshService is NULL!");
|
LOG_ERROR("MeshService is NULL!");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Service unavailable";
|
setResultText("Service unavailable");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@@ -514,7 +625,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Failed to send";
|
setResultText("Failed to send");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@@ -532,7 +643,7 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
if (node == 0 || node == NODENUM_BROADCAST) {
|
if (node == 0 || node == NODENUM_BROADCAST) {
|
||||||
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Invalid node";
|
setResultText("Invalid node");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@@ -546,7 +657,7 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
if (node == nodeDB->getNodeNum()) {
|
if (node == nodeDB->getNodeNum()) {
|
||||||
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Cannot trace self";
|
setResultText("Cannot trace self");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@@ -568,6 +679,8 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
||||||
bannerText = String("Wait for ") + String(wait) + String("s");
|
bannerText = String("Wait for ") + String(wait) + String("s");
|
||||||
runState = TRACEROUTE_STATE_COOLDOWN;
|
runState = TRACEROUTE_STATE_COOLDOWN;
|
||||||
|
resultText = "";
|
||||||
|
clearResultLines();
|
||||||
|
|
||||||
requestFocus();
|
requestFocus();
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
@@ -580,6 +693,8 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
runState = TRACEROUTE_STATE_TRACKING;
|
runState = TRACEROUTE_STATE_TRACKING;
|
||||||
tracingNode = node;
|
tracingNode = node;
|
||||||
lastTraceRouteTime = now;
|
lastTraceRouteTime = now;
|
||||||
|
resultText = "";
|
||||||
|
clearResultLines();
|
||||||
bannerText = String("Tracing ") + getNodeName(node);
|
bannerText = String("Tracing ") + getNodeName(node);
|
||||||
|
|
||||||
requestFocus();
|
requestFocus();
|
||||||
@@ -614,14 +729,14 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
} else {
|
} else {
|
||||||
LOG_ERROR("MeshService is NULL!");
|
LOG_ERROR("MeshService is NULL!");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Service unavailable";
|
setResultText("Service unavailable");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "Failed to send";
|
setResultText("Failed to send");
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
}
|
}
|
||||||
@@ -629,7 +744,7 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
|
|
||||||
void TraceRouteModule::handleTraceRouteResult(const String &result)
|
void TraceRouteModule::handleTraceRouteResult(const String &result)
|
||||||
{
|
{
|
||||||
resultText = result;
|
setResultText(result);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
@@ -679,83 +794,15 @@ void TraceRouteModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
if (resultText.length() > 0) {
|
if (resultText.length() > 0) {
|
||||||
std::vector<String> lines;
|
if (resultLinesDirty) {
|
||||||
String currentLine = "";
|
rebuildResultLines(display);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int lineHeight = FONT_HEIGHT_SMALL + 1; // Use proper font height with 1px spacing
|
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);
|
int lineY = contentStartY + (i * lineHeight);
|
||||||
if (lineY + FONT_HEIGHT_SMALL <= display->getHeight()) {
|
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) {
|
if (runState == TRACEROUTE_STATE_TRACKING && now - lastTraceRouteTime > trackingTimeoutMs) {
|
||||||
LOG_INFO("TraceRoute timeout, no response received");
|
LOG_INFO("TraceRoute timeout, no response received");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultText = "No response received";
|
setResultText("No response received");
|
||||||
resultShowTime = now;
|
resultShowTime = now;
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@@ -815,6 +862,8 @@ int32_t TraceRouteModule::runOnce()
|
|||||||
// Cooldown finished
|
// Cooldown finished
|
||||||
LOG_INFO("TraceRoute cooldown finished, returning to IDLE");
|
LOG_INFO("TraceRoute cooldown finished, returning to IDLE");
|
||||||
runState = TRACEROUTE_STATE_IDLE;
|
runState = TRACEROUTE_STATE_IDLE;
|
||||||
|
resultText = "";
|
||||||
|
clearResultLines();
|
||||||
bannerText = "";
|
bannerText = "";
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||||
@@ -828,6 +877,7 @@ int32_t TraceRouteModule::runOnce()
|
|||||||
LOG_INFO("TraceRoute result display timeout, returning to IDLE");
|
LOG_INFO("TraceRoute result display timeout, returning to IDLE");
|
||||||
runState = TRACEROUTE_STATE_IDLE;
|
runState = TRACEROUTE_STATE_IDLE;
|
||||||
resultText = "";
|
resultText = "";
|
||||||
|
clearResultLines();
|
||||||
bannerText = "";
|
bannerText = "";
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
#include "OLEDDisplayUi.h"
|
#include "OLEDDisplayUi.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#define ROUTE_SIZE sizeof(((meshtastic_RouteDiscovery *)0)->route) / sizeof(((meshtastic_RouteDiscovery *)0)->route[0])
|
#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;
|
virtual int32_t runOnce() override;
|
||||||
|
|
||||||
private:
|
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
|
// 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);
|
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
|
||||||
|
|
||||||
@@ -74,6 +80,8 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
|
|||||||
unsigned long trackingTimeoutMs = 10000;
|
unsigned long trackingTimeoutMs = 10000;
|
||||||
String bannerText;
|
String bannerText;
|
||||||
String resultText;
|
String resultText;
|
||||||
|
std::vector<String> resultLines;
|
||||||
|
bool resultLinesDirty = false;
|
||||||
NodeNum tracingNode = 0;
|
NodeNum tracingNode = 0;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ void BMX160Sensor::calibrate(uint16_t forSeconds)
|
|||||||
{
|
{
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
||||||
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
||||||
|
highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
||||||
|
|
||||||
doCalibration = true;
|
doCalibration = true;
|
||||||
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
||||||
@@ -127,4 +128,4 @@ void BMX160Sensor::calibrate(uint16_t forSeconds)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ void ICM20948Sensor::calibrate(uint16_t forSeconds)
|
|||||||
{
|
{
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
|
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
|
||||||
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
||||||
|
highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
||||||
|
|
||||||
doCalibration = true;
|
doCalibration = true;
|
||||||
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
||||||
@@ -295,4 +296,4 @@ bool ICM20948Singleton::setWakeOnMotion()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+4
-1
@@ -692,7 +692,10 @@ void MQTT::publishNodeInfo()
|
|||||||
}
|
}
|
||||||
void MQTT::publishQueuedMessages()
|
void MQTT::publishQueuedMessages()
|
||||||
{
|
{
|
||||||
if (mqttQueue.isEmpty() || !isConnected)
|
if (mqttQueue.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!moduleConfig.mqtt.proxy_to_client_enabled && !isConnected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG_DEBUG("Publish enqueued MQTT message");
|
LOG_DEBUG("Publish enqueued MQTT message");
|
||||||
|
|||||||
@@ -20,13 +20,14 @@
|
|||||||
#include "PowerStatus.h"
|
#include "PowerStatus.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
|
||||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
#include "host/ble_gap.h"
|
#include "host/ble_gap.h"
|
||||||
#else
|
#else
|
||||||
#include "nimble/nimble/host/include/host/ble_gap.h"
|
#include "nimble/nimble/host/include/host/ble_gap.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr uint16_t kPreferredBleMtu = 517;
|
constexpr uint16_t kPreferredBleMtu = 517;
|
||||||
@@ -776,16 +777,35 @@ bool NimbleBluetooth::isConnected()
|
|||||||
|
|
||||||
int NimbleBluetooth::getRssi()
|
int NimbleBluetooth::getRssi()
|
||||||
{
|
{
|
||||||
if (bleServer && isConnected()) {
|
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||||
auto service = bleServer->getServiceByUUID(MESH_SERVICE_UUID);
|
if (!bleServer || !isConnected()) {
|
||||||
uint16_t handle = service->getHandle();
|
return 0; // No active BLE connection
|
||||||
#ifdef NIMBLE_TWO
|
|
||||||
return NimBLEDevice::getClientByHandle(handle)->getRssi();
|
|
||||||
#else
|
|
||||||
return NimBLEDevice::getClientByID(handle)->getRssi();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return 0; // FIXME figure out where to source this
|
|
||||||
|
uint16_t connHandle = nimbleBluetoothConnHandle.load();
|
||||||
|
|
||||||
|
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
const auto peers = bleServer->getPeerDevices();
|
||||||
|
if (!peers.empty()) {
|
||||||
|
connHandle = peers.front();
|
||||||
|
nimbleBluetoothConnHandle = connHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
return 0; // Connection handle not available yet
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t rssi = 0;
|
||||||
|
const int rc = ble_gap_conn_rssi(connHandle, &rssi);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
return rssi;
|
||||||
|
}
|
||||||
|
LOG_DEBUG("BLE RSSI read failed, rc=%d", rc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NimbleBluetooth::setup()
|
void NimbleBluetooth::setup()
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
[VERSION]
|
[VERSION]
|
||||||
major = 2
|
major = 2
|
||||||
minor = 7
|
minor = 7
|
||||||
build = 16
|
build = 16
|
||||||
Reference in New Issue
Block a user