Compare commits

..

5 Commits

Author SHA1 Message Date
vidplace7
1324faa68c Remove YardSale channel 2025-07-09 00:00:36 -04:00
todd-herbert
856ab293dc Open Sauce Demo: custom applet selection (#7219) 2025-07-09 00:00:36 -04:00
rcarteraz
6165449cee add 800x480 2025-07-09 00:00:36 -04:00
rcarteraz
3896086469 add mui logo images 2025-07-09 00:00:36 -04:00
vidplace7
67ff575279 Open Sauce 2025 userPrefs 2025-07-09 00:00:36 -04:00
29 changed files with 267 additions and 316 deletions

BIN
branding/logo_320x240.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
branding/logo_320x480.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
branding/logo_480x320.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
branding/logo_480x480.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
branding/logo_800x480.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -129,7 +129,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit MCP9808 packageName=adafruit/library/Adafruit MCP9808 Library
adafruit/Adafruit MCP9808 Library@2.0.2
# renovate: datasource=custom.pio depName=Adafruit INA260 packageName=adafruit/library/Adafruit INA260 Library
adafruit/Adafruit INA260 Library@1.5.3
adafruit/Adafruit INA260 Library@1.5.2
# renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219
adafruit/Adafruit INA219@1.2.3
# renovate: datasource=custom.pio depName=Adafruit PM25 AQI Sensor packageName=adafruit/library/Adafruit PM25 AQI Sensor

View File

@@ -171,7 +171,7 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
}
// Called to trigger a banner with custom message and duration
void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback)
void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(int)> bannerCallback)
{
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
@@ -196,6 +196,7 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct
void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits,
std::function<void(uint32_t)> bannerCallback)
{
LOG_WARN("Show Number Picker");
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif
@@ -1329,7 +1330,7 @@ int Screen::handleInputEvent(const InputEvent *event)
setFastFramerate(); // Draw ASAP
#endif
if (NotificationRenderer::isOverlayBannerShowing()) {
NotificationRenderer::inEvent = *event;
NotificationRenderer::inEvent = event->inputEvent;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
setFastFramerate(); // Draw ASAP

View File

@@ -92,7 +92,6 @@ class Screen
#include "commands.h"
#include "concurrency/LockGuard.h"
#include "concurrency/OSThread.h"
#include "graphics/draw/MenuHandler.h"
#include "input/InputBroker.h"
#include "mesh/MeshModule.h"
#include "modules/AdminModule.h"
@@ -309,15 +308,9 @@ class Screen : public concurrency::OSThread
void showSimpleBanner(const char *message, uint32_t durationMs = 0);
void showOverlayBanner(BannerOverlayOptions);
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback);
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(int)> bannerCallback);
void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback);
void requestMenu(graphics::menuHandler::screenMenus menuToShow)
{
graphics::menuHandler::menuQueue = menuToShow;
runNow();
}
void startFirmwareUpdateScreen()
{
ScreenCmd cmd;

View File

@@ -13,7 +13,6 @@
#include "main.h"
#include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h"
#include "modules/KeyVerificationModule.h"
extern uint16_t TFT_MESH;
@@ -238,25 +237,27 @@ void menuHandler::clockMenu()
void menuHandler::messageResponseMenu()
{
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
int options = 3;
static const char **optionsArrayPtr;
int options;
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3 };
if (kb_found) {
optionsArray[options] = "Reply via Freetext";
optionsEnumArray[options++] = Freetext;
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext"};
optionsArrayPtr = optionsArray;
options = 4;
} else {
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset"};
optionsArrayPtr = optionsArray;
options = 3;
}
#ifdef HAS_I2S
optionsArray[options] = "Read Aloud";
optionsEnumArray[options++] = Aloud;
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext", "Read Aloud"};
optionsArrayPtr = optionsArray;
options = 5;
#endif
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsArrayPtr = optionsArrayPtr;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Dismiss) {
@@ -275,7 +276,7 @@ void menuHandler::messageResponseMenu()
}
}
#ifdef HAS_I2S
else if (selected == Aloud) {
else if (selected == 4) {
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
const char *msg = reinterpret_cast<const char *>(mp.decoded.payload.bytes);
@@ -288,10 +289,10 @@ void menuHandler::messageResponseMenu()
void menuHandler::homeBaseMenu()
{
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep, enumEnd };
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
static const char *optionsArray[6] = {"Back"};
static int optionsEnumArray[6] = {Back};
int options = 1;
#ifdef PIN_EINK_EN
@@ -353,9 +354,9 @@ void menuHandler::systemBaseMenu()
hasSupportBrightness = true;
#endif
enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test };
static const char *optionsArray[7] = {"Back"};
static int optionsEnumArray[7] = {Back};
int options = 1;
optionsArray[options] = "Reboot";
@@ -418,22 +419,21 @@ void menuHandler::systemBaseMenu()
void menuHandler::favoriteBaseMenu()
{
enum optionsNumbers { Back, Preset, Freetext, Remove, enumEnd };
static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
static int optionsEnumArray[enumEnd] = {Back, Preset};
int options = 2;
int options;
static const char **optionsArrayPtr;
if (kb_found) {
optionsArray[options] = "New Freetext Msg";
optionsEnumArray[options++] = Freetext;
static const char *optionsArray[] = {"Back", "New Preset Msg", "New Freetext Msg", "Remove Favorite"};
optionsArrayPtr = optionsArray;
options = 4;
} else {
static const char *optionsArray[] = {"Back", "New Preset Msg", "Remove Favorite"};
optionsArrayPtr = optionsArray;
options = 3;
}
optionsArray[options] = "Remove Favorite";
optionsEnumArray[options++] = Remove;
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Favorites Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsArrayPtr = optionsArrayPtr;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) {
@@ -450,29 +450,34 @@ void menuHandler::favoriteBaseMenu()
void menuHandler::positionBaseMenu()
{
enum optionsNumbers { Back, GPSToggle, CompassMenu, CompassCalibrate, enumEnd };
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "Compass"};
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, CompassMenu};
int options = 3;
int options;
static const char **optionsArrayPtr;
static const char *optionsArray[] = {"Back", "GPS Toggle", "Compass"};
static const char *optionsArrayCalibrate[] = {"Back", "GPS Toggle", "Compass", "Compass Calibrate"};
if (accelerometerThread) {
optionsArray[options] = "Compass Calibrate";
optionsEnumArray[options++] = CompassCalibrate;
optionsArrayPtr = optionsArrayCalibrate;
options = 4;
} else {
optionsArrayPtr = optionsArray;
options = 3;
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Position Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsArrayPtr = optionsArrayPtr;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == GPSToggle) {
if (selected == 1) {
#if MESHTASTIC_EXCLUDE_GPS
menuQueue = menu_none;
#else
menuQueue = gps_toggle_menu;
screen->runNow();
} else if (selected == CompassMenu) {
#endif
} else if (selected == 2) {
menuQueue = compass_point_north_menu;
screen->runNow();
} else if (selected == CompassCalibrate) {
} else if (selected == 3) {
accelerometerThread->calibrate(30);
}
};
@@ -481,20 +486,16 @@ void menuHandler::positionBaseMenu()
void menuHandler::nodeListMenu()
{
enum optionsNumbers { Back, Favorite, Verify, Reset };
static const char *optionsArray[] = {"Back", "Add Favorite", "Key Verification", "Reset NodeDB"};
static const char *optionsArray[] = {"Back", "Add Favorite", "Reset NodeDB"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Node Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 4;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Favorite) {
if (selected == 1) {
menuQueue = add_favorite;
screen->runNow();
} else if (selected == Verify) {
menuQueue = key_verification_init;
screen->runNow();
} else if (selected == Reset) {
} else if (selected == 2) {
menuQueue = reset_node_db_menu;
screen->runNow();
}
@@ -522,7 +523,6 @@ void menuHandler::resetNodeDBMenu()
void menuHandler::compassNorthMenu()
{
enum optionsNumbers { Back, Dynamic, Fixed, Freeze };
static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "North Directions?";
@@ -530,28 +530,28 @@ void menuHandler::compassNorthMenu()
bannerOptions.optionsCount = 4;
bannerOptions.InitialSelected = uiconfig.compass_mode + 1;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Dynamic) {
if (selected == 1) {
if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) {
uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
} else if (selected == Fixed) {
} else if (selected == 2) {
if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) {
uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
} else if (selected == Freeze) {
} else if (selected == 3) {
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
} else if (selected == Back) {
} else if (selected == 0) {
menuQueue = position_base_menu;
screen->runNow();
}
@@ -562,7 +562,6 @@ void menuHandler::compassNorthMenu()
#if !MESHTASTIC_EXCLUDE_GPS
void menuHandler::GPSToggleMenu()
{
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Toggle GPS";
@@ -797,7 +796,7 @@ void menuHandler::rebootMenu()
void menuHandler::addFavoriteMenu()
{
screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
screen->showNodePicker("Node To Favorite", 30000, [](int nodenum) -> void {
LOG_WARN("Nodenum: %u", nodenum);
nodeDB->set_favorite(true, nodenum);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
@@ -888,37 +887,6 @@ void menuHandler::wifiToggleMenu()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::keyVerificationInitMenu()
{
screen->showNodePicker("Node to Verify", 30000,
[](uint32_t selected) -> void { keyVerificationModule->sendInitialRequest(selected); });
}
void menuHandler::keyVerificationFinalPrompt()
{
char message[40] = {0};
memset(message, 0, sizeof(message));
sprintf(message, "Verification: \n");
keyVerificationModule->generateVerificationCode(message + 15); // send the toPhone packet
if (screen) {
static const char *optionsArray[] = {"Reject", "Accept"};
graphics::BannerOverlayOptions options;
options.message = message;
options.durationMs = 30000;
options.optionsArrayPtr = optionsArray;
options.optionsCount = 2;
options.notificationType = graphics::notificationTypeEnum::selection_picker;
options.bannerCallback = [=](int selected) {
if (selected == 1) {
auto remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode());
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
}
};
screen->showOverlayBanner(options);
}
}
void menuHandler::handleMenuSwitch(OLEDDisplay *display)
{
if (menuQueue != menu_none)
@@ -985,18 +953,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case wifi_toggle_menu:
wifiToggleMenu();
break;
case key_verification_init:
keyVerificationInitMenu();
break;
case key_verification_final_prompt:
keyVerificationFinalPrompt();
break;
case bluetooth_toggle_menu:
BluetoothToggleMenu();
break;
case throttle_message:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break;
}
menuQueue = menu_none;
}

View File

@@ -1,5 +1,3 @@
#pragma once
#if HAS_SCREEN
#include "configuration.h"
namespace graphics
{
@@ -28,10 +26,7 @@ class menuHandler
test_menu,
number_test,
wifi_toggle_menu,
key_verification_init,
key_verification_final_prompt,
bluetooth_toggle_menu,
throttle_message
bluetooth_toggle_menu
};
static screenMenus menuQueue;
@@ -61,10 +56,7 @@ class menuHandler
static void numberTest();
static void wifiBaseMenu();
static void wifiToggleMenu();
static void keyVerificationInitMenu();
static void keyVerificationFinalPrompt();
static void BluetoothToggleMenu();
};
} // namespace graphics
#endif
} // namespace graphics

View File

@@ -26,7 +26,7 @@ extern bool hasUnreadMessage;
namespace graphics
{
InputEvent NotificationRenderer::inEvent;
char NotificationRenderer::inEvent = INPUT_BROKER_NONE;
int8_t NotificationRenderer::curSelected = 0;
char NotificationRenderer::alertBannerMessage[256] = {0};
uint32_t NotificationRenderer::alertBannerUntil = 0; // 0 is a special case meaning forever
@@ -72,25 +72,11 @@ void NotificationRenderer::resetBanner()
{
alertBannerMessage[0] = '\0';
current_notification_type = notificationTypeEnum::none;
inEvent.inputEvent = INPUT_BROKER_NONE;
inEvent.kbchar = 0;
curSelected = 0;
alertBannerOptions = 0; // last x lines are seelctable options
optionsArrayPtr = nullptr;
optionsEnumPtr = nullptr;
alertBannerCallback = NULL;
pauseBanner = false;
numDigits = 0;
currentNumber = 0;
nodeDB->pause_sort(false);
}
void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
{
if (!isOverlayBannerShowing() && alertBannerMessage[0] != '\0')
resetBanner();
if (!isOverlayBannerShowing() || pauseBanner)
return;
switch (current_notification_type) {
@@ -129,40 +115,31 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
// modulo to extract
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
// Handle input
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) {
if (this_digit == 9) {
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
} else {
currentNumber += (pow_of_10(numDigits - curSelected - 1));
}
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) {
if (this_digit == 0) {
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
} else {
currentNumber -= (pow_of_10(numDigits - curSelected - 1));
}
} else if (inEvent.inputEvent == INPUT_BROKER_ANYKEY) {
if (inEvent.kbchar > 47 && inEvent.kbchar < 58) { // have a digit
currentNumber -= this_digit * (pow_of_10(numDigits - curSelected - 1));
currentNumber += (inEvent.kbchar - 48) * (pow_of_10(numDigits - curSelected - 1));
curSelected++;
}
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_RIGHT) {
} else if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_RIGHT) {
curSelected++;
} else if (inEvent.inputEvent == INPUT_BROKER_LEFT) {
} else if (inEvent == INPUT_BROKER_LEFT) {
curSelected--;
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
alertBannerUntil != 0) {
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) {
resetBanner();
return;
}
if (curSelected == numDigits) {
alertBannerCallback(currentNumber);
resetBanner();
return;
alertBannerCallback(currentNumber);
}
inEvent.inputEvent = INPUT_BROKER_NONE;
inEvent = INPUT_BROKER_NONE;
if (alertBannerMessage[0] == '\0')
return;
@@ -216,18 +193,16 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
}
// Handle input
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) {
curSelected--;
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) {
curSelected++;
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
} else if (inEvent == INPUT_BROKER_SELECT) {
resetBanner();
alertBannerCallback(selectedNodenum);
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) {
resetBanner();
return;
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
alertBannerUntil != 0) {
resetBanner();
return;
}
if (curSelected == -1)
@@ -235,7 +210,7 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
if (curSelected == alertBannerOptions)
curSelected = 0;
inEvent.inputEvent = INPUT_BROKER_NONE;
inEvent = INPUT_BROKER_NONE;
if (alertBannerMessage[0] == '\0')
return;
@@ -333,11 +308,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
// Handle input
if (alertBannerOptions > 0) {
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) {
curSelected--;
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) {
curSelected++;
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
} else if (inEvent == INPUT_BROKER_SELECT) {
if (optionsEnumPtr != nullptr) {
alertBannerCallback(optionsEnumPtr[curSelected]);
optionsEnumPtr = nullptr;
@@ -345,11 +320,8 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
alertBannerCallback(curSelected);
}
resetBanner();
return;
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
alertBannerUntil != 0) {
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) {
resetBanner();
return;
}
if (curSelected == -1)
@@ -357,14 +329,12 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
if (curSelected == alertBannerOptions)
curSelected = 0;
} else {
if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_ALT_LONG ||
inEvent.inputEvent == INPUT_BROKER_CANCEL) {
if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_ALT_LONG || inEvent == INPUT_BROKER_CANCEL) {
resetBanner();
return;
}
}
inEvent.inputEvent = INPUT_BROKER_NONE;
inEvent = INPUT_BROKER_NONE;
if (alertBannerMessage[0] == '\0')
return;

