Compare commits

..

2 Commits

Author SHA1 Message Date
Ben Meadors
a06eee3af8 Remove guard 2025-12-17 08:41:29 -06:00
Ben Meadors
f3785e4ceb Upgrade all esp32 targets to NimBLE 2.X 2025-12-17 08:04:22 -06:00
20 changed files with 27 additions and 150 deletions

View File

@@ -56,18 +56,16 @@ jobs:
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
- name: Job summary
- name: Echo manifest from release/firmware-*.mt.json to job summary
if: ${{ always() }}
env:
PIO_ENV: ${{ inputs.pio_env }}
run: |
echo "## $PIO_ENV" >> $GITHUB_STEP_SUMMARY
echo "<details><summary><strong>Manifest</strong></summary>" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo "## Manifest: \`$PIO_ENV\`" >> $GITHUB_STEP_SUMMARY
echo '```json' >> $GITHUB_STEP_SUMMARY
cat release/firmware-*.mt.json >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
- name: Store binaries as an artifact
uses: actions/upload-artifact@v6

View File

@@ -9,14 +9,14 @@ plugins:
lint:
enabled:
- checkov@3.2.495
- renovate@42.64.1
- renovate@42.58.4
- prettier@3.7.4
- trufflehog@3.92.3
- yamllint@1.37.1
- bandit@1.9.2
- trivy@0.68.2
- taplo@0.10.0
- ruff@0.14.10
- ruff@0.14.9
- isort@7.0.0
- markdownlint@0.47.0
- oxipng@10.0.0

View File

@@ -21,14 +21,13 @@ rm -f $BUILDDIR/firmware*
export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
ota_basename=${basename}-ota
pio run --environment $1 -t mtjson # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying NRF52 dfu (OTA) file"
cp $BUILDDIR/$basename.zip $OUTDIR/$ota_basename.zip
cp $BUILDDIR/$basename.zip $OUTDIR/$basename.zip
echo "Copying NRF52 UF2 file"
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2

View File

@@ -17,8 +17,6 @@ lfsbin = f"{progname.replace('firmware-', 'littlefs-')}.bin"
def manifest_gather(source, target, env):
out = []
board_platform = env.BoardConfig().get("platform")
needs_ota_suffix = board_platform == "nordicnrf52"
check_paths = [
progname,
f"{progname}.elf",
@@ -34,11 +32,8 @@ def manifest_gather(source, target, env):
for p in check_paths:
f = env.File(env.subst(f"$BUILD_DIR/{p}"))
if f.exists():
manifest_name = p
if needs_ota_suffix and p == f"{progname}.zip":
manifest_name = f"{progname}-ota.zip"
d = {
"name": manifest_name,
"name": p,
"md5": f.get_content_hash(), # Returns MD5 hash
"bytes": f.get_size() # Returns file size in bytes
}

View File

@@ -440,11 +440,9 @@ void setup()
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
#if defined(ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
#ifndef SENSECAP_INDICATOR
// use PSRAM for malloc calls > 256 bytes
heap_caps_malloc_extmem_enable(256);
#endif
#endif
#if defined(DEBUG_MUTE) && defined(DEBUG_PORT)
DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n");

View File

@@ -124,10 +124,6 @@ void FloodingRouter::perhapsCancelDupe(const meshtastic_MeshPacket *p)
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE && iface) {
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
}
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE && iface && nodeDB &&
nodeDB->isFromOrToFavoritedNode(*p)) {
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
}
}
bool FloodingRouter::isRebroadcaster()

View File

