mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-17 23:37:44 +00:00
Compare commits
100 Commits
InkHUD-Imp
...
sfpp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f565fd524 | ||
|
|
5af6a48326 | ||
|
|
5633848d75 | ||
|
|
8b8c1881a8 | ||
|
|
a1d6978626 | ||
|
|
a67cf0f726 | ||
|
|
456fa3ddeb | ||
|
|
5adc9663b7 | ||
|
|
5de0654819 | ||
|
|
ab781e9f2d | ||
|
|
595b5f19b3 | ||
|
|
4bb93c1ed2 | ||
|
|
5582e94009 | ||
|
|
e33fbca8d6 | ||
|
|
aca7fe9f95 | ||
|
|
5a0644cd4f | ||
|
|
e990198628 | ||
|
|
6c69d9e74c | ||
|
|
e03f1b5c5e | ||
|
|
f46a9dfe7b | ||
|
|
9824357c50 | ||
|
|
942f2cb3d1 | ||
|
|
76beeda392 | ||
|
|
821735495a | ||
|
|
9ab2ee3483 | ||
|
|
4e92f7fa09 | ||
|
|
ae2a06eccd | ||
|
|
3ae331eb89 | ||
|
|
74a6c9f447 | ||
|
|
c77709a327 | ||
|
|
325f7d2e55 | ||
|
|
c6fc7986f1 | ||
|
|
8ecce1eb5c | ||
|
|
21c0dcaabb | ||
|
|
1b13f872db | ||
|
|
8b5141ddb7 | ||
|
|
ee25a0a0e1 | ||
|
|
436f174bce | ||
|
|
a34cd4ca6f | ||
|
|
8c37669213 | ||
|
|
8a059bae23 | ||
|
|
1869f2108d | ||
|
|
f5b41c2f2c | ||
|
|
83c8875060 | ||
|
|
9134239faa | ||
|
|
b3d1d563e9 | ||
|
|
c7f816e63f | ||
|
|
dd4fb6b0bc | ||
|
|
87798429fa | ||
|
|
78baaf4484 | ||
|
|
b3b115b6a6 | ||
|
|
b90b5ff40e | ||
|
|
b7028fff08 | ||
|
|
7d6a0f20c6 | ||
|
|
9b7384507d | ||
|
|
7d7091ef94 | ||
|
|
baccd0c532 | ||
|
|
1fecdc7603 | ||
|
|
1625fd88d7 | ||
|
|
fe22460f25 | ||
|
|
f634b7dd60 | ||
|
|
f56e651787 | ||
|
|
55af6c4726 | ||
|
|
d272b28ed4 | ||
|
|
f8c27d1714 | ||
|
|
25383c9523 | ||
|
|
6d90b6536e | ||
|
|
0759197ab3 | ||
|
|
bbfca12d50 | ||
|
|
d44c3a8e1a | ||
|
|
1cef1094a0 | ||
|
|
02d4ca2983 | ||
|
|
36e8a498f1 | ||
|
|
d63b583ea2 | ||
|
|
14073e2c9f | ||
|
|
39a6ffc664 | ||
|
|
8be790890c | ||
|
|
426a7c19dd | ||
|
|
39c0824abb | ||
|
|
a8a5086b6d | ||
|
|
428b839254 | ||
|
|
a70d350ce3 | ||
|
|
00a3249c56 | ||
|
|
b51235d4fd | ||
|
|
d07f5be548 | ||
|
|
739ad0dc31 | ||
|
|
e8fd5174ec | ||
|
|
96726d22cd | ||
|
|
3330d297b1 | ||
|
|
e9ed2c0335 | ||
|
|
3cbc5b7a8d | ||
|
|
20bf822a48 | ||
|
|
14ee1ed075 | ||
|
|
4d48d517e0 | ||
|
|
ffdb3bc393 | ||
|
|
6e83a9a0b3 | ||
|
|
73cfa3c884 | ||
|
|
f2b6383cbb | ||
|
|
28d507f043 | ||
|
|
d508de9568 |
@@ -203,6 +203,16 @@ 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
|
||||||
|
|||||||
@@ -95,7 +95,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/>
|
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/> -<modules/Native/>
|
||||||
|
|
||||||
; 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]
|
||||||
|
|||||||
@@ -155,18 +155,6 @@ void InkHUD::LogoApplet::onShutdown()
|
|||||||
// This is then drawn by InkHUD::Events::onShutdown, with a blocking FULL update, after InkHUD's flash write is complete
|
// This is then drawn by InkHUD::Events::onShutdown, with a blocking FULL update, after InkHUD's flash write is complete
|
||||||
}
|
}
|
||||||
|
|
||||||
void InkHUD::LogoApplet::onApplyingChanges()
|
|
||||||
{
|
|
||||||
bringToForeground();
|
|
||||||
|
|
||||||
textLeft = "";
|
|
||||||
textRight = "";
|
|
||||||
textTitle = "Applying changes";
|
|
||||||
fontTitle = fontSmall;
|
|
||||||
|
|
||||||
inkhud->forceUpdate(Drivers::EInk::FAST, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InkHUD::LogoApplet::onReboot()
|
void InkHUD::LogoApplet::onReboot()
|
||||||
{
|
{
|
||||||
bringToForeground();
|
bringToForeground();
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class LogoApplet : public SystemApplet, public concurrency::OSThread
|
|||||||
void onBackground() override;
|
void onBackground() override;
|
||||||
void onShutdown() override;
|
void onShutdown() override;
|
||||||
void onReboot() override;
|
void onReboot() override;
|
||||||
void onApplyingChanges();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int32_t runOnce() override;
|
int32_t runOnce() override;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ enum MenuAction {
|
|||||||
STORE_CANNEDMESSAGE_SELECTION,
|
STORE_CANNEDMESSAGE_SELECTION,
|
||||||
SEND_CANNEDMESSAGE,
|
SEND_CANNEDMESSAGE,
|
||||||
SHUTDOWN,
|
SHUTDOWN,
|
||||||
BACK,
|
|
||||||
NEXT_TILE,
|
NEXT_TILE,
|
||||||
TOGGLE_BACKLIGHT,
|
TOGGLE_BACKLIGHT,
|
||||||
TOGGLE_GPS,
|
TOGGLE_GPS,
|
||||||
@@ -37,84 +36,6 @@ enum MenuAction {
|
|||||||
TOGGLE_NOTIFICATIONS,
|
TOGGLE_NOTIFICATIONS,
|
||||||
TOGGLE_INVERT_COLOR,
|
TOGGLE_INVERT_COLOR,
|
||||||
TOGGLE_12H_CLOCK,
|
TOGGLE_12H_CLOCK,
|
||||||
// Regions
|
|
||||||
SET_REGION_US,
|
|
||||||
SET_REGION_EU_868,
|
|
||||||
SET_REGION_EU_433,
|
|
||||||
SET_REGION_CN,
|
|
||||||
SET_REGION_JP,
|
|
||||||
SET_REGION_ANZ,
|
|
||||||
SET_REGION_KR,
|
|
||||||
SET_REGION_TW,
|
|
||||||
SET_REGION_RU,
|
|
||||||
SET_REGION_IN,
|
|
||||||
SET_REGION_NZ_865,
|
|
||||||
SET_REGION_TH,
|
|
||||||
SET_REGION_LORA_24,
|
|
||||||
SET_REGION_UA_433,
|
|
||||||
SET_REGION_UA_868,
|
|
||||||
SET_REGION_MY_433,
|
|
||||||
SET_REGION_MY_919,
|
|
||||||
SET_REGION_SG_923,
|
|
||||||
SET_REGION_PH_433,
|
|
||||||
SET_REGION_PH_868,
|
|
||||||
SET_REGION_PH_915,
|
|
||||||
SET_REGION_ANZ_433,
|
|
||||||
SET_REGION_KZ_433,
|
|
||||||
SET_REGION_KZ_863,
|
|
||||||
SET_REGION_NP_865,
|
|
||||||
SET_REGION_BR_902,
|
|
||||||
// Device Roles
|
|
||||||
SET_ROLE_CLIENT,
|
|
||||||
SET_ROLE_CLIENT_MUTE,
|
|
||||||
SET_ROLE_ROUTER,
|
|
||||||
SET_ROLE_REPEATER,
|
|
||||||
// Presets
|
|
||||||
SET_PRESET_LONG_SLOW,
|
|
||||||
SET_PRESET_LONG_MODERATE,
|
|
||||||
SET_PRESET_LONG_FAST,
|
|
||||||
SET_PRESET_MEDIUM_SLOW,
|
|
||||||
SET_PRESET_MEDIUM_FAST,
|
|
||||||
SET_PRESET_SHORT_SLOW,
|
|
||||||
SET_PRESET_SHORT_FAST,
|
|
||||||
SET_PRESET_SHORT_TURBO,
|
|
||||||
// Timezones
|
|
||||||
SET_TZ_US_HAWAII,
|
|
||||||
SET_TZ_US_ALASKA,
|
|
||||||
SET_TZ_US_PACIFIC,
|
|
||||||
SET_TZ_US_ARIZONA,
|
|
||||||
SET_TZ_US_MOUNTAIN,
|
|
||||||
SET_TZ_US_CENTRAL,
|
|
||||||
SET_TZ_US_EASTERN,
|
|
||||||
SET_TZ_BR_BRAZILIA,
|
|
||||||
SET_TZ_UTC,
|
|
||||||
SET_TZ_EU_WESTERN,
|
|
||||||
SET_TZ_EU_CENTRAL,
|
|
||||||
SET_TZ_EU_EASTERN,
|
|
||||||
SET_TZ_ASIA_KOLKATA,
|
|
||||||
SET_TZ_ASIA_HONG_KONG,
|
|
||||||
SET_TZ_AU_AWST,
|
|
||||||
SET_TZ_AU_ACST,
|
|
||||||
SET_TZ_AU_AEST,
|
|
||||||
SET_TZ_PACIFIC_NZ,
|
|
||||||
// Power
|
|
||||||
TOGGLE_POWER_SAVE,
|
|
||||||
CALIBRATE_ADC,
|
|
||||||
// Bluetooth
|
|
||||||
TOGGLE_BLUETOOTH,
|
|
||||||
TOGGLE_BLUETOOTH_PAIR_MODE,
|
|
||||||
// Channel
|
|
||||||
TOGGLE_CHANNEL_UPLINK,
|
|
||||||
TOGGLE_CHANNEL_DOWNLINK,
|
|
||||||
TOGGLE_CHANNEL_POSITION,
|
|
||||||
SET_CHANNEL_PRECISION,
|
|
||||||
// Display
|
|
||||||
TOGGLE_DISPLAY_UNITS,
|
|
||||||
// Network
|
|
||||||
TOGGLE_WIFI,
|
|
||||||
// Administration
|
|
||||||
RESET_NODEDB_ALL,
|
|
||||||
RESET_NODEDB_KEEP_FAVORITES,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace NicheGraphics::InkHUD
|
} // namespace NicheGraphics::InkHUD
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,6 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
|||||||
void onRender() override;
|
void onRender() override;
|
||||||
|
|
||||||
void show(Tile *t); // Open the menu, onto a user tile
|
void show(Tile *t); // Open the menu, onto a user tile
|
||||||
void setStartPage(MenuPage page);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Drivers::LatchingBacklight *backlight = nullptr; // Convenient access to the backlight singleton
|
Drivers::LatchingBacklight *backlight = nullptr; // Convenient access to the backlight singleton
|
||||||
@@ -57,7 +56,6 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
|||||||
void sendText(NodeNum dest, ChannelIndex channel, const char *message); // Send a text message to mesh
|
void sendText(NodeNum dest, ChannelIndex channel, const char *message); // Send a text message to mesh
|
||||||
void freeCannedMessageResources(); // Clear MenuApplet's canned message processing data
|
void freeCannedMessageResources(); // Clear MenuApplet's canned message processing data
|
||||||
|
|
||||||
MenuPage startPageOverride = MenuPage::ROOT;
|
|
||||||
MenuPage currentPage = MenuPage::ROOT;
|
MenuPage currentPage = MenuPage::ROOT;
|
||||||
MenuPage previousPage = MenuPage::EXIT;
|
MenuPage previousPage = MenuPage::EXIT;
|
||||||
uint8_t cursor = 0; // Which menu item is currently highlighted
|
uint8_t cursor = 0; // Which menu item is currently highlighted
|
||||||
@@ -65,15 +63,7 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
|||||||
|
|
||||||
uint16_t systemInfoPanelHeight = 0; // Need to know before we render
|
uint16_t systemInfoPanelHeight = 0; // Need to know before we render
|
||||||
|
|
||||||
std::vector<MenuItem> items; // MenuItems for the current page. Filled by ShowPage
|
std::vector<MenuItem> items; // MenuItems for the current page. Filled by ShowPage
|
||||||
std::vector<std::string> nodeConfigLabels; // Persistent labels for Node Config pages
|
|
||||||
uint8_t selectedChannelIndex = 0; // Currently selected LoRa channel (Node Config → Radio → Channel)
|
|
||||||
bool channelPositionEnabled = false;
|
|
||||||
bool gpsEnabled = false;
|
|
||||||
|
|
||||||
// Recents menu checkbox state (derived from settings.recentlyActiveSeconds)
|
|
||||||
static constexpr uint8_t RECENTS_COUNT = 6;
|
|
||||||
bool recentsSelected[RECENTS_COUNT] = {};
|
|
||||||
|
|
||||||
// Data for selecting and sending canned messages via the menu
|
// Data for selecting and sending canned messages via the menu
|
||||||
// Placed into a sub-class for organization only
|
// Placed into a sub-class for organization only
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ class MenuItem
|
|||||||
MenuAction action = NO_ACTION;
|
MenuAction action = NO_ACTION;
|
||||||
MenuPage nextPage = EXIT;
|
MenuPage nextPage = EXIT;
|
||||||
bool *checkState = nullptr;
|
bool *checkState = nullptr;
|
||||||
bool isHeader = false; // Non-selectable section label
|
|
||||||
|
|
||||||
// Various constructors, depending on the intended function of the item
|
// Various constructors, depending on the intended function of the item
|
||||||
|
|
||||||
@@ -41,12 +40,6 @@ class MenuItem
|
|||||||
: label(label), action(action), nextPage(nextPage), checkState(checkState)
|
: label(label), action(action), nextPage(nextPage), checkState(checkState)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
static MenuItem Header(const char *label)
|
|
||||||
{
|
|
||||||
MenuItem item(label, NO_ACTION, EXIT);
|
|
||||||
item.isHeader = true;
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace NicheGraphics::InkHUD
|
} // namespace NicheGraphics::InkHUD
|
||||||
|
|||||||
@@ -20,27 +20,10 @@ enum MenuPage : uint8_t {
|
|||||||
SEND,
|
SEND,
|
||||||
CANNEDMESSAGE_RECIPIENT, // Select destination for a canned message
|
CANNEDMESSAGE_RECIPIENT, // Select destination for a canned message
|
||||||
OPTIONS,
|
OPTIONS,
|
||||||
NODE_CONFIG,
|
|
||||||
NODE_CONFIG_LORA,
|
|
||||||
NODE_CONFIG_CHANNELS, // List of channels
|
|
||||||
NODE_CONFIG_CHANNEL_DETAIL, // Per-channel options
|
|
||||||
NODE_CONFIG_CHANNEL_PRECISION,
|
|
||||||
NODE_CONFIG_PRESET,
|
|
||||||
NODE_CONFIG_DEVICE,
|
|
||||||
NODE_CONFIG_DEVICE_ROLE,
|
|
||||||
NODE_CONFIG_POWER,
|
|
||||||
NODE_CONFIG_POWER_ADC_CAL,
|
|
||||||
NODE_CONFIG_NETWORK,
|
|
||||||
NODE_CONFIG_DISPLAY,
|
|
||||||
NODE_CONFIG_BLUETOOTH,
|
|
||||||
NODE_CONFIG_POSITION,
|
|
||||||
NODE_CONFIG_ADMIN_RESET,
|
|
||||||
TIMEZONE,
|
|
||||||
APPLETS,
|
APPLETS,
|
||||||
AUTOSHOW,
|
AUTOSHOW,
|
||||||
RECENTS, // Select length of "recentlyActiveSeconds"
|
RECENTS, // Select length of "recentlyActiveSeconds"
|
||||||
REGION,
|
EXIT, // Dismiss the menu applet
|
||||||
EXIT, // Dismiss the menu applet
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace NicheGraphics::InkHUD
|
} // namespace NicheGraphics::InkHUD
|
||||||
|
|||||||
@@ -10,37 +10,34 @@ using namespace NicheGraphics;
|
|||||||
|
|
||||||
InkHUD::TipsApplet::TipsApplet()
|
InkHUD::TipsApplet::TipsApplet()
|
||||||
{
|
{
|
||||||
bool needsRegion = (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET);
|
// Decide which tips (if any) should be shown to user after the boot screen
|
||||||
|
|
||||||
bool showTutorialTips = (settings->tips.firstBoot || needsRegion);
|
|
||||||
|
|
||||||
// Welcome screen
|
// Welcome screen
|
||||||
if (showTutorialTips)
|
if (settings->tips.firstBoot)
|
||||||
tipQueue.push_back(Tip::WELCOME);
|
tipQueue.push_back(Tip::WELCOME);
|
||||||
|
|
||||||
// Finish setup
|
// Antenna, region, timezone
|
||||||
if (needsRegion)
|
// Shown at boot if region not yet set
|
||||||
|
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET)
|
||||||
tipQueue.push_back(Tip::FINISH_SETUP);
|
tipQueue.push_back(Tip::FINISH_SETUP);
|
||||||
|
|
||||||
// Using the UI
|
|
||||||
if (showTutorialTips) {
|
|
||||||
tipQueue.push_back(Tip::CUSTOMIZATION);
|
|
||||||
tipQueue.push_back(Tip::BUTTONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown info
|
// Shutdown info
|
||||||
// Shown until user performs one valid shutdown
|
// Shown until user performs one valid shutdown
|
||||||
if (!settings->tips.safeShutdownSeen)
|
if (!settings->tips.safeShutdownSeen)
|
||||||
tipQueue.push_back(Tip::SAFE_SHUTDOWN);
|
tipQueue.push_back(Tip::SAFE_SHUTDOWN);
|
||||||
|
|
||||||
|
// Using the UI
|
||||||
|
if (settings->tips.firstBoot) {
|
||||||
|
tipQueue.push_back(Tip::CUSTOMIZATION);
|
||||||
|
tipQueue.push_back(Tip::BUTTONS);
|
||||||
|
}
|
||||||
|
|
||||||
// Catch an incorrect attempt at rotating display
|
// Catch an incorrect attempt at rotating display
|
||||||
if (config.display.flip_screen)
|
if (config.display.flip_screen)
|
||||||
tipQueue.push_back(Tip::ROTATION);
|
tipQueue.push_back(Tip::ROTATION);
|
||||||
|
|
||||||
// Region picker
|
// Applet is foreground immediately at boot, but is obscured by LogoApplet, which is also foreground
|
||||||
if (needsRegion)
|
// LogoApplet can be considered to have a higher Z-index, because it is placed before TipsApplet in the systemApplets vector
|
||||||
tipQueue.push_back(Tip::PICK_REGION);
|
|
||||||
|
|
||||||
if (!tipQueue.empty())
|
if (!tipQueue.empty())
|
||||||
bringToForeground();
|
bringToForeground();
|
||||||
}
|
}
|
||||||
@@ -54,109 +51,81 @@ void InkHUD::TipsApplet::onRender()
|
|||||||
|
|
||||||
case Tip::FINISH_SETUP: {
|
case Tip::FINISH_SETUP: {
|
||||||
setFont(fontMedium);
|
setFont(fontMedium);
|
||||||
const char *title = "Tip: Finish Setup";
|
printAt(0, 0, "Tip: Finish Setup");
|
||||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
|
||||||
printWrapped(0, 0, width(), title);
|
|
||||||
|
|
||||||
setFont(fontSmall);
|
setFont(fontSmall);
|
||||||
int16_t cursorY = h + fontSmall.lineHeight();
|
int16_t cursorY = fontMedium.lineHeight() * 1.5;
|
||||||
|
printAt(0, cursorY, "- connect antenna");
|
||||||
|
|
||||||
auto drawBullet = [&](const char *text) {
|
cursorY += fontSmall.lineHeight() * 1.2;
|
||||||
uint16_t bh = getWrappedTextHeight(0, width(), text);
|
printAt(0, cursorY, "- connect a client app");
|
||||||
printWrapped(0, cursorY, width(), text);
|
|
||||||
cursorY += bh + (fontSmall.lineHeight() / 3);
|
|
||||||
};
|
|
||||||
|
|
||||||
drawBullet("- connect antenna");
|
// Only if region not set
|
||||||
drawBullet("- connect a client app");
|
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||||
|
cursorY += fontSmall.lineHeight() * 1.2;
|
||||||
|
printAt(0, cursorY, "- set region");
|
||||||
|
}
|
||||||
|
|
||||||
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET)
|
// Only if tz not set
|
||||||
drawBullet("- set region");
|
if (!(*config.device.tzdef && config.device.tzdef[0] != 0)) {
|
||||||
|
cursorY += fontSmall.lineHeight() * 1.2;
|
||||||
|
printAt(0, cursorY, "- set timezone");
|
||||||
|
}
|
||||||
|
|
||||||
if (!(*config.device.tzdef && config.device.tzdef[0] != 0))
|
cursorY += fontSmall.lineHeight() * 1.5;
|
||||||
drawBullet("- set timezone");
|
printAt(0, cursorY, "More info at meshtastic.org");
|
||||||
|
|
||||||
cursorY += fontSmall.lineHeight() / 2;
|
|
||||||
drawBullet("More info at meshtastic.org");
|
|
||||||
|
|
||||||
|
setFont(fontSmall);
|
||||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Tip::PICK_REGION: {
|
|
||||||
setFont(fontMedium);
|
|
||||||
printAt(0, 0, "Set Region");
|
|
||||||
|
|
||||||
setFont(fontSmall);
|
|
||||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(), "Please select your LoRa region to complete setup.");
|
|
||||||
|
|
||||||
printAt(0, Y(1.0), "Press button to choose", LEFT, BOTTOM);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Tip::SAFE_SHUTDOWN: {
|
case Tip::SAFE_SHUTDOWN: {
|
||||||
setFont(fontMedium);
|
setFont(fontMedium);
|
||||||
|
printAt(0, 0, "Tip: Shutdown");
|
||||||
const char *title = "Tip: Shutdown";
|
|
||||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
|
||||||
printWrapped(0, 0, width(), title);
|
|
||||||
|
|
||||||
setFont(fontSmall);
|
setFont(fontSmall);
|
||||||
int16_t cursorY = h + fontSmall.lineHeight();
|
std::string shutdown;
|
||||||
|
shutdown += "Before removing power, please shut down from InkHUD menu, or a client app. \n";
|
||||||
const char *body = "Before removing power, please shut down from InkHUD menu, or a client app.\n\n"
|
shutdown += "\n";
|
||||||
"This ensures data is saved.";
|
shutdown += "This ensures data is saved.";
|
||||||
|
printWrapped(0, fontMedium.lineHeight() * 1.5, width(), shutdown);
|
||||||
uint16_t bodyH = getWrappedTextHeight(0, width(), body);
|
|
||||||
printWrapped(0, cursorY, width(), body);
|
|
||||||
cursorY += bodyH + (fontSmall.lineHeight() / 2);
|
|
||||||
|
|
||||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Tip::CUSTOMIZATION: {
|
case Tip::CUSTOMIZATION: {
|
||||||
setFont(fontMedium);
|
setFont(fontMedium);
|
||||||
|
printAt(0, 0, "Tip: Customization");
|
||||||
const char *title = "Tip: Customization";
|
|
||||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
|
||||||
printWrapped(0, 0, width(), title);
|
|
||||||
|
|
||||||
setFont(fontSmall);
|
setFont(fontSmall);
|
||||||
int16_t cursorY = h + fontSmall.lineHeight();
|
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||||
|
"Configure & control display with the InkHUD menu. Optional features, layout, rotation, and more.");
|
||||||
const char *body = "Configure & control display with the InkHUD menu. "
|
|
||||||
"Optional features, layout, rotation, and more.";
|
|
||||||
|
|
||||||
uint16_t bodyH = getWrappedTextHeight(0, width(), body);
|
|
||||||
printWrapped(0, cursorY, width(), body);
|
|
||||||
cursorY += bodyH + (fontSmall.lineHeight() / 2);
|
|
||||||
|
|
||||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Tip::BUTTONS: {
|
case Tip::BUTTONS: {
|
||||||
setFont(fontMedium);
|
setFont(fontMedium);
|
||||||
|
printAt(0, 0, "Tip: Buttons");
|
||||||
const char *title = "Tip: Buttons";
|
|
||||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
|
||||||
printWrapped(0, 0, width(), title);
|
|
||||||
|
|
||||||
setFont(fontSmall);
|
setFont(fontSmall);
|
||||||
int16_t cursorY = h + fontSmall.lineHeight();
|
int16_t cursorY = fontMedium.lineHeight() * 1.5;
|
||||||
|
|
||||||
auto drawBullet = [&](const char *text) {
|
|
||||||
uint16_t bh = getWrappedTextHeight(0, width(), text);
|
|
||||||
printWrapped(0, cursorY, width(), text);
|
|
||||||
cursorY += bh + (fontSmall.lineHeight() / 3);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!settings->joystick.enabled) {
|
if (!settings->joystick.enabled) {
|
||||||
drawBullet("User Button");
|
printAt(0, cursorY, "User Button");
|
||||||
drawBullet("- short press: next");
|
cursorY += fontSmall.lineHeight() * 1.2;
|
||||||
drawBullet("- long press: select or open menu");
|
printAt(0, cursorY, "- short press: next");
|
||||||
|
cursorY += fontSmall.lineHeight() * 1.2;
|
||||||
|
printAt(0, cursorY, "- long press: select / open menu");
|
||||||
} else {
|
} else {
|
||||||
drawBullet("Joystick");
|
printAt(0, cursorY, "Joystick");
|
||||||
drawBullet("- press: open menu or select");
|
cursorY += fontSmall.lineHeight() * 1.2;
|
||||||
drawBullet("Exit Button");
|
printAt(0, cursorY, "- open menu / select");
|
||||||
drawBullet("- press: switch tile or close menu");
|
cursorY += fontSmall.lineHeight() * 1.5;
|
||||||
|
printAt(0, cursorY, "Exit Button");
|
||||||
|
cursorY += fontSmall.lineHeight() * 1.2;
|
||||||
|
printAt(0, cursorY, "- switch tile / close menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||||
@@ -164,21 +133,12 @@ void InkHUD::TipsApplet::onRender()
|
|||||||
|
|
||||||
case Tip::ROTATION: {
|
case Tip::ROTATION: {
|
||||||
setFont(fontMedium);
|
setFont(fontMedium);
|
||||||
|
printAt(0, 0, "Tip: Rotation");
|
||||||
const char *title = "Tip: Rotation";
|
|
||||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
|
||||||
printWrapped(0, 0, width(), title);
|
|
||||||
|
|
||||||
setFont(fontSmall);
|
setFont(fontSmall);
|
||||||
if (!settings->joystick.enabled) {
|
if (!settings->joystick.enabled) {
|
||||||
int16_t cursorY = h + fontSmall.lineHeight();
|
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||||
|
"To rotate the display, use the InkHUD menu. Long-press the user button > Options > Rotate.");
|
||||||
const char *body = "To rotate the display, use the InkHUD menu. "
|
|
||||||
"Long-press the user button > Options > Rotate.";
|
|
||||||
|
|
||||||
uint16_t bh = getWrappedTextHeight(0, width(), body);
|
|
||||||
printWrapped(0, cursorY, width(), body);
|
|
||||||
cursorY += bh + (fontSmall.lineHeight() / 2);
|
|
||||||
} else {
|
} else {
|
||||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||||
"To rotate the display, use the InkHUD menu. Press the user button > Options > Rotate.");
|
"To rotate the display, use the InkHUD menu. Press the user button > Options > Rotate.");
|
||||||
@@ -199,15 +159,12 @@ void InkHUD::TipsApplet::renderWelcome()
|
|||||||
{
|
{
|
||||||
uint16_t padW = X(0.05);
|
uint16_t padW = X(0.05);
|
||||||
|
|
||||||
// Detect portrait orientation
|
|
||||||
bool portrait = height() > width();
|
|
||||||
|
|
||||||
// Block 1 - logo & title
|
// Block 1 - logo & title
|
||||||
// ========================
|
// ========================
|
||||||
|
|
||||||
// Logo size
|
// Logo size
|
||||||
uint16_t logoWLimit = portrait ? X(0.5) : X(0.3);
|
uint16_t logoWLimit = X(0.3);
|
||||||
uint16_t logoHLimit = portrait ? Y(0.25) : Y(0.3);
|
uint16_t logoHLimit = Y(0.3);
|
||||||
uint16_t logoW = getLogoWidth(logoWLimit, logoHLimit);
|
uint16_t logoW = getLogoWidth(logoWLimit, logoHLimit);
|
||||||
uint16_t logoH = getLogoHeight(logoWLimit, logoHLimit);
|
uint16_t logoH = getLogoHeight(logoWLimit, logoHLimit);
|
||||||
|
|
||||||
@@ -220,7 +177,7 @@ void InkHUD::TipsApplet::renderWelcome()
|
|||||||
|
|
||||||
// Center the block
|
// Center the block
|
||||||
// Desired effect: equal margin from display edge for logo left and title right
|
// Desired effect: equal margin from display edge for logo left and title right
|
||||||
int16_t block1Y = portrait ? Y(0.2) : Y(0.3);
|
int16_t block1Y = Y(0.3);
|
||||||
int16_t block1CX = X(0.5) + (logoW / 2) - (titleW / 2);
|
int16_t block1CX = X(0.5) + (logoW / 2) - (titleW / 2);
|
||||||
int16_t logoCX = block1CX - (logoW / 2) - (padW / 2);
|
int16_t logoCX = block1CX - (logoW / 2) - (padW / 2);
|
||||||
int16_t titleCX = block1CX + (titleW / 2) + (padW / 2);
|
int16_t titleCX = block1CX + (titleW / 2) + (padW / 2);
|
||||||
@@ -235,7 +192,7 @@ void InkHUD::TipsApplet::renderWelcome()
|
|||||||
std::string subtitle = "InkHUD";
|
std::string subtitle = "InkHUD";
|
||||||
if (width() >= 200)
|
if (width() >= 200)
|
||||||
subtitle += " - A Heads-Up Display"; // Future proofing: narrower for tiny display
|
subtitle += " - A Heads-Up Display"; // Future proofing: narrower for tiny display
|
||||||
printAt(X(0.5), portrait ? Y(0.45) : Y(0.6), subtitle, CENTER, MIDDLE);
|
printAt(X(0.5), Y(0.6), subtitle, CENTER, MIDDLE);
|
||||||
|
|
||||||
// Block 3 - press to continue
|
// Block 3 - press to continue
|
||||||
// ============================
|
// ============================
|
||||||
@@ -267,37 +224,26 @@ void InkHUD::TipsApplet::onBackground()
|
|||||||
// While our SystemApplet::handleInput flag is true
|
// While our SystemApplet::handleInput flag is true
|
||||||
void InkHUD::TipsApplet::onButtonShortPress()
|
void InkHUD::TipsApplet::onButtonShortPress()
|
||||||
{
|
{
|
||||||
bool needsRegion = (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET);
|
|
||||||
// If we're prompting the user to pick a region, hand off to the menu
|
|
||||||
if (!tipQueue.empty() && tipQueue.front() == Tip::PICK_REGION) {
|
|
||||||
tipQueue.pop_front();
|
|
||||||
|
|
||||||
// Signal InkHUD to open the menu on Region page
|
|
||||||
inkhud->forceRegionMenu = true;
|
|
||||||
|
|
||||||
// Close tips and open menu
|
|
||||||
sendToBackground();
|
|
||||||
inkhud->openMenu();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Consume current tip
|
|
||||||
tipQueue.pop_front();
|
tipQueue.pop_front();
|
||||||
|
|
||||||
// All tips done
|
// All tips done
|
||||||
if (tipQueue.empty()) {
|
if (tipQueue.empty()) {
|
||||||
// Record that user has now seen the "tutorial" set of tips
|
// Record that user has now seen the "tutorial" set of tips
|
||||||
// Don't show them on subsequent boots
|
// Don't show them on subsequent boots
|
||||||
if (settings->tips.firstBoot && !needsRegion) {
|
if (settings->tips.firstBoot) {
|
||||||
settings->tips.firstBoot = false;
|
settings->tips.firstBoot = false;
|
||||||
inkhud->persistence->saveSettings();
|
inkhud->persistence->saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close applet and clean the screen
|
// Close applet, and full refresh to clean the screen
|
||||||
|
// Need to force update, because our request would be ignored otherwise, as we are now background
|
||||||
sendToBackground();
|
sendToBackground();
|
||||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||||
} else {
|
|
||||||
requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// More tips left
|
||||||
|
else
|
||||||
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions the same as the user button in this instance
|
// Functions the same as the user button in this instance
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class TipsApplet : public SystemApplet
|
|||||||
enum class Tip {
|
enum class Tip {
|
||||||
WELCOME,
|
WELCOME,
|
||||||
FINISH_SETUP,
|
FINISH_SETUP,
|
||||||
PICK_REGION,
|
|
||||||
SAFE_SHUTDOWN,
|
SAFE_SHUTDOWN,
|
||||||
CUSTOMIZATION,
|
CUSTOMIZATION,
|
||||||
BUTTONS,
|
BUTTONS,
|
||||||
|
|||||||
@@ -276,15 +276,6 @@ int InkHUD::Events::beforeDeepSleep(void *unused)
|
|||||||
return 0; // We agree: deep sleep now
|
return 0; // We agree: deep sleep now
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display an intermediate screen while configuration changes are applied
|
|
||||||
void InkHUD::Events::applyingChanges()
|
|
||||||
{
|
|
||||||
// Bring the logo applet forward with a temporary message
|
|
||||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
|
||||||
sa->onApplyingChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback for rebootObserver
|
// Callback for rebootObserver
|
||||||
// Same as shutdown, without drawing the logoApplet
|
// Same as shutdown, without drawing the logoApplet
|
||||||
// Makes sure we don't lose message history / InkHUD config
|
// Makes sure we don't lose message history / InkHUD config
|
||||||
|
|||||||
@@ -29,13 +29,12 @@ class Events
|
|||||||
|
|
||||||
void onButtonShort(); // User button: short press
|
void onButtonShort(); // User button: short press
|
||||||
void onButtonLong(); // User button: long press
|
void onButtonLong(); // User button: long press
|
||||||
void applyingChanges();
|
void onExitShort(); // Exit button: short press
|
||||||
void onExitShort(); // Exit button: short press
|
void onExitLong(); // Exit button: long press
|
||||||
void onExitLong(); // Exit button: long press
|
void onNavUp(); // Navigate up
|
||||||
void onNavUp(); // Navigate up
|
void onNavDown(); // Navigate down
|
||||||
void onNavDown(); // Navigate down
|
void onNavLeft(); // Navigate left
|
||||||
void onNavLeft(); // Navigate left
|
void onNavRight(); // Navigate right
|
||||||
void onNavRight(); // Navigate right
|
|
||||||
|
|
||||||
int beforeDeepSleep(void *unused); // Prepare for shutdown
|
int beforeDeepSleep(void *unused); // Prepare for shutdown
|
||||||
int beforeReboot(void *unused); // Prepare for reboot
|
int beforeReboot(void *unused); // Prepare for reboot
|
||||||
|
|||||||
@@ -53,13 +53,6 @@ void InkHUD::InkHUD::addApplet(const char *name, Applet *a, bool defaultActive,
|
|||||||
windowManager->addApplet(name, a, defaultActive, defaultAutoshow, onTile);
|
windowManager->addApplet(name, a, defaultActive, defaultAutoshow, onTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InkHUD::InkHUD::notifyApplyingChanges()
|
|
||||||
{
|
|
||||||
if (events) {
|
|
||||||
events->applyingChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start InkHUD!
|
// Start InkHUD!
|
||||||
// Call this only after you have configured InkHUD
|
// Call this only after you have configured InkHUD
|
||||||
void InkHUD::InkHUD::begin()
|
void InkHUD::InkHUD::begin()
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ class InkHUD
|
|||||||
void setDriver(Drivers::EInk *driver);
|
void setDriver(Drivers::EInk *driver);
|
||||||
void setDisplayResilience(uint8_t fastPerFull = 5, float stressMultiplier = 2.0);
|
void setDisplayResilience(uint8_t fastPerFull = 5, float stressMultiplier = 2.0);
|
||||||
void addApplet(const char *name, Applet *a, bool defaultActive = false, bool defaultAutoshow = false, uint8_t onTile = -1);
|
void addApplet(const char *name, Applet *a, bool defaultActive = false, bool defaultAutoshow = false, uint8_t onTile = -1);
|
||||||
void notifyApplyingChanges();
|
|
||||||
|
|
||||||
void begin();
|
void begin();
|
||||||
|
|
||||||
@@ -77,9 +76,6 @@ class InkHUD
|
|||||||
void rotateJoystick(uint8_t angle = 1); // rotate 90 deg by default
|
void rotateJoystick(uint8_t angle = 1); // rotate 90 deg by default
|
||||||
void toggleBatteryIcon();
|
void toggleBatteryIcon();
|
||||||
|
|
||||||
// Used by TipsApplet to force menu to start on Region selection
|
|
||||||
bool forceRegionMenu = false;
|
|
||||||
|
|
||||||
// Updating the display
|
// Updating the display
|
||||||
// - called by various InkHUD components
|
// - called by various InkHUD components
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ class SystemApplet : public Applet
|
|||||||
bool lockRequests = false; // - prevent other applets from triggering display updates
|
bool lockRequests = false; // - prevent other applets from triggering display updates
|
||||||
|
|
||||||
virtual void onReboot() { onShutdown(); } // - handle reboot specially
|
virtual void onReboot() { onShutdown(); } // - handle reboot specially
|
||||||
virtual void onApplyingChanges() {}
|
|
||||||
|
|
||||||
// Other system applets may take precedence over our own system applet though
|
// Other system applets may take precedence over our own system applet though
|
||||||
// The order an applet is passed to WindowManager::addSystemApplet determines this hierarchy (added earlier = higher rank)
|
// The order an applet is passed to WindowManager::addSystemApplet determines this hierarchy (added earlier = higher rank)
|
||||||
|
|||||||
@@ -145,6 +145,18 @@ bool NextHopRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
|
|||||||
tosend->hop_start -= (tosend->hop_limit - 2);
|
tosend->hop_start -= (tosend->hop_limit - 2);
|
||||||
tosend->hop_limit = 2;
|
tosend->hop_limit = 2;
|
||||||
}
|
}
|
||||||
|
#elif ARCH_PORTDUINO
|
||||||
|
if (tosend->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
|
||||||
|
portduino_config.nohop_ports.size()) {
|
||||||
|
for (const auto &port : portduino_config.nohop_ports) {
|
||||||
|
if (port == tosend->decoded.portnum) {
|
||||||
|
LOG_DEBUG("0-hopping portnum %u", tosend->decoded.portnum);
|
||||||
|
tosend->hop_start -= tosend->hop_limit;
|
||||||
|
tosend->hop_limit = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (p->next_hop == NO_NEXT_HOP_PREFERENCE) {
|
if (p->next_hop == NO_NEXT_HOP_PREFERENCE) {
|
||||||
|
|||||||
@@ -326,9 +326,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 =
|
std::string out = DEBUG_PORT.mt_sprintf(
|
||||||
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,
|
"%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,
|
||||||
p->from, p->to, p->transport_mechanism, p->want_ack, p->hop_limit, p->channel);
|
p->to, p->transport_mechanism, p->want_ack, p->hop_limit, p->hop_start, 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;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "Default.h"
|
#include "Default.h"
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
|
#include "modules/Native/StoreForwardPlusPlus.h"
|
||||||
#include "platform/portduino/PortduinoGlue.h"
|
#include "platform/portduino/PortduinoGlue.h"
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
|
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
|
||||||
@@ -359,6 +360,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
|||||||
abortSendAndNak(encodeResult, p);
|
abortSendAndNak(encodeResult, p);
|
||||||
return encodeResult; // FIXME - this isn't a valid ErrorCode
|
return encodeResult; // FIXME - this isn't a valid ErrorCode
|
||||||
}
|
}
|
||||||
|
#if ARCH_PORTDUINO
|
||||||
|
if (p_decoded->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP &&
|
||||||
|
(p->from == 0 || p->from == nodeDB->getNodeNum()) && storeForwardPlusPlusModule && portduino_config.sfpp_enabled) {
|
||||||
|
storeForwardPlusPlusModule->handleEncrypted(p_decoded, p);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if !MESHTASTIC_EXCLUDE_MQTT
|
#if !MESHTASTIC_EXCLUDE_MQTT
|
||||||
// Only publish to MQTT if we're the original transmitter of the packet
|
// Only publish to MQTT if we're the original transmitter of the packet
|
||||||
if (moduleConfig.mqtt.enabled && isFromUs(p) && mqtt) {
|
if (moduleConfig.mqtt.enabled && isFromUs(p) && mqtt) {
|
||||||
@@ -735,6 +742,22 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
|||||||
cancelSending(p->from, p->id);
|
cancelSending(p->from, p->id);
|
||||||
skipHandle = true;
|
skipHandle = true;
|
||||||
}
|
}
|
||||||
|
#if ARCH_PORTDUINO
|
||||||
|
if (portduino_config.whitelist_enabled) {
|
||||||
|
bool allowed = false;
|
||||||
|
for (const auto &port : portduino_config.whitelist_ports) {
|
||||||
|
if (port == p->decoded.portnum) {
|
||||||
|
allowed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allowed) {
|
||||||
|
LOG_DEBUG("Dropping packet not on Portduino Whitelist");
|
||||||
|
cancelSending(p->from, p->id);
|
||||||
|
skipHandle = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
printPacket("packet decoding failed or skipped (no PSK?)", p);
|
printPacket("packet decoding failed or skipped (no PSK?)", p);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
#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"
|
||||||
@@ -243,6 +244,11 @@ void setupModules()
|
|||||||
#endif
|
#endif
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
new HostMetricsModule();
|
new HostMetricsModule();
|
||||||
|
#if SFPP_ENABLED
|
||||||
|
if (portduino_config.sfpp_enabled) {
|
||||||
|
storeForwardPlusPlusModule = new StoreForwardPlusPlusModule();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if HAS_TELEMETRY
|
#if HAS_TELEMETRY
|
||||||
new DeviceTelemetryModule();
|
new DeviceTelemetryModule();
|
||||||
|
|||||||
2113
src/modules/Native/StoreForwardPlusPlus.cpp
Normal file
2113
src/modules/Native/StoreForwardPlusPlus.cpp
Normal file
File diff suppressed because it is too large
Load Diff
310
src/modules/Native/StoreForwardPlusPlus.h
Normal file
310
src/modules/Native/StoreForwardPlusPlus.h
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEncrypted(const meshtastic_MeshPacket *, const meshtastic_MeshPacket *);
|
||||||
|
|
||||||
|
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 *getLinkFromMessageHashStmt;
|
||||||
|
sqlite3_stmt *getHashFromRootStmt;
|
||||||
|
sqlite3_stmt *addRootToMappingsStmt;
|
||||||
|
sqlite3_stmt *getRootFromChannelHashStmt;
|
||||||
|
sqlite3_stmt *getFullRootHashStmt;
|
||||||
|
sqlite3_stmt *setChainCountStmt;
|
||||||
|
sqlite3_stmt *getChainCountStmt;
|
||||||
|
sqlite3_stmt *getScratchCountStmt;
|
||||||
|
sqlite3_stmt *getRootCanonScratchCountStmt;
|
||||||
|
sqlite3_stmt *pruneScratchQueueStmt;
|
||||||
|
sqlite3_stmt *trimOldestLinkStmt;
|
||||||
|
sqlite3_stmt *maybeAddPeerStmt;
|
||||||
|
sqlite3_stmt *getPeerStmt;
|
||||||
|
sqlite3_stmt *updatePeerStmt;
|
||||||
|
sqlite3_stmt *clearChainStmt;
|
||||||
|
sqlite3_stmt *canon_scratch_insert_stmt;
|
||||||
|
sqlite3_stmt *getCanonScratchCountStmt;
|
||||||
|
sqlite3_stmt *getCanonScratchStmt;
|
||||||
|
sqlite3_stmt *removeCanonScratch;
|
||||||
|
sqlite3_stmt *clearCanonScratchStmt;
|
||||||
|
|
||||||
|
// 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 &);
|
||||||
|
|
||||||
|
// 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 *);
|
||||||
|
|
||||||
|
// retrieves a link object from the canonical chain database given a commit hash
|
||||||
|
link_object getLink(uint8_t *, size_t);
|
||||||
|
|
||||||
|
// retrieves a link object from the canonical chain database given a message hash
|
||||||
|
link_object getLinkFromMessageHash(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);
|
||||||
|
|
||||||
|
// get the chain count for the given root hash
|
||||||
|
uint32_t getChainCount(uint8_t *, size_t);
|
||||||
|
|
||||||
|
// get the scratch count for the given root hash
|
||||||
|
uint32_t getScratchCount(uint8_t *, size_t);
|
||||||
|
|
||||||
|
// get the canon scratch count for the given root hash
|
||||||
|
uint32_t getCanonScratchCount(uint8_t *, size_t);
|
||||||
|
|
||||||
|
link_object getLinkFromPositionFromTip(uint32_t, uint8_t *, size_t);
|
||||||
|
|
||||||
|
void pruneScratchQueue();
|
||||||
|
|
||||||
|
void trimOldestLink(uint8_t *, size_t);
|
||||||
|
|
||||||
|
void clearChain(uint8_t *, size_t);
|
||||||
|
|
||||||
|
void recalculateMessageHash(link_object &);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
void maybeMoveFromCanonScratch(uint8_t *, size_t);
|
||||||
|
|
||||||
|
void addToCanonScratch(link_object &);
|
||||||
|
|
||||||
|
link_object getfromCanonScratch(uint8_t *, size_t);
|
||||||
|
void removeFromCanonScratch(uint8_t *, size_t);
|
||||||
|
|
||||||
|
void clearCanonScratch(uint8_t *, size_t, uint32_t);
|
||||||
|
|
||||||
|
bool isInCanonScratch(uint8_t *, size_t);
|
||||||
|
|
||||||
|
void logLinkObject(link_object &);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
bool did_announce_last = false;
|
||||||
|
|
||||||
|
uint32_t texts_rebroadcast = 0;
|
||||||
|
uint32_t links_speculated = 0;
|
||||||
|
uint32_t canon_announces = 0;
|
||||||
|
uint32_t links_requested = 0;
|
||||||
|
uint32_t links_provided = 0;
|
||||||
|
uint32_t links_added = 0;
|
||||||
|
uint32_t links_from_canon_scratch = 0;
|
||||||
|
uint32_t links_from_scratch = 0;
|
||||||
|
uint32_t split_links_sent = 0;
|
||||||
|
uint32_t split_links_received = 0;
|
||||||
|
uint32_t links_pruned = 0;
|
||||||
|
uint32_t scratch_timed_out = 0;
|
||||||
|
uint32_t sent_from_scratch = 0;
|
||||||
|
uint32_t received_from_scratch = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern StoreForwardPlusPlusModule *storeForwardPlusPlusModule;
|
||||||
|
#endif
|
||||||
@@ -177,6 +177,7 @@ void portduinoSetup()
|
|||||||
|
|
||||||
if (portduino_config.force_simradio == true) {
|
if (portduino_config.force_simradio == true) {
|
||||||
portduino_config.lora_module = use_simradio;
|
portduino_config.lora_module = use_simradio;
|
||||||
|
portduino_config.sfpp_enabled = false;
|
||||||
} else if (configPath != nullptr) {
|
} else if (configPath != nullptr) {
|
||||||
if (loadConfig(configPath)) {
|
if (loadConfig(configPath)) {
|
||||||
if (!yamlOnly)
|
if (!yamlOnly)
|
||||||
@@ -859,6 +860,28 @@ 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["Routing"]) {
|
||||||
|
if (yamlConfig["Routing"]["WhitelistPorts"]) {
|
||||||
|
portduino_config.whitelist_ports = (yamlConfig["Routing"]["WhitelistPorts"]).as<std::vector<int>>();
|
||||||
|
if (portduino_config.whitelist_ports.size() > 0) {
|
||||||
|
portduino_config.whitelist_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (yamlConfig["Routing"]["NoHopPorts"]) {
|
||||||
|
portduino_config.nohop_ports = (yamlConfig["Routing"]["NoHopPorts"]).as<std::vector<int>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
||||||
|
|||||||
@@ -171,6 +171,26 @@ 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
|
||||||
|
|
||||||
|
// Routing
|
||||||
|
bool whitelist_enabled = false;
|
||||||
|
std::vector<int> whitelist_ports = {};
|
||||||
|
std::vector<int> nohop_ports = {};
|
||||||
|
|
||||||
// General
|
// General
|
||||||
std::string mac_address = "";
|
std::string mac_address = "";
|
||||||
bool mac_address_explicit = false;
|
bool mac_address_explicit = false;
|
||||||
@@ -504,6 +524,29 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routing
|
||||||
|
if (whitelist_enabled || nohop_ports.size() > 0) {
|
||||||
|
out << YAML::Key << "Routing" << YAML::Value << YAML::BeginMap;
|
||||||
|
out << YAML::Key << "WhitelistPorts" << YAML::Value << whitelist_ports;
|
||||||
|
out << YAML::Key << "NoHopPorts" << YAML::Value << nohop_ports;
|
||||||
|
out << YAML::EndMap; // Routing
|
||||||
|
}
|
||||||
|
|
||||||
// General
|
// General
|
||||||
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
|
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
|
||||||
if (config_directory != "")
|
if (config_directory != "")
|
||||||
|
|||||||
@@ -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}
|
build_src_filter = ${portduino_base.build_src_filter} +<modules/Native/>
|
||||||
|
|
||||||
[env:native]
|
[env:native]
|
||||||
extends = native_base
|
extends = native_base
|
||||||
@@ -20,6 +20,7 @@ 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
|
||||||
@@ -46,6 +47,7 @@ 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}
|
||||||
|
|
||||||
@@ -75,6 +77,7 @@ 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}
|
||||||
|
|
||||||
@@ -108,6 +111,7 @@ 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]
|
||||||
|
|||||||
Reference in New Issue
Block a user