Compare commits

...

36 Commits

Author SHA1 Message Date
Jason P
66bdf331dd Fix menu name from Position to GPS 2026-01-02 10:24:20 -06:00
Jason P
54d6509118 Refactored some of the system menus to the new DRY method 2026-01-02 10:07:46 -06:00
github-actions[bot]
caceaf424a Automated version bumps (#9030)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2026-01-02 06:56:02 -06:00
Ben Meadors
75144d2028 Update security policy to reflect new stage 2026-01-02 06:42:28 -06:00
Ben Meadors
27b522b55a Merge branch 'master' into develop 2026-01-01 18:25:18 -06:00
renovate[bot]
11b5f1a4fe chore(deps): update dorny/test-reporter action to v2.4.0 (#9135)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-01 18:04:13 -06:00
renovate[bot]
f9c9350f45 chore(deps): update meshtastic/device-ui digest to a8e2f94 (#9140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-01 18:03:27 -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
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
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
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
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
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
40 changed files with 894 additions and 558 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

@@ -143,7 +143,7 @@ jobs:
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v2.3.0
uses: dorny/test-reporter@v2.4.0
with:
name: PlatformIO Tests
path: testreport.xml

View File

@@ -8,8 +8,8 @@ plugins:
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.495
- renovate@42.66.8
- checkov@3.2.496
- renovate@42.66.14
- prettier@3.7.4
- trufflehog@3.92.4
- yamllint@1.37.1

View File

@@ -4,8 +4,8 @@
| Firmware Version | Supported |
| ---------------- | ------------------ |
| 2.6.x | :white_check_mark: |
| <= 2.5.x | :x: |
| 2.7.x | :white_check_mark: |
| <= 2.6.x | :x: |
## Reporting a Vulnerability

View File

@@ -87,6 +87,9 @@
</screenshots>
<releases>
<release version="2.7.18" date="2026-01-02">
<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>

6
debian/changelog vendored
View File

@@ -1,3 +1,9 @@
meshtasticd (2.7.18.0) unstable; urgency=medium
* Version 2.7.18
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Fri, 02 Jan 2026 12:45:36 +0000
meshtasticd (2.7.17.0) unstable; urgency=medium
* Version 2.7.17

View File

@@ -119,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/a8e2f947f7abaf0c5ac8e6dd189a22156335beaa.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;

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);

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,6 @@ class menuHandler
number_test,
wifi_toggle_menu,
bluetooth_toggle_menu,
notifications_menu,
screen_options_menu,
power_menu,
system_base_menu,
@@ -98,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();
@@ -130,7 +128,28 @@ template <typename T> struct MenuOption {
MenuOption(const char *labelIn, OptionsAction actionIn) : label(labelIn), action(actionIn), hasValue(false), value() {}
};
struct ScreenColor {
uint8_t r;
uint8_t g;
uint8_t b;
bool useVariant;
ScreenColor(uint8_t rIn = 0, uint8_t gIn = 0, uint8_t bIn = 0, bool variantIn = false)
: r(rIn), g(gIn), b(bIn), useVariant(variantIn)
{
}
};
using RadioPresetOption = MenuOption<meshtastic_Config_LoRaConfig_ModemPreset>;
using LoraRegionOption = MenuOption<meshtastic_Config_LoRaConfig_RegionCode>;
using TimezoneOption = MenuOption<const char *>;
using CompassOption = MenuOption<meshtastic_CompassMode>;
using ScreenColorOption = MenuOption<ScreenColor>;
using GPSToggleOption = MenuOption<meshtastic_Config_PositionConfig_GpsMode>;
using GPSFormatOption = MenuOption<meshtastic_DeviceUIConfig_GpsCoordinateFormat>;
using NodeNameOption = MenuOption<bool>;
using PositionMenuOption = MenuOption<int>;
using ClockFaceOption = MenuOption<bool>;
} // namespace graphics
#endif

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

@@ -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);

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

@@ -1549,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)
@@ -1801,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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -38,7 +38,6 @@ build_flags =
-DAXP_DEBUG_PORT=Serial
-DCONFIG_BT_NIMBLE_ENABLED
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
-DCONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
@@ -61,11 +60,11 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@2.3.7
h2zero/NimBLE-Arduino@^1.4.3
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
lewisxhe/XPowersLib@0.3.2
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto

View File

@@ -5,4 +5,4 @@ extends = esp32_common
custom_esp32_kind = esp32
build_flags =
${esp32_common.build_flags}
${esp32_common.build_flags}

View File

@@ -2,7 +2,7 @@
[env:tbeam]
extends = esp32_base
board = ttgo-t-beam
board_level = extra
board_check = true
build_flags = ${esp32_base.build_flags}
-D TBEAM_V10
@@ -13,7 +13,7 @@ upload_speed = 921600
[env:tbeam-displayshield]
extends = env:tbeam
board_level = extra
build_flags =
${env:tbeam.build_flags}
-D USE_ST7796

View File

@@ -4,8 +4,3 @@ custom_esp32_kind = esp32c3
monitor_speed = 115200
monitor_filters = esp32_c3_exception_decoder
build_flags =
${esp32_common.build_flags}
-DCONFIG_BT_NIMBLE_EXT_ADV=1
-DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2

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

@@ -3,8 +3,3 @@ extends = esp32_common
custom_esp32_kind = esp32s3
monitor_speed = 115200
build_flags =
${esp32_common.build_flags}
-DCONFIG_BT_NIMBLE_EXT_ADV=1
-DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2

View File

@@ -17,6 +17,8 @@ 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)
@@ -25,4 +27,51 @@ static const uint8_t SCK = 13;
// 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

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