@@ -276,10 +276,6 @@ bool MeshService::trySendPosition(NodeNum dest, bool wantReplies)
if (nodeDB->hasValidPosition(node)) {
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
if (positionModule) {
if (!config.position.fixed_position && !nodeDB->hasLocalPositionSinceBoot()) {
LOG_DEBUG("Skip position ping; no fresh position since boot");
return false;
}
LOG_INFO("Send position ping to 0x%x, wantReplies=%d, channel=%d", dest, wantReplies, node->channel);
positionModule->sendOurPosition(dest, wantReplies, node->channel);
return true;

View File

@@ -805,11 +805,11 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 500;
moduleConfig.external_notification.nag_timeout = 2;
#endif
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
// Default to PIN_LED2 for external notification output (LED color depends on device variant)
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE)
// Default to RAK led pin 2 (blue)
moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = PIN_LED2;
#if defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
#if defined(MUZI_BASE)
moduleConfig.external_notification.active = false;
#else
moduleConfig.external_notification.active = true;
@@ -1043,7 +1043,6 @@ void NodeDB::clearLocalPosition()
node->position.altitude = 0;
node->position.time = 0;
setLocalPosition(meshtastic_Position_init_default);
localPositionUpdatedSinceBoot = false;
}
void NodeDB::cleanupMeshDB()

View File

@@ -279,13 +279,9 @@ class NodeDB
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
position.time, position.timestamp);
localPosition = position;
if (position.latitude_i != 0 || position.longitude_i != 0) {
localPositionUpdatedSinceBoot = true;
}
}
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
bool hasLocalPositionSinceBoot() const { return localPositionUpdatedSinceBoot; }
#if !defined(MESHTASTIC_EXCLUDE_PKI)
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
@@ -305,7 +301,6 @@ class NodeDB
private:
bool duplicateWarned = false;
bool localPositionUpdatedSinceBoot = false;
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
uint32_t lastSort = 0; // When last sorted the nodeDB

View File

@@ -296,6 +296,11 @@ bool RadioInterface::shouldRebroadcastEarlyLikeRouter(meshtastic_MeshPacket *p)
return true;
}
// If we are a CLIENT_BASE and the packet is from or to a favorited node, we should rebroadcast early
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) {
return nodeDB->isFromOrToFavoritedNode(*p);
}
return false;
}

View File

