Compare commits

..

2 Commits

Author SHA1 Message Date
Jonathan Bennett
66cb549817 Merge branch 'develop' into merge-master-fix 2026-01-01 16:12:28 -06:00
Jonathan Bennett
c351c49a72 Merge remote-tracking branch 'origin/master' into develop 2026-01-01 15:36:26 -06:00
30 changed files with 405 additions and 2483 deletions

View File

@@ -20,7 +20,7 @@ ENV PIP_ROOT_USER_ACTION=ignore
RUN apt-get update && apt-get install --no-install-recommends -y \ RUN apt-get update && apt-get install --no-install-recommends -y \
cmake git zip libgpiod-dev libbluetooth-dev libi2c-dev \ cmake git zip libgpiod-dev libbluetooth-dev libi2c-dev \
libunistring-dev libmicrohttpd-dev libgnutls28-dev libgcrypt20-dev \ libunistring-dev libmicrohttpd-dev libgnutls28-dev libgcrypt20-dev \
libusb-1.0-0-dev libssl-dev pkg-config libsqlite3-dev && \ libusb-1.0-0-dev libssl-dev pkg-config && \
apt-get clean && rm -rf /var/lib/apt/lists/* && \ apt-get clean && rm -rf /var/lib/apt/lists/* && \
pip install --no-cache-dir -U \ pip install --no-cache-dir -U \
platformio==6.1.16 \ platformio==6.1.16 \

View File

@@ -22,7 +22,7 @@ jobs:
### @{fc-author}, Welcome to Meshtastic! :wave: ### @{fc-author}, Welcome to Meshtastic! :wave:
Thanks for opening your first issue. If it's helpful, an easy way 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). 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 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) in the [discussions tab](https://github.com/meshtastic/firmware/discussions/categories/ideas)

View File

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

View File

@@ -14,7 +14,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
curl wget g++ zip git ca-certificates pkg-config \ curl wget g++ zip git ca-certificates pkg-config \
libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev libuv1-dev \ libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev libuv1-dev \
libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev \ libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev \
libx11-dev libinput-dev libxkbcommon-x11-dev libsqlite3-dev \ libx11-dev libinput-dev libxkbcommon-x11-dev \
&& apt-get clean && rm -rf /var/lib/apt/lists/* \ && apt-get clean && rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir -U platformio \ && pip install --no-cache-dir -U platformio \
&& mkdir /tmp/firmware && mkdir /tmp/firmware

View File

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

View File

@@ -11,7 +11,7 @@ RUN apk --no-cache add \
bash g++ libstdc++-dev linux-headers zip git ca-certificates libbsd-dev \ bash g++ libstdc++-dev linux-headers zip git ca-certificates libbsd-dev \
libgpiod-dev yaml-cpp-dev bluez-dev \ libgpiod-dev yaml-cpp-dev bluez-dev \
libusb-dev i2c-tools-dev libuv-dev openssl-dev pkgconf argp-standalone \ libusb-dev i2c-tools-dev libuv-dev openssl-dev pkgconf argp-standalone \
libx11-dev libinput-dev libxkbcommon-dev libsqlite3-dev \ libx11-dev libinput-dev libxkbcommon-dev \
&& rm -rf /var/cache/apk/* \ && rm -rf /var/cache/apk/* \
&& pip install --no-cache-dir -U platformio \ && pip install --no-cache-dir -U platformio \
&& mkdir /tmp/firmware && mkdir /tmp/firmware

View File

@@ -201,16 +201,6 @@ HostMetrics:
# UserStringCommand: cat /sys/firmware/devicetree/base/serial-number # Command to execute, to send the results as the userString # UserStringCommand: cat /sys/firmware/devicetree/base/serial-number # Command to execute, to send the results as the userString
StoreAndForward:
# Enabled: true # Enable Store and Forward++, true by default
# DBPath: /var/lib/meshtasticd/ # Path to the S&F++ Sqlite DB
# Stratum0: false # Specify if this node is a Stratum 0 node, the controller node.
# InitialSync: 10 # Number of messages to
# Hops: 3 # Number of hops to use for SF++ messages
# AnnounceInterval: 5 # Interval in minutes between announcing tip of chain hash
# MaxChainLength: 1000 # Maximum number of messages to store in a chain
Config: Config:
# DisplayMode: TWOCOLOR # uncomment to force BaseUI # DisplayMode: TWOCOLOR # uncomment to force BaseUI
# DisplayMode: COLOR # uncomment to force MUI # DisplayMode: COLOR # uncomment to force MUI

View File

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

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> Fri, 02 Jan 2026 12:45:36 +0000
meshtasticd (2.7.17.0) unstable; urgency=medium meshtasticd (2.7.17.0) unstable; urgency=medium
* Version 2.7.17 * Version 2.7.17

3
debian/control vendored
View File

@@ -25,8 +25,7 @@ Build-Depends: debhelper-compat (= 13),
liborcania-dev, liborcania-dev,
libx11-dev, libx11-dev,
libinput-dev, libinput-dev,
libxkbcommon-x11-dev, libxkbcommon-x11-dev
libsqlite3-dev
Standards-Version: 4.6.2 Standards-Version: 4.6.2
Homepage: https://github.com/meshtastic/firmware Homepage: https://github.com/meshtastic/firmware
Rules-Requires-Root: no Rules-Requires-Root: no

View File

@@ -39,7 +39,6 @@ BuildRequires: pkgconfig(bluez)
BuildRequires: pkgconfig(libusb-1.0) BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: libi2c-devel BuildRequires: libi2c-devel
BuildRequires: pkgconfig(libuv) BuildRequires: pkgconfig(libuv)
BuildRequires: pkgconfig(sqlite3)
# Web components: # Web components:
BuildRequires: pkgconfig(openssl) BuildRequires: pkgconfig(openssl)
BuildRequires: pkgconfig(liborcania) BuildRequires: pkgconfig(liborcania)

View File

@@ -94,7 +94,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL # renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
end2endzone/NonBlockingRTTTL@1.4.0 end2endzone/NonBlockingRTTTL@1.4.0
build_flags = ${env.build_flags} -Os build_flags = ${env.build_flags} -Os
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/> -<modules/Native/> build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
; Common libs for communicating over TCP/IP networks such as MQTT ; Common libs for communicating over TCP/IP networks such as MQTT
[networking_base] [networking_base]
@@ -119,7 +119,7 @@ lib_deps =
[device-ui_base] [device-ui_base]
lib_deps = lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/a8e2f947f7abaf0c5ac8e6dd189a22156335beaa.zip https://github.com/meshtastic/device-ui/archive/940ba8570f59c59c3508643f4d72840de716ce20.zip
; Common libs for environmental measurements in telemetry module ; Common libs for environmental measurements in telemetry module
[environmental_base] [environmental_base]

View File

@@ -24,9 +24,6 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
switch (event->inputEvent) { switch (event->inputEvent) {
case INPUT_BROKER_USER_PRESS: case INPUT_BROKER_USER_PRESS:
case INPUT_BROKER_ALT_PRESS: case INPUT_BROKER_ALT_PRESS:
playClick(); // Low delay feedback
break;
case INPUT_BROKER_SELECT: case INPUT_BROKER_SELECT:
case INPUT_BROKER_SELECT_LONG: case INPUT_BROKER_SELECT_LONG:
playBeep(); // Confirmation feedback playBeep(); // Confirmation feedback
@@ -61,4 +58,4 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
} }
return 0; // Allow other handlers to process the event return 0; // Allow other handlers to process the event
} }

View File

@@ -113,14 +113,7 @@ void playShutdownMelody()
void playChirp() void playChirp()
{ {
// A short, friendly "chirp" sound for key presses // A short, friendly "chirp" sound for key presses
ToneDuration melody[] = {{NOTE_AS3, 20}}; // Short AS3 note ToneDuration melody[] = {{NOTE_AS3, 20}}; // Very short AS3 note
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
void playClick()
{
// A very short "click" sound with minimum delay; ideal for rotary encoder events
ToneDuration melody[] = {{NOTE_AS3, 1}}; // Very Short AS3
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }

View File

@@ -9,7 +9,6 @@ void playGPSDisableBeep();
void playComboTune(); void playComboTune();
void playBoop(); void playBoop();
void playChirp(); void playChirp();
void playClick();
void playLongPressLeadUp(); void playLongPressLeadUp();
bool playNextLeadUpNote(); // Play the next note in the lead-up sequence bool playNextLeadUpNote(); // Play the next note in the lead-up sequence
void resetLeadUpSequence(); // Reset the lead-up sequence to start from beginning void resetLeadUpSequence(); // Reset the lead-up sequence to start from beginning

View File

@@ -107,60 +107,50 @@ void menuHandler::OnboardMessage()
void menuHandler::LoraRegionPicker(uint32_t duration) void menuHandler::LoraRegionPicker(uint32_t duration)
{ {
static const LoraRegionOption regionOptions[] = { static const char *optionsArray[] = {"Back",
{"Back", OptionsAction::Back}, "US",
{"US", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_US}, "EU_433",
{"EU_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_433}, "EU_868",
{"EU_868", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_868}, "CN",
{"CN", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_CN}, "JP",
{"JP", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_JP}, "ANZ",
{"ANZ", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_ANZ}, "KR",
{"KR", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_KR}, "TW",
{"TW", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_TW}, "RU",
{"RU", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_RU}, "IN",
{"IN", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_IN}, "NZ_865",
{"NZ_865", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_NZ_865}, "TH",
{"TH", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_TH}, "LORA_24",
{"LORA_24", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_LORA_24}, "UA_433",
{"UA_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_UA_433}, "UA_868",
{"UA_868", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_UA_868}, "MY_433",
{"MY_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_MY_433}, "MY_"
{"MY_919", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_MY_919}, "919",
{"SG_923", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_SG_923}, "SG_"
{"PH_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_PH_433}, "923",
{"PH_868", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_PH_868}, "PH_433",
{"PH_915", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_PH_915}, "PH_868",
{"ANZ_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_ANZ_433}, "PH_915",
{"KZ_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_KZ_433}, "ANZ_433",
{"KZ_863", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_KZ_863}, "KZ_433",
{"NP_865", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_NP_865}, "KZ_863",
{"BR_902", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_BR_902}, "NP_865",
}; "BR_902"};
BannerOverlayOptions bannerOptions;
constexpr size_t regionCount = sizeof(regionOptions) / sizeof(regionOptions[0]); bannerOptions.message = "Set the LoRa region";
static std::array<const char *, regionCount> regionLabels{};
const char *bannerMessage = "Set the LoRa region";
if (currentResolution == ScreenResolution::UltraLow) { if (currentResolution == ScreenResolution::UltraLow) {
bannerMessage = "LoRa Region"; bannerOptions.message = "LoRa Region";
} }
bannerOptions.durationMs = duration;
auto bannerOptions = bannerOptions.optionsArrayPtr = optionsArray;
createStaticBannerOptions(bannerMessage, regionOptions, regionLabels, [](const LoraRegionOption &option, int) -> void { bannerOptions.optionsCount = 27;
if (!option.hasValue) { bannerOptions.InitialSelected = 0;
return; bannerOptions.bannerCallback = [](int selected) -> void {
} if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected);
auto selectedRegion = option.value;
if (config.lora.region == selectedRegion) {
return;
}
config.lora.region = selectedRegion;
auto changes = SEGMENT_CONFIG; auto changes = SEGMENT_CONFIG;
// FIXME: This should be a method consolidated with the same logic in the admin message as well // This is needed as we wait til picking the LoRa region to generate keys for the first time.
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI) #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
if (!owner.is_licensed) { if (!owner.is_licensed) {
bool keygenSuccess = false; bool keygenSuccess = false;
@@ -197,19 +187,8 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
service->reloadConfig(changes); service->reloadConfig(changes);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
});
bannerOptions.durationMs = duration;
int initialSelection = 0;
for (size_t i = 0; i < regionCount; ++i) {
if (regionOptions[i].hasValue && regionOptions[i].value == config.lora.region) {
initialSelection = static_cast<int>(i);
break;
} }
} };
bannerOptions.InitialSelected = initialSelection;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
@@ -324,100 +303,102 @@ void menuHandler::showConfirmationBanner(const char *message, std::function<void
void menuHandler::ClockFacePicker() void menuHandler::ClockFacePicker()
{ {
static const ClockFaceOption clockFaceOptions[] = { static const char *optionsArray[] = {"Back", "Digital", "Analog"};
{"Back", OptionsAction::Back}, enum optionsNumbers { Back = 0, Digital = 1, Analog = 2 };
{"Digital", OptionsAction::Select, false}, BannerOverlayOptions bannerOptions;
{"Analog", OptionsAction::Select, true}, bannerOptions.message = "Which Face?";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
menuHandler::menuQueue = menuHandler::clock_menu;
screen->runNow();
} else if (selected == Digital) {
uiconfig.is_clockface_analog = false;
saveUIConfig();
screen->setFrames(Screen::FOCUS_CLOCK);
} else {
uiconfig.is_clockface_analog = true;
saveUIConfig();
screen->setFrames(Screen::FOCUS_CLOCK);
}
}; };
constexpr size_t clockFaceCount = sizeof(clockFaceOptions) / sizeof(clockFaceOptions[0]);
static std::array<const char *, clockFaceCount> clockFaceLabels{};
auto bannerOptions = createStaticBannerOptions("Which Face?", clockFaceOptions, clockFaceLabels,
[](const ClockFaceOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::clock_menu;
screen->runNow();
return;
}
if (!option.hasValue) {
return;
}
if (uiconfig.is_clockface_analog == option.value) {
return;
}
uiconfig.is_clockface_analog = option.value;
saveUIConfig();
screen->setFrames(Screen::FOCUS_CLOCK);
});
bannerOptions.InitialSelected = uiconfig.is_clockface_analog ? 2 : 1; bannerOptions.InitialSelected = uiconfig.is_clockface_analog ? 2 : 1;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::TZPicker() void menuHandler::TZPicker()
{ {
static const TimezoneOption timezoneOptions[] = { static const char *optionsArray[] = {"Back",
{"Back", OptionsAction::Back}, "US/Hawaii",
{"US/Hawaii", OptionsAction::Select, "HST10"}, "US/Alaska",
{"US/Alaska", OptionsAction::Select, "AKST9AKDT,M3.2.0,M11.1.0"}, "US/Pacific",
{"US/Pacific", OptionsAction::Select, "PST8PDT,M3.2.0,M11.1.0"}, "US/Arizona",
{"US/Arizona", OptionsAction::Select, "MST7"}, "US/Mountain",
{"US/Mountain", OptionsAction::Select, "MST7MDT,M3.2.0,M11.1.0"}, "US/Central",
{"US/Central", OptionsAction::Select, "CST6CDT,M3.2.0,M11.1.0"}, "US/Eastern",
{"US/Eastern", OptionsAction::Select, "EST5EDT,M3.2.0,M11.1.0"}, "BR/Brasilia",
{"BR/Brasilia", OptionsAction::Select, "BRT3"}, "UTC",
{"UTC", OptionsAction::Select, "UTC0"}, "EU/Western",
{"EU/Western", OptionsAction::Select, "GMT0BST,M3.5.0/1,M10.5.0"}, "EU/"
{"EU/Central", OptionsAction::Select, "CET-1CEST,M3.5.0,M10.5.0/3"}, "Central",
{"EU/Eastern", OptionsAction::Select, "EET-2EEST,M3.5.0/3,M10.5.0/4"}, "EU/Eastern",
{"Asia/Kolkata", OptionsAction::Select, "IST-5:30"}, "Asia/Kolkata",
{"Asia/Hong_Kong", OptionsAction::Select, "HKT-8"}, "Asia/Hong_Kong",
{"AU/AWST", OptionsAction::Select, "AWST-8"}, "AU/AWST",
{"AU/ACST", OptionsAction::Select, "ACST-9:30ACDT,M10.1.0,M4.1.0/3"}, "AU/ACST",
{"AU/AEST", OptionsAction::Select, "AEST-10AEDT,M10.1.0,M4.1.0/3"}, "AU/AEST",
{"Pacific/NZ", OptionsAction::Select, "NZST-12NZDT,M9.5.0,M4.1.0/3"}, "Pacific/NZ"};
}; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Pick Timezone";
constexpr size_t timezoneCount = sizeof(timezoneOptions) / sizeof(timezoneOptions[0]); bannerOptions.optionsArrayPtr = optionsArray;
static std::array<const char *, timezoneCount> timezoneLabels{}; bannerOptions.optionsCount = 19;
bannerOptions.bannerCallback = [](int selected) -> void {
auto bannerOptions = createStaticBannerOptions( if (selected == 0) {
"Pick Timezone", timezoneOptions, timezoneLabels, [](const TimezoneOption &option, int) -> void { menuHandler::menuQueue = menuHandler::clock_menu;
if (option.action == OptionsAction::Back) { screen->runNow();
menuHandler::menuQueue = menuHandler::clock_menu; } else if (selected == 1) { // Hawaii
screen->runNow(); strncpy(config.device.tzdef, "HST10", sizeof(config.device.tzdef));
return; } else if (selected == 2) { // Alaska
} strncpy(config.device.tzdef, "AKST9AKDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
} else if (selected == 3) { // Pacific
if (!option.hasValue) { strncpy(config.device.tzdef, "PST8PDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
return; } else if (selected == 4) { // Arizona
} strncpy(config.device.tzdef, "MST7", sizeof(config.device.tzdef));
} else if (selected == 5) { // Mountain
if (strncmp(config.device.tzdef, option.value, sizeof(config.device.tzdef)) == 0) { strncpy(config.device.tzdef, "MST7MDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
return; } else if (selected == 6) { // Central
} strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
} else if (selected == 7) { // Eastern
strncpy(config.device.tzdef, option.value, sizeof(config.device.tzdef)); strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
config.device.tzdef[sizeof(config.device.tzdef) - 1] = '\0'; } else if (selected == 8) { // Brazil
strncpy(config.device.tzdef, "BRT3", sizeof(config.device.tzdef));
} else if (selected == 9) { // UTC
strncpy(config.device.tzdef, "UTC0", sizeof(config.device.tzdef));
} else if (selected == 10) { // EU/Western
strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef));
} else if (selected == 11) { // EU/Central
strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef));
} else if (selected == 12) { // EU/Eastern
strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef));
} else if (selected == 13) { // Asia/Kolkata
strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef));
} else if (selected == 14) { // China
strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef));
} else if (selected == 15) { // AU/AWST
strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef));
} else if (selected == 16) { // AU/ACST
strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
} else if (selected == 17) { // AU/AEST
strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
} else if (selected == 18) { // NZ
strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef));
}
if (selected != 0) {
setenv("TZ", config.device.tzdef, 1); setenv("TZ", config.device.tzdef, 1);
service->reloadConfig(SEGMENT_CONFIG); service->reloadConfig(SEGMENT_CONFIG);
});
int initialSelection = 0;
for (size_t i = 0; i < timezoneCount; ++i) {
if (timezoneOptions[i].hasValue &&
strncmp(config.device.tzdef, timezoneOptions[i].value, sizeof(config.device.tzdef)) == 0) {
initialSelection = static_cast<int>(i);
break;
} }
} };
bannerOptions.InitialSelected = initialSelection;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
@@ -477,9 +458,10 @@ void menuHandler::messageResponseMenu()
#endif #endif
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Action";
if (currentResolution == ScreenResolution::UltraLow) { if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Message"; bannerOptions.message = "Message";
} else {
bannerOptions.message = "Message Action";
} }
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
@@ -928,12 +910,8 @@ void menuHandler::homeBaseMenu()
} else if (selected == Sleep) { } else if (selected == Sleep) {
screen->setOn(false); screen->setOn(false);
} else if (selected == Position) { } else if (selected == Position) {
service->refreshLocalMeshNode(); InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SEND_PING, .kbchar = 0, .touchX = 0, .touchY = 0};
if (service->trySendPosition(NODENUM_BROADCAST, true)) { inputBroker->injectInputEvent(&event);
IF_SCREEN(screen->showSimpleBanner("Position\nSent", 3000));
} else {
IF_SCREEN(screen->showSimpleBanner("Node Info\nSent", 3000));
}
} else if (selected == Preset) { } else if (selected == Preset) {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST); cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
} else if (selected == Freetext) { } else if (selected == Freetext) {
@@ -1130,92 +1108,57 @@ void menuHandler::favoriteBaseMenu()
void menuHandler::positionBaseMenu() void menuHandler::positionBaseMenu()
{ {
enum class PositionAction { enum optionsNumbers {
GpsToggle, Back,
GpsFormat, GPSToggle,
GPSFormat,
CompassMenu, CompassMenu,
CompassCalibrate, CompassCalibrate,
GPSSmartPosition, GPSSmartPosition,
GPSUpdateInterval, GPSUpdateInterval,
GPSPositionBroadcast GPSPositionBroadcast,
enumEnd
}; };
static const PositionMenuOption baseOptions[] = { static const char *optionsArray[enumEnd] = {
{"Back", OptionsAction::Back}, "Back", "On/Off Toggle", "Format", "Smart Position", "Update Interval", "Broadcast Interval", "Compass"};
{"On/Off Toggle", OptionsAction::Select, static_cast<int>(PositionAction::GpsToggle)}, static int optionsEnumArray[enumEnd] = {
{"Format", OptionsAction::Select, static_cast<int>(PositionAction::GpsFormat)}, Back, GPSToggle, GPSFormat, GPSSmartPosition, GPSUpdateInterval, GPSPositionBroadcast, CompassMenu};
{"Smart Position", OptionsAction::Select, static_cast<int>(PositionAction::GPSSmartPosition)}, int options = 7;
{"Update Interval", OptionsAction::Select, static_cast<int>(PositionAction::GPSUpdateInterval)},
{"Broadcast Interval", OptionsAction::Select, static_cast<int>(PositionAction::GPSPositionBroadcast)},
{"Compass", OptionsAction::Select, static_cast<int>(PositionAction::CompassMenu)},
};
static const PositionMenuOption calibrateOptions[] = {
{"Back", OptionsAction::Back},
{"On/Off Toggle", OptionsAction::Select, static_cast<int>(PositionAction::GpsToggle)},
{"Format", OptionsAction::Select, static_cast<int>(PositionAction::GpsFormat)},
{"Smart Position", OptionsAction::Select, static_cast<int>(PositionAction::GPSSmartPosition)},
{"Update Interval", OptionsAction::Select, static_cast<int>(PositionAction::GPSUpdateInterval)},
{"Broadcast Interval", OptionsAction::Select, static_cast<int>(PositionAction::GPSPositionBroadcast)},
{"Compass", OptionsAction::Select, static_cast<int>(PositionAction::CompassMenu)},
{"Compass Calibrate", OptionsAction::Select, static_cast<int>(PositionAction::CompassCalibrate)},
};
constexpr size_t baseCount = sizeof(baseOptions) / sizeof(baseOptions[0]);
constexpr size_t calibrateCount = sizeof(calibrateOptions) / sizeof(calibrateOptions[0]);
static std::array<const char *, baseCount> baseLabels{};
static std::array<const char *, calibrateCount> calibrateLabels{};
auto onSelection = [](const PositionMenuOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
return;
}
if (!option.hasValue) {
return;
}
auto action = static_cast<PositionAction>(option.value);
switch (action) {
case PositionAction::GpsToggle:
menuQueue = gps_toggle_menu;
screen->runNow();
break;
case PositionAction::GpsFormat:
menuQueue = gps_format_menu;
screen->runNow();
break;
case PositionAction::CompassMenu:
menuQueue = compass_point_north_menu;
screen->runNow();
break;
case PositionAction::CompassCalibrate:
if (accelerometerThread) {
accelerometerThread->calibrate(30);
}
break;
case PositionAction::GPSSmartPosition:
menuQueue = gps_smart_position_menu;
screen->runNow();
break;
case PositionAction::GPSUpdateInterval:
menuQueue = gps_update_interval_menu;
screen->runNow();
break;
case PositionAction::GPSPositionBroadcast:
menuQueue = gps_position_broadcast_menu;
screen->runNow();
break;
}
};
BannerOverlayOptions bannerOptions;
if (accelerometerThread) { if (accelerometerThread) {
bannerOptions = createStaticBannerOptions("GPS Action", calibrateOptions, calibrateLabels, onSelection); optionsArray[options] = "Compass Calibrate";
} else { optionsEnumArray[options++] = CompassCalibrate;
bannerOptions = createStaticBannerOptions("GPS Action", baseOptions, baseLabels, onSelection);
} }
BannerOverlayOptions bannerOptions;
bannerOptions.message = "GPS Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == GPSToggle) {
menuQueue = gps_toggle_menu;
screen->runNow();
} else if (selected == GPSFormat) {
menuQueue = gps_format_menu;
screen->runNow();
} else if (selected == CompassMenu) {
menuQueue = compass_point_north_menu;
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); screen->showOverlayBanner(bannerOptions);
} }
@@ -1271,38 +1214,27 @@ void menuHandler::nodeListMenu()
void menuHandler::nodeNameLengthMenu() void menuHandler::nodeNameLengthMenu()
{ {
static const NodeNameOption nodeNameOptions[] = { enum OptionsNumbers { Back, Long, Short };
{"Back", OptionsAction::Back}, static const char *optionsArray[] = {"Back", "Long", "Short"};
{"Long", OptionsAction::Select, true}, BannerOverlayOptions bannerOptions;
{"Short", OptionsAction::Select, false}, bannerOptions.message = "Node Name Length";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Long) {
// Set names to long
LOG_INFO("Setting names to long");
config.display.use_long_node_name = true;
} else if (selected == Short) {
// Set names to short
LOG_INFO("Setting names to short");
config.display.use_long_node_name = false;
} else if (selected == Back) {
menuQueue = node_base_menu;
screen->runNow();
}
}; };
bannerOptions.InitialSelected = config.display.use_long_node_name == true ? 1 : 2;
constexpr size_t nodeNameCount = sizeof(nodeNameOptions) / sizeof(nodeNameOptions[0]);
static std::array<const char *, nodeNameCount> nodeNameLabels{};
auto bannerOptions = createStaticBannerOptions("Node Name Length", nodeNameOptions, nodeNameLabels,
[](const NodeNameOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuQueue = node_base_menu;
screen->runNow();
return;
}
if (!option.hasValue) {
return;
}
if (config.display.use_long_node_name == option.value) {
return;
}
config.display.use_long_node_name = option.value;
LOG_INFO("Setting names to %s", option.value ? "long" : "short");
});
int initialSelection = config.display.use_long_node_name ? 1 : 2;
bannerOptions.InitialSelected = initialSelection;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
@@ -1336,169 +1268,119 @@ void menuHandler::resetNodeDBMenu()
void menuHandler::compassNorthMenu() void menuHandler::compassNorthMenu()
{ {
static const CompassOption compassOptions[] = { enum optionsNumbers { Back, Dynamic, Fixed, Freeze };
{"Back", OptionsAction::Back}, static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"};
{"Dynamic", OptionsAction::Select, meshtastic_CompassMode_DYNAMIC}, BannerOverlayOptions bannerOptions;
{"Fixed Ring", OptionsAction::Select, meshtastic_CompassMode_FIXED_RING}, bannerOptions.message = "North Directions?";
{"Freeze Heading", OptionsAction::Select, meshtastic_CompassMode_FREEZE_HEADING}, bannerOptions.optionsArrayPtr = optionsArray;
}; bannerOptions.optionsCount = 4;
bannerOptions.InitialSelected = uiconfig.compass_mode + 1;
constexpr size_t compassCount = sizeof(compassOptions) / sizeof(compassOptions[0]); bannerOptions.bannerCallback = [](int selected) -> void {
static std::array<const char *, compassCount> compassLabels{}; if (selected == Dynamic) {
if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) {
auto bannerOptions = createStaticBannerOptions("North Directions?", compassOptions, compassLabels, uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC;
[](const CompassOption &option, int) -> void { saveUIConfig();
if (option.action == OptionsAction::Back) { screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
menuQueue = position_base_menu; }
screen->runNow(); } else if (selected == Fixed) {
return; if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) {
} uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING;
saveUIConfig();
if (!option.hasValue) { screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
return; }
} } else if (selected == Freeze) {
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
if (uiconfig.compass_mode == option.value) { uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING;
return; saveUIConfig();
} screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
uiconfig.compass_mode = option.value; } else if (selected == Back) {
saveUIConfig(); menuQueue = position_base_menu;
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); screen->runNow();
});
int initialSelection = 0;
for (size_t i = 0; i < compassCount; ++i) {
if (compassOptions[i].hasValue && uiconfig.compass_mode == compassOptions[i].value) {
initialSelection = static_cast<int>(i);
break;
} }
} };
bannerOptions.InitialSelected = initialSelection;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
#if !MESHTASTIC_EXCLUDE_GPS #if !MESHTASTIC_EXCLUDE_GPS
void menuHandler::GPSToggleMenu() void menuHandler::GPSToggleMenu()
{ {
static const GPSToggleOption gpsToggleOptions[] = {
{"Back", OptionsAction::Back},
{"Enabled", OptionsAction::Select, meshtastic_Config_PositionConfig_GpsMode_ENABLED},
{"Disabled", OptionsAction::Select, meshtastic_Config_PositionConfig_GpsMode_DISABLED},
};
constexpr size_t toggleCount = sizeof(gpsToggleOptions) / sizeof(gpsToggleOptions[0]); static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
static std::array<const char *, toggleCount> toggleLabels{}; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Toggle GPS";
auto bannerOptions = bannerOptions.optionsArrayPtr = optionsArray;
createStaticBannerOptions("Toggle GPS", gpsToggleOptions, toggleLabels, [](const GPSToggleOption &option, int) -> void { bannerOptions.optionsCount = 3;
if (option.action == OptionsAction::Back) { bannerOptions.bannerCallback = [](int selected) -> void {
menuQueue = position_base_menu; if (selected == 1) {
screen->runNow(); config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
return; playGPSEnableBeep();
} gps->enable();
if (!option.hasValue) {
return;
}
if (config.position.gps_mode == option.value) {
return;
}
config.position.gps_mode = option.value;
if (option.value == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
playGPSEnableBeep();
gps->enable();
} else {
playGPSDisableBeep();
gps->disable();
}
service->reloadConfig(SEGMENT_CONFIG); service->reloadConfig(SEGMENT_CONFIG);
}); } else if (selected == 2) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
int initialSelection = 0; playGPSDisableBeep();
for (size_t i = 0; i < toggleCount; ++i) { gps->disable();
if (gpsToggleOptions[i].hasValue && config.position.gps_mode == gpsToggleOptions[i].value) { service->reloadConfig(SEGMENT_CONFIG);
initialSelection = static_cast<int>(i); } else {
break; menuQueue = position_base_menu;
screen->runNow();
} }
} };
bannerOptions.InitialSelected = initialSelection; bannerOptions.InitialSelected = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::GPSFormatMenu() void menuHandler::GPSFormatMenu()
{ {
static const GPSFormatOption formatOptionsHigh[] = {
{"Back", OptionsAction::Back},
{"Decimal Degrees", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC},
{"Degrees Minutes Seconds", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS},
{"Universal Transverse Mercator", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM},
{"Military Grid Reference System", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS},
{"Open Location Code", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC},
{"Ordnance Survey Grid Ref", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR},
{"Maidenhead Locator", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS},
};
static const GPSFormatOption formatOptionsLow[] = { static const char *optionsArray[] = {"Back",
{"Back", OptionsAction::Back}, (currentResolution == ScreenResolution::High) ? "Decimal Degrees" : "DEC",
{"DEC", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC}, (currentResolution == ScreenResolution::High) ? "Degrees Minutes Seconds" : "DMS",
{"DMS", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS}, (currentResolution == ScreenResolution::High) ? "Universal Transverse Mercator" : "UTM",
{"UTM", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM}, (currentResolution == ScreenResolution::High) ? "Military Grid Reference System"
{"MGRS", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS}, : "MGRS",
{"OLC", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC}, (currentResolution == ScreenResolution::High) ? "Open Location Code" : "OLC",
{"OSGR", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR}, (currentResolution == ScreenResolution::High) ? "Ordnance Survey Grid Ref" : "OSGR",
{"MLS", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS}, (currentResolution == ScreenResolution::High) ? "Maidenhead Locator" : "MLS"};
}; BannerOverlayOptions bannerOptions;
bannerOptions.message = "GPS Format";
constexpr size_t formatCount = sizeof(formatOptionsHigh) / sizeof(formatOptionsHigh[0]); bannerOptions.optionsArrayPtr = optionsArray;
static std::array<const char *, formatCount> formatLabelsHigh{}; bannerOptions.optionsCount = 8;
static std::array<const char *, formatCount> formatLabelsLow{}; bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) {
auto onSelection = [](const GPSFormatOption &option, int) -> void { uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC;
if (option.action == OptionsAction::Back) { saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == 2) {
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == 3) {
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == 4) {
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == 5) {
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == 6) {
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == 7) {
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
} else {
menuQueue = position_base_menu; menuQueue = position_base_menu;
screen->runNow(); screen->runNow();
return;
} }
if (!option.hasValue) {
return;
}
if (uiconfig.gps_format == option.value) {
return;
}
uiconfig.gps_format = option.value;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
}; };
bannerOptions.InitialSelected = uiconfig.gps_format + 1;
BannerOverlayOptions bannerOptions;
int initialSelection = 0;
if (currentResolution == ScreenResolution::High) {
bannerOptions = createStaticBannerOptions("GPS Format", formatOptionsHigh, formatLabelsHigh, onSelection);
for (size_t i = 0; i < formatCount; ++i) {
if (formatOptionsHigh[i].hasValue && uiconfig.gps_format == formatOptionsHigh[i].value) {
initialSelection = static_cast<int>(i);
break;
}
}
} else {
bannerOptions = createStaticBannerOptions("GPS Format", formatOptionsLow, formatLabelsLow, onSelection);
for (size_t i = 0; i < formatCount; ++i) {
if (formatOptionsLow[i].hasValue && uiconfig.gps_format == formatOptionsLow[i].value) {
initialSelection = static_cast<int>(i);
break;
}
}
}
bannerOptions.InitialSelected = initialSelection;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
@@ -1819,63 +1701,100 @@ void menuHandler::switchToMUIMenu()
void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
{ {
static const ScreenColorOption colorOptions[] = { static const char *optionsArray[] = {
{"Back", OptionsAction::Back}, "Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Blue", "Teal", "Cyan", "Ice", "Pink",
{"Default", OptionsAction::Select, ScreenColor(0, 0, 0, true)}, "White", "Gray"};
{"Meshtastic Green", OptionsAction::Select, ScreenColor(103, 234, 148)}, BannerOverlayOptions bannerOptions;
{"Yellow", OptionsAction::Select, ScreenColor(255, 255, 128)}, bannerOptions.message = "Select Screen Color";
{"Red", OptionsAction::Select, ScreenColor(255, 64, 64)}, bannerOptions.optionsArrayPtr = optionsArray;
{"Orange", OptionsAction::Select, ScreenColor(255, 160, 20)}, bannerOptions.optionsCount = 14;
{"Purple", OptionsAction::Select, ScreenColor(204, 153, 255)}, bannerOptions.bannerCallback = [display](int selected) -> void {
{"Blue", OptionsAction::Select, ScreenColor(0, 0, 255)},
{"Teal", OptionsAction::Select, ScreenColor(16, 102, 102)},
{"Cyan", OptionsAction::Select, ScreenColor(0, 255, 255)},
{"Ice", OptionsAction::Select, ScreenColor(173, 216, 230)},
{"Pink", OptionsAction::Select, ScreenColor(255, 105, 180)},
{"White", OptionsAction::Select, ScreenColor(255, 255, 255)},
{"Gray", OptionsAction::Select, ScreenColor(128, 128, 128)},
};
constexpr size_t colorCount = sizeof(colorOptions) / sizeof(colorOptions[0]);
static std::array<const char *, colorCount> colorLabels{};
auto bannerOptions = createStaticBannerOptions(
"Select Screen Color", colorOptions, colorLabels, [display](const ScreenColorOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuQueue = system_base_menu;
screen->runNow();
return;
}
if (!option.hasValue) {
return;
}
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \ #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
HAS_TFT || defined(HACKADAY_COMMUNICATOR) HAS_TFT || defined(HACKADAY_COMMUNICATOR)
const ScreenColor &color = option.value; uint8_t TFT_MESH_r = 0;
if (color.useVariant) { uint8_t TFT_MESH_g = 0;
LOG_INFO("Setting color to system default or defined variant"); uint8_t TFT_MESH_b = 0;
} else { if (selected == 1) {
LOG_INFO("Setting color to %s", option.label); LOG_INFO("Setting color to system default or defined variant");
} // Given just before we set all these to zero, we will allow this to go through
} else if (selected == 2) {
uint8_t r = color.r; LOG_INFO("Setting color to Meshtastic Green");
uint8_t g = color.g; TFT_MESH_r = 103;
uint8_t b = color.b; TFT_MESH_g = 234;
TFT_MESH_b = 148;
} else if (selected == 3) {
LOG_INFO("Setting color to Yellow");
TFT_MESH_r = 255;
TFT_MESH_g = 255;
TFT_MESH_b = 128;
} else if (selected == 4) {
LOG_INFO("Setting color to Red");
TFT_MESH_r = 255;
TFT_MESH_g = 64;
TFT_MESH_b = 64;
} else if (selected == 5) {
LOG_INFO("Setting color to Orange");
TFT_MESH_r = 255;
TFT_MESH_g = 160;
TFT_MESH_b = 20;
} else if (selected == 6) {
LOG_INFO("Setting color to Purple");
TFT_MESH_r = 204;
TFT_MESH_g = 153;
TFT_MESH_b = 255;
} else if (selected == 7) {
LOG_INFO("Setting color to Blue");
TFT_MESH_r = 0;
TFT_MESH_g = 0;
TFT_MESH_b = 255;
} else if (selected == 8) {
LOG_INFO("Setting color to Teal");
TFT_MESH_r = 16;
TFT_MESH_g = 102;
TFT_MESH_b = 102;
} else if (selected == 9) {
LOG_INFO("Setting color to Cyan");
TFT_MESH_r = 0;
TFT_MESH_g = 255;
TFT_MESH_b = 255;
} else if (selected == 10) {
LOG_INFO("Setting color to Ice");
TFT_MESH_r = 173;
TFT_MESH_g = 216;
TFT_MESH_b = 230;
} else if (selected == 11) {
LOG_INFO("Setting color to Pink");
TFT_MESH_r = 255;
TFT_MESH_g = 105;
TFT_MESH_b = 180;
} else if (selected == 12) {
LOG_INFO("Setting color to White");
TFT_MESH_r = 255;
TFT_MESH_g = 255;
TFT_MESH_b = 255;
} else if (selected == 13) {
LOG_INFO("Setting color to Gray");
TFT_MESH_r = 128;
TFT_MESH_g = 128;
TFT_MESH_b = 128;
} else {
menuQueue = system_base_menu;
screen->runNow();
}
if (selected != 0) {
display->setColor(BLACK); display->setColor(BLACK);
display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
display->setColor(WHITE); display->setColor(WHITE);
if (color.useVariant || (r == 0 && g == 0 && b == 0)) { if (TFT_MESH_r == 0 && TFT_MESH_g == 0 && TFT_MESH_b == 0) {
#ifdef TFT_MESH_OVERRIDE #ifdef TFT_MESH_OVERRIDE
TFT_MESH = TFT_MESH_OVERRIDE; TFT_MESH = TFT_MESH_OVERRIDE;
#else #else
TFT_MESH = COLOR565(0x67, 0xEA, 0x94); TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
#endif #endif
} else { } else {
TFT_MESH = COLOR565(r, g, b); TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
} }
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190)
@@ -1883,40 +1802,16 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
#endif #endif
screen->setFrames(graphics::Screen::FOCUS_SYSTEM); screen->setFrames(graphics::Screen::FOCUS_SYSTEM);
if (color.useVariant || (r == 0 && g == 0 && b == 0)) { if (TFT_MESH_r == 0 && TFT_MESH_g == 0 && TFT_MESH_b == 0) {
uiconfig.screen_rgb_color = 0; uiconfig.screen_rgb_color = 0;
} else { } else {
uiconfig.screen_rgb_color = uiconfig.screen_rgb_color = (TFT_MESH_r << 16) | (TFT_MESH_g << 8) | TFT_MESH_b;
(static_cast<uint32_t>(r) << 16) | (static_cast<uint32_t>(g) << 8) | static_cast<uint32_t>(b);
} }
LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color); LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color);
saveUIConfig(); saveUIConfig();
#endif
});
int initialSelection = 0;
if (uiconfig.screen_rgb_color == 0) {
initialSelection = 1;
} else {
uint32_t currentColor = uiconfig.screen_rgb_color;
for (size_t i = 0; i < colorCount; ++i) {
if (!colorOptions[i].hasValue) {
continue;
}
const ScreenColor &color = colorOptions[i].value;
if (color.useVariant) {
continue;
}
uint32_t encoded =
(static_cast<uint32_t>(color.r) << 16) | (static_cast<uint32_t>(color.g) << 8) | static_cast<uint32_t>(color.b);
if (encoded == currentColor) {
initialSelection = static_cast<int>(i);
break;
}
} }
} #endif
bannerOptions.InitialSelected = initialSelection; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }

View File

@@ -128,28 +128,7 @@ template <typename T> struct MenuOption {
MenuOption(const char *labelIn, OptionsAction actionIn) : label(labelIn), action(actionIn), hasValue(false), value() {} 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 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 } // namespace graphics
#endif #endif

View File

@@ -324,9 +324,9 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(meshtastic_MeshPacket *p)
void printPacket(const char *prefix, const meshtastic_MeshPacket *p) void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
{ {
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) #if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
std::string out = DEBUG_PORT.mt_sprintf( std::string out =
"%s (id=0x%08x fr=0x%08x to=0x%08x, transport = %u, WantAck=%d, HopLim=%d HopStart=%d Ch=0x%x", prefix, p->id, p->from, DEBUG_PORT.mt_sprintf("%s (id=0x%08x fr=0x%08x to=0x%08x, transport = %u, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id,
p->to, p->transport_mechanism, p->want_ack, p->hop_limit, p->hop_start, p->channel); p->from, p->to, p->transport_mechanism, p->want_ack, p->hop_limit, p->channel);
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
auto &s = p->decoded; auto &s = p->decoded;
@@ -520,10 +520,6 @@ void RadioInterface::applyModemConfig()
sf = 12; sf = 12;
break; break;
} }
if (loraConfig.coding_rate >= 5 && loraConfig.coding_rate <= 8 && loraConfig.coding_rate != cr) {
cr = loraConfig.coding_rate;
LOG_INFO("Using custom Coding Rate %u", cr);
}
} else { } else {
sf = loraConfig.spread_factor; sf = loraConfig.spread_factor;
cr = loraConfig.coding_rate; cr = loraConfig.coding_rate;

View File

@@ -61,7 +61,6 @@
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#include "input/LinuxInputImpl.h" #include "input/LinuxInputImpl.h"
#include "input/SeesawRotary.h" #include "input/SeesawRotary.h"
#include "modules/Native/StoreForwardPlusPlus.h"
#include "modules/Telemetry/HostMetrics.h" #include "modules/Telemetry/HostMetrics.h"
#if !MESHTASTIC_EXCLUDE_STOREFORWARD #if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h" #include "modules/StoreForwardModule.h"
@@ -244,11 +243,6 @@ void setupModules()
#endif #endif
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
new HostMetricsModule(); new HostMetricsModule();
#if SFPP_ENABLED
if (portduino_config.sfpp_enabled) {
new StoreForwardPlusPlusModule();
}
#endif
#endif #endif
#if HAS_TELEMETRY #if HAS_TELEMETRY
new DeviceTelemetryModule(); new DeviceTelemetryModule();

File diff suppressed because it is too large Load Diff

View File

@@ -1,257 +0,0 @@
#pragma once
#if __has_include("sqlite3.h")
#define SFPP_ENABLED 1
#include "Channels.h"
#include "ProtobufModule.h"
#include "Router.h"
#include "SinglePortModule.h"
#include "sqlite3.h"
#define SFPP_HASH_SIZE 16
#define SFPP_SHORT_HASH_SIZE 8
/**
* Store and forward ++ module
* There's an obvious need for a store-and-forward mechanism in Meshtastic.
* This module takes heavy inspiration from Git, building a chain of messages that can be synced between nodes.
* Each message is hashed, and the chain is built by hashing the previous commit hash and the current message hash.
* Nodes can request missing messages by requesting the next message after a given commit hash.
*
* The current focus is text messages, limited to the primary channel.
*
* Each chain is identified by a root hash, which is derived from the channelHash, the local nodenum, and the timestamp when
* created.
*
* Each message is also given a message hash, derived from the encrypted payload, the to, from, id.
* Notably not the timestamp, as we want these to match across nodes, even if the timestamps differ.
*
* The authoritative node for the chain will generate a commit hash for each message when adding it to the chain.
* The first message's commit hash is derived from the root hash and the message hash.
* Subsequent messages' commit hashes are derived from the previous commit hash and the current message hash.
* This allows a node to see only the last commit hash, and confirm it hasn't missed any messages.
*
* Nodes can request the next message in the chain by sending a LINK_REQUEST message with the root hash and the last known commit
* hash. Any node that has the next message can respond with a LINK_PROVIDE message containing the next message.
*
* When a satellite node sees a new text message, it stores it in a scratch database.
* These messages are periodically offered to the authoritative node for inclusion in the chain.
*
* The LINK_PROVIDE message does double-duty, sending both on-chain and off-chain messages.
* The differentiator is whether the commit hash is set or left empty.
*
* When a satellite node receives a canonical link message, it checks if it has the message in scratch.
* And evicts it when adding it to the canonical chain.
*
* This approach allows a node to know whether it has seen a given message before, or if it is new coming via SFPP.
* If new, and the timestamp is within the rebroadcast timeout, it will process that message as if it were just received from the
* mesh, allowing it to be decrypted, shown to the user, and rebroadcast.
*/
class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForwardPlusPlus>, private concurrency::OSThread
{
struct link_object {
uint32_t to;
uint32_t from;
uint32_t id;
uint32_t rx_time = 0;
ChannelHash channel_hash;
uint8_t encrypted_bytes[256] = {0};
size_t encrypted_len;
uint8_t message_hash[SFPP_HASH_SIZE] = {0};
size_t message_hash_len = 0;
uint8_t root_hash[SFPP_HASH_SIZE] = {0};
size_t root_hash_len = 0;
uint8_t commit_hash[SFPP_HASH_SIZE] = {0};
size_t commit_hash_len = 0;
uint32_t counter = 0;
std::string payload;
bool validObject = true; // set this false when a chain calulation fails, etc.
};
public:
/** Constructor
*
*/
StoreForwardPlusPlusModule();
/*
-Override the wantPacket method.
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
p->decoded.portnum == (portduino_config.sfpp_steal_port ? meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP
: meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP)) {
return true;
} else {
return false;
}
}
protected:
/** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
it
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_StoreForwardPlusPlus *t) override;
virtual int32_t runOnce() override;
private:
sqlite3 *ppDb;
sqlite3_stmt *chain_insert_stmt;
sqlite3_stmt *scratch_insert_stmt;
sqlite3_stmt *checkDupMessageHash;
sqlite3_stmt *checkDupCommitHash;
sqlite3_stmt *checkScratch;
sqlite3_stmt *removeScratch;
sqlite3_stmt *updatePayloadStmt;
sqlite3_stmt *getPayloadFromScratchStmt;
sqlite3_stmt *fromScratchStmt;
sqlite3_stmt *fromScratchByHashStmt;
sqlite3_stmt *getNextHashStmt;
sqlite3_stmt *getChainEndStmt;
sqlite3_stmt *getLinkStmt;
sqlite3_stmt *getHashFromRootStmt;
sqlite3_stmt *addRootToMappingsStmt;
sqlite3_stmt *getRootFromChannelHashStmt;
sqlite3_stmt *getFullRootHashStmt;
sqlite3_stmt *setChainCountStmt;
sqlite3_stmt *getChainCountStmt;
sqlite3_stmt *pruneScratchQueueStmt;
sqlite3_stmt *trimOldestLinkStmt;
sqlite3_stmt *maybeAddPeerStmt;
sqlite3_stmt *getPeerStmt;
sqlite3_stmt *updatePeerStmt;
sqlite3_stmt *clearChainStmt;
// For a given Meshtastic ChannelHash, fills the root_hash buffer with a 32-byte root hash
// returns true if the root hash was found
bool getRootFromChannelHash(ChannelHash, uint8_t *);
// For a given root hash, returns the ChannelHash
// can handle partial root hashes
ChannelHash getChannelHashFromRoot(uint8_t *_root_hash, size_t);
// given a root hash and commit hash, returns the next commit hash in the chain
// can handle partial root and commit hashes, always fills the buffer with 32 bytes
// returns true if a next hash was found
bool getNextHash(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
// For a given Meshtastic ChannelHash, fills the root_hash buffer with a 32-byte root hash
// but this function will add the root hash if it is not already present
// returns hash size or 0 if not found/added
size_t getOrAddRootFromChannelHash(ChannelHash, uint8_t *);
// adds the ChannelHash and root_hash to the mappings table
void addRootToMappings(ChannelHash, uint8_t *);
// requests the next message in the chain from the mesh network
// Sends a LINK_REQUEST message
void requestNextMessage(uint8_t *, size_t, uint8_t *, size_t);
// request the message X entries from the end.
// used to bootstrap a chain, without downloading all of the history
void requestMessageCount(uint8_t *, size_t, uint32_t);
// sends a LINK_PROVIDE message broadcasting the given link object
void broadcastLink(uint8_t *, size_t);
// sends a LINK_PROVIDE message broadcasting the given link object
void broadcastLink(link_object &, bool, bool = false);
// sends a LINK_PROVIDE message broadcasting the given link object from scratch message store
bool sendFromScratch(uint8_t *);
// Adds the given link object to the canonical chain database
bool addToChain(link_object &);
// Adds an incoming text message to the scratch database
bool addToScratch(link_object &);
// sends a CANON_ANNOUNCE message, specifying the given root and commit hashes
void canonAnnounce(link_object &, uint8_t *, uint8_t *, uint8_t *, uint32_t);
// checks if the message hash is present in the canonical chain database
bool isInDB(uint8_t *, size_t);
// checks if the commit hash is present in the canonical chain database
bool isCommitInDB(uint8_t *, size_t);
// checks if the message hash is present in the scratch database
bool isInScratch(uint8_t *, size_t);
// retrieves a link object from the scratch database
link_object getFromScratch(uint8_t *, size_t);
// removes a link object from the scratch database
void removeFromScratch(uint8_t *, size_t);
// iterate through our scratch database, and see if we can speculate a chain up to the given commit hash
bool speculateScratchChain(uint8_t *, size_t, uint8_t *, uint8_t *);
// retrieves the next link object from scratch given a root hash
link_object getNextScratchObject(uint8_t *);
// fills the payload section with the decrypted data for the given message hash
// probably not needed for production, but useful for testing
void updatePayload(uint8_t *, size_t, std::string);
// Takes the decrypted MeshPacket and the encrypted packet copy, and builds a link_object
// Generates a message hash, but does not set the commit hash
link_object ingestTextPacket(const meshtastic_MeshPacket &, const meshtastic_MeshPacket *);
// ingests a LINK_PROVIDE message and builds a link_object
// confirms the root hash and commit hash
link_object ingestLinkMessage(meshtastic_StoreForwardPlusPlus *, bool = true);
// retrieves a link object from the canonical chain database given a message hash
link_object getLink(uint8_t *, size_t);
// puts the encrypted payload back into the queue as if it were just received
void rebroadcastLinkObject(link_object &);
// Check if an incoming link object's commit hash matches the calculated commit hash
bool checkCommitHash(link_object &lo, uint8_t *commit_hash_bytes, size_t hash_len);
// given a partial root hash, looks up the full 32-byte root hash
// returns true if found
bool lookUpFullRootHash(uint8_t *partial_root_hash, size_t partial_root_hash_len, uint8_t *full_root_hash);
// update the mappings table to set the chain count for the given root hash
void setChainCount(uint8_t *, size_t, uint32_t);
// query the mappings table for the chain count for the given root hash
uint32_t getChainCount(uint8_t *, size_t);
link_object getLinkFromCount(uint32_t, uint8_t *, size_t);
void pruneScratchQueue();
void trimOldestLink(uint8_t *, size_t);
void clearChain(uint8_t *, size_t);
// given a link object with a payload and other fields, recalculates the message hash
// returns true if a match
bool recalculateHash(link_object &, uint8_t *, size_t, uint8_t *, size_t);
void updatePeers(const meshtastic_MeshPacket &, meshtastic_StoreForwardPlusPlus_SFPP_message_type);
// Track if we have a scheduled runOnce pending
// useful to not accudentally delay a scheduled runOnce
bool pendingRun = false;
// Once we have multiple chain types, we can extend this
enum chain_types {
channel_chain = 0,
};
uint32_t rebroadcastTimeout = 3600; // Messages older than this (in seconds) will not be rebroadcast
bool doing_split_send = false;
link_object split_link_out;
bool doing_split_receive = false;
link_object split_link_in;
};
#endif

View File

@@ -788,18 +788,6 @@ bool loadConfig(const char *configPath)
} }
} }
if (yamlConfig["StoreAndForward"]) {
portduino_config.sfpp_stratum0 = (yamlConfig["StoreAndForward"]["Stratum0"]).as<bool>(false);
portduino_config.sfpp_enabled = (yamlConfig["StoreAndForward"]["Enabled"]).as<bool>(true);
portduino_config.sfpp_db_path = (yamlConfig["StoreAndForward"]["DBPath"]).as<std::string>("/var/lib/meshtasticd/");
portduino_config.sfpp_initial_sync = (yamlConfig["StoreAndForward"]["InitialSync"]).as<int>(10);
portduino_config.sfpp_hops = (yamlConfig["StoreAndForward"]["Hops"]).as<int>(3);
portduino_config.sfpp_announce_interval = (yamlConfig["StoreAndForward"]["AnnounceInterval"]).as<int>(5);
portduino_config.sfpp_max_chain = (yamlConfig["StoreAndForward"]["MaxChainLength"]).as<uint32_t>(1000);
portduino_config.sfpp_backlog_limit = (yamlConfig["StoreAndForward"]["BacklogLimit"]).as<uint32_t>(100);
portduino_config.sfpp_steal_port = (yamlConfig["StoreAndForward"]["StealPort"]).as<bool>(false);
}
if (yamlConfig["General"]) { if (yamlConfig["General"]) {
portduino_config.MaxNodes = (yamlConfig["General"]["MaxNodes"]).as<int>(200); portduino_config.MaxNodes = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
portduino_config.maxtophone = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100); portduino_config.maxtophone = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);

View File

@@ -169,21 +169,6 @@ extern struct portduino_config_struct {
int configDisplayMode = 0; int configDisplayMode = 0;
bool has_configDisplayMode = false; bool has_configDisplayMode = false;
// Store and Forward++
std::string sfpp_db_path = "/var/lib/meshtasticd/";
bool sfpp_stratum0 = false;
bool sfpp_enabled = true;
bool sfpp_steal_port = false;
int sfpp_initial_sync = 10;
int sfpp_hops = 3;
int sfpp_announce_interval = 5; // minutes
uint32_t sfpp_max_chain = 1000;
uint32_t sfpp_backlog_limit = 100;
// allowed root hashes
// upstream node
// Are we allowing unknown channel hashes? Does this even make sense?
// Allow DMs
// General // General
std::string mac_address = ""; std::string mac_address = "";
bool mac_address_explicit = false; bool mac_address_explicit = false;
@@ -503,21 +488,6 @@ extern struct portduino_config_struct {
out << YAML::EndMap; // Config out << YAML::EndMap; // Config
} }
// StoreAndForward
if (sfpp_enabled) {
out << YAML::Key << "StoreAndForward" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "Enabled" << YAML::Value << sfpp_enabled;
out << YAML::Key << "DBPath" << YAML::Value << sfpp_db_path;
out << YAML::Key << "Stratum0" << YAML::Value << sfpp_stratum0;
out << YAML::Key << "InitialSync" << YAML::Value << sfpp_initial_sync;
out << YAML::Key << "Hops" << YAML::Value << sfpp_hops;
out << YAML::Key << "AnnounceInterval" << YAML::Value << sfpp_announce_interval;
out << YAML::Key << "BacklogLimit" << YAML::Value << sfpp_backlog_limit;
out << YAML::Key << "MaxChainLength" << YAML::Value << sfpp_max_chain;
out << YAML::Key << "StealPort" << YAML::Value << sfpp_steal_port;
out << YAML::EndMap; // StoreAndForward
}
// General // General
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap; out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
if (config_directory != "") if (config_directory != "")

View File

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

View File

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

View File

@@ -4,3 +4,8 @@ custom_esp32_kind = esp32c3
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = esp32_c3_exception_decoder 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

@@ -3,3 +3,8 @@ extends = esp32_common
custom_esp32_kind = esp32s3 custom_esp32_kind = esp32s3
monitor_speed = 115200 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

@@ -13,7 +13,8 @@ build_flags =
[env:rak3112] [env:rak3112]
extends = esp32s3_base extends = esp32s3_base
board = wiscore_rak3312 board = wiscore_rak3312
board_level = extra board_level = pr
board_check = true
upload_protocol = esptool upload_protocol = esptool
build_flags = build_flags =

View File

@@ -9,7 +9,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=Melopero RV3028 packageName=melopero/library/Melopero RV3028 # renovate: datasource=custom.pio depName=Melopero RV3028 packageName=melopero/library/Melopero RV3028
melopero/Melopero RV3028@1.2.0 melopero/Melopero RV3028@1.2.0
build_src_filter = ${portduino_base.build_src_filter} +<modules/Native/> build_src_filter = ${portduino_base.build_src_filter}
[env:native] [env:native]
extends = native_base extends = native_base
@@ -20,7 +20,6 @@ build_flags = ${native_base.build_flags}
!pkg-config --libs openssl --silence-errors || : !pkg-config --libs openssl --silence-errors || :
!pkg-config --cflags --libs sdl2 --silence-errors || : !pkg-config --cflags --libs sdl2 --silence-errors || :
!pkg-config --cflags --libs libbsd-overlay --silence-errors || : !pkg-config --cflags --libs libbsd-overlay --silence-errors || :
!pkg-config --cflags --libs sqlite3 --silence-errors || :
[env:native-tft] [env:native-tft]
extends = native_base extends = native_base
@@ -47,7 +46,6 @@ build_flags = ${native_base.build_flags} -Os -lX11 -linput -lxkbcommon -ffunctio
!pkg-config --libs openssl --silence-errors || : !pkg-config --libs openssl --silence-errors || :
!pkg-config --cflags --libs sdl2 --silence-errors || : !pkg-config --cflags --libs sdl2 --silence-errors || :
!pkg-config --cflags --libs libbsd-overlay --silence-errors || : !pkg-config --cflags --libs libbsd-overlay --silence-errors || :
!pkg-config --cflags --libs sqlite3 --silence-errors || :
build_src_filter = build_src_filter =
${native_base.build_src_filter} ${native_base.build_src_filter}
@@ -77,7 +75,6 @@ build_flags = ${native_base.build_flags} -Os -ffunction-sections -fdata-sections
!pkg-config --libs libulfius --silence-errors || : !pkg-config --libs libulfius --silence-errors || :
!pkg-config --libs openssl --silence-errors || : !pkg-config --libs openssl --silence-errors || :
!pkg-config --cflags --libs libbsd-overlay --silence-errors || : !pkg-config --cflags --libs libbsd-overlay --silence-errors || :
!pkg-config --cflags --libs sqlite3 --silence-errors || :
build_src_filter = build_src_filter =
${native_base.build_src_filter} ${native_base.build_src_filter}
@@ -111,7 +108,6 @@ build_flags = ${native_base.build_flags} -O0 -fsanitize=address -lX11 -linput -l
!pkg-config --libs libulfius --silence-errors || : !pkg-config --libs libulfius --silence-errors || :
!pkg-config --libs openssl --silence-errors || : !pkg-config --libs openssl --silence-errors || :
!pkg-config --cflags --libs libbsd-overlay --silence-errors || : !pkg-config --cflags --libs libbsd-overlay --silence-errors || :
!pkg-config --cflags --libs sqlite3 --silence-errors || :
build_src_filter = ${env:native-tft.build_src_filter} build_src_filter = ${env:native-tft.build_src_filter}
[env:coverage] [env:coverage]

View File

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