Compare commits

..

53 Commits

Author SHA1 Message Date
Jonathan Bennett
66cb549817 Merge branch 'develop' into merge-master-fix 2026-01-01 16:12:28 -06:00
Tom Fifield
a2ce4c7f18 KZ_863 is not wide lora (#9075)
KZ_863 was set to wide_lora = true. This was a mistake, induced because the
regulations would allow SHORT_TURBO. However, that interpretation of the code
was incorrect and wide_lora has a different meaning.

Fixes https://github.com/meshtastic/firmware/issues/9054
2026-01-02 09:03:05 +11:00
Tom Fifield
45335532ca Syntax fix for first timer welcome bot. (#9144)
URL formatting was inverted.
2026-01-02 08:57:13 +11:00
Jonathan Bennett
c351c49a72 Merge remote-tracking branch 'origin/master' into develop 2026-01-01 15:36:26 -06:00
Jonathan Bennett
a5b2d4a9d5 Add null check for p_encrypted before MQTT publish (#9136)
* Add null check for p_encrypted before MQTT publish

A user on BayMesh observed a strange crash in MQTT::onSend that seemed to be a null pointer dereference of this value.

* Trunk
2026-01-01 13:53:36 -06:00
Ben Meadors
7fb95841e4 Apparently I marked board level extra on the wrong tbeam target 2026-01-01 08:25:33 -06:00
Ford Jones
4f1a56d480 Rak3112 support (#8591)
* Add rak3112 to board variants

* Add rak3112 to architecture definitions

* Disable SDcard support

* Update comments

* Remove duplicate definitions and use expected SPI naming for SDcard module

* SDcard module serial interface chip set definition

* Refactor modular variant into existing environment

* Make requested changes

* Extend 3312 variants

* Remove duplicate architecture definition

* Fix definition naming
2025-12-31 19:23:24 -06:00
renovate[bot]
eaab8f04b5 chore(deps): update meshtastic/device-ui digest to 940ba85 (#9129)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-01 10:58:56 +11:00
Jason P
25acce2a8d Add Temporary Mute to Home frame and unbury Notification Options (#9097)
* Add Temporary Mute to Home frame and unbury Notifications

* Only show Temporary Mute if it applies

* Remove banner notification, we display the icon immediately

* Remove extranous isMuted, there are better ways!
2025-12-31 09:19:05 -06:00
Jonathan Bennett
da9d71190f Add STORE_FORWARD_PLUSPLUS_APP to core portnum checks (#9127) 2025-12-30 19:04:19 -06:00
Tom Fifield
1443a32f1b Add a welcome message for new contributors (#9119)
To assist with onboarding the denizens of the greater internet with
our norms and ways of working, this action will post a message on
PRs and issues from first-timers.
2025-12-30 19:04:05 -06:00
Eric Severance
9058ccecf9 Calculate hops correctly even when hop_start==0 (#9120)
* Calculate hops correctly even when hop_start==0.

* Use the same type (int8_t) in the loop, avoiding signed/unsigned mismatches.

* Clarify defaultIfUnknown is returned for encrypted packets.
2025-12-30 19:03:51 -06:00
Ben Meadors
1b83501ee2 Revert "Upgrade all esp32 targets to NimBLE 2.X (#9003)" (#9125)
This reverts commit 40f1f91c0d.
2025-12-30 17:23:50 -06:00
Eric Severance
1b2dc10e77 Calculate hops correctly even when hop_start==0 (#9120)
* Calculate hops correctly even when hop_start==0.

* Use the same type (int8_t) in the loop, avoiding signed/unsigned mismatches.

* Clarify defaultIfUnknown is returned for encrypted packets.
2025-12-30 15:31:35 -06:00
github-actions[bot]
ac571d5dd2 Upgrade trunk (#9121)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-30 07:10:36 -06:00
renovate[bot]
ef30fd850d Update meshtastic/device-ui digest to 7656d49 (#9111)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-29 19:09:44 +01:00
Austin
3a723ceae8 Noop "download" portion of #shame (#9114) 2025-12-29 12:13:36 -05:00
github-actions[bot]
dc36f5df7a Update protobufs (#9109)
Co-authored-by: jp-bennett <5630967+jp-bennett@users.noreply.github.com>
2025-12-29 07:52:36 -06:00
renovate[bot]
b9a0015149 chore(deps): update meshtastic/device-ui digest to d234bd9 (#9108)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-29 06:50:12 -06:00
github-actions[bot]
9673cfb0b2 Upgrade trunk (#9106)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-29 06:03:03 -06:00
renovate[bot]
757f7b68d6 Update meshtastic/device-ui digest to caff403 (#9104)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-29 13:35:31 +11:00
Jonathan Bennett
63aadba526 Use IF_SCREEN macro to guard against null screen object 2025-12-28 11:33:14 -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
Jason P
5510dae8d3 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-27 06:53:55 -06:00
Tom
52fd362720 Fix gps pin defs for various NRF variants. (#9034)
* fix on nrf52_promicro

* try fix for GPS issue

* fix GPS pin assignment in variant.h

* cleared up some comments and confirmed pinouts from schematics

---------

Co-authored-by: macvenez <macvenez@gmail.com>
2025-12-27 06:50:07 -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
github-actions[bot]
33e1f58f6e Upgrade trunk (#9076)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-26 17:45:57 -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
Jonathan Bennett
9dc7ef612e 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 14:33:17 -06:00
github-actions[bot]
b2c82bdc41 Upgrade trunk (#9072)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-25 06:34:38 -06:00
Jonathan Bennett
54a928f47f M6 shutdown and LEDs work (#9065)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-24 07:48:14 -06:00
github-actions[bot]
33f18659c8 Upgrade trunk (#9067)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-24 05:20:22 -06:00
github-actions[bot]
3a7093a973 Upgrade trunk (#9047)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-23 18:55:54 -06:00
renovate[bot]
a4f6f4515a Update meshtastic-esp8266-oled-ssd1306 digest to b34c681 (#9062)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 18:55:37 -06:00
Jonathan Bennett
d609d05698 In statusLEDModule, also detect isCharging (#9050) 2025-12-23 07:48:55 -06:00
Ben Meadors
83c6161ac6 Revert "Automated version bumps (#9025)"
This reverts commit 1021d967da.
2025-12-20 14:10:02 -06:00
Ben Meadors
d93d68d31e Fix -ota.zip in manifest and build output 2025-12-20 14:09:05 -06:00
122 changed files with 921 additions and 258 deletions

View File

@@ -0,0 +1,47 @@
name: Welcome First-Time Contributor
on:
issues:
types: opened
pull_request_target:
types: opened
permissions: {}
jobs:
welcome:
runs-on: ubuntu-latest
permissions:
issues: write # Required to post comments and labels on issues
pull-requests: write # Required to post comments and labels on PRs
steps:
- uses: plbstl/first-contribution@v4
with:
labels: first-contribution
issue-opened-msg: |
### @{fc-author}, Welcome to Meshtastic! :wave:
Thanks for opening your first issue. If it's helpful, an easy way
to get logs is the "Open Serial Monitor" button on the (Web Flasher](https://flasher.meshtastic.org).
If you have ideas for features, note that we often debate big ideas
in the [discussions tab](https://github.com/meshtastic/firmware/discussions/categories/ideas)
first. This tracker tends to be for ideas that have community
consensus and a clear implementation.
We're very active [on discord](https://discord.com/invite/meshtastic),
especially the \#firmware and \#alphanauts-testing channels. If you'll
be around for a while, we'd love to see you there!
Welcome to the community! :heart:
pr-opened-msg: |
### @{fc-author}, Welcome to Meshtastic!
Thanks for opening your first pull request. We really appreciate it.
We discuss work as a team in discord, please join us in the [#firmware channel](https://discord.com/invite/meshtastic).
There's a big backlog of patches at the moment. If you have time,
please help us with some code review and testing of [other PRs](https://github.com/meshtastic/firmware/pulls)!
Welcome to the team :smile:

View File

@@ -240,6 +240,7 @@ jobs:
needs: [build]
steps:
- uses: actions/checkout@v6
if: github.event_name == 'pull_request_target'
with:
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
@@ -253,19 +254,26 @@ jobs:
uses: actions/upload-artifact@v6
id: upload-manifest
with:
name: manifests-all
name: manifests-${{ github.sha }}
overwrite: true
path: |
manifests-new/*.mt.json
path: manifests-new/*.mt.json
- name: Find the merge base
if: github.event_name == 'pull_request_target'
run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV
env:
base: ${{ github.base_ref }}
head: ${{ github.head_ref }}
- name: Download the old manifests
run: gh run download -R ${{ github.repository }} --commit ${{ env.MERGE_BASE }} --name manifests-all --dir manifest-old/
- name: Do scan and post comment
run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
head: ${{ github.sha }}
# Currently broken (for-loop through EVERY artifact -- rate limiting)
# - name: Download the old manifests
# 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
# if: github.event_name == 'pull_request_target'
# run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
release-artifacts:
runs-on: ubuntu-latest

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)
data/boot/logo.*
# pioarduino platform
managed_components/*
arduino-lib-builder*
dependencies.lock
idf_component.yml
CMakeLists.txt
sdkconfig.*
.dummy/*

View File

@@ -8,10 +8,10 @@ plugins:
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.495
- renovate@42.64.1
- checkov@3.2.496
- renovate@42.66.14
- prettier@3.7.4
- trufflehog@3.92.3
- trufflehog@3.92.4
- yamllint@1.37.1
- bandit@1.9.2
- trivy@0.68.2

View File

@@ -21,13 +21,14 @@ 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/$basename.zip
cp $BUILDDIR/$basename.zip $OUTDIR/$ota_basename.zip
echo "Copying NRF52 UF2 file"
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2

View File

@@ -87,9 +87,6 @@
</screenshots>
<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">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
</release>

View File

@@ -17,6 +17,8 @@ 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",
@@ -32,8 +34,11 @@ 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": p,
"name": manifest_name,
"md5": f.get_content_hash(), # Returns MD5 hash
"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
* Version 2.7.17

View File

@@ -55,7 +55,6 @@ build_flags = -Wno-missing-field-initializers
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
-D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
-DLED_BUILTIN=-1
#-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now
#-D OLED_PL=1
#-D DEBUG_HEAP=1 ; uncomment to add free heap space / memory leak debugging logs
@@ -65,7 +64,7 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# 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
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
@@ -120,7 +119,7 @@ lib_deps =
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/862ed040c4ab44f0dfbbe492691f144886102588.zip
https://github.com/meshtastic/device-ui/archive/940ba8570f59c59c3508643f4d72840de716ce20.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]

View File

@@ -8,6 +8,7 @@
#include "graphics/draw/UIRenderer.h"
#include "main.h"
#include "meshtastic/config.pb.h"
#include "modules/ExternalNotificationModule.h"
#include "power.h"
#include <OLEDDisplay.h>
#include <graphics/images.h>
@@ -56,7 +57,6 @@ void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second)
// === Shared External State ===
bool hasUnreadMessage = false;
bool isMuted = false;
ScreenResolution currentResolution = ScreenResolution::Low;
// === 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);
}
} else if (isMuted) {
} else if (externalNotificationModule->getMute()) {
if (currentResolution == ScreenResolution::High) {
int iconX = iconRightEdge - mute_symbol_big_width;
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 iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
if (isInverted) {
if (isInverted && !force_no_invert) {
display->setColor(WHITE);
display->fillRect(iconX - 1, iconY - 1, mute_symbol_width + 2, mute_symbol_height + 2);
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;
display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
}
} else if (isMuted) {
} else if (externalNotificationModule->getMute()) {
if (currentResolution == ScreenResolution::High) {
int iconX = iconRightEdge - mute_symbol_big_width;
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;
}
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 output;
bool inReplacement = false;
for (char c : input) {
if (std::isalnum(static_cast<unsigned char>(c)) || isAllowedPunctuation(c)) {
// Make a mutable copy so we can normalize UTF-8 “smart punctuation” into ASCII first.
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;
inReplacement = false;
} else {
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;
}
}

View File

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

View File

@@ -20,8 +20,8 @@
#include "mesh/MeshTypes.h"
#include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/KeyVerificationModule.h"
#include "modules/TraceRouteModule.h"
#include <algorithm>
#include <array>
@@ -843,12 +843,21 @@ void menuHandler::messageViewModeMenu()
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 int optionsEnumArray[enumEnd] = {Back};
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)
optionsArray[options] = "Toggle Backlight";
optionsEnumArray[options++] = Backlight;
@@ -872,7 +881,13 @@ void menuHandler::homeBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
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 (uiconfig.screen_brightness == 1) {
uiconfig.screen_brightness = 0;
@@ -949,6 +964,7 @@ void menuHandler::systemBaseMenu()
optionsArray[options] = "Notifications";
optionsEnumArray[options++] = Notifications;
optionsArray[options] = "Display Options";
optionsEnumArray[options++] = ScreenOptions;
@@ -985,7 +1001,7 @@ void menuHandler::systemBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Notifications) {
menuHandler::menuQueue = menuHandler::notifications_menu;
menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
screen->runNow();
} else if (selected == ScreenOptions) {
menuHandler::menuQueue = menuHandler::screen_options_menu;
@@ -1092,11 +1108,23 @@ void menuHandler::favoriteBaseMenu()
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 int optionsEnumArray[enumEnd] = {Back, GPSToggle, GPSFormat, CompassMenu};
int options = 4;
static const char *optionsArray[enumEnd] = {
"Back", "On/Off Toggle", "Format", "Smart Position", "Update Interval", "Broadcast Interval", "Compass"};
static int optionsEnumArray[enumEnd] = {
Back, GPSToggle, GPSFormat, GPSSmartPosition, GPSUpdateInterval, GPSPositionBroadcast, CompassMenu};
int options = 7;
if (accelerometerThread) {
optionsArray[options] = "Compass Calibrate";
@@ -1104,7 +1132,7 @@ void menuHandler::positionBaseMenu()
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Position Action";
bannerOptions.message = "GPS Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
@@ -1120,6 +1148,15 @@ void menuHandler::positionBaseMenu()
screen->runNow();
} else if (selected == CompassCalibrate) {
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);
@@ -1346,6 +1383,217 @@ void menuHandler::GPSFormatMenu()
bannerOptions.InitialSelected = uiconfig.gps_format + 1;
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
void menuHandler::BluetoothToggleMenu()
@@ -1372,9 +1620,9 @@ void menuHandler::BluetoothToggleMenu()
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;
bannerOptions.message = "Buzzer Mode";
bannerOptions.message = "Notification Sounds";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 5;
bannerOptions.bannerCallback = [](int selected) -> void {
@@ -1749,30 +1997,6 @@ void menuHandler::wifiToggleMenu()
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()
{
// Check if brightness is supported
@@ -2126,6 +2350,15 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case gps_format_menu:
GPSFormatMenu();
break;
case gps_smart_position_menu:
GPSSmartPositionMenu();
break;
case gps_update_interval_menu:
GPSUpdateIntervalMenu();
break;
case gps_position_broadcast_menu:
GPSPositionBroadcastMenu();
break;
#endif
case compass_point_north_menu:
compassNorthMenu();
@@ -2181,9 +2414,6 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case bluetooth_toggle_menu:
BluetoothToggleMenu();
break;
case notifications_menu:
notificationsMenu();
break;
case screen_options_menu:
screenOptionsMenu();
break;

View File

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

View File

@@ -1,6 +1,7 @@
#ifdef MESHTASTIC_INCLUDE_INKHUD
#include "./PositionsApplet.h"
#include "NodeDB.h"
using namespace NicheGraphics;
@@ -49,8 +50,8 @@ ProcessMessage InkHUD::PositionsApplet::handleReceived(const meshtastic_MeshPack
if (!hasPosition)
return ProcessMessage::CONTINUE;
bool hasHopsAway = (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start); // From NodeDB::updateFrom
uint8_t hopsAway = mp.hop_start - mp.hop_limit;
const int8_t hopsAway = getHopsAway(mp);
const bool hasHopsAway = hopsAway >= 0;
// Determine if the position packet would change anything on-screen
// -----------------------------------------------------------------

View File

@@ -1186,11 +1186,6 @@ void setup()
#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
// 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,

View File

@@ -195,7 +195,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
// bad.
routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel,
routingModule->getHopLimitForResponse(mp.hop_start, mp.hop_limit));
routingModule->getHopLimitForResponse(mp));
}
}
@@ -235,7 +235,7 @@ void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to)
assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
p->channel = to.channel; // Use the same channel that the request came in on
p->hop_limit = routingModule->getHopLimitForResponse(to.hop_start, to.hop_limit);
p->hop_limit = routingModule->getHopLimitForResponse(to);
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
p->want_ack = (to.from != 0) ? to.want_ack : false;

View File

@@ -95,11 +95,8 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) {
if (airTime->isTxAllowedChannelUtil(true)) {
// Hops used by the request. If somebody in between running modified firmware modified it, ignore it
auto hopStart = mp->hop_start;
auto hopLimit = mp->hop_limit;
uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit;
if (hopsUsed > config.lora.hop_limit + 2) {
const int8_t hopsUsed = getHopsAway(*mp, config.lora.hop_limit);
if (hopsUsed > (int32_t)(config.lora.hop_limit + 2)) {
LOG_DEBUG("Skip send NodeInfo: %d hops away is too far away", hopsUsed);
} else {
LOG_INFO("Heard new node on ch. %d, send NodeInfo and ask for response", mp->channel);
@@ -195,15 +192,13 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p)
p.rx_time = getValidTime(RTCQualityFromNet); // Record the time the packet arrived from the phone
#if HAS_SCREEN
if (p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 && p.to != NODENUM_BROADCAST &&
p.to != 0) // DM only
{
perhapsDecode(&p);
const StoredMessage &sm = messageStore.addFromPacket(p);
graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI
}
#endif
IF_SCREEN(if (p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 &&
p.to != NODENUM_BROADCAST && p.to != 0) // DM only
{
perhapsDecode(&p);
const StoredMessage &sm = messageStore.addFromPacket(p);
graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI
})
// Send the packet into the mesh
DEBUG_HEAP_BEFORE;
auto a = packetPool.allocCopy(p);

View File

@@ -64,7 +64,7 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
perhapsRebroadcast(p);
}
} else {
bool isRepeated = p->hop_start > 0 && p->hop_start == p->hop_limit;
bool isRepeated = getHopsAway(*p) == 0;
// If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again
if (isRepeated) {
if (!findInTxQueue(p->from, p->id)) {
@@ -101,8 +101,7 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
bool wasAlreadyRelayer = wasRelayer(p->relay_node, p->decoded.request_id, p->to);
bool weWereSoleRelayer = false;
bool weWereRelayer = wasRelayer(ourRelayID, p->decoded.request_id, p->to, &weWereSoleRelayer);
if ((weWereRelayer && wasAlreadyRelayer) ||
(p->hop_start != 0 && p->hop_start == p->hop_limit && weWereSoleRelayer)) {
if ((weWereRelayer && wasAlreadyRelayer) || (getHopsAway(*p) == 0 && weWereSoleRelayer)) {
if (origTx->next_hop != p->relay_node) { // Not already set
LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply (was relayer %d we were sole %d)", p->from,
p->relay_node, wasAlreadyRelayer, weWereSoleRelayer);

View File

@@ -805,7 +805,8 @@ 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)
#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)
moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = PIN_LED2;
@@ -1548,6 +1549,23 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p)
return delta;
}
int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown)
{
// Firmware prior to 2.3.0 (585805c) lacked a hop_start field. Firmware version 2.5.0 (bf34329) introduced a
// bitfield that is always present. Use the presence of the bitfield to determine if the origin's firmware
// version is guaranteed to have hop_start populated. Note that this can only be done for decoded packets as
// the bitfield is encrypted under the channel encryption key. For encrypted packets, this returns
// defaultIfUnknown when hop_start is 0.
if (p.hop_start == 0 && !(p.which_payload_variant == meshtastic_MeshPacket_decoded_tag && p.decoded.has_bitfield))
return defaultIfUnknown; // Cannot reliably determine the number of hops.
// Guard against invalid values.
if (p.hop_start < p.hop_limit)
return defaultIfUnknown;
return p.hop_start - p.hop_limit;
}
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
@@ -1800,9 +1818,10 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
const int8_t hopsAway = getHopsAway(mp);
if (hopsAway >= 0) {
info->has_hops_away = true;
info->hops_away = mp.hop_start - mp.hop_limit;
info->hops_away = hopsAway;
}
sortMeshDB();
}

View File

@@ -110,6 +110,10 @@ uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
/// Given a packet, return how many seconds in the past (vs now) it was received
uint32_t sinceReceived(const meshtastic_MeshPacket *p);
/// Given a packet, return the number of hops used to reach this node.
/// Returns defaultIfUnknown if the number of hops couldn't be determined.
int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown = -1);
enum LoadFileResult {
// Successfully opened the file
LOAD_SUCCESS = 1,

View File

@@ -171,7 +171,8 @@ const RegionInfo regions[] = {
863 - 868 MHz <25 mW EIRP, 500kHz channels allowed, must not be used at airfields
https://github.com/meshtastic/firmware/issues/7204
*/
RDEF(KZ_433, 433.075f, 434.775f, 100, 0, 10, true, false, false), RDEF(KZ_863, 863.0f, 868.0f, 100, 0, 30, true, false, true),
RDEF(KZ_433, 433.075f, 434.775f, 100, 0, 10, true, false, false),
RDEF(KZ_863, 863.0f, 868.0f, 100, 0, 30, true, false, false),
/*
Nepal

View File

@@ -1,6 +1,7 @@
#include "ReliableRouter.h"
#include "Default.h"
#include "MeshTypes.h"
#include "NodeDB.h"
#include "configuration.h"
#include "memGet.h"
#include "mesh-pb-constants.h"
@@ -108,12 +109,12 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
// If this packet should always be ACKed reliably with want_ack back to the original sender, make sure we
// do that unconditionally.
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit), true);
routingModule->getHopLimitForResponse(*p), true);
} else if (!p->decoded.request_id && !p->decoded.reply_id) {
// If it's not an ACK or a reply, send an ACK.
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
} else if ((p->hop_start > 0 && p->hop_start == p->hop_limit) || p->next_hop != NO_NEXT_HOP_PREFERENCE) {
routingModule->getHopLimitForResponse(*p));
} else if ((getHopsAway(*p) == 0) || p->next_hop != NO_NEXT_HOP_PREFERENCE) {
// If we received the packet directly from the original sender, send a 0-hop ACK since the original sender
// won't overhear any implicit ACKs. If we received the packet via NextHopRouter, also send a 0-hop ACK to
// stop the immediate relayer's retransmissions.
@@ -123,11 +124,11 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) {
LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY");
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
routingModule->getHopLimitForResponse(*p));
} else {
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(),
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
routingModule->getHopLimitForResponse(*p));
}
} else if (p->next_hop == nodeDB->getLastByteOfNodeNum(getNodeNum()) && p->hop_limit > 0) {
// No wantAck, but we need to ACK with hop limit of 0 if we were the next hop to stop their retransmissions

View File

@@ -81,8 +81,7 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA
bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p)
{
// First hop MUST always decrement to prevent retry issues
bool isFirstHop = (p->hop_start != 0 && p->hop_start == p->hop_limit);
if (isFirstHop) {
if (getHopsAway(*p) == 0) {
return true; // Always decrement on first hop
}
@@ -730,7 +729,8 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
meshtastic_PortNum_POSITION_APP, meshtastic_PortNum_NODEINFO_APP, meshtastic_PortNum_ROUTING_APP,
meshtastic_PortNum_TELEMETRY_APP, meshtastic_PortNum_ADMIN_APP, meshtastic_PortNum_ALERT_APP,
meshtastic_PortNum_KEY_VERIFICATION_APP, meshtastic_PortNum_WAYPOINT_APP,
meshtastic_PortNum_STORE_FORWARD_APP, meshtastic_PortNum_TRACEROUTE_APP)) {
meshtastic_PortNum_STORE_FORWARD_APP, meshtastic_PortNum_TRACEROUTE_APP,
meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP)) {
LOG_DEBUG("Ignore packet on non-standard portnum for CORE_PORTNUMS_ONLY");
cancelSending(p->from, p->id);
skipHandle = true;
@@ -745,15 +745,19 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
MeshModule::callModules(*p, src);
#if !MESHTASTIC_EXCLUDE_MQTT
// Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not to
// us (because we would be able to decrypt it)
if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 &&
!isBroadcast(p->to) && !isToUs(p))
p_encrypted->pki_encrypted = true;
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled &&
!isFromUs(p) && mqtt)
mqtt->onSend(*p_encrypted, *p, p->channel);
if (p_encrypted == nullptr) {
LOG_WARN("p_encrypted is null, skipping MQTT publish");
} else {
// Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not
// to us (because we would be able to decrypt it)
if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 &&
!isBroadcast(p->to) && !isToUs(p))
p_encrypted->pki_encrypted = true;
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled &&
!isFromUs(p) && mqtt)
mqtt->onSend(*p_encrypted, *p, p->channel);
}
#endif
}

View File

@@ -822,6 +822,8 @@ typedef struct _meshtastic_StoreForwardPlusPlus {
uint32_t encapsulated_from;
/* The receive time of the message in question */
uint32_t encapsulated_rxtime;
/* Used in a LINK_REQUEST to specify the message X spots back from head */
uint32_t chain_count;
} meshtastic_StoreForwardPlusPlus;
/* Waypoint message, used to share arbitrary locations across the mesh */
@@ -1428,7 +1430,7 @@ extern "C" {
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
#define meshtastic_KeyVerification_init_default {0, {0, {0}}, {0, {0}}}
#define meshtastic_StoreForwardPlusPlus_init_default {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
#define meshtastic_StoreForwardPlusPlus_init_default {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
@@ -1460,7 +1462,7 @@ extern "C" {
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
#define meshtastic_KeyVerification_init_zero {0, {0, {0}}, {0, {0}}}
#define meshtastic_StoreForwardPlusPlus_init_zero {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
#define meshtastic_StoreForwardPlusPlus_init_zero {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
@@ -1548,6 +1550,7 @@ extern "C" {
#define meshtastic_StoreForwardPlusPlus_encapsulated_to_tag 7
#define meshtastic_StoreForwardPlusPlus_encapsulated_from_tag 8
#define meshtastic_StoreForwardPlusPlus_encapsulated_rxtime_tag 9
#define meshtastic_StoreForwardPlusPlus_chain_count_tag 10
#define meshtastic_Waypoint_id_tag 1
#define meshtastic_Waypoint_latitude_i_tag 2
#define meshtastic_Waypoint_longitude_i_tag 3
@@ -1773,7 +1776,8 @@ X(a, STATIC, SINGULAR, BYTES, message, 5) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_id, 6) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_to, 7) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_from, 8) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_rxtime, 9)
X(a, STATIC, SINGULAR, UINT32, encapsulated_rxtime, 9) \
X(a, STATIC, SINGULAR, UINT32, chain_count, 10)
#define meshtastic_StoreForwardPlusPlus_CALLBACK NULL
#define meshtastic_StoreForwardPlusPlus_DEFAULT NULL
@@ -2143,7 +2147,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_QueueStatus_size 23
#define meshtastic_RouteDiscovery_size 256
#define meshtastic_Routing_size 259
#define meshtastic_StoreForwardPlusPlus_size 371
#define meshtastic_StoreForwardPlusPlus_size 377
#define meshtastic_ToRadio_size 504
#define meshtastic_User_size 115
#define meshtastic_Waypoint_size 165

View File

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

View File

@@ -170,7 +170,7 @@ bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
} else {
LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)");
}
} else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) {
} else if (getHopsAway(mp) == 0) {
LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr);
// If the hopLimit is the same as hopStart, then it is a neighbor
getOrCreateNeighbor(mp.from, mp.from, 0,

View File

@@ -429,7 +429,9 @@ int32_t PositionModule::runOnce()
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)) {
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
// we set the minimum interval to 5m
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
const uint32_t minimumTimeThreshold =
Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);

View File

@@ -58,12 +58,11 @@ void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketI
router->sendLocal(p); // we sometimes send directly to the local node
}
uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit)
uint8_t RoutingModule::getHopLimitForResponse(const meshtastic_MeshPacket &mp)
{
if (hopStart != 0) {
// Hops used by the request. If somebody in between running modified firmware modified it, ignore it
uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit;
if (hopsUsed > config.lora.hop_limit) {
const int8_t hopsUsed = getHopsAway(mp);
if (hopsUsed >= 0) {
if (hopsUsed > (int32_t)(config.lora.hop_limit)) {
// In event mode, we never want to send packets with more than our default 3 hops.
#if !(EVENTMODE) // This falls through to the default.
return hopsUsed; // If the request used more hops than the limit, use the same amount of hops

View File

@@ -20,7 +20,7 @@ class RoutingModule : public ProtobufModule<meshtastic_Routing>
uint8_t hopLimit = 0);
// Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response
uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit);
uint8_t getHopLimitForResponse(const meshtastic_MeshPacket &mp);
protected:
friend class Router;

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
#include "TraceRouteModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
@@ -359,10 +360,10 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro
}
// Only insert unknown hops if hop_start is valid
if (p.hop_start != 0 && p.hop_limit <= p.hop_start) {
uint8_t hopsTaken = p.hop_start - p.hop_limit;
const int8_t hopsTaken = getHopsAway(p);
if (hopsTaken >= 0) {
int8_t diff = hopsTaken - *route_count;
for (uint8_t i = 0; i < diff; i++) {
for (int8_t i = 0; i < diff; i++) {
if (*route_count < ROUTE_SIZE) {
route[*route_count] = NODENUM_BROADCAST; // This will represent an unknown hop
*route_count += 1;
@@ -370,7 +371,7 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro
}
// Add unknown SNR values if necessary
diff = *route_count - *snr_count;
for (uint8_t i = 0; i < diff; i++) {
for (int8_t i = 0; i < diff; i++) {
if (*snr_count < ROUTE_SIZE) {
snr_list[*snr_count] = INT8_MIN; // This will represent an unknown SNR
*snr_count += 1;

View File

@@ -14,11 +14,11 @@
#include <atomic>
#include <mutex>
#ifdef NIMBLE_TWO
#include "NimBLEAdvertising.h"
#ifdef CONFIG_BT_NIMBLE_EXT_ADV
#include "NimBLEExtAdvertising.h"
#endif
#include "PowerStatus.h"
#endif
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h"
@@ -26,12 +26,15 @@
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
namespace
{
constexpr uint16_t kPreferredBleMtu = 517;
constexpr uint16_t kPreferredBleTxOctets = 251;
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
} // namespace
#endif
// Debugging options: careful, they slow things down quite a bit!
// #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration
@@ -310,9 +313,11 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
{
PhoneAPI::onNowHasData(fromRadioNum);
#ifdef DEBUG_NIMBLE_NOTIFY
int currentNotifyCount = notifyCount.fetch_add(1);
uint8_t cc = bleServer->getConnectedCount();
#ifdef DEBUG_NIMBLE_NOTIFY
// This logging slows things down when there are lots of packets going to the phone, like initial connection:
LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc);
#endif
@@ -321,7 +326,13 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
put_le32(val, fromRadioNum);
fromNumCharacteristic->setValue(val, sizeof(val));
#ifdef NIMBLE_TWO
// NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be
// notify().
fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE);
#else
fromNumCharacteristic->notify();
#endif
}
/// Check the current underlying physical link to see if the client is currently connected
@@ -386,7 +397,12 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
{
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override
#ifdef NIMBLE_TWO
virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
#else
virtual void onWrite(NimBLECharacteristic *pCharacteristic)
#endif
{
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
// Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls.
@@ -433,7 +449,11 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
{
void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override
#ifdef NIMBLE_TWO
virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
#else
virtual void onRead(NimBLECharacteristic *pCharacteristic)
#endif
{
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
@@ -541,27 +561,32 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
{
#ifdef NIMBLE_TWO
public:
explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {}
NimbleBluetoothServerCallback(NimbleBluetooth *ble) { this->ble = ble; }
private:
NimbleBluetooth *ble;
uint32_t onPassKeyDisplay() override
virtual uint32_t onPassKeyDisplay()
#else
virtual uint32_t onPassKeyRequest()
#endif
{
uint32_t passkey = config.bluetooth.fixed_pin;
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
LOG_INFO("Use random passkey");
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
passkey = random(100000, 999999);
}
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
LOG_INFO("*** Enter passkey %d on the peer side ***", passkey);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
bluetoothStatus->updateStatus(&newStatus);
#if HAS_SCREEN
#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (screen) {
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888";
@@ -590,29 +615,39 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
});
}
#endif
passkeyShowing = true;
return passkey;
}
void onAuthenticationComplete(NimBLEConnInfo &connInfo) override
#ifdef NIMBLE_TWO
virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo)
#else
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc)
#endif
{
LOG_INFO("BLE authentication complete");
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newStatus);
// Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (passkeyShowing) {
passkeyShowing = false;
if (screen) {
if (screen)
screen->endAlert();
}
}
// Store the connection handle for future use
#ifdef NIMBLE_TWO
nimbleBluetoothConnHandle = connInfo.getConnHandle();
#else
nimbleBluetoothConnHandle = desc->conn_handle;
#endif
}
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override
#ifdef NIMBLE_TWO
virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo)
{
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
@@ -637,12 +672,21 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
pServer->updateConnParams(connHandle, 6, 12, 0, 200);
}
#endif
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override
#ifdef NIMBLE_TWO
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
{
LOG_INFO("BLE disconnect reason: %d", reason);
#else
virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc)
{
LOG_INFO("BLE disconnect");
#endif
#ifdef NIMBLE_TWO
if (ble->isDeInit)
return;
#endif
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newStatus);
@@ -666,69 +710,35 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
bluetoothPhoneAPI->writeCount = 0;
}
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
memset(lastToRadio, 0, sizeof(lastToRadio));
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE;
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection"
#ifdef NIMBLE_TWO
// Restart Advertising
ble->startAdvertising();
#else
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
if (!pAdvertising->start(0)) {
if (pAdvertising->isAdvertising()) {
LOG_DEBUG("BLE advertising already running");
} else {
LOG_ERROR("BLE failed to restart advertising");
}
}
#endif
}
};
static NimbleBluetoothToRadioCallback *toRadioCallbacks;
static NimbleBluetoothFromRadioCallback *fromRadioCallbacks;
void NimbleBluetooth::startAdvertising()
{
#if defined(CONFIG_BT_NIMBLE_EXT_ADV)
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
NimBLEExtAdvertisement legacyAdvertising;
legacyAdvertising.setLegacyAdvertising(true);
legacyAdvertising.setScannable(true);
legacyAdvertising.setConnectable(true);
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
if (powerStatus->getHasBattery() == 1) {
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
}
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
legacyAdvertising.setMinInterval(500);
legacyAdvertising.setMaxInterval(1000);
NimBLEExtAdvertisement legacyScanResponse;
legacyScanResponse.setLegacyAdvertising(true);
legacyScanResponse.setConnectable(true);
legacyScanResponse.setName(getDeviceName());
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
LOG_ERROR("BLE failed to set legacyAdvertising");
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
LOG_ERROR("BLE failed to set legacyScanResponse");
} else if (!pAdvertising->start(0, 0, 0)) {
LOG_ERROR("BLE failed to start legacyAdvertising");
}
#else
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->reset();
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
if (powerStatus->getHasBattery() == 1) {
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f));
}
NimBLEAdvertisementData scan;
scan.setName(getDeviceName());
pAdvertising->setScanResponseData(scan);
pAdvertising->enableScanResponse(true);
if (!pAdvertising->start(0)) {
LOG_ERROR("BLE failed to start advertising");
}
#endif
LOG_DEBUG("BLE Advertising started");
}
void NimbleBluetooth::shutdown()
{
// No measurable power saving for ESP32 during light-sleep(?)
#ifndef ARCH_ESP32
// Shutdown bluetooth for minimum power draw
LOG_INFO("Disable bluetooth");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->reset();
@@ -736,6 +746,7 @@ void NimbleBluetooth::shutdown()
#endif
}
// Proper shutdown for ESP32. Needs reboot to reverse.
void NimbleBluetooth::deinit()
{
#ifdef ARCH_ESP32
@@ -749,17 +760,21 @@ void NimbleBluetooth::deinit()
digitalWrite(BLE_LED, LOW);
#endif
#endif
#ifndef NIMBLE_TWO
NimBLEDevice::deinit();
#endif
#endif
}
// Has initial setup been completed
bool NimbleBluetooth::isActive()
{
return bleServer != nullptr;
return bleServer;
}
bool NimbleBluetooth::isConnected()
{
return bleServer && bleServer->getConnectedCount() > 0;
return bleServer->getConnectedCount() > 0;
}
int NimbleBluetooth::getRssi()
@@ -803,7 +818,7 @@ void NimbleBluetooth::setup()
LOG_INFO("Init the NimBLE bluetooth module");
NimBLEDevice::init(getDeviceName());
NimBLEDevice::setPower(9);
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
@@ -836,7 +851,11 @@ void NimbleBluetooth::setup()
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
}
bleServer = NimBLEDevice::createServer();
auto *serverCallbacks = new NimbleBluetoothServerCallback(this);
#ifdef NIMBLE_TWO
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this);
#else
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
#endif
bleServer->setCallbacks(serverCallbacks, true);
setupService();
startAdvertising();
@@ -881,7 +900,11 @@ void NimbleBluetooth::setupService()
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1);
#ifdef NIMBLE_TWO
NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904();
#else
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
#endif
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setNamespace(1);
batteryLevelDescriptor->setUnit(0x27ad);
@@ -889,12 +912,54 @@ void NimbleBluetooth::setupService()
batteryService->start();
}
void NimbleBluetooth::startAdvertising()
{
#ifdef NIMBLE_TWO
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
NimBLEExtAdvertisement legacyAdvertising;
legacyAdvertising.setLegacyAdvertising(true);
legacyAdvertising.setScannable(true);
legacyAdvertising.setConnectable(true);
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
if (powerStatus->getHasBattery() == 1) {
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
}
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
legacyAdvertising.setMinInterval(500);
legacyAdvertising.setMaxInterval(1000);
NimBLEExtAdvertisement legacyScanResponse;
legacyScanResponse.setLegacyAdvertising(true);
legacyScanResponse.setConnectable(true);
legacyScanResponse.setName(getDeviceName());
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
LOG_ERROR("BLE failed to set legacyAdvertising");
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
LOG_ERROR("BLE failed to set legacyScanResponse");
} else if (!pAdvertising->start(0, 0, 0)) {
LOG_ERROR("BLE failed to start legacyAdvertising");
}
#else
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->reset();
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
pAdvertising->start(0);
#endif
}
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level)
{
if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) {
BatteryCharacteristic->setValue(&level, 1);
#ifdef NIMBLE_TWO
BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE);
#else
BatteryCharacteristic->notify();
#endif
}
}
@@ -909,7 +974,11 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
if (!bleServer || !isConnected() || length > 512) {
return;
}
#ifdef NIMBLE_TWO
logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE);
#else
logRadioCharacteristic->notify(logMessage, length, true);
#endif
}
void clearNVS()

View File

@@ -12,11 +12,16 @@ class NimbleBluetooth : BluetoothApi
bool isConnected();
int getRssi();
void sendLog(const uint8_t *logMessage, size_t length);
#if defined(NIMBLE_TWO)
void startAdvertising();
#endif
bool isDeInit = false;
private:
void setupService();
#if !defined(NIMBLE_TWO)
void startAdvertising();
#endif
};
void setBluetoothEnable(bool enable);

View File

@@ -279,7 +279,7 @@ void portduinoSetup()
// RAK6421-13300-S1:aabbcc123456:5ba85807d92138b7519cfb60460573af:3061e8d8
// <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
if (strlen(autoconf_product) < 6) {
if (strlen(autoconf_product) < 6 && portduino_config.i2cdev != "") {
try {
char *mac_start = nullptr;
char *devID_start = nullptr;
@@ -869,4 +869,4 @@ void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault
destPin.line = destPin.pin;
destPin.gpiochip = portduino_config.lora_default_gpiochip;
}
}
}

View File

@@ -418,8 +418,9 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
if (mp->rx_snr != 0)
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit));
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
}
@@ -450,8 +451,9 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
if (mp->rx_snr != 0)
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit));
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
}
jsonObj["size"] = new JSONValue((unsigned int)mp->encrypted.size);