@@ -417,9 +417,6 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
case meshtastic_AdminMessage_enter_dfu_mode_request_tag: {
LOG_INFO("Client requesting to enter DFU mode");
#if HAS_SCREEN
IF_SCREEN(screen->showSimpleBanner("Device is rebooting\ninto DFU mode.", 0));
#endif
#if defined(ARCH_NRF52) || defined(ARCH_RP2040)
enterDfuMode();
#endif

View File

@@ -7,41 +7,17 @@
#include "configuration.h"
#include "main.h"
#include <Throttle.h>
#include <algorithm>
#ifndef USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS
#define USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS (12 * 60 * 60)
#endif
NodeInfoModule *nodeInfoModule;
static constexpr uint32_t NodeInfoReplySuppressSeconds = USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS;
bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_User *pptr)
{
suppressReplyForCurrentRequest = false;
if (mp.from == nodeDB->getNodeNum()) {
LOG_WARN("Ignoring packet supposed to be from our own node: %08x", mp.from);
return false;
}
auto p = *pptr;
if (mp.decoded.want_response) {
const NodeNum sender = getFrom(&mp);
const uint32_t now = mp.rx_time ? mp.rx_time : getTime();
auto it = lastNodeInfoSeen.find(sender);
if (it != lastNodeInfoSeen.end()) {
uint32_t sinceLast = now >= it->second ? now - it->second : 0;
if (sinceLast < NodeInfoReplySuppressSeconds) {
suppressReplyForCurrentRequest = true;
}
}
lastNodeInfoSeen[sender] = now;
pruneLastNodeInfoCache();
}
if (p.is_licensed != owner.is_licensed) {
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
return true;
@@ -66,8 +42,6 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
service->sendToPhone(packetCopy);
}
pruneLastNodeInfoCache();
// LOG_DEBUG("did handleReceived");
return false; // Let others look at this message also if they want
}
@@ -94,11 +68,9 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
if (p) { // Check whether we didn't ignore it
p->to = dest;
bool requestWantResponse = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
wantReplies;
p->decoded.want_response = requestWantResponse;
if (_shorterTimeout)
p->priority = meshtastic_MeshPacket_Priority_DEFAULT;
else
@@ -117,13 +89,6 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
meshtastic_MeshPacket *NodeInfoModule::allocReply()
{
if (suppressReplyForCurrentRequest) {
LOG_DEBUG("Skip send NodeInfo since we heard the requester <12h ago");
ignoreRequest = true;
suppressReplyForCurrentRequest = false;
return NULL;
}
if (!airTime->isTxAllowedChannelUtil(false)) {
ignoreRequest = true; // Mark it as ignored for MeshModule
LOG_DEBUG("Skip send NodeInfo > 40%% ch. util");
@@ -160,29 +125,6 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
}
}
void NodeInfoModule::pruneLastNodeInfoCache()
{
if (!nodeDB || !nodeDB->meshNodes)
return;
const size_t maxEntries = nodeDB->meshNodes->size();
for (auto it = lastNodeInfoSeen.begin(); it != lastNodeInfoSeen.end();) {
if (!nodeDB->getMeshNode(it->first)) {
it = lastNodeInfoSeen.erase(it);
} else {
++it;
}
}
while (!lastNodeInfoSeen.empty() && lastNodeInfoSeen.size() > maxEntries) {
auto oldestIt = std::min_element(lastNodeInfoSeen.begin(), lastNodeInfoSeen.end(),
[](const std::pair<const NodeNum, uint32_t> &lhs,
const std::pair<const NodeNum, uint32_t> &rhs) { return lhs.second < rhs.second; });
lastNodeInfoSeen.erase(oldestIt);
}
}
NodeInfoModule::NodeInfoModule()
: ProtobufModule("nodeinfo", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg), concurrency::OSThread("NodeInfo")
{

View File

@@ -1,6 +1,5 @@
#pragma once
#include "ProtobufModule.h"
#include <map>
/**
* NodeInfo module for sending/receiving NodeInfos into the mesh
@@ -44,10 +43,6 @@ class NodeInfoModule : public ProtobufModule<meshtastic_User>, private concurren
private:
uint32_t lastSentToMesh = 0; // Last time we sent our NodeInfo to the mesh
bool shorterTimeout = false;
bool suppressReplyForCurrentRequest = false;
std::map<NodeNum, uint32_t> lastNodeInfoSeen;
void pruneLastNodeInfoCache();
};
extern NodeInfoModule *nodeInfoModule;

View File

@@ -349,11 +349,6 @@ void PositionModule::sendOurPosition()
void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t channel)
{
if (!config.position.fixed_position && !nodeDB->hasLocalPositionSinceBoot()) {
LOG_DEBUG("Skip position send; no fresh position since boot");
return;
}
// cancel any not yet sent (now stale) position packets
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
service->cancelSending(prevPacketId);
@@ -425,14 +420,8 @@ int32_t PositionModule::runOnce()
return RUNONCE_INTERVAL;
}
bool waitingForFreshPosition = (lastGpsSend == 0) && !config.position.fixed_position && !nodeDB->hasLocalPositionSinceBoot();
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
if (waitingForFreshPosition) {
#ifdef GPS_DEBUG
LOG_DEBUG("Skip initial position send; no fresh position since boot");
#endif
} else if (nodeDB->hasValidPosition(node)) {
if (nodeDB->hasValidPosition(node)) {
lastGpsSend = now;
lastGpsLatitude = node->position.latitude_i;

View File

@@ -20,17 +20,13 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
switch (arg->getStatusType()) {
case STATUS_TYPE_POWER: {
meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg;
if (powerStatus->getHasUSB() || powerStatus->getIsCharging()) {
if (powerStatus->getHasUSB()) {
power_state = charging;
if (powerStatus->getBatteryChargePercent() >= 100) {
power_state = charged;
}
} else {
if (powerStatus->getBatteryChargePercent() > 5) {
power_state = discharging;
} else {
power_state = critical;
}
}
break;
}
@@ -62,33 +58,16 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
int32_t StatusLEDModule::runOnce()
{
my_interval = 1000;
if (power_state == charging) {
CHARGE_LED_state = !CHARGE_LED_state;
} else if (power_state == charged) {
CHARGE_LED_state = LED_STATE_ON;
} else if (power_state == critical) {
if (POWER_LED_starttime + 30000 < millis() && !doing_fast_blink) {
doing_fast_blink = true;
POWER_LED_starttime = millis();
}
if (doing_fast_blink) {
PAIRING_LED_state = LED_STATE_OFF;
CHARGE_LED_state = !CHARGE_LED_state;
my_interval = 250;
if (POWER_LED_starttime + 2000 < millis()) {
doing_fast_blink = false;
}
} else {
CHARGE_LED_state = LED_STATE_OFF;
}
} else {
CHARGE_LED_state = LED_STATE_OFF;
}
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis() || doing_fast_blink) {
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis()) {
PAIRING_LED_state = LED_STATE_OFF;
} else if (ble_state == unpaired) {
if (slowTrack) {

View File

@@ -31,10 +31,8 @@ class StatusLEDModule : private concurrency::OSThread
bool PAIRING_LED_state = LED_STATE_OFF;
uint32_t PAIRING_LED_starttime = 0;
uint32_t POWER_LED_starttime = 0;
bool doing_fast_blink = false;
enum PowerState { discharging, charging, charged, critical };
enum PowerState { discharging, charging, charged };
PowerState power_state = discharging;

View File

@@ -55,7 +55,6 @@
// "USERPREFS_MQTT_TLS_ENABLED": "false",
// "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME",
// "USERPREFS_RINGTONE_NAG_SECS": "60",
// "USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS": "43200",
"USERPREFS_RINGTONE_RTTTL": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
// "USERPREFS_NETWORK_IPV6_ENABLED": "1",
"USERPREFS_TZ_STRING": "tzplaceholder "

View File

@@ -54,7 +54,6 @@ extern "C" {
#define LED_POWER red_LED_PIN
#define LED_CHARGE LED_POWER // Signals the Status LED Module to handle this LED
#define green_LED_PIN 35
#define PIN_LED2 green_LED_PIN
#define LED_BLUE 37
#define LED_PAIRING LED_BLUE // Signals the Status LED Module to handle this LED

View File

@@ -2,12 +2,12 @@
[rp2040_base]
platform =
# TODO renovate
https://github.com/maxgerhardt/platform-raspberrypi#cc24cfef37ed22ca9f2a6aead28c2deb76c39f24
; For arduino-pico >= 5.4.4
https://github.com/maxgerhardt/platform-raspberrypi#76ecf3c7e9dd4503af0331154c4ca1cddc4b03e5
; For arduino-pico >= 4.4.3
extends = arduino_base
platform_packages =
# TODO renovate
arduino-pico@https://github.com/earlephilhower/arduino-pico/releases/download/5.4.4/rp2040-5.4.4.zip
framework-arduinopico@https://github.com/earlephilhower/arduino-pico#4.4.3
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
@@ -17,7 +17,6 @@ build_flags =
-Isrc/platform/rp2xx0/hardware_rosc/include
-Isrc/platform/rp2xx0/pico_sleep/include
-D__PLAT_RP2040__
-D__FREERTOS=1
# -D _POSIX_THREADS
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/> -<mesh/wifi/> -<mesh/http/> -<mesh/raspihttp>

View File

@@ -2,12 +2,12 @@
[rp2350_base]
platform =
# TODO renovate
https://github.com/maxgerhardt/platform-raspberrypi#cc24cfef37ed22ca9f2a6aead28c2deb76c39f24
; For arduino-pico >= 5.4.4
https://github.com/maxgerhardt/platform-raspberrypi#76ecf3c7e9dd4503af0331154c4ca1cddc4b03e5
; For arduino-pico >= 4.4.3
extends = arduino_base
platform_packages =
# TODO renovate
arduino-pico@https://github.com/earlephilhower/arduino-pico/releases/download/5.4.4/rp2040-5.4.4.zip
framework-arduinopico@https://github.com/earlephilhower/arduino-pico#4.4.3
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
@@ -15,7 +15,6 @@ build_flags =
${arduino_base.build_flags} -Wno-unused-variable -Wcast-align
-Isrc/platform/rp2xx0
-D__PLAT_RP2350__
-D__FREERTOS=1
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/> -<mesh/wifi/> -<mesh/http/> -<mesh/raspihttp> -<platform/rp2xx0/pico_sleep> -<platform/rp2xx0/hardware_rosc>