mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-04 09:02:21 +00:00
Compare commits
2 Commits
sfpp
...
merge-mast
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66cb549817 | ||
|
|
c351c49a72 |
@@ -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 \
|
||||||
|
|||||||
2
.github/workflows/first_time_contributor.yml
vendored
2
.github/workflows/first_time_contributor.yml
vendored
@@ -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)
|
||||||
|
|||||||
2
.github/workflows/test_native.yml
vendored
2
.github/workflows/test_native.yml
vendored
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
6
debian/changelog
vendored
@@ -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
3
debian/control
vendored
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 != "")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[VERSION]
|
[VERSION]
|
||||||
major = 2
|
major = 2
|
||||||
minor = 7
|
minor = 7
|
||||||
build = 18
|
build = 17
|
||||||
|
|||||||
Reference in New Issue
Block a user