View File

@@ -358,8 +358,9 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
jsonObj["rssi"] = (int)mp->rx_rssi;
if (mp->rx_snr != 0)
jsonObj["snr"] = (float)mp->rx_snr;
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = (unsigned int)(hopsAway);
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
}
@@ -393,8 +394,9 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa
jsonObj["rssi"] = (int)mp->rx_rssi;
if (mp->rx_snr != 0)
jsonObj["snr"] = (float)mp->rx_snr;
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = (unsigned int)(hopsAway);
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
}
jsonObj["size"] = (unsigned int)mp->encrypted.size;

View File

@@ -6,6 +6,8 @@
#define LED_GREEN 12
#define LED_BLUE 2
#define LED_BUILTIN LED_GREEN
static const uint8_t TX = 1;
static const uint8_t RX = 3;

View File

@@ -6,6 +6,8 @@
#define LED_GREEN 12
#define LED_BLUE 2
#define LED_BUILTIN LED_GREEN
static const uint8_t TX = 1;
static const uint8_t RX = 3;

View File

@@ -2,19 +2,18 @@
[env:tbeam]
extends = esp32_base
board = ttgo-t-beam
board_level = extra
board_check = true
build_flags = ${esp32_base.build_flags}
-D TBEAM_V10
-I variants/esp32/tbeam
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-ULED_BUILTIN
upload_speed = 921600
[env:tbeam-displayshield]
extends = env:tbeam
board_level = extra
build_flags =
${env:tbeam.build_flags}
-D USE_ST7796