View File

@@ -11,8 +11,7 @@ namespace graphics
class NotificationRenderer
{
public:
static InputEvent inEvent;
static char inKeypress;
static char inEvent;
static int8_t curSelected;
static char alertBannerMessage[256];
static uint32_t alertBannerUntil; // 0 is a special case meaning forever

View File

@@ -13,9 +13,8 @@ template <class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi
#if HAS_SCREEN
#define IF_SCREEN(X) \
if (screen) { \
X; \
}
if (screen) \
X;
#else
#define IF_SCREEN(...)
#endif

View File

@@ -1327,6 +1327,12 @@ void AdminModule::handleSendInputEvent(const meshtastic_AdminMessage_InputEvent
LOG_DEBUG("Processing input event: event_code=%u, kb_char=%u, touch_x=%u, touch_y=%u", inputEvent.event_code,
inputEvent.kb_char, inputEvent.touch_x, inputEvent.touch_y);
// Validate input parameters
if (inputEvent.event_code > INPUT_BROKER_ANYKEY) {
LOG_WARN("Invalid input event code: %u", inputEvent.event_code);
return;
}
// Create InputEvent for injection
InputEvent event = {.inputEvent = (input_broker_event)inputEvent.event_code,
.kbchar = (unsigned char)inputEvent.kb_char,

View File

@@ -716,7 +716,7 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
}
// Backspace
if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) {
if (event->inputEvent == INPUT_BROKER_BACK) {
payload = 0x08;
lastTouchMillis = millis();
runOnce();
@@ -739,8 +739,7 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
}
// Cancel (dismiss freetext screen)
if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG ||
(event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() == 0)) {
if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG) {
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
freetext = "";
cursor = 0;
@@ -990,7 +989,6 @@ int32_t CannedMessageModule::runOnce()
}
this->cursor--;
}
} else {
}
break;
case INPUT_BROKER_MSG_TAB: // Tab key: handled by input handler

