Compare commits

...

26 Commits

Author SHA1 Message Date
Jason P
6a557e4f39 Remove extranous isMuted, there are better ways! 2025-12-29 08:06:23 -06:00
Jason P
c7844410ac Merge branch 'develop' into baseui_temporarymute 2025-12-28 11:49:09 -06:00
Jonathan Bennett
63aadba526 Use IF_SCREEN macro to guard against null screen object 2025-12-28 11:33:14 -06:00
Jason P
4c27c84b5f Remove banner notification, we display the icon immediately 2025-12-27 21:13:33 -06:00
Jason P
fbd866fda3 Only show Temporary Mute if it applies 2025-12-27 21:07:09 -06:00
Jason P
ae770e9c6c Add Temporary Mute to Home frame and unbury Notifications 2025-12-27 20:57:08 -06:00
Jason P
759a972f77 GPS Menu Validation Fix - Missed in Reviews (#9093)
* Reviews sometimes miss things, whoops
* Validation is hard - but this fixes it
2025-12-27 12:42:22 -06:00
Jason P
d1db4433f4 Add menus for Smart Position, Broadcast Interval and Position Interval (#9080)
* Add menus for Smart Position, Broadcast Interval and Position Interval

* Realigned time intervals to match Android app options

* Fixed missing last option
2025-12-27 11:18:16 -06:00
Jason P
2c68710e8c Improve sanitizeString function for Node Names (#9086) 2025-12-27 11:17:55 -06:00
Tom Fifield
9f8f4471aa PIN_PWR_DELAY_MS --> PERIPHERAL_WARMUP_MS (#8467)
It turns out we had two methods for delaying startup while peripherals
warmed up. They were invented within months of each other and just missed
the chance to merge.

Let's delete PIN_PWR_DELAY_MS and use PERIPHERAL_WARMUP_MS, since it's
most common and earlier in the sequence.
2025-12-27 22:36:34 +11:00
github-actions[bot]
cf03caff10 Upgrade trunk (#9076)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-26 22:22:32 -06:00
Jonathan Bennett
ac937766cd In autoconf, don't probe Wire unless i2c device is set (#9081)
Found another bit of code that crashes my desktop, by probing the wrong i2c bus.
2025-12-26 22:22:32 -06:00
github-actions[bot]
9e215213a7 Upgrade trunk (#9072)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-26 22:22:32 -06:00
Jonathan Bennett
4fbe5356ca M6 shutdown and LEDs work (#9065)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-26 22:22:32 -06:00
github-actions[bot]
e899e84ef9 Upgrade trunk (#9067)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-26 22:22:32 -06:00
github-actions[bot]
69c3c0151f Upgrade trunk (#9047)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-26 22:22:32 -06:00
renovate[bot]
2b977b4830 Update meshtastic-esp8266-oled-ssd1306 digest to b34c681 (#9062)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-26 22:22:32 -06:00
Jonathan Bennett
b88a8bf968 In statusLEDModule, also detect isCharging (#9050) 2025-12-26 22:22:32 -06:00
Ben Meadors
514f8590fe Revert "Automated version bumps (#9025)"
This reverts commit 1021d967da.
2025-12-26 22:22:32 -06:00
Ben Meadors
cbd40faa89 Fix -ota.zip in manifest and build output 2025-12-26 22:22:32 -06:00
Jorropo
7dd9c8b223 pass GH_TOKEN to shame's gh run download step (#9087) 2025-12-26 22:04:18 -05:00
Austin
3473c32e81 Fix PR#8061 SensorLib nRF ThinkNode M-series (#9084) 2025-12-26 18:55:47 -06:00
Austin
db2224ed0e pioarduino .gitignore (#9085)
I'm already going insane!
2025-12-26 17:45:43 -06:00
Austin
29c5713a7e Correctly set type for event_mode max() position threshold (#9083)
Fixes EVENT_MODE firmware builds
2025-12-27 10:04:43 +11:00
Jorropo
82cf2bf16a action: skip trying to comment binary size change results if it is not a PR (#9033)
* action: skip trying to comment binary size change results if it is not a PR

* action: fix halucinations in the clanker's code

* action: cleanup one line

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Austin <vidplace7@gmail.com>
2025-12-26 17:26:48 -05:00
Jason P
ef530db44c Implement HAS_PHYSICAL_KEYBOARD for devices with physical keyboards (#9071)
- Implement HAS_PHYSICAL_KEYBOARD for devices with physical keyboards
- Add HAS_PHYSICAL_KEYBOARD to variant.h for:
  - TDeck
  - TLora Pager
  - TDeck Pro
2025-12-26 07:34:25 -06:00
35 changed files with 425 additions and 112 deletions

View File

@@ -240,6 +240,7 @@ jobs:
needs: [build] needs: [build]
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
if: github.event_name == 'pull_request_target'
with: with:
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head) filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
fetch-depth: 0 fetch-depth: 0
@@ -253,18 +254,24 @@ jobs:
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
id: upload-manifest id: upload-manifest
with: with:
name: manifests-all name: manifests-${{ github.sha }}
overwrite: true overwrite: true
path: | path: manifests-new/*.mt.json
manifests-new/*.mt.json
- name: Find the merge base - name: Find the merge base
if: github.event_name == 'pull_request_target'
run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV
env: env:
base: ${{ github.base_ref }} base: ${{ github.base_ref }}
head: ${{ github.head_ref }} head: ${{ github.sha }}
- name: Download the old manifests - name: Download the old manifests
run: gh run download -R ${{ github.repository }} --commit ${{ env.MERGE_BASE }} --name manifests-all --dir manifest-old/ if: github.event_name == 'pull_request_target'
run: gh run download -R "$repo" --name "manifests-$merge_base" --dir manifest-old/
env:
GH_TOKEN: ${{ github.token }}
merge_base: ${{ env.MERGE_BASE }}
repo: ${{ github.repository }}
- name: Do scan and post comment - name: Do scan and post comment
if: github.event_name == 'pull_request_target'
run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/ run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
release-artifacts: release-artifacts:

9
.gitignore vendored
View File

@@ -41,3 +41,12 @@ src/mesh/raspihttp/private_key.pem
# Ignore logo (set at build time with platformio-custom.py) # Ignore logo (set at build time with platformio-custom.py)
data/boot/logo.* data/boot/logo.*
# pioarduino platform
managed_components/*
arduino-lib-builder*
dependencies.lock
idf_component.yml
CMakeLists.txt
sdkconfig.*
.dummy/*

View File

@@ -9,9 +9,9 @@ plugins:
lint: lint:
enabled: enabled:
- checkov@3.2.495 - checkov@3.2.495
- renovate@42.64.1 - renovate@42.66.8
- prettier@3.7.4 - prettier@3.7.4
- trufflehog@3.92.3 - trufflehog@3.92.4
- yamllint@1.37.1 - yamllint@1.37.1
- bandit@1.9.2 - bandit@1.9.2
- trivy@0.68.2 - trivy@0.68.2

View File

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

View File

@@ -87,9 +87,6 @@
</screenshots> </screenshots>
<releases> <releases>
<release version="2.7.18" date="2025-12-20">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.18</url>
</release>
<release version="2.7.17" date="2025-11-28"> <release version="2.7.17" date="2025-11-28">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url> <url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
</release> </release>

View File

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

6
debian/changelog vendored
View File

@@ -1,9 +1,3 @@
meshtasticd (2.7.18.0) unstable; urgency=medium
* Version 2.7.18
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 20 Dec 2025 15:47:25 +0000
meshtasticd (2.7.17.0) unstable; urgency=medium meshtasticd (2.7.17.0) unstable; urgency=medium
* Version 2.7.17 * Version 2.7.17

View File

@@ -64,7 +64,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/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip https://github.com/meshtastic/esp8266-oled-ssd1306/archive/b34c6817c25d6faabb3a8a162b5d14fb75395433.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

View File

@@ -8,6 +8,7 @@
#include "graphics/draw/UIRenderer.h" #include "graphics/draw/UIRenderer.h"
#include "main.h" #include "main.h"
#include "meshtastic/config.pb.h" #include "meshtastic/config.pb.h"
#include "modules/ExternalNotificationModule.h"
#include "power.h" #include "power.h"
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <graphics/images.h> #include <graphics/images.h>
@@ -56,7 +57,6 @@ void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second)
// === Shared External State === // === Shared External State ===
bool hasUnreadMessage = false; bool hasUnreadMessage = false;
bool isMuted = false;
ScreenResolution currentResolution = ScreenResolution::Low; ScreenResolution currentResolution = ScreenResolution::Low;
// === Internal State === // === Internal State ===
@@ -306,7 +306,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
} }
display->drawXbm(iconX, iconY, mail_width, mail_height, mail); display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
} }
} else if (isMuted) { } else if (externalNotificationModule->getMute()) {
if (currentResolution == ScreenResolution::High) { if (currentResolution == ScreenResolution::High) {
int iconX = iconRightEdge - mute_symbol_big_width; int iconX = iconRightEdge - mute_symbol_big_width;
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2; int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
@@ -325,7 +325,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
int iconX = iconRightEdge - mute_symbol_width; int iconX = iconRightEdge - mute_symbol_width;
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2; int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
if (isInverted) { if (isInverted && !force_no_invert) {
display->setColor(WHITE); display->setColor(WHITE);
display->fillRect(iconX - 1, iconY - 1, mute_symbol_width + 2, mute_symbol_height + 2); display->fillRect(iconX - 1, iconY - 1, mute_symbol_width + 2, mute_symbol_height + 2);
display->setColor(BLACK); display->setColor(BLACK);
@@ -383,7 +383,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2; int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
display->drawXbm(iconX, iconY, mail_width, mail_height, mail); display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
} }
} else if (isMuted) { } else if (externalNotificationModule->getMute()) {
if (currentResolution == ScreenResolution::High) { if (currentResolution == ScreenResolution::High) {
int iconX = iconRightEdge - mute_symbol_big_width; int iconX = iconRightEdge - mute_symbol_big_width;
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2; int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
@@ -470,18 +470,49 @@ bool isAllowedPunctuation(char c)
return allowed.find(c) != std::string::npos; return allowed.find(c) != std::string::npos;
} }
static void replaceAll(std::string &s, const std::string &from, const std::string &to)
{
if (from.empty())
return;
size_t pos = 0;
while ((pos = s.find(from, pos)) != std::string::npos) {
s.replace(pos, from.size(), to);
pos += to.size();
}
}
std::string sanitizeString(const std::string &input) std::string sanitizeString(const std::string &input)
{ {
std::string output; std::string output;
bool inReplacement = false; bool inReplacement = false;
for (char c : input) { // Make a mutable copy so we can normalize UTF-8 “smart punctuation” into ASCII first.
if (std::isalnum(static_cast<unsigned char>(c)) || isAllowedPunctuation(c)) { std::string s = input;
// Curly single quotes:
replaceAll(s, "\xE2\x80\x98", "'"); // U+2018
replaceAll(s, "\xE2\x80\x99", "'"); // U+2019
// Curly double quotes: “ ”
replaceAll(s, "\xE2\x80\x9C", "\""); // U+201C
replaceAll(s, "\xE2\x80\x9D", "\""); // U+201D
// En dash / Em dash:
replaceAll(s, "\xE2\x80\x93", "-"); // U+2013
replaceAll(s, "\xE2\x80\x94", "-"); // U+2014
// Non-breaking space
replaceAll(s, "\xC2\xA0", " "); // U+00A0
// Now do your original sanitize pass over the normalized string.
for (unsigned char uc : s) {
char c = static_cast<char>(uc);
if (std::isalnum(uc) || isAllowedPunctuation(c)) {
output += c; output += c;
inReplacement = false; inReplacement = false;
} else { } else {
if (!inReplacement) { if (!inReplacement) {
output += 0xbf; // ISO-8859-1 for inverted question mark output += static_cast<char>(0xBF); // ISO-8859-1 for inverted question mark
inReplacement = true; inReplacement = true;
} }
} }

View File

@@ -41,7 +41,6 @@ namespace graphics
// Shared state (declare inside namespace) // Shared state (declare inside namespace)
extern bool hasUnreadMessage; extern bool hasUnreadMessage;
extern bool isMuted;
enum class ScreenResolution : uint8_t { UltraLow = 0, Low = 1, High = 2 }; enum class ScreenResolution : uint8_t { UltraLow = 0, Low = 1, High = 2 };
extern ScreenResolution currentResolution; extern ScreenResolution currentResolution;
ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenwidth); ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenwidth);

View File

@@ -20,8 +20,8 @@
#include "mesh/MeshTypes.h" #include "mesh/MeshTypes.h"
#include "modules/AdminModule.h" #include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h" #include "modules/CannedMessageModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/KeyVerificationModule.h" #include "modules/KeyVerificationModule.h"
#include "modules/TraceRouteModule.h" #include "modules/TraceRouteModule.h"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@@ -843,12 +843,21 @@ void menuHandler::messageViewModeMenu()
void menuHandler::homeBaseMenu() void menuHandler::homeBaseMenu()
{ {
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Sleep, enumEnd }; enum optionsNumbers { Back, Mute, Backlight, Position, Preset, Freetext, Sleep, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"}; static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back}; static int optionsEnumArray[enumEnd] = {Back};
int options = 1; int options = 1;
if (moduleConfig.external_notification.enabled && externalNotificationModule &&
config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED) {
if (!externalNotificationModule->getMute()) {
optionsArray[options] = "Temporarily Mute";
} else {
optionsArray[options] = "Unmute";
}
optionsEnumArray[options++] = Mute;
}
#if defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN) #if defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN)
optionsArray[options] = "Toggle Backlight"; optionsArray[options] = "Toggle Backlight";
optionsEnumArray[options++] = Backlight; optionsEnumArray[options++] = Backlight;
@@ -872,7 +881,13 @@ void menuHandler::homeBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options; bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Backlight) { if (selected == Mute) {
if (moduleConfig.external_notification.enabled && externalNotificationModule) {
externalNotificationModule->setMute(!externalNotificationModule->getMute());
IF_SCREEN(if (!externalNotificationModule->getMute()) externalNotificationModule->stopNow();)
}
} else if (selected == Backlight) {
screen->setOn(false);
#if defined(PIN_EINK_EN) #if defined(PIN_EINK_EN)
if (uiconfig.screen_brightness == 1) { if (uiconfig.screen_brightness == 1) {
uiconfig.screen_brightness = 0; uiconfig.screen_brightness = 0;
@@ -949,6 +964,7 @@ void menuHandler::systemBaseMenu()
optionsArray[options] = "Notifications"; optionsArray[options] = "Notifications";
optionsEnumArray[options++] = Notifications; optionsEnumArray[options++] = Notifications;
optionsArray[options] = "Display Options"; optionsArray[options] = "Display Options";
optionsEnumArray[options++] = ScreenOptions; optionsEnumArray[options++] = ScreenOptions;
@@ -985,7 +1001,7 @@ void menuHandler::systemBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Notifications) { if (selected == Notifications) {
menuHandler::menuQueue = menuHandler::notifications_menu; menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
screen->runNow(); screen->runNow();
} else if (selected == ScreenOptions) { } else if (selected == ScreenOptions) {
menuHandler::menuQueue = menuHandler::screen_options_menu; menuHandler::menuQueue = menuHandler::screen_options_menu;
@@ -1092,11 +1108,23 @@ void menuHandler::favoriteBaseMenu()
void menuHandler::positionBaseMenu() void menuHandler::positionBaseMenu()
{ {
enum optionsNumbers { Back, GPSToggle, GPSFormat, CompassMenu, CompassCalibrate, enumEnd }; enum optionsNumbers {
Back,
GPSToggle,
GPSFormat,
CompassMenu,
CompassCalibrate,
GPSSmartPosition,
GPSUpdateInterval,
GPSPositionBroadcast,
enumEnd
};
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "GPS Format", "Compass"}; static const char *optionsArray[enumEnd] = {
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, GPSFormat, CompassMenu}; "Back", "On/Off Toggle", "Format", "Smart Position", "Update Interval", "Broadcast Interval", "Compass"};
int options = 4; static int optionsEnumArray[enumEnd] = {
Back, GPSToggle, GPSFormat, GPSSmartPosition, GPSUpdateInterval, GPSPositionBroadcast, CompassMenu};
int options = 7;
if (accelerometerThread) { if (accelerometerThread) {
optionsArray[options] = "Compass Calibrate"; optionsArray[options] = "Compass Calibrate";
@@ -1104,7 +1132,7 @@ void menuHandler::positionBaseMenu()
} }
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Position Action"; bannerOptions.message = "GPS Action";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options; bannerOptions.optionsCount = options;
@@ -1120,6 +1148,15 @@ void menuHandler::positionBaseMenu()
screen->runNow(); screen->runNow();
} else if (selected == CompassCalibrate) { } else if (selected == CompassCalibrate) {
accelerometerThread->calibrate(30); accelerometerThread->calibrate(30);
} else if (selected == GPSSmartPosition) {
menuQueue = gps_smart_position_menu;
screen->runNow();
} else if (selected == GPSUpdateInterval) {
menuQueue = gps_update_interval_menu;
screen->runNow();
} else if (selected == GPSPositionBroadcast) {
menuQueue = gps_position_broadcast_menu;
screen->runNow();
} }
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -1346,6 +1383,217 @@ void menuHandler::GPSFormatMenu()
bannerOptions.InitialSelected = uiconfig.gps_format + 1; bannerOptions.InitialSelected = uiconfig.gps_format + 1;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::GPSSmartPositionMenu()
{
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Toggle Smart Position";
if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Smrt Postn";
}
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
screen->runNow();
} else if (selected == 1) {
config.position.position_broadcast_smart_enabled = true;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == 2) {
config.position.position_broadcast_smart_enabled = false;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
bannerOptions.InitialSelected = config.position.position_broadcast_smart_enabled ? 1 : 2;
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::GPSUpdateIntervalMenu()
{
static const char *optionsArray[] = {"Back", "8 seconds", "20 seconds", "40 seconds", "1 minute", "80 seconds",
"2 minutes", "5 minutes", "10 minutes", "15 minutes", "30 minutes", "1 hour",
"6 hours", "12 hours", "24 hours", "At Boot Only"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Update Interval";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 16;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
screen->runNow();
} else if (selected == 1) {
config.position.gps_update_interval = 8;
} else if (selected == 2) {
config.position.gps_update_interval = 20;
} else if (selected == 3) {
config.position.gps_update_interval = 40;
} else if (selected == 4) {
config.position.gps_update_interval = 60;
} else if (selected == 5) {
config.position.gps_update_interval = 80;
} else if (selected == 6) {
config.position.gps_update_interval = 120;
} else if (selected == 7) {
config.position.gps_update_interval = 300;
} else if (selected == 8) {
config.position.gps_update_interval = 600;
} else if (selected == 9) {
config.position.gps_update_interval = 900;
} else if (selected == 10) {
config.position.gps_update_interval = 1800;
} else if (selected == 11) {
config.position.gps_update_interval = 3600;
} else if (selected == 12) {
config.position.gps_update_interval = 21600;
} else if (selected == 13) {
config.position.gps_update_interval = 43200;
} else if (selected == 14) {
config.position.gps_update_interval = 86400;
} else if (selected == 15) {
config.position.gps_update_interval = 2147483647; // At Boot Only
}
if (selected != 0) {
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
if (config.position.gps_update_interval == 8) {
bannerOptions.InitialSelected = 1;
} else if (config.position.gps_update_interval == 20) {
bannerOptions.InitialSelected = 2;
} else if (config.position.gps_update_interval == 40) {
bannerOptions.InitialSelected = 3;
} else if (config.position.gps_update_interval == 60) {
bannerOptions.InitialSelected = 4;
} else if (config.position.gps_update_interval == 80) {
bannerOptions.InitialSelected = 5;
} else if (config.position.gps_update_interval == 120) {
bannerOptions.InitialSelected = 6;
} else if (config.position.gps_update_interval == 300) {
bannerOptions.InitialSelected = 7;
} else if (config.position.gps_update_interval == 600) {
bannerOptions.InitialSelected = 8;
} else if (config.position.gps_update_interval == 900) {
bannerOptions.InitialSelected = 9;
} else if (config.position.gps_update_interval == 1800) {
bannerOptions.InitialSelected = 10;
} else if (config.position.gps_update_interval == 3600) {
bannerOptions.InitialSelected = 11;
} else if (config.position.gps_update_interval == 21600) {
bannerOptions.InitialSelected = 12;
} else if (config.position.gps_update_interval == 43200) {
bannerOptions.InitialSelected = 13;
} else if (config.position.gps_update_interval == 86400) {
bannerOptions.InitialSelected = 14;
} else if (config.position.gps_update_interval == 2147483647) { // At Boot Only
bannerOptions.InitialSelected = 15;
} else {
bannerOptions.InitialSelected = 0;
}
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::GPSPositionBroadcastMenu()
{
static const char *optionsArray[] = {"Back", "1 minute", "90 seconds", "5 minutes", "15 minutes", "1 hour",
"2 hours", "3 hours", "4 hours", "5 hours", "6 hours", "12 hours",
"18 hours", "24 hours", "36 hours", "48 hours", "72 hours"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Broadcast Interval";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 17;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
screen->runNow();
} else if (selected == 1) {
config.position.position_broadcast_secs = 60;
} else if (selected == 2) {
config.position.position_broadcast_secs = 90;
} else if (selected == 3) {
config.position.position_broadcast_secs = 300;
} else if (selected == 4) {
config.position.position_broadcast_secs = 900;
} else if (selected == 5) {
config.position.position_broadcast_secs = 3600;
} else if (selected == 6) {
config.position.position_broadcast_secs = 7200;
} else if (selected == 7) {
config.position.position_broadcast_secs = 10800;
} else if (selected == 8) {
config.position.position_broadcast_secs = 14400;
} else if (selected == 9) {
config.position.position_broadcast_secs = 18000;
} else if (selected == 10) {
config.position.position_broadcast_secs = 21600;
} else if (selected == 11) {
config.position.position_broadcast_secs = 43200;
} else if (selected == 12) {
config.position.position_broadcast_secs = 64800;
} else if (selected == 13) {
config.position.position_broadcast_secs = 86400;
} else if (selected == 14) {
config.position.position_broadcast_secs = 129600;
} else if (selected == 15) {
config.position.position_broadcast_secs = 172800;
} else if (selected == 16) {
config.position.position_broadcast_secs = 259200;
}
if (selected != 0) {
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
if (config.position.position_broadcast_secs == 60) {
bannerOptions.InitialSelected = 1;
} else if (config.position.position_broadcast_secs == 90) {
bannerOptions.InitialSelected = 2;
} else if (config.position.position_broadcast_secs == 300) {
bannerOptions.InitialSelected = 3;
} else if (config.position.position_broadcast_secs == 900) {
bannerOptions.InitialSelected = 4;
} else if (config.position.position_broadcast_secs == 3600) {
bannerOptions.InitialSelected = 5;
} else if (config.position.position_broadcast_secs == 7200) {
bannerOptions.InitialSelected = 6;
} else if (config.position.position_broadcast_secs == 10800) {
bannerOptions.InitialSelected = 7;
} else if (config.position.position_broadcast_secs == 14400) {
bannerOptions.InitialSelected = 8;
} else if (config.position.position_broadcast_secs == 18000) {
bannerOptions.InitialSelected = 9;
} else if (config.position.position_broadcast_secs == 21600) {
bannerOptions.InitialSelected = 10;
} else if (config.position.position_broadcast_secs == 43200) {
bannerOptions.InitialSelected = 11;
} else if (config.position.position_broadcast_secs == 64800) {
bannerOptions.InitialSelected = 12;
} else if (config.position.position_broadcast_secs == 86400) {
bannerOptions.InitialSelected = 13;
} else if (config.position.position_broadcast_secs == 129600) {
bannerOptions.InitialSelected = 14;
} else if (config.position.position_broadcast_secs == 172800) {
bannerOptions.InitialSelected = 15;
} else if (config.position.position_broadcast_secs == 259200) {
bannerOptions.InitialSelected = 16;
} else {
bannerOptions.InitialSelected = 0;
}
screen->showOverlayBanner(bannerOptions);
}
#endif #endif
void menuHandler::BluetoothToggleMenu() void menuHandler::BluetoothToggleMenu()
@@ -1372,9 +1620,9 @@ void menuHandler::BluetoothToggleMenu()
void menuHandler::BuzzerModeMenu() void menuHandler::BuzzerModeMenu()
{ {
static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only", "DMs Only"}; static const char *optionsArray[] = {"All Enabled", "All Disabled", "Notifications", "System Only", "DMs Only"};
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Buzzer Mode"; bannerOptions.message = "Notification Sounds";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 5; bannerOptions.optionsCount = 5;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
@@ -1749,30 +1997,6 @@ void menuHandler::wifiToggleMenu()
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::notificationsMenu()
{
enum optionsNumbers { Back, BuzzerActions };
static const char *optionsArray[] = {"Back", "Buzzer Actions"};
static int optionsEnumArray[] = {Back, BuzzerActions};
int options = 2;
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Notifications";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == BuzzerActions) {
menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
screen->runNow();
} else {
menuQueue = system_base_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::screenOptionsMenu() void menuHandler::screenOptionsMenu()
{ {
// Check if brightness is supported // Check if brightness is supported
@@ -2126,6 +2350,15 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case gps_format_menu: case gps_format_menu:
GPSFormatMenu(); GPSFormatMenu();
break; break;
case gps_smart_position_menu:
GPSSmartPositionMenu();
break;
case gps_update_interval_menu:
GPSUpdateIntervalMenu();
break;
case gps_position_broadcast_menu:
GPSPositionBroadcastMenu();
break;
#endif #endif
case compass_point_north_menu: case compass_point_north_menu:
compassNorthMenu(); compassNorthMenu();
@@ -2181,9 +2414,6 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case bluetooth_toggle_menu: case bluetooth_toggle_menu:
BluetoothToggleMenu(); BluetoothToggleMenu();
break; break;
case notifications_menu:
notificationsMenu();
break;
case screen_options_menu: case screen_options_menu:
screenOptionsMenu(); screenOptionsMenu();
break; break;

View File

@@ -22,6 +22,9 @@ class menuHandler
node_base_menu, node_base_menu,
gps_toggle_menu, gps_toggle_menu,
gps_format_menu, gps_format_menu,
gps_smart_position_menu,
gps_update_interval_menu,
gps_position_broadcast_menu,
compass_point_north_menu, compass_point_north_menu,
reset_node_db_menu, reset_node_db_menu,
buzzermodemenupicker, buzzermodemenupicker,
@@ -36,7 +39,6 @@ class menuHandler
number_test, number_test,
wifi_toggle_menu, wifi_toggle_menu,
bluetooth_toggle_menu, bluetooth_toggle_menu,
notifications_menu,
screen_options_menu, screen_options_menu,
power_menu, power_menu,
system_base_menu, system_base_menu,
@@ -77,6 +79,9 @@ class menuHandler
static void compassNorthMenu(); static void compassNorthMenu();
static void GPSToggleMenu(); static void GPSToggleMenu();
static void GPSFormatMenu(); static void GPSFormatMenu();
static void GPSSmartPositionMenu();
static void GPSUpdateIntervalMenu();
static void GPSPositionBroadcastMenu();
static void BuzzerModeMenu(); static void BuzzerModeMenu();
static void switchToMUIMenu(); static void switchToMUIMenu();
static void TFTColorPickerMenu(OLEDDisplay *display); static void TFTColorPickerMenu(OLEDDisplay *display);
@@ -92,7 +97,6 @@ class menuHandler
static void numberTest(); static void numberTest();
static void wifiBaseMenu(); static void wifiBaseMenu();
static void wifiToggleMenu(); static void wifiToggleMenu();
static void notificationsMenu();
static void screenOptionsMenu(); static void screenOptionsMenu();
static void powerMenu(); static void powerMenu();
static void nodeNameLengthMenu(); static void nodeNameLengthMenu();

View File

@@ -27,7 +27,9 @@ bool RotaryEncoderInterruptImpl1::init()
RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB, RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB,
RotaryEncoderInterruptImpl1::handleIntPressed); RotaryEncoderInterruptImpl1::handleIntPressed);
inputBroker->registerSource(this); inputBroker->registerSource(this);
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true; osk_found = true;
#endif
return true; return true;
} }

View File

@@ -45,7 +45,9 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef
LOG_DEBUG("Trackball GPIO initialized - UP:%d DOWN:%d LEFT:%d RIGHT:%d PRESS:%d", this->_pinUp, this->_pinDown, LOG_DEBUG("Trackball GPIO initialized - UP:%d DOWN:%d LEFT:%d RIGHT:%d PRESS:%d", this->_pinUp, this->_pinDown,
this->_pinLeft, this->_pinRight, pinPress); this->_pinLeft, this->_pinRight, pinPress);
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true; osk_found = true;
#endif
this->setInterval(100); this->setInterval(100);
} }

View File

@@ -29,7 +29,9 @@ bool UpDownInterruptImpl1::init()
eventDownLong, UpDownInterruptImpl1::handleIntDown, UpDownInterruptImpl1::handleIntUp, eventDownLong, UpDownInterruptImpl1::handleIntDown, UpDownInterruptImpl1::handleIntUp,
UpDownInterruptImpl1::handleIntPressed); UpDownInterruptImpl1::handleIntPressed);
inputBroker->registerSource(this); inputBroker->registerSource(this);
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true; osk_found = true;
#endif
return true; return true;
} }

View File

@@ -1186,11 +1186,6 @@ void setup()
#endif #endif
#endif #endif
#ifdef PIN_PWR_DELAY_MS
// This may be required to give the peripherals time to power up.
delay(PIN_PWR_DELAY_MS);
#endif
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
// as one can't use a function pointer to the class constructor: // as one can't use a function pointer to the class constructor:
auto loraModuleInterface = [](LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, auto loraModuleInterface = [](LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
@@ -1465,8 +1460,10 @@ void setup()
#endif #endif
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2) #if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true; osk_found = true;
#endif #endif
#endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
// Start web server thread. // Start web server thread.

View File

@@ -195,15 +195,13 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p)
p.rx_time = getValidTime(RTCQualityFromNet); // Record the time the packet arrived from the phone p.rx_time = getValidTime(RTCQualityFromNet); // Record the time the packet arrived from the phone
#if HAS_SCREEN IF_SCREEN(if (p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 &&
if (p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 && p.to != NODENUM_BROADCAST && p.to != NODENUM_BROADCAST && p.to != 0) // DM only
p.to != 0) // DM only {
{ perhapsDecode(&p);
perhapsDecode(&p); const StoredMessage &sm = messageStore.addFromPacket(p);
const StoredMessage &sm = messageStore.addFromPacket(p); graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI
graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI })
}
#endif
// Send the packet into the mesh // Send the packet into the mesh
DEBUG_HEAP_BEFORE; DEBUG_HEAP_BEFORE;
auto a = packetPool.allocCopy(p); auto a = packetPool.allocCopy(p);

View File

@@ -805,7 +805,8 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 500; moduleConfig.external_notification.output_ms = 500;
moduleConfig.external_notification.nag_timeout = 2; moduleConfig.external_notification.nag_timeout = 2;
#endif #endif
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) #if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) || \
defined(ELECROW_ThinkNode_M6)
// Default to PIN_LED2 for external notification output (LED color depends on device variant) // Default to PIN_LED2 for external notification output (LED color depends on device variant)
moduleConfig.external_notification.enabled = true; moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = PIN_LED2; moduleConfig.external_notification.output = PIN_LED2;

View File

@@ -115,7 +115,6 @@ namespace graphics
extern int bannerSignalBars; extern int bannerSignalBars;
} }
extern ScanI2C::DeviceAddress cardkb_found; extern ScanI2C::DeviceAddress cardkb_found;
extern bool graphics::isMuted;
extern bool osk_found; extern bool osk_found;
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto"; static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";

View File

@@ -429,7 +429,9 @@ int32_t PositionModule::runOnce()
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) { if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
if (waitingForFreshPosition) { if (waitingForFreshPosition) {
#ifdef GPS_DEBUG
LOG_DEBUG("Skip initial position send; no fresh position since boot"); LOG_DEBUG("Skip initial position send; no fresh position since boot");
#endif
} else if (nodeDB->hasValidPosition(node)) { } else if (nodeDB->hasValidPosition(node)) {
lastGpsSend = now; lastGpsSend = now;

View File

@@ -69,7 +69,7 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
// In event mode we want to prevent excessive position broadcasts // In event mode we want to prevent excessive position broadcasts
// we set the minimum interval to 5m // we set the minimum interval to 5m
const uint32_t minimumTimeThreshold = const uint32_t minimumTimeThreshold =
max(300000, Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30)); max(uint32_t(300000), Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30));
#else #else
const uint32_t minimumTimeThreshold = const uint32_t minimumTimeThreshold =
Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);

View File

@@ -20,7 +20,7 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
switch (arg->getStatusType()) { switch (arg->getStatusType()) {
case STATUS_TYPE_POWER: { case STATUS_TYPE_POWER: {
meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg; meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg;
if (powerStatus->getHasUSB()) { if (powerStatus->getHasUSB() || powerStatus->getIsCharging()) {
power_state = charging; power_state = charging;
if (powerStatus->getBatteryChargePercent() >= 100) { if (powerStatus->getBatteryChargePercent() >= 100) {
power_state = charged; power_state = charged;

View File

@@ -45,10 +45,9 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
// Mute // Mute
case INPUT_BROKER_MSG_MUTE_TOGGLE: case INPUT_BROKER_MSG_MUTE_TOGGLE:
if (moduleConfig.external_notification.enabled && externalNotificationModule) { if (moduleConfig.external_notification.enabled && externalNotificationModule) {
bool isMuted = externalNotificationModule->getMute(); externalNotificationModule->setMute(externalNotificationModule->getMute());
externalNotificationModule->setMute(!isMuted); IF_SCREEN(if (!externalNotificationModule->getMute()) externalNotificationModule->stopNow(); screen->showSimpleBanner(
IF_SCREEN(graphics::isMuted = !isMuted; if (!isMuted) externalNotificationModule->stopNow(); externalNotificationModule->getMute() ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);)
screen->showSimpleBanner(isMuted ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);)
} }
return 0; return 0;
// Bluetooth // Bluetooth

View File

@@ -21,18 +21,17 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
// We only store/display messages destined for us. // We only store/display messages destined for us.
devicestate.rx_text_message = mp; devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true; devicestate.has_rx_text_message = true;
#if HAS_SCREEN IF_SCREEN(
// Guard against running in MeshtasticUI // Guard against running in MeshtasticUI or with no screen
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
// Store in the central message history // Store in the central message history
const StoredMessage &sm = messageStore.addFromPacket(mp); const StoredMessage &sm = messageStore.addFromPacket(mp);
// Pass message to renderer (banner + thread switching + scroll reset) // Pass message to renderer (banner + thread switching + scroll reset)
// Use the global Screen singleton to retrieve the current OLED display // Use the global Screen singleton to retrieve the current OLED display
auto *display = screen ? screen->getDisplayDevice() : nullptr; auto *display = screen ? screen->getDisplayDevice() : nullptr;
graphics::MessageRenderer::handleNewMessage(display, sm, mp); graphics::MessageRenderer::handleNewMessage(display, sm, mp);
} })
#endif
// Only trigger screen wake if configuration allows it // Only trigger screen wake if configuration allows it
if (shouldWakeOnReceivedMessage()) { if (shouldWakeOnReceivedMessage()) {
powerFSM.trigger(EVENT_RECEIVED_MSG); powerFSM.trigger(EVENT_RECEIVED_MSG);

View File

@@ -279,7 +279,7 @@ void portduinoSetup()
// RAK6421-13300-S1:aabbcc123456:5ba85807d92138b7519cfb60460573af:3061e8d8 // RAK6421-13300-S1:aabbcc123456:5ba85807d92138b7519cfb60460573af:3061e8d8
// <model string>:mac address :<16 random unique bytes in hexidecimal> : crc32 // <model string>:mac address :<16 random unique bytes in hexidecimal> : crc32
// crc32 is calculated on the eeprom string up to but not including the final colon // crc32 is calculated on the eeprom string up to but not including the final colon
if (strlen(autoconf_product) < 6) { if (strlen(autoconf_product) < 6 && portduino_config.i2cdev != "") {
try { try {
char *mac_start = nullptr; char *mac_start = nullptr;
char *devID_start = nullptr; char *devID_start = nullptr;
@@ -869,4 +869,4 @@ void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault
destPin.line = destPin.pin; destPin.line = destPin.pin;
destPin.gpiochip = portduino_config.lora_default_gpiochip; destPin.gpiochip = portduino_config.lora_default_gpiochip;
} }
} }

View File

@@ -100,3 +100,5 @@
#define MODEM_DTR 8 #define MODEM_DTR 8
#define MODEM_RX 10 #define MODEM_RX 10
#define MODEM_TX 11 #define MODEM_TX 11
#define HAS_PHYSICAL_KEYBOARD 1

View File

@@ -23,6 +23,7 @@
#define SCREEN_TRANSITION_FRAMERATE 5 #define SCREEN_TRANSITION_FRAMERATE 5
#define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness #define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness
#define USE_TFTDISPLAY 1 #define USE_TFTDISPLAY 1
#define HAS_PHYSICAL_KEYBOARD 1
#define HAS_TOUCHSCREEN 1 #define HAS_TOUCHSCREEN 1
#define SCREEN_TOUCH_INT 16 #define SCREEN_TOUCH_INT 16

View File

@@ -21,6 +21,7 @@
#define SCREEN_TRANSITION_FRAMERATE 5 #define SCREEN_TRANSITION_FRAMERATE 5
#define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness #define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness
#define USE_TFTDISPLAY 1 #define USE_TFTDISPLAY 1
#define HAS_PHYSICAL_KEYBOARD 1
#define I2C_SDA SDA #define I2C_SDA SDA
#define I2C_SCL SCL #define I2C_SCL SCL

View File

@@ -15,5 +15,5 @@ lib_deps =
${nrf52840_base.lib_deps} ${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=nRF52_PWM packageName=khoih-prog/library/nRF52_PWM # renovate: datasource=custom.pio depName=nRF52_PWM packageName=khoih-prog/library/nRF52_PWM
khoih-prog/nRF52_PWM@1.0.1 khoih-prog/nRF52_PWM@1.0.1
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library ; # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/PCF8563_Library@1.0.1 ; lewisxhe/SensorLib@0.3.3

View File

@@ -114,7 +114,8 @@ extern "C" {
#define LR11X0_DIO_AS_RF_SWITCH #define LR11X0_DIO_AS_RF_SWITCH
// PCF8563 RTC Module // PCF8563 RTC Module
#define PCF8563_RTC 0x51 // REVISIT https://github.com/meshtastic/firmware/pull/9084
// #define PCF8563_RTC 0x51
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -12,5 +12,5 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ELECROW-ThinkNode-M6> build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ELECROW-ThinkNode-M6>
lib_deps = lib_deps =
${nrf52840_base.lib_deps} ${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library ; # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/PCF8563_Library@1.0.1 ; lewisxhe/SensorLib@0.3.3

View File

@@ -41,3 +41,30 @@ void initVariant()
pinMode(VDD_FLASH_EN, OUTPUT); pinMode(VDD_FLASH_EN, OUTPUT);
digitalWrite(VDD_FLASH_EN, HIGH); digitalWrite(VDD_FLASH_EN, HIGH);
} }
// called from main-nrf52.cpp during the cpuDeepSleep() function
void variant_shutdown()
{
// This sets the pin to OUTPUT and LOW for the pins *not* in the if block.
for (int pin = 0; pin < 48; pin++) {
if (pin == PIN_GPS_EN || pin == ADC_CTRL || pin == PIN_BUTTON1 || pin == PIN_SPI_MISO || pin == PIN_SPI_MOSI ||
pin == PIN_SPI_SCK) {
continue;
}
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
if (pin >= 32) {
NRF_P1->DIRCLR = (1 << (pin - 32));
} else {
NRF_GPIO->DIRCLR = (1 << pin);
}
}
digitalWrite(PIN_GPS_EN, LOW);
digitalWrite(ADC_CTRL, LOW);
// digitalWrite(RTC_POWER, LOW);
nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input
nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW;
nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense1);
}

View File

@@ -44,8 +44,10 @@ extern "C" {
#define LED_BLUE -1 #define LED_BLUE -1
#define LED_CHARGE (12) #define LED_CHARGE (12)
#define LED_PAIRING (7) #define LED_PAIRING (7)
#define PIN_LED2 LED_PAIRING
#define LED_STATE_ON 1 #define LED_STATE_ON HIGH
#define LED_STATE_OFF LOW
// USB power detection // USB power detection
#define EXT_PWR_DETECT (13) #define EXT_PWR_DETECT (13)
@@ -119,7 +121,8 @@ static const uint8_t A0 = PIN_A0;
#define PIN_SERIAL2_TX (24) #define PIN_SERIAL2_TX (24)
// PCF8563 RTC Module // PCF8563 RTC Module
#define PCF8563_RTC 0x51 // REVISIT https://github.com/meshtastic/firmware/pull/9084
// #define PCF8563_RTC 0x51
// SPI // SPI
#define SPI_INTERFACES_COUNT 1 #define SPI_INTERFACES_COUNT 1

View File

@@ -103,7 +103,7 @@ static const uint8_t A0 = PIN_A0;
#define EXTERNAL_FLASH_USE_QSPI #define EXTERNAL_FLASH_USE_QSPI
// Add a delay on startup to allow LoRa and GPS to power up // Add a delay on startup to allow LoRa and GPS to power up
#define PIN_PWR_DELAY_MS 100 #define PERIPHERAL_WARMUP_MS 100
/* /*
* Lora radio * Lora radio
@@ -178,4 +178,4 @@ static const uint8_t A0 = PIN_A0;
* Arduino objects - C++ only * Arduino objects - C++ only
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
#endif #endif

View File

@@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 2 major = 2
minor = 7 minor = 7
build = 18 build = 17