View File

@@ -26,6 +26,7 @@ build_flags =
-D HAS_BLUETOOTH=1
-DCONFIG_BT_NIMBLE_EXT_ADV=1
-DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2
-D NIMBLE_TWO
monitor_speed=115200
lib_ignore =
NonBlockingRTTTL

View File

@@ -8,4 +8,3 @@ build_flags =
-I variants/esp32c6/tlora_c6
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1
-ULED_BUILTIN

View File

@@ -8,4 +8,3 @@ build_flags =
${esp32s3_base.build_flags}
-D HELTEC_V3
-I variants/esp32s3/heltec_v3
-ULED_BUILTIN

View File

@@ -6,6 +6,10 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t LED_BUILTIN = 35;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -3,6 +3,10 @@
#include <stdint.h>
static const uint8_t LED_BUILTIN = 45; // LED is not populated on earliest board variant
#define BUILTIN_LED LED_BUILTIN // Backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -3,6 +3,10 @@
#include <stdint.h>
static const uint8_t LED_BUILTIN = 45; // LED is not populated on earliest board variant
#define BUILTIN_LED LED_BUILTIN // Backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -3,6 +3,10 @@
#include <stdint.h>
static const uint8_t LED_BUILTIN = 35;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -3,6 +3,10 @@
#include <stdint.h>
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -3,6 +3,10 @@
#include <stdint.h>
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t KEY_BUILTIN = 0;
static const uint8_t TX = 43;