View File

@@ -2,9 +2,7 @@
#include "KeyVerificationModule.h"
#include "MeshService.h"
#include "RTC.h"
#include "graphics/draw/MenuHandler.h"
#include "main.h"
#include "meshUtils.h"
#include "modules/AdminModule.h"
#include <SHA256.h>
@@ -50,22 +48,18 @@ AdminMessageHandleResult KeyVerificationModule::handleAdminMessageForModule(cons
bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_KeyVerification *r)
{
updateState();
if (mp.pki_encrypted == false) {
if (mp.pki_encrypted == false)
return false;
}
if (mp.from != currentRemoteNode) { // because the inital connection request is handled in allocReply()
if (mp.from != currentRemoteNode) // because the inital connection request is handled in allocReply()
return false;
}
if (currentState == KEY_VERIFICATION_IDLE) {
return false; // if we're idle, the only acceptable message is an init, which should be handled by allocReply()
}
if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 &&
r->hash1.size == 0) {
} else if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 &&
r->hash1.size == 0) {
memcpy(hash2, r->hash2.bytes, 32);
IF_SCREEN(screen->showNumberPicker("Enter Security Number", 60000, 6, [](int number_picked) -> void {
keyVerificationModule->processSecurityNumber(number_picked);
});)
if (screen)
screen->showSimpleBanner("Enter Security Number", 30000);
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING;
@@ -85,18 +79,23 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
memset(message, 0, sizeof(message));
sprintf(message, "Verification: \n");
generateVerificationCode(message + 15);
static const char *optionsArray[] = {"ACCEPT", "REJECT"};
LOG_INFO("Hash1 matches!");
IF_SCREEN(static const char *optionsArray[] = {"Reject", "Accept"}; graphics::BannerOverlayOptions options;
options.message = message; options.durationMs = 30000; options.optionsArrayPtr = optionsArray;
options.optionsCount = 2; options.notificationType = graphics::notificationTypeEnum::selection_picker;
options.bannerCallback =
[=](int selected) {
if (selected == 1) {
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
}
};
screen->showOverlayBanner(options);)
if (screen) {
graphics::BannerOverlayOptions options;
options.message = message;
options.durationMs = 30000;
options.optionsArrayPtr = optionsArray;
options.optionsCount = 2;
options.notificationType = graphics::notificationTypeEnum::selection_picker;
options.bannerCallback = [=](int selected) {
if (selected == 0) {
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
}
};
screen->showOverlayBanner(options);
}
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message);
@@ -121,7 +120,6 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode)
// generate nonce
updateState();
if (currentState != KEY_VERIFICATION_IDLE) {
IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message;)
return false;
}
currentNonce = random();
@@ -192,8 +190,11 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply()
responsePacket = allocDataProtobuf(response);
responsePacket->pki_encrypted = true;
IF_SCREEN(snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000);
screen->showSimpleBanner(message, 30000); LOG_WARN("%s", message);)
if (screen) {
snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000);
screen->showSimpleBanner(message, 30000);
LOG_WARN("%s", message);
}
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Incoming Key Verification.\nSecurity Number\n%03u %03u", currentSecurityNumber / 1000,
@@ -257,7 +258,12 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
p->priority = meshtastic_MeshPacket_Priority_HIGH;
service->sendToMesh(p, RX_SRC_LOCAL, true);
currentState = KEY_VERIFICATION_SENDER_AWAITING_USER;
IF_SCREEN(screen->requestMenu(graphics::menuHandler::key_verification_final_prompt);)
memset(message, 0, sizeof(message));
sprintf(message, "Verification: \n");
generateVerificationCode(message + 15); // send the toPhone packet
if (screen) {
screen->showSimpleBanner(message, 30000);
}
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message);
@@ -276,7 +282,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
void KeyVerificationModule::updateState()
{
if (currentState != KEY_VERIFICATION_IDLE) {
// check for the 60 second timeout
// check for the 30 second timeout
if (currentNonceTimestamp < getTime() - 60) {
resetToIdle();
} else {

View File

@@ -27,8 +27,6 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
}*/
virtual bool wantUIFrame() { return false; };
bool sendInitialRequest(NodeNum remoteNode);
void generateVerificationCode(char *); // fills char with the user readable verification code
uint32_t getCurrentRemoteNode() { return currentRemoteNode; }
protected:
/* Called to handle a particular incoming message
@@ -58,8 +56,9 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
char message[40] = {0};
void processSecurityNumber(uint32_t);
void updateState(); // check the timeouts and maybe reset the state to idle
void resetToIdle(); // Zero out module state
void updateState(); // check the timeouts and maybe reset the state to idle
void resetToIdle(); // Zero out module state
void generateVerificationCode(char *); // fills char with the user readable verification code
};
extern KeyVerificationModule *keyVerificationModule;

View File

@@ -1,15 +1,15 @@
{
// "USERPREFS_BUTTON_PIN": "36",
// "USERPREFS_CHANNELS_TO_WRITE": "3",
"USERPREFS_CHANNELS_TO_WRITE": "2",
// "USERPREFS_CHANNEL_0_DOWNLINK_ENABLED": "false",
// "USERPREFS_CHANNEL_0_NAME": "REPLACEME",
"USERPREFS_CHANNEL_0_NAME": "Sauce",
// "USERPREFS_CHANNEL_0_PRECISION": "14",
// "USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }",
// "USERPREFS_CHANNEL_0_UPLINK_ENABLED": "true",
"USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }",
"USERPREFS_CHANNEL_0_UPLINK_ENABLED": "true",
// "USERPREFS_CHANNEL_1_DOWNLINK_ENABLED": "false",
// "USERPREFS_CHANNEL_1_NAME": "NodeChat",
"USERPREFS_CHANNEL_1_NAME": "Exhibits",
// "USERPREFS_CHANNEL_1_PRECISION": "14",
// "USERPREFS_CHANNEL_1_PSK": "{ 0x4e, 0x22, 0x1d, 0x8b, 0xc3, 0x09, 0x1b, 0xe2, 0x11, 0x9c, 0x89, 0x12, 0xf2, 0x25, 0x19, 0x5d, 0x15, 0x3e, 0x30, 0x7b, 0x86, 0xb6, 0xec, 0xc4, 0x6a, 0xc3, 0x96, 0x5e, 0x9e, 0x10, 0x9d, 0xd5 }",
"USERPREFS_CHANNEL_1_PSK": "{ 0x4e, 0x22, 0x1d, 0x8b, 0xc3, 0x09, 0x1b, 0xe2, 0x11, 0x9c, 0x89, 0x12, 0xf2, 0x25, 0x19, 0x5d, 0x15, 0x3e, 0x30, 0x7b, 0x86, 0xb6, 0xec, 0xc4, 0x6a, 0xc3, 0x96, 0x5e, 0x9e, 0x10, 0x9d, 0xd5 }",
// "USERPREFS_CHANNEL_1_UPLINK_ENABLED": "false",
// "USERPREFS_CHANNEL_2_DOWNLINK_ENABLED": "false",
// "USERPREFS_CHANNEL_2_NAME": "YardSale",
@@ -17,12 +17,12 @@
// "USERPREFS_CHANNEL_2_PSK": "{ 0x15, 0x6f, 0xfe, 0x46, 0xd4, 0x56, 0x63, 0x8a, 0x54, 0x43, 0x13, 0xf2, 0xef, 0x6c, 0x63, 0x89, 0xf0, 0x06, 0x30, 0x52, 0xce, 0x36, 0x5e, 0xb1, 0xe8, 0xbb, 0x86, 0xe6, 0x26, 0x5b, 0x1d, 0x58 }",
// "USERPREFS_CHANNEL_2_UPLINK_ENABLED": "false",
// "USERPREFS_CONFIG_GPS_MODE": "meshtastic_Config_PositionConfig_GpsMode_ENABLED",
// "USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true",
"USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true",
// "USERPREFS_CONFIG_LORA_REGION": "meshtastic_Config_LoRaConfig_RegionCode_US",
// "USERPREFS_CONFIG_OWNER_LONG_NAME": "My Long Name",
// "USERPREFS_CONFIG_OWNER_SHORT_NAME": "MLN",
// "USERPREFS_CONFIG_DEVICE_ROLE": "meshtastic_Config_DeviceConfig_Role_CLIENT", // Defaults to CLIENT. ROUTER*, LOST AND FOUND, and REPEATER roles are restricted.
// "USERPREFS_EVENT_MODE": "1",
"USERPREFS_EVENT_MODE": "1",
// "USERPREFS_FIXED_BLUETOOTH": "121212",
// "USERPREFS_FIXED_GPS": "",
// "USERPREFS_FIXED_GPS_ALT": "0",
@@ -32,16 +32,16 @@
// "USERPREFS_CONFIG_GPS_UPDATE_INTERVAL": "600",
// "USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL": "1800",
// "USERPREFS_CONFIG_DEVICE_TELEM_UPDATE_INTERVAL": "900", // Device telemetry update interval in seconds
// "USERPREFS_LORACONFIG_CHANNEL_NUM": "31",
// "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST",
"USERPREFS_LORACONFIG_CHANNEL_NUM": "31",
"USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO",
// "USERPREFS_USE_ADMIN_KEY_0": "{ 0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c }",
// "USERPREFS_USE_ADMIN_KEY_1": "{}",
// "USERPREFS_USE_ADMIN_KEY_2": "{}",
// "USERPREFS_OEM_TEXT": "Caterham Car Club",
// "USERPREFS_OEM_FONT_SIZE": "0",
// "USERPREFS_OEM_IMAGE_WIDTH": "50",
// "USERPREFS_OEM_IMAGE_HEIGHT": "28",
// "USERPREFS_OEM_IMAGE_DATA": "{ 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x61, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0x18, 0x1F, 0xF0, 0x67, 0x00, 0x00, 0x00, 0x30, 0x1F, 0xF8, 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFC, 0x31, 0x00, 0x00, 0x00, 0x60, 0x00, 0xFE, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x7E, 0x18, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x1F, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x81, 0x1F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xC1, 0x0F, 0x06, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00}",
"USERPREFS_OEM_TEXT": "OpenSauce 2025",
"USERPREFS_OEM_FONT_SIZE": "0",
"USERPREFS_OEM_IMAGE_WIDTH": "49",
"USERPREFS_OEM_IMAGE_HEIGHT": "65",
"USERPREFS_OEM_IMAGE_DATA": "{ 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0xfe, 0xc0, 0x03, 0x00, 0x00, 0x80, 0x07, 0xfe, 0x00, 0x3f, 0x00, 0x00, 0xf8, 0x01, 0xfe, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0xfe, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0xfe, 0x00, 0x70, 0x00, 0x00, 0x1c, 0x00, 0xfe, 0x00, 0xe0, 0x03, 0x81, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x8c, 0x43, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xfe, 0x00, 0x80, 0x07, 0xc1, 0x03, 0x00, 0xfe, 0x00, 0xf0, 0x01, 0x01, 0x1f, 0x00, 0xfe, 0x00, 0x70, 0x00, 0x01, 0x1c, 0x00, 0xfe, 0x00, 0x30, 0x80, 0x03, 0x18, 0x00, 0xfe, 0x00, 0x3c, 0xf0, 0x0f, 0x78, 0x00, 0xfe, 0x80, 0x0f, 0xf8, 0x3f, 0xe0, 0x03, 0xfe, 0xe0, 0x00, 0xfc, 0x7f, 0x00, 0x0e, 0xfe, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0x00, 0xf0, 0xff, 0xff, 0x1f, 0x00, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfe, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xc1, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0x9f, 0xc1, 0xff, 0xff, 0x07, 0x6f, 0xff, 0xbb, 0x07, 0xfc, 0x7f, 0xf0, 0xef, 0xff, 0xb3, 0xef, 0xc1, 0x0f, 0xdf, 0xfe, 0xfe, 0xb3, 0xed, 0x1b, 0xf0, 0xdb, 0x8f, 0xff, 0xb3, 0xed, 0x7b, 0xbc, 0xfb, 0xe0, 0xff, 0xb3, 0x6d, 0xf8, 0xb2, 0x7f, 0xd9, 0xff, 0xb3, 0x6d, 0xd8, 0xfe, 0x70, 0xbc, 0xff, 0xb3, 0x6d, 0xd8, 0x0e, 0x56, 0x7e, 0xff, 0xb3, 0x6f, 0xd8, 0x80, 0x76, 0x72, 0xff, 0xb3, 0x6f, 0xd8, 0xee, 0x75, 0x63, 0xff, 0xb3, 0xef, 0xd9, 0xf6, 0x73, 0x61, 0xff, 0xb3, 0xe1, 0xdb, 0x96, 0x53, 0x61, 0xff, 0xb3, 0xe1, 0xdb, 0x16, 0x77, 0x63, 0xff, 0xb3, 0x61, 0xd8, 0x0e, 0x77, 0x62, 0xff, 0xbf, 0x61, 0xd8, 0x0e, 0x77, 0xb6, 0xff, 0xbf, 0x61, 0xd8, 0x0e, 0x57, 0xbd, 0xff, 0x9c, 0x61, 0xd8, 0x16, 0x57, 0xc3, 0xff, 0x83, 0x61, 0xd8, 0x16, 0x73, 0xff, 0xff, 0x8f, 0xe0, 0xd8, 0xb6, 0x33, 0xff, 0xff, 0xef, 0xe0, 0xdb, 0xee, 0xcd, 0xff, 0xff, 0xeb, 0xc5, 0xdb, 0x1e, 0xfe, 0xc7, 0xff, 0xeb, 0x2d, 0xd8, 0xfe, 0xff, 0xb0, 0xff, 0x6b, 0x2d, 0xc3, 0xfe, 0x1f, 0xa4, 0xff, 0x63, 0xad, 0x07, 0xfe, 0x83, 0xa4, 0xff, 0x66, 0xad, 0xf7, 0x7e, 0x88, 0xa4, 0xff, 0xec, 0xad, 0xf5, 0x0e, 0x89, 0xa4, 0xff, 0xeb, 0xad, 0xb5, 0x0e, 0x89, 0xa4, 0xff, 0xeb, 0xad, 0x35, 0x1e, 0x89, 0x9c, 0xff, 0x6b, 0xad, 0x35, 0x1e, 0x89, 0xc7, 0xff, 0x6f, 0xad, 0x31, 0x1e, 0xf9, 0xf8, 0xff, 0x6f, 0xad, 0x71, 0x1e, 0x1d, 0xff, 0xfe, 0x6c, 0xad, 0xf1, 0xbe, 0xe7, 0x7f, 0xfe, 0x60, 0xad, 0xf5, 0xee, 0xfc, 0x1f, 0xfe, 0x00, 0xbd, 0x35, 0x1e, 0xff, 0x03, 0xfe, 0x00, 0xb8, 0x35, 0xfe, 0x7f, 0x00, 0xfe, 0x00, 0xb0, 0x37, 0xfe, 0x1f, 0x00, 0xfe, 0x00, 0x00, 0x37, 0xfe, 0x03, 0x00, 0xfe, 0x00, 0x00, 0x76, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xf0, 0x1e, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0xfe }",
// "USERPREFS_NETWORK_ENABLED_PROTOCOLS": "1", // Enable UDP mesh
// "USERPREFS_NETWORK_WIFI_ENABLED": "true",
// "USERPREFS_NETWORK_WIFI_SSID": "wifi_ssid",
@@ -52,6 +52,6 @@
// "USERPREFS_MQTT_PASSWORD": "large4cats",
// "USERPREFS_MQTT_ENCRYPTION_ENABLED": "true",
// "USERPREFS_MQTT_TLS_ENABLED": "false",
// "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME",
"USERPREFS_TZ_STRING": "tzplaceholder "
"USERPREFS_MQTT_ROOT_TOPIC": "event/OpenSauce",
"USERPREFS_TZ_STRING": "PST8PDT,M3.2.0,M11.1.0"
}

View File

@@ -61,8 +61,9 @@ void setupNicheGraphics()
InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252;
// Customize default settings
inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Setup backlight controller
// Note: button is attached further down
@@ -70,14 +71,16 @@ void setupNicheGraphics()
backlight->setPin(PIN_EINK_EN);
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();

View File

@@ -65,16 +65,19 @@ void setupNicheGraphics()
inkhud->persistence->settings.rotation = (driver->height > driver->width ? 1 : 0); // Rotate 90deg to landscape, if needed
inkhud->persistence->settings.userTiles.maxCount = 4;
inkhud->persistence->settings.optionalFeatures.batteryIcon = true;
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, false, 3); // Default on tile 3
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 2); // Default on tile 2
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1));
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true, false, 1); // Default on tile 1
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet, true, false, 0); // Default on tile 0
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true); // Background
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();

View File

@@ -66,16 +66,20 @@ void setupNicheGraphics()
inkhud->persistence->settings.rotation = (driver->height > driver->width ? 1 : 0); // Rotate 90deg to landscape, if needed
inkhud->persistence->settings.userTiles.maxCount = 4;
inkhud->persistence->settings.optionalFeatures.batteryIcon = true;
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, false, 3); // Default on tile 3
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 2); // Default on tile 2
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1));
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true, false, 1); // Default on tile 1
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet, true, false, 0); // Default on tile 0
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true); // Background
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Background
// Start running InkHUD
inkhud->begin();

View File

@@ -59,16 +59,19 @@ void setupNicheGraphics()
inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
inkhud->persistence->settings.optionalMenuItems.nextTile = true;
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();

View File

@@ -78,17 +78,20 @@ void setupNicheGraphics()
inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle?
inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Behavior handled by aux button instead
inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Behavior handled by aux button instead
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();

View File

@@ -75,17 +75,20 @@ void setupNicheGraphics()
inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle?
inkhud->persistence->settings.rotation = 1; // 90 degrees clockwise
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Behavior handled by aux button instead
inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Behavior handled by aux button instead
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();

View File

@@ -77,16 +77,18 @@ void setupNicheGraphics()
inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle?
inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();

View File

@@ -67,6 +67,7 @@ void setupNicheGraphics()
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
inkhud->persistence->settings.optionalMenuItems.backlight = true; // Until proves capacitive button works by touching it
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Setup backlight controller
// Note: AUX button attached further down
@@ -74,14 +75,16 @@ void setupNicheGraphics()
backlight->setPin(PIN_EINK_EN);
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
inkhud->persistence->settings.rotation = 1;
// inkhud->persistence->printSettings(&inkhud->persistence->settings);

View File

@@ -68,7 +68,6 @@
#define TB_LEFT 1
#define TB_RIGHT 2
#define TB_PRESS 0 // BUTTON_PIN
#define TB_DIRECTION FALLING
// microphone
#define ES7210_SCK 47

View File

@@ -62,10 +62,11 @@ void setupNicheGraphics()
InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252;
// Customize default settings
inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side
inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
inkhud->persistence->settings.optionalMenuItems.backlight = true; // Until proves capacitive button works by touching it
inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side
inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
inkhud->persistence->settings.optionalMenuItems.backlight = true; // Until proves capacitive button works by touching it
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Setup backlight controller
// Note: AUX button attached further down
@@ -73,14 +74,16 @@ void setupNicheGraphics()
backlight->setPin(PIN_EINK_EN);
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();

View File

@@ -59,16 +59,19 @@ void setupNicheGraphics()
inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle?
inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
inkhud->persistence->settings.optionalFeatures.notifications = false; // No notifications. Busy mesh.
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
// Custom selection for OpenSauce
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 0); // Default tile 0
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), true);
inkhud->addApplet("Channel 2", new InkHUD::ThreadedMessageApplet(2), true);
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, true); // Autoshown if new message
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 1); // Default tile 1
// Disabled by default
inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet);
inkhud->addApplet("Positions", new InkHUD::PositionsApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
// Start running InkHUD
inkhud->begin();