View File

@@ -11,6 +11,10 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -11,6 +11,10 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -10,6 +10,10 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -10,7 +10,10 @@
// Some boards have too low voltage on this pin (board design bug)
// Use different pin with 3V and connect with 48
// and change this setup for the chosen pin (for example 38)
#define RGB_BUILTIN SOC_GPIO_PIN_COUNT + 48
static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + 48;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
#define RGB_BUILTIN LED_BUILTIN
#define RGB_BRIGHTNESS 64
static const uint8_t TX = 43;

View File

@@ -49,6 +49,10 @@ static const uint8_t T14 = 14;
static const uint8_t VBAT_SENSE = 2;
static const uint8_t VBUS_SENSE = 34;
// User LED
#define LED_BUILTIN 13
#define BUILTIN_LED LED_BUILTIN // backward compatibility
static const uint8_t RGB_DATA = 40;
// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API neopixelWrite()
#define RGB_BUILTIN (RGB_DATA + SOC_GPIO_PIN_COUNT)

View File

@@ -17,9 +17,61 @@ static const uint8_t MOSI = 11;
static const uint8_t MISO = 10;
static const uint8_t SCK = 13;
#define SPI_INTERFACES_COUNT 1
#define SPI_MOSI (11)
#define SPI_SCK (13)
#define SPI_MISO (10)
#define SPI_CS (12)
// LEDs
#define LED_BUILTIN LED_GREEN
#ifdef _VARIANT_RAK3112_
/*
* Serial interfaces
*/
// TXD1 RXD1 on Base Board
#define PIN_SERIAL1_RX (44)
#define PIN_SERIAL1_TX (43)
/*
* Internal SPI to LoRa transceiver
*/
#define LORA_SX126X_SCK 5
#define LORA_SX126X_MISO 3
#define LORA_SX126X_MOSI 6
/*
* Analog pins
*/
#define PIN_A0 (21)
#define PIN_A1 (14)
/*
* Wire Interfaces
*/
#define WIRE_INTERFACES_COUNT 2
#define PIN_WIRE1_SDA (17)
#define PIN_WIRE1_SCL (18)
/*
* GPIO's
*/
#define WB_IO1 21
#define WB_IO2 2
// #define WB_IO2 14
#define WB_IO3 41
#define WB_IO4 42
#define WB_IO5 38
#define WB_IO6 39
// #define WB_SW1 35 NC
#define WB_A0 1
#define WB_A1 2
#define WB_CS 12
#define WB_LED1 46
#define WB_LED2 45
#endif
#endif /* Pins_Arduino_h */

View File

@@ -9,3 +9,16 @@ build_flags =
${esp32s3_base.build_flags}
-D RAK3312
-I variants/esp32s3/rak3312
[env:rak3112]
extends = esp32s3_base
board = wiscore_rak3312
board_level = pr
board_check = true
upload_protocol = esptool
build_flags =
${esp32_base.build_flags}
-D RAK3312
-D _VARIANT_RAK3112_
-I variants/esp32s3/rak3312

View File

@@ -18,11 +18,6 @@
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
#define SX126X_POWER_EN (4)
#define PIN_POWER_EN PIN_3V3_EN
#define PIN_3V3_EN (14)
#define LED_GREEN 46
#define LED_BLUE 45
@@ -35,10 +30,30 @@
#define LED_STATE_ON 1 // State when LED is litted
#define BATTERY_PIN 1
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
#ifdef _VARIANT_RAK3112_ // Modular variant (stamp)
#define ADC_MULTIPLIER 2.11
#define BUTTON_NEED_PULLUP
#define HAS_SDCARD
#define SDCARD_USE_SPI1
#define SDCARD_CS SPI_CS
#define I2C_SDA1 PIN_WIRE1_SDA
#define I2C_SCL1 PIN_WIRE1_SCL
#else // Generic 3312 variant (40-pin standard connector)
#define ADC_MULTIPLIER 1.667
#define SX126X_POWER_EN (4)
#define PIN_POWER_EN PIN_3V3_EN
#define PIN_3V3_EN (14)
#define HAS_GPS 1
#define GPS_TX_PIN 43
#define GPS_RX_PIN 44
#define BATTERY_PIN 1
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
#define ADC_MULTIPLIER 1.667
#endif

View File

@@ -22,4 +22,7 @@ static const uint8_t SCK = 13;
#define SPI_MISO (10)
#define SPI_CS (12)
// LEDs
#define LED_BUILTIN LED_GREEN
#endif /* Pins_Arduino_h */

View File

@@ -3,6 +3,8 @@
#include <stdint.h>
// static const uint8_t LED_BUILTIN = -1;
// static const uint8_t TX = 43;
// static const uint8_t RX = 44;

View File

@@ -6,6 +6,8 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
// static const uint8_t LED_BUILTIN = -1;
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -3,6 +3,8 @@
#include <stdint.h>
// static const uint8_t LED_BUILTIN = -1;
// static const uint8_t TX = 43;
// static const uint8_t RX = 44;

View File

@@ -11,6 +11,10 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -11,6 +11,10 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -11,6 +11,10 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t LED_BUILTIN = 18;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -6,6 +6,9 @@
#define USB_VID 0x16D0
#define USB_PID 0x1178
#define LED_BUILTIN 13
#define BUILTIN_LED LED_BUILTIN // backward compatibility
static const uint8_t TX = 43;
static const uint8_t RX = 44;

View File

@@ -50,6 +50,7 @@ extern "C" {
#define RGBLED_BLUE (0 + 12) // Blue of RGB P0.12
#define RGBLED_CA // comment out this line if you have a common cathode type, as defined use common anode logic
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -55,6 +55,7 @@ extern "C" {
#define LED_RED PIN_LED3
#define LED_BLUE PIN_LED1
#define LED_GREEN PIN_LED2
#define LED_BUILTIN LED_BLUE
#define LED_CONN PIN_GREEN
#define LED_STATE_ON 0 // State when LED is lit // LED灯亮时的状态
#define PIN_BUZZER (0 + 6)

View File

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

View File

@@ -58,6 +58,7 @@ extern "C" {
#define LED_BLUE 37
#define LED_PAIRING LED_BLUE // Signals the Status LED Module to handle this LED
#define LED_BUILTIN -1
#define LED_STATE_ON LOW
#define LED_STATE_OFF HIGH
@@ -113,7 +114,8 @@ extern "C" {
#define LR11X0_DIO_AS_RF_SWITCH
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
// REVISIT https://github.com/meshtastic/firmware/pull/9084
// #define PCF8563_RTC 0x51
#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>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
; # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
; lewisxhe/SensorLib@0.3.3

View File

@@ -41,3 +41,30 @@ void initVariant()
pinMode(VDD_FLASH_EN, OUTPUT);
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

@@ -40,11 +40,14 @@ extern "C" {
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define LED_BUILTIN -1
#define LED_BLUE -1
#define LED_CHARGE (12)
#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
#define EXT_PWR_DETECT (13)
@@ -118,7 +121,8 @@ static const uint8_t A0 = PIN_A0;
#define PIN_SERIAL2_TX (24)
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
// REVISIT https://github.com/meshtastic/firmware/pull/9084
// #define PCF8563_RTC 0x51
// SPI
#define SPI_INTERFACES_COUNT 1

View File

@@ -51,6 +51,7 @@ extern "C" {
#define PIN_LED1 (32 + 7) // P1.07 Blue D2
#define LED_PIN PIN_LED1
#define LED_BUILTIN -1
#define LED_BLUE -1
#define LED_STATE_ON 1 // State when LED is lit

View File

@@ -51,6 +51,7 @@ extern "C" {
#define PIN_LED1 (32 + 7) // P1.07 Blue D2
#define LED_PIN PIN_LED1
#define LED_BUILTIN -1
#define LED_BLUE -1
#define LED_STATE_ON 1 // State when LED is lit

View File

@@ -51,6 +51,7 @@ extern "C" {
#define PIN_LED1 (-1)
#define LED_PIN PIN_LED1
#define LED_BUILTIN -1
#define LED_BLUE -1
#define LED_STATE_ON 1 // State when LED is lit

View File

@@ -29,6 +29,7 @@ extern "C" {
#define PIN_LED1 (32 + 10) // LED P1.15
#define PIN_LED2 (-1) //
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -29,6 +29,7 @@ extern "C" {
#define PIN_LED1 (32 + 10) // LED P1.15
#define PIN_LED2 (-1) //
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -34,6 +34,7 @@ extern "C" {
// #define PIN_LED1 (32 + 9) Green
// #define PIN_LED1 (0 + 12) Blue
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -52,6 +52,7 @@ extern "C" {
#define LED_BLUE PIN_LED1
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED3
#define LED_STATE_ON 0 // State when LED is lit
@@ -102,7 +103,7 @@ static const uint8_t A0 = PIN_A0;
#define EXTERNAL_FLASH_USE_QSPI
// 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
@@ -177,4 +178,4 @@ static const uint8_t A0 = PIN_A0;
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif
#endif

View File

@@ -81,6 +81,7 @@ NRF52 PRO MICRO PIN ASSIGNMENT
// LED
#define PIN_LED1 (0 + 15) // P0.15
#define LED_BUILTIN PIN_LED1
// Actually red
#define LED_BLUE PIN_LED1
#define LED_STATE_ON 1 // State when LED is lit

View File

@@ -38,6 +38,7 @@ extern "C" {
#define PIN_LED PIN_LED1
#define LED_PWR (PINS_COUNT)
#define LED_BUILTIN PIN_LED
#define LED_STATE_ON 1 // State when LED is lit
// XIAO Wio-SX1262 Shield User button

View File

@@ -49,6 +49,8 @@ extern "C" {
#define PIN_LED1 (32 + 15) // P1.15 3
#define PIN_LED2 (32 + 10) // P1.10 4
#define LED_BUILTIN PIN_LED1
#define LED_GREEN PIN_LED2 // Actually red
#define LED_BLUE PIN_LED1

View File

@@ -48,6 +48,7 @@ extern "C" {
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -30,6 +30,7 @@ extern "C" {
#define PIN_LED1 (32 + 3) // green (confirmed on 1.0 board)
#define LED_BLUE PIN_LED1 // fake for bluefruit library
#define LED_GREEN PIN_LED1
#define LED_BUILTIN LED_GREEN
#define LED_STATE_ON 0 // State when LED is lit
#define HAS_NEOPIXEL // Enable the use of neopixels

View File

@@ -74,6 +74,7 @@ extern "C" {
#define PIN_LED1 (32 + 3) // green (confirmed on 1.0 board)
#define LED_BLUE PIN_LED1 // fake for bluefruit library
#define LED_GREEN PIN_LED1
#define LED_BUILTIN LED_GREEN
#define LED_STATE_ON 0 // State when LED is lit
#define HAS_NEOPIXEL // Enable the use of neopixels

View File

@@ -26,6 +26,7 @@ extern "C" {
#define LED_RED PIN_LED1
#define LED_BLUE PIN_LED1
#define LED_GREEN PIN_LED1
#define LED_BUILTIN LED_BLUE
#define LED_CONN LED_BLUE
#define LED_STATE_ON 0 // State when LED is lit

View File

@@ -42,6 +42,7 @@ extern "C" {
#define PIN_LED1 (0 + 4) // green (confirmed on 1.0 board)
#define LED_BLUE PIN_LED1 // fake for bluefruit library
#define LED_GREEN PIN_LED1
#define LED_BUILTIN LED_GREEN
#define LED_STATE_ON 0 // State when LED is lit
#define HAS_NEOPIXEL // Enable the use of neopixels

View File

@@ -31,6 +31,7 @@ extern "C" {
// LEDs
#define PIN_LED1 (24) // Built in white led for status
#define LED_BLUE PIN_LED1
#define LED_BUILTIN PIN_LED1
#define LED_STATE_ON 0 // State when LED is litted
#define LED_INVERTED 1

View File

@@ -49,6 +49,7 @@ extern "C" {
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -52,6 +52,7 @@ extern "C" {
#define PIN_LED1 (35)
#define PIN_LED2 (36) // Connected to WWAN host LED (if present)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -45,6 +45,7 @@ extern "C" {
#define PIN_LED1 (32 + 3) // P1.03, Green
#define PIN_LED2 (32 + 4) // P1.04, Blue
#define LED_BUILTIN -1 // PIN_LED1
#define LED_BLUE PIN_LED2
#define LED_STATE_ON 0 // State when LED is lit

View File

@@ -50,6 +50,7 @@ extern "C" {
#define LED_BLUE PIN_LED1
#define LED_GREEN PIN_LED2
#define LED_BUILTIN LED_BLUE
#define LED_CONN PIN_GREEN
#define LED_STATE_ON 0 // State when LED is lit

View File

@@ -47,6 +47,7 @@ extern "C" {
#define PIN_LED1 (32 + 4) // P1.04 Controls Green LED
#define PIN_LED2 (28) // P0.28 Controls Blue LED
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -48,6 +48,7 @@ extern "C" {
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -47,6 +47,7 @@ extern "C" {
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -47,6 +47,7 @@ extern "C" {
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

View File

@@ -47,6 +47,7 @@ extern "C" {
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1

Some files were not shown because too many files have changed in this diff Show More