Compare commits

..

1 Commits

Author SHA1 Message Date
Ben Meadors
7b1fa550a1 Fix OTA filename determination to use unified format for ESP32 2026-01-30 06:35:36 -06:00
168 changed files with 565 additions and 1277 deletions

1
.envrc
View File

@@ -1 +0,0 @@
use nix

3
.gitignore vendored
View File

@@ -50,6 +50,3 @@ idf_component.yml
CMakeLists.txt CMakeLists.txt
/sdkconfig.* /sdkconfig.*
.dummy/* .dummy/*
# PYTHONPATH used by the Nix shell
.python3

44
flake.lock generated
View File

@@ -1,44 +0,0 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1766314097,
"narHash": "sha256-laJftWbghBehazn/zxVJ8NdENVgjccsWAdAqKXhErrM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "306ea70f9eb0fb4e040f8540e2deab32ed7e2055",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,66 +0,0 @@
{
description = "Nix flake to compile Meshtastic firmware";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# Shim to make flake.nix work with stable Nix.
flake-compat = {
url = "github:NixOS/flake-compat";
flake = false;
};
};
outputs =
inputs:
let
lib = inputs.nixpkgs.lib;
forAllSystems =
fn:
lib.genAttrs lib.systems.flakeExposed (
system:
fn {
pkgs = import inputs.nixpkgs {
inherit system;
};
inherit system;
}
);
in
{
devShells = forAllSystems (
{ pkgs, ... }:
let
python3 = pkgs.python312.withPackages (
ps: with ps; [
google
]
);
in
{
default = pkgs.mkShell {
buildInputs = with pkgs; [
python3
platformio
];
shellHook = ''
# Set up PlatformIO to use a local core directory.
export PLATFORMIO_CORE_DIR=$PWD/.platformio
# Tell pip to put packages into $PIP_PREFIX instead of the usual
# location. This is especially necessary under NixOS to avoid having
# pip trying to write to the read-only Nix store. For more info,
# see https://wiki.nixos.org/wiki/Python
export PIP_PREFIX=$PWD/.python3
export PYTHONPATH="$PIP_PREFIX/${python3.sitePackages}"
export PATH="$PIP_PREFIX/bin:$PATH"
# Avoids reproducibility issues with some Python packages
# See https://nixos.org/manual/nixpkgs/stable/#python-setup.py-bdist_wheel-cannot-create-.whl
unset SOURCE_DATE_EPOCH
'';
};
}
);
};
}

View File

@@ -120,7 +120,7 @@ lib_deps =
[device-ui_base] [device-ui_base]
lib_deps = lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/48e3a59ddfd746dfbde562a7f447cf9da4f369cf.zip https://github.com/meshtastic/device-ui/archive/63967a4a557d33d56fc5746f9128200dde2d88c5.zip
; Common libs for environmental measurements in telemetry module ; Common libs for environmental measurements in telemetry module
[environmental_base] [environmental_base]

View File

@@ -1,12 +0,0 @@
(import (
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
nodeName = lock.nodes.root.inputs.flake-compat;
in
fetchTarball {
url =
lock.nodes.${nodeName}.locked.url
or "https://github.com/NixOS/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
sha256 = lock.nodes.${nodeName}.locked.narHash;
}
) { src = ./.; }).shellNix

View File

@@ -816,9 +816,6 @@ void Power::shutdown()
#endif #endif
#ifdef PIN_LED3 #ifdef PIN_LED3
ledOff(PIN_LED3); ledOff(PIN_LED3);
#endif
#ifdef LED_NOTIFICATION
ledOff(LED_NOTIFICATION);
#endif #endif
doDeepSleep(DELAY_FOREVER, true, true); doDeepSleep(DELAY_FOREVER, true, true);
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)

View File

@@ -274,9 +274,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define TCA9535_ADDR 0x20 #define TCA9535_ADDR 0x20
#define TCA9555_ADDR 0x26 #define TCA9555_ADDR 0x26
// used for display brightness control
#define STC8H1K28_ADDR 0x30
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Touchscreen // Touchscreen
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -393,6 +390,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef HAS_RADIO #ifndef HAS_RADIO
#define HAS_RADIO 0 #define HAS_RADIO 0
#endif #endif
#ifndef HAS_RTC
#define HAS_RTC 0
#endif
#ifndef HAS_CPU_SHUTDOWN #ifndef HAS_CPU_SHUTDOWN
#define HAS_CPU_SHUTDOWN 0 #define HAS_CPU_SHUTDOWN 0
#endif #endif
@@ -428,16 +428,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HAS_RGB_LED #define HAS_RGB_LED
#endif #endif
#ifndef LED_STATE_OFF
#define LED_STATE_OFF 0
#endif
#ifndef LED_STATE_ON #ifndef LED_STATE_ON
#define LED_STATE_ON 1 #define LED_STATE_ON 1
#endif #endif
#ifndef LED_STATE_OFF
#define LED_STATE_OFF (LED_STATE_ON ^ 1)
#endif
#ifndef ledOff
#define ledOff(pin) pinMode(pin, INPUT)
#endif
// default mapping of pins // default mapping of pins
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN) #if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)

View File

@@ -63,7 +63,6 @@ class ScanI2C
NAU7802, NAU7802,
FT6336U, FT6336U,
STK8BAXX, STK8BAXX,
STC8H1K28,
ICM20948, ICM20948,
SCD4X, SCD4X,
MAX30102, MAX30102,

View File

@@ -243,8 +243,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#endif #endif
#ifdef HAS_LP5562 #ifdef HAS_LP5562
SCAN_SIMPLE_CASE(LP5562_ADDR, LP5562, "LP5562", (uint8_t)addr.address); SCAN_SIMPLE_CASE(LP5562_ADDR, LP5562, "LP5562", (uint8_t)addr.address);
#else
SCAN_SIMPLE_CASE(STC8H1K28_ADDR, LP5562, "STC8H1K28", (uint8_t)addr.address);
#endif #endif
case XPOWERS_AXP192_AXP2101_ADDRESS: case XPOWERS_AXP192_AXP2101_ADDRESS:
// Do we have the axp2101/192 or the TCA8418 // Do we have the axp2101/192 or the TCA8418

View File

@@ -276,7 +276,11 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
settimeofday(tv, NULL); settimeofday(tv, NULL);
#endif #endif
// nrf52 doesn't have a readable RTC (yet - software not written)
#if HAS_RTC
readFromRTC(); readFromRTC();
#endif
return RTCSetResultSuccess; return RTCSetResultSuccess;
} else { } else {
return RTCSetResultNotSet; // RTC was already set with a higher quality time return RTCSetResultNotSet; // RTC was already set with a higher quality time

View File

@@ -55,7 +55,7 @@ InkHUD::Tile *InkHUD::Applet::getTile()
} }
// Draw the applet // Draw the applet
void InkHUD::Applet::render(bool full) void InkHUD::Applet::render()
{ {
assert(assignedTile); // Ensure that we have a tile assert(assignedTile); // Ensure that we have a tile
assert(assignedTile->getAssignedApplet() == this); // Ensure that we have a reciprocal link with the tile assert(assignedTile->getAssignedApplet() == this); // Ensure that we have a reciprocal link with the tile
@@ -65,11 +65,10 @@ void InkHUD::Applet::render(bool full)
wantRender = false; // Flag set by requestUpdate wantRender = false; // Flag set by requestUpdate
wantAutoshow = false; // Flag set by requestAutoShow. May or may not have been honored. wantAutoshow = false; // Flag set by requestAutoShow. May or may not have been honored.
wantUpdateType = Drivers::EInk::UpdateTypes::UNSPECIFIED; // Update type we wanted. May on may not have been granted. wantUpdateType = Drivers::EInk::UpdateTypes::UNSPECIFIED; // Update type we wanted. May on may not have been granted.
wantFullRender = true; // Default to a full render
updateDimensions(); updateDimensions();
resetDrawingSpace(); resetDrawingSpace();
onRender(full); // Draw the applet onRender(); // Derived applet's drawing takes place here
// Handle "Tile Highlighting" // Handle "Tile Highlighting"
// Some devices may use an auxiliary button to switch between tiles // Some devices may use an auxiliary button to switch between tiles
@@ -116,11 +115,6 @@ Drivers::EInk::UpdateTypes InkHUD::Applet::wantsUpdateType()
return wantUpdateType; return wantUpdateType;
} }
bool InkHUD::Applet::wantsFullRender()
{
return wantFullRender;
}
// Get size of the applet's drawing space from its tile // Get size of the applet's drawing space from its tile
// Performed immediately before derived applet's drawing code runs // Performed immediately before derived applet's drawing code runs
void InkHUD::Applet::updateDimensions() void InkHUD::Applet::updateDimensions()
@@ -148,11 +142,10 @@ void InkHUD::Applet::resetDrawingSpace()
// Once the renderer has given other applets a chance to process whatever event we just detected, // Once the renderer has given other applets a chance to process whatever event we just detected,
// it will run Applet::render(), which may draw our applet to screen, if it is shown (foreground) // it will run Applet::render(), which may draw our applet to screen, if it is shown (foreground)
// We should requestUpdate even if our applet is currently background, because this might be changed by autoshow // We should requestUpdate even if our applet is currently background, because this might be changed by autoshow
void InkHUD::Applet::requestUpdate(Drivers::EInk::UpdateTypes type, bool full) void InkHUD::Applet::requestUpdate(Drivers::EInk::UpdateTypes type)
{ {
wantRender = true; wantRender = true;
wantUpdateType = type; wantUpdateType = type;
wantFullRender = full;
inkhud->requestUpdate(); inkhud->requestUpdate();
} }

View File

@@ -64,11 +64,10 @@ class Applet : public GFX
// Rendering // Rendering
void render(bool full); // Draw the applet void render(); // Draw the applet
bool wantsToRender(); // Check whether applet wants to render bool wantsToRender(); // Check whether applet wants to render
bool wantsToAutoshow(); // Check whether applet wants to become foreground bool wantsToAutoshow(); // Check whether applet wants to become foreground
Drivers::EInk::UpdateTypes wantsUpdateType(); // Check which display update type the applet would prefer Drivers::EInk::UpdateTypes wantsUpdateType(); // Check which display update type the applet would prefer
bool wantsFullRender(); // Check whether applet wants to render over its previous render
void updateDimensions(); // Get current size from tile void updateDimensions(); // Get current size from tile
void resetDrawingSpace(); // Makes sure every render starts with same parameters void resetDrawingSpace(); // Makes sure every render starts with same parameters
@@ -83,7 +82,7 @@ class Applet : public GFX
// Event handlers // Event handlers
virtual void onRender(bool full) = 0; // For drawing the applet virtual void onRender() = 0; // All drawing happens here
virtual void onActivate() {} virtual void onActivate() {}
virtual void onDeactivate() {} virtual void onDeactivate() {}
virtual void onForeground() {} virtual void onForeground() {}
@@ -97,9 +96,6 @@ class Applet : public GFX
virtual void onNavDown() {} virtual void onNavDown() {}
virtual void onNavLeft() {} virtual void onNavLeft() {}
virtual void onNavRight() {} virtual void onNavRight() {}
virtual void onFreeText(char c) {}
virtual void onFreeTextDone() {}
virtual void onFreeTextCancel() {}
virtual bool approveNotification(Notification &n); // Allow an applet to veto a notification virtual bool approveNotification(Notification &n); // Allow an applet to veto a notification
@@ -112,9 +108,8 @@ class Applet : public GFX
protected: protected:
void drawPixel(int16_t x, int16_t y, uint16_t color) override; // Place a single pixel. All drawing output passes through here void drawPixel(int16_t x, int16_t y, uint16_t color) override; // Place a single pixel. All drawing output passes through here
void requestUpdate(EInk::UpdateTypes type = EInk::UpdateTypes::UNSPECIFIED, void requestUpdate(EInk::UpdateTypes type = EInk::UpdateTypes::UNSPECIFIED); // Ask WindowManager to schedule a display update
bool full = true); // Ask WindowManager to schedule a display update void requestAutoshow(); // Ask for applet to be moved to foreground
void requestAutoshow(); // Ask for applet to be moved to foreground
uint16_t X(float f); // Map applet width, mapped from 0 to 1.0 uint16_t X(float f); // Map applet width, mapped from 0 to 1.0
uint16_t Y(float f); // Map applet height, mapped from 0 to 1.0 uint16_t Y(float f); // Map applet height, mapped from 0 to 1.0
@@ -169,7 +164,6 @@ class Applet : public GFX
bool wantAutoshow = false; // Does the applet have new data it would like to display in foreground? bool wantAutoshow = false; // Does the applet have new data it would like to display in foreground?
NicheGraphics::Drivers::EInk::UpdateTypes wantUpdateType = NicheGraphics::Drivers::EInk::UpdateTypes wantUpdateType =
NicheGraphics::Drivers::EInk::UpdateTypes::UNSPECIFIED; // Which update method we'd prefer when redrawing the display NicheGraphics::Drivers::EInk::UpdateTypes::UNSPECIFIED; // Which update method we'd prefer when redrawing the display
bool wantFullRender = true; // Render with a fresh canvas
using GFX::setFont; // Make sure derived classes use AppletFont instead of AdafruitGFX fonts directly using GFX::setFont; // Make sure derived classes use AppletFont instead of AdafruitGFX fonts directly
using GFX::setRotation; // Block setRotation calls. Rotation is handled globally by WindowManager. using GFX::setRotation; // Block setRotation calls. Rotation is handled globally by WindowManager.

View File

@@ -4,7 +4,7 @@
using namespace NicheGraphics; using namespace NicheGraphics;
void InkHUD::MapApplet::onRender(bool full) void InkHUD::MapApplet::onRender()
{ {
// Abort if no markers to render // Abort if no markers to render
if (!enoughMarkers()) { if (!enoughMarkers()) {

View File

@@ -27,7 +27,7 @@ namespace NicheGraphics::InkHUD
class MapApplet : public Applet class MapApplet : public Applet
{ {
public: public:
void onRender(bool full) override; void onRender() override;
protected: protected:
virtual bool shouldDrawNode(meshtastic_NodeInfoLite *node) { return true; } // Allow derived applets to filter the nodes virtual bool shouldDrawNode(meshtastic_NodeInfoLite *node) { return true; } // Allow derived applets to filter the nodes

View File

@@ -103,7 +103,7 @@ uint8_t InkHUD::NodeListApplet::maxCards()
} }
// Draw, using info which derived applet placed into NodeListApplet::cards for us // Draw, using info which derived applet placed into NodeListApplet::cards for us
void InkHUD::NodeListApplet::onRender(bool full) void InkHUD::NodeListApplet::onRender()
{ {
// ================================ // ================================

View File

@@ -46,7 +46,7 @@ class NodeListApplet : public Applet, public MeshModule
public: public:
NodeListApplet(const char *name); NodeListApplet(const char *name);
void onRender(bool full) override; void onRender() override;
bool wantPacket(const meshtastic_MeshPacket *p) override; bool wantPacket(const meshtastic_MeshPacket *p) override;
ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override; ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;

View File

@@ -6,7 +6,7 @@ using namespace NicheGraphics;
// All drawing happens here // All drawing happens here
// Our basic example doesn't do anything useful. It just passively prints some text. // Our basic example doesn't do anything useful. It just passively prints some text.
void InkHUD::BasicExampleApplet::onRender(bool full) void InkHUD::BasicExampleApplet::onRender()
{ {
printAt(0, 0, "Hello, World!"); printAt(0, 0, "Hello, World!");

View File

@@ -28,7 +28,7 @@ class BasicExampleApplet : public Applet
// You must have an onRender() method // You must have an onRender() method
// All drawing happens here // All drawing happens here
void onRender(bool full) override; void onRender() override;
}; };
} // namespace NicheGraphics::InkHUD } // namespace NicheGraphics::InkHUD

View File

@@ -35,7 +35,7 @@ ProcessMessage InkHUD::NewMsgExampleApplet::handleReceived(const meshtastic_Mesh
// We can trigger a render by calling requestUpdate() // We can trigger a render by calling requestUpdate()
// Render might be called by some external source // Render might be called by some external source
// We should always be ready to draw // We should always be ready to draw
void InkHUD::NewMsgExampleApplet::onRender(bool full) void InkHUD::NewMsgExampleApplet::onRender()
{ {
printAt(0, 0, "Example: NewMsg", LEFT, TOP); // Print top-left corner of text at (0,0) printAt(0, 0, "Example: NewMsg", LEFT, TOP); // Print top-left corner of text at (0,0)

View File

@@ -34,7 +34,7 @@ class NewMsgExampleApplet : public Applet, public SinglePortModule
NewMsgExampleApplet() : SinglePortModule("NewMsgExampleApplet", meshtastic_PortNum_TEXT_MESSAGE_APP) {} NewMsgExampleApplet() : SinglePortModule("NewMsgExampleApplet", meshtastic_PortNum_TEXT_MESSAGE_APP) {}
// All drawing happens here // All drawing happens here
void onRender(bool full) override; void onRender() override;
// Your applet might also want to use some of these // Your applet might also want to use some of these
// Useful for setting up or tidying up // Useful for setting up or tidying up

View File

@@ -10,7 +10,7 @@ InkHUD::AlignStickApplet::AlignStickApplet()
bringToForeground(); bringToForeground();
} }
void InkHUD::AlignStickApplet::onRender(bool full) void InkHUD::AlignStickApplet::onRender()
{ {
setFont(fontMedium); setFont(fontMedium);
printAt(0, 0, "Align Joystick:"); printAt(0, 0, "Align Joystick:");
@@ -152,17 +152,19 @@ void InkHUD::AlignStickApplet::onBackground()
// Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background // Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background
// Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case // Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case
inkhud->forceUpdate(EInk::UpdateTypes::FULL, true); inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::AlignStickApplet::onButtonLongPress() void InkHUD::AlignStickApplet::onButtonLongPress()
{ {
sendToBackground(); sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::AlignStickApplet::onExitLong() void InkHUD::AlignStickApplet::onExitLong()
{ {
sendToBackground(); sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::AlignStickApplet::onNavUp() void InkHUD::AlignStickApplet::onNavUp()
@@ -170,6 +172,7 @@ void InkHUD::AlignStickApplet::onNavUp()
settings->joystick.aligned = true; settings->joystick.aligned = true;
sendToBackground(); sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::AlignStickApplet::onNavDown() void InkHUD::AlignStickApplet::onNavDown()
@@ -178,6 +181,7 @@ void InkHUD::AlignStickApplet::onNavDown()
settings->joystick.aligned = true; settings->joystick.aligned = true;
sendToBackground(); sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::AlignStickApplet::onNavLeft() void InkHUD::AlignStickApplet::onNavLeft()
@@ -186,6 +190,7 @@ void InkHUD::AlignStickApplet::onNavLeft()
settings->joystick.aligned = true; settings->joystick.aligned = true;
sendToBackground(); sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::AlignStickApplet::onNavRight() void InkHUD::AlignStickApplet::onNavRight()
@@ -194,6 +199,7 @@ void InkHUD::AlignStickApplet::onNavRight()
settings->joystick.aligned = true; settings->joystick.aligned = true;
sendToBackground(); sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
#endif #endif

View File

@@ -23,7 +23,7 @@ class AlignStickApplet : public SystemApplet
public: public:
AlignStickApplet(); AlignStickApplet();
void onRender(bool full) override; void onRender() override;
void onForeground() override; void onForeground() override;
void onBackground() override; void onBackground() override;
void onButtonLongPress() override; void onButtonLongPress() override;

View File

@@ -6,8 +6,6 @@ using namespace NicheGraphics;
InkHUD::BatteryIconApplet::BatteryIconApplet() InkHUD::BatteryIconApplet::BatteryIconApplet()
{ {
alwaysRender = true; // render everytime the screen is updated
// Show at boot, if user has previously enabled the feature // Show at boot, if user has previously enabled the feature
if (settings->optionalFeatures.batteryIcon) if (settings->optionalFeatures.batteryIcon)
bringToForeground(); bringToForeground();
@@ -46,7 +44,7 @@ int InkHUD::BatteryIconApplet::onPowerStatusUpdate(const meshtastic::Status *sta
return 0; // Tell Observable to continue informing other observers return 0; // Tell Observable to continue informing other observers
} }
void InkHUD::BatteryIconApplet::onRender(bool full) void InkHUD::BatteryIconApplet::onRender()
{ {
// Fill entire tile // Fill entire tile
// - size of icon controlled by size of tile // - size of icon controlled by size of tile

View File

@@ -23,7 +23,7 @@ class BatteryIconApplet : public SystemApplet
public: public:
BatteryIconApplet(); BatteryIconApplet();
void onRender(bool full) override; void onRender() override;
int onPowerStatusUpdate(const meshtastic::Status *status); // Called when new info about battery is available int onPowerStatusUpdate(const meshtastic::Status *status); // Called when new info about battery is available
private: private:

View File

@@ -1,257 +0,0 @@
#ifdef MESHTASTIC_INCLUDE_INKHUD
#include "./KeyboardApplet.h"
using namespace NicheGraphics;
InkHUD::KeyboardApplet::KeyboardApplet()
{
// Calculate row widths
for (uint8_t row = 0; row < KBD_ROWS; row++) {
rowWidths[row] = 0;
for (uint8_t col = 0; col < KBD_COLS; col++)
rowWidths[row] += keyWidths[row * KBD_COLS + col];
}
}
void InkHUD::KeyboardApplet::onRender(bool full)
{
uint16_t em = fontSmall.lineHeight(); // 16 pt
uint16_t keyH = Y(1.0) / KBD_ROWS;
int16_t keyTopPadding = (keyH - fontSmall.lineHeight()) / 2;
if (full) { // Draw full keyboard
for (uint8_t row = 0; row < KBD_ROWS; row++) {
// Calculate the remaining space to be used as padding
int16_t keyXPadding = X(1.0) - ((rowWidths[row] * em) >> 4);
// Draw keys
uint16_t xPos = 0;
for (uint8_t col = 0; col < KBD_COLS; col++) {
Color fgcolor = BLACK;
uint8_t index = row * KBD_COLS + col;
uint16_t keyX = ((xPos * em) >> 4) + ((col * keyXPadding) / (KBD_COLS - 1));
uint16_t keyY = row * keyH;
uint16_t keyW = (keyWidths[index] * em) >> 4;
if (index == selectedKey) {
fgcolor = WHITE;
fillRect(keyX, keyY, keyW, keyH, BLACK);
}
drawKeyLabel(keyX, keyY + keyTopPadding, keyW, keys[index], fgcolor);
xPos += keyWidths[index];
}
}
} else { // Only draw the difference
if (selectedKey != prevSelectedKey) {
// Draw previously selected key
uint8_t row = prevSelectedKey / KBD_COLS;
int16_t keyXPadding = X(1.0) - ((rowWidths[row] * em) >> 4);
uint16_t xPos = 0;
for (uint8_t i = prevSelectedKey - (prevSelectedKey % KBD_COLS); i < prevSelectedKey; i++)
xPos += keyWidths[i];
uint16_t keyX = ((xPos * em) >> 4) + (((prevSelectedKey % KBD_COLS) * keyXPadding) / (KBD_COLS - 1));
uint16_t keyY = row * keyH;
uint16_t keyW = (keyWidths[prevSelectedKey] * em) >> 4;
fillRect(keyX, keyY, keyW, keyH, WHITE);
drawKeyLabel(keyX, keyY + keyTopPadding, keyW, keys[prevSelectedKey], BLACK);
// Draw newly selected key
row = selectedKey / KBD_COLS;
keyXPadding = X(1.0) - ((rowWidths[row] * em) >> 4);
xPos = 0;
for (uint8_t i = selectedKey - (selectedKey % KBD_COLS); i < selectedKey; i++)
xPos += keyWidths[i];
keyX = ((xPos * em) >> 4) + (((selectedKey % KBD_COLS) * keyXPadding) / (KBD_COLS - 1));
keyY = row * keyH;
keyW = (keyWidths[selectedKey] * em) >> 4;
fillRect(keyX, keyY, keyW, keyH, BLACK);
drawKeyLabel(keyX, keyY + keyTopPadding, keyW, keys[selectedKey], WHITE);
}
}
prevSelectedKey = selectedKey;
}
// Draw the key label corresponding to the char
// for most keys it draws the character itself
// for ['\b', '\n', ' ', '\x1b'] it draws special glyphs
void InkHUD::KeyboardApplet::drawKeyLabel(uint16_t left, uint16_t top, uint16_t width, char key, Color color)
{
if (key == '\b') {
// Draw backspace glyph: 13 x 9 px
/**
* [][][][][][][][][]
* [][] []
* [][] [] [] []
* [][] [] [] []
* [][] [] []
* [][] [] [] []
* [][] [] [] []
* [][] []
* [][][][][][][][][]
*/
const uint8_t bsBitmap[] = {0x0f, 0xf8, 0x18, 0x08, 0x32, 0x28, 0x61, 0x48, 0xc0,
0x88, 0x61, 0x48, 0x32, 0x28, 0x18, 0x08, 0x0f, 0xf8};
uint16_t leftPadding = (width - 13) >> 1;
drawBitmap(left + leftPadding, top + 1, bsBitmap, 13, 9, color);
} else if (key == '\n') {
// Draw done glyph: 12 x 9 px
/**
* [][]
* [][]
* [][]
* [][]
* [][]
* [][] [][]
* [][] [][]
* [][][]
* []
*/
const uint8_t doneBitmap[] = {0x00, 0x30, 0x00, 0x60, 0x00, 0xc0, 0x01, 0x80, 0x03,
0x00, 0xc6, 0x00, 0x6c, 0x00, 0x38, 0x00, 0x10, 0x00};
uint16_t leftPadding = (width - 12) >> 1;
drawBitmap(left + leftPadding, top + 1, doneBitmap, 12, 9, color);
} else if (key == ' ') {
// Draw space glyph: 13 x 9 px
/**
*
*
*
*
* [] []
* [] []
* [][][][][][][][][][][][][]
*
*
*/
const uint8_t spaceBitmap[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x08, 0x80, 0x08, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00};
uint16_t leftPadding = (width - 13) >> 1;
drawBitmap(left + leftPadding, top + 1, spaceBitmap, 13, 9, color);
} else if (key == '\x1b') {
setTextColor(color);
std::string keyText = "ESC";
uint16_t leftPadding = (width - getTextWidth(keyText)) >> 1;
printAt(left + leftPadding, top, keyText);
} else {
setTextColor(color);
if (key >= 0x61)
key -= 32; // capitalize
std::string keyText = std::string(1, key);
uint16_t leftPadding = (width - getTextWidth(keyText)) >> 1;
printAt(left + leftPadding, top, keyText);
}
}
void InkHUD::KeyboardApplet::onForeground()
{
handleInput = true; // Intercept the button input for our applet
// Select the first key
selectedKey = 0;
prevSelectedKey = 0;
}
void InkHUD::KeyboardApplet::onBackground()
{
handleInput = false;
}
void InkHUD::KeyboardApplet::onButtonShortPress()
{
char key = keys[selectedKey];
if (key == '\n') {
inkhud->freeTextDone();
inkhud->closeKeyboard();
} else if (key == '\x1b') {
inkhud->freeTextCancel();
inkhud->closeKeyboard();
} else {
inkhud->freeText(key);
}
}
void InkHUD::KeyboardApplet::onButtonLongPress()
{
char key = keys[selectedKey];
if (key == '\n') {
inkhud->freeTextDone();
inkhud->closeKeyboard();
} else if (key == '\x1b') {
inkhud->freeTextCancel();
inkhud->closeKeyboard();
} else {
if (key >= 0x61)
key -= 32; // capitalize
inkhud->freeText(key);
}
}
void InkHUD::KeyboardApplet::onExitShort()
{
inkhud->freeTextCancel();
inkhud->closeKeyboard();
}
void InkHUD::KeyboardApplet::onExitLong()
{
inkhud->freeTextCancel();
inkhud->closeKeyboard();
}
void InkHUD::KeyboardApplet::onNavUp()
{
if (selectedKey < KBD_COLS) // wrap
selectedKey += KBD_COLS * (KBD_ROWS - 1);
else // move 1 row back
selectedKey -= KBD_COLS;
// Request rendering over the previously drawn render
requestUpdate(EInk::UpdateTypes::FAST, false);
// Force an update to bypass lockRequests
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
}
void InkHUD::KeyboardApplet::onNavDown()
{
selectedKey += KBD_COLS;
selectedKey %= (KBD_COLS * KBD_ROWS);
// Request rendering over the previously drawn render
requestUpdate(EInk::UpdateTypes::FAST, false);
// Force an update to bypass lockRequests
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
}
void InkHUD::KeyboardApplet::onNavLeft()
{
if (selectedKey % KBD_COLS == 0) // wrap
selectedKey += KBD_COLS - 1;
else // move 1 column back
selectedKey--;
// Request rendering over the previously drawn render
requestUpdate(EInk::UpdateTypes::FAST, false);
// Force an update to bypass lockRequests
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
}
void InkHUD::KeyboardApplet::onNavRight()
{
if (selectedKey % KBD_COLS == KBD_COLS - 1) // wrap
selectedKey -= KBD_COLS - 1;
else // move 1 column forward
selectedKey++;
// Request rendering over the previously drawn render
requestUpdate(EInk::UpdateTypes::FAST, false);
// Force an update to bypass lockRequests
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
}
uint16_t InkHUD::KeyboardApplet::getKeyboardHeight()
{
const uint16_t keyH = fontSmall.lineHeight() * 1.2;
return keyH * KBD_ROWS;
}
#endif

View File

@@ -1,66 +0,0 @@
#ifdef MESHTASTIC_INCLUDE_INKHUD
/*
System Applet to render an on-screeen keyboard
*/
#pragma once
#include "configuration.h"
#include "graphics/niche/InkHUD/InkHUD.h"
#include "graphics/niche/InkHUD/SystemApplet.h"
#include <string>
namespace NicheGraphics::InkHUD
{
class KeyboardApplet : public SystemApplet
{
public:
KeyboardApplet();
void onRender(bool full) override;
void onForeground() override;
void onBackground() override;
void onButtonShortPress() override;
void onButtonLongPress() override;
void onExitShort() override;
void onExitLong() override;
void onNavUp() override;
void onNavDown() override;
void onNavLeft() override;
void onNavRight() override;
static uint16_t getKeyboardHeight(); // used to set the keyboard tile height
private:
void drawKeyLabel(uint16_t left, uint16_t top, uint16_t width, char key, Color color);
static const uint8_t KBD_COLS = 11;
static const uint8_t KBD_ROWS = 4;
const char keys[KBD_COLS * KBD_ROWS] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\b', // row 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '\n', // row 1
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '!', ' ', // row 2
'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '?', '\x1b' // row 3
};
// This array represents the widths of each key in points
// 16 pt = line height of the text
const uint16_t keyWidths[KBD_COLS * KBD_ROWS] = {
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, // row 0
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, // row 1
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, // row 2
16, 16, 16, 16, 16, 16, 16, 10, 10, 12, 40 // row 3
};
uint16_t rowWidths[KBD_ROWS];
uint8_t selectedKey = 0; // selected key index
uint8_t prevSelectedKey = 0;
};
} // namespace NicheGraphics::InkHUD
#endif

View File

@@ -30,7 +30,7 @@ InkHUD::LogoApplet::LogoApplet() : concurrency::OSThread("LogoApplet")
// This is then drawn with a FULL refresh by Renderer::begin // This is then drawn with a FULL refresh by Renderer::begin
} }
void InkHUD::LogoApplet::onRender(bool full) void InkHUD::LogoApplet::onRender()
{ {
// Size of the region which the logo should "scale to fit" // Size of the region which the logo should "scale to fit"
uint16_t logoWLimit = X(0.8); uint16_t logoWLimit = X(0.8);
@@ -120,7 +120,7 @@ void InkHUD::LogoApplet::onBackground()
// Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background // Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background
// Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case // Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case
inkhud->forceUpdate(EInk::UpdateTypes::FULL, true); inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
// Begin displaying the screen which is shown at shutdown // Begin displaying the screen which is shown at shutdown
@@ -138,10 +138,10 @@ void InkHUD::LogoApplet::onShutdown()
// Intention is to restore display health. // Intention is to restore display health.
inverted = true; inverted = true;
inkhud->forceUpdate(Drivers::EInk::FULL, true, false); inkhud->forceUpdate(Drivers::EInk::FULL, false);
delay(1000); // Cooldown. Back to back updates aren't great for health. delay(1000); // Cooldown. Back to back updates aren't great for health.
inverted = false; inverted = false;
inkhud->forceUpdate(Drivers::EInk::FULL, true, false); inkhud->forceUpdate(Drivers::EInk::FULL, false);
delay(1000); // Cooldown delay(1000); // Cooldown
// Prepare for the powered-off screen now // Prepare for the powered-off screen now
@@ -176,7 +176,7 @@ void InkHUD::LogoApplet::onReboot()
textTitle = "Rebooting..."; textTitle = "Rebooting...";
fontTitle = fontSmall; fontTitle = fontSmall;
inkhud->forceUpdate(Drivers::EInk::FULL, true, false); inkhud->forceUpdate(Drivers::EInk::FULL, false);
// Perform the update right now, waiting here until complete // Perform the update right now, waiting here until complete
} }

View File

@@ -21,7 +21,7 @@ class LogoApplet : public SystemApplet, public concurrency::OSThread
{ {
public: public:
LogoApplet(); LogoApplet();
void onRender(bool full) override; void onRender() override;
void onForeground() override; void onForeground() override;
void onBackground() override; void onBackground() override;
void onShutdown() override; void onShutdown() override;

View File

@@ -19,10 +19,10 @@ namespace NicheGraphics::InkHUD
enum MenuAction { enum MenuAction {
NO_ACTION, NO_ACTION,
SEND_PING, SEND_PING,
FREE_TEXT,
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,

View File

@@ -90,8 +90,6 @@ void InkHUD::MenuApplet::onForeground()
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL); OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::enabled = true; OSThread::enabled = true;
freeTextMode = false;
// Upgrade the refresh to FAST, for guaranteed responsiveness // Upgrade the refresh to FAST, for guaranteed responsiveness
inkhud->forceUpdate(EInk::UpdateTypes::FAST); inkhud->forceUpdate(EInk::UpdateTypes::FAST);
} }
@@ -118,8 +116,6 @@ void InkHUD::MenuApplet::onBackground()
SystemApplet::lockRequests = false; SystemApplet::lockRequests = false;
SystemApplet::handleInput = false; SystemApplet::handleInput = false;
handleFreeText = false;
// Restore the user applet whose tile we borrowed // Restore the user applet whose tile we borrowed
if (borrowedTileOwner) if (borrowedTileOwner)
borrowedTileOwner->bringToForeground(); borrowedTileOwner->bringToForeground();
@@ -329,6 +325,10 @@ void InkHUD::MenuApplet::execute(MenuItem item)
} }
break; break;
case BACK:
showPage(item.nextPage);
return;
case NEXT_TILE: case NEXT_TILE:
inkhud->nextTile(); inkhud->nextTile();
// Unselect menu item after tile change // Unselect menu item after tile change
@@ -344,26 +344,12 @@ void InkHUD::MenuApplet::execute(MenuItem item)
inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL); inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL);
break; break;
case FREE_TEXT:
OSThread::enabled = false;
handleFreeText = true;
cm.freeTextItem.rawText.erase(); // clear the previous freetext message
freeTextMode = true; // render input field instead of normal menu
// Open the on-screen keyboard if the joystick is enabled
if (settings->joystick.enabled)
inkhud->openKeyboard();
break;
case STORE_CANNEDMESSAGE_SELECTION: case STORE_CANNEDMESSAGE_SELECTION:
if (!settings->joystick.enabled) cm.selectedMessageItem = &cm.messageItems.at(cursor - 1); // Minus one: offset for the initial "Send Ping" entry
cm.selectedMessageItem = &cm.messageItems.at(cursor - 1); // Minus one: offset for the initial "Send Ping" entry
else
cm.selectedMessageItem = &cm.messageItems.at(cursor - 2); // Minus two: offset for the "Send Ping" and free text entry
break; break;
case SEND_CANNEDMESSAGE: case SEND_CANNEDMESSAGE:
cm.selectedRecipientItem = &cm.recipientItems.at(cursor); cm.selectedRecipientItem = &cm.recipientItems.at(cursor);
// send selected message
sendText(cm.selectedRecipientItem->dest, cm.selectedRecipientItem->channelIndex, cm.selectedMessageItem->rawText.c_str()); sendText(cm.selectedRecipientItem->dest, cm.selectedRecipientItem->channelIndex, cm.selectedMessageItem->rawText.c_str());
inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL); // Next refresh should be FULL. Lots of button pressing to get here inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL); // Next refresh should be FULL. Lots of button pressing to get here
break; break;
@@ -882,7 +868,6 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
switch (page) { switch (page) {
case ROOT: case ROOT:
previousPage = MenuPage::EXIT;
// Optional: next applet // Optional: next applet
if (settings->optionalMenuItems.nextTile && settings->userTiles.count > 1) if (settings->optionalMenuItems.nextTile && settings->userTiles.count > 1)
items.push_back(MenuItem("Next Tile", MenuAction::NEXT_TILE, MenuPage::ROOT)); // Only if multiple applets shown items.push_back(MenuItem("Next Tile", MenuAction::NEXT_TILE, MenuPage::ROOT)); // Only if multiple applets shown
@@ -893,6 +878,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
items.push_back(MenuItem("Node Config", MenuPage::NODE_CONFIG)); items.push_back(MenuItem("Node Config", MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Save & Shut Down", MenuAction::SHUTDOWN)); items.push_back(MenuItem("Save & Shut Down", MenuAction::SHUTDOWN));
items.push_back(MenuItem("Exit", MenuPage::EXIT)); items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::EXIT;
break; break;
case SEND: case SEND:
@@ -902,12 +888,11 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
case CANNEDMESSAGE_RECIPIENT: case CANNEDMESSAGE_RECIPIENT:
populateRecipientPage(); populateRecipientPage();
previousPage = MenuPage::SEND; previousPage = MenuPage::OPTIONS;
break; break;
case OPTIONS: case OPTIONS:
previousPage = MenuPage::ROOT; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::ROOT));
items.push_back(MenuItem("Back", previousPage));
// Optional: backlight // Optional: backlight
if (settings->optionalMenuItems.backlight) if (settings->optionalMenuItems.backlight)
items.push_back(MenuItem(backlight->isLatched() ? "Backlight Off" : "Keep Backlight On", // Label items.push_back(MenuItem(backlight->isLatched() ? "Backlight Off" : "Keep Backlight On", // Label
@@ -931,32 +916,31 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
invertedColors = (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED); invertedColors = (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
items.push_back(MenuItem("Invert Color", MenuAction::TOGGLE_INVERT_COLOR, MenuPage::OPTIONS, &invertedColors)); items.push_back(MenuItem("Invert Color", MenuAction::TOGGLE_INVERT_COLOR, MenuPage::OPTIONS, &invertedColors));
items.push_back(MenuItem("Exit", MenuPage::EXIT)); items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::ROOT;
break; break;
case APPLETS: case APPLETS:
previousPage = MenuPage::OPTIONS;
populateAppletPage(); // must be first populateAppletPage(); // must be first
items.insert(items.begin(), MenuItem("Back", previousPage)); items.insert(items.begin(), MenuItem("Back", MenuAction::BACK, MenuPage::OPTIONS));
items.push_back(MenuItem("Exit", MenuPage::EXIT)); items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::OPTIONS;
break; break;
case AUTOSHOW: case AUTOSHOW:
previousPage = MenuPage::OPTIONS;
populateAutoshowPage(); // must be first populateAutoshowPage(); // must be first
items.insert(items.begin(), MenuItem("Back", previousPage)); items.insert(items.begin(), MenuItem("Back", MenuAction::BACK, MenuPage::OPTIONS));
items.push_back(MenuItem("Exit", MenuPage::EXIT)); items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::OPTIONS;
break; break;
case RECENTS: case RECENTS:
previousPage = MenuPage::OPTIONS;
populateRecentsPage(); // builds only the options populateRecentsPage(); // builds only the options
items.insert(items.begin(), MenuItem("Back", previousPage)); items.insert(items.begin(), MenuItem("Back", MenuAction::BACK, MenuPage::OPTIONS));
items.push_back(MenuItem("Exit", MenuPage::EXIT)); items.push_back(MenuItem("Exit", MenuPage::EXIT));
break; break;
case NODE_CONFIG: case NODE_CONFIG:
previousPage = MenuPage::ROOT; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::ROOT));
items.push_back(MenuItem("Back", previousPage));
// Radio Config Section // Radio Config Section
items.push_back(MenuItem::Header("Radio Config")); items.push_back(MenuItem::Header("Radio Config"));
items.push_back(MenuItem("LoRa", MenuPage::NODE_CONFIG_LORA)); items.push_back(MenuItem("LoRa", MenuPage::NODE_CONFIG_LORA));
@@ -981,8 +965,8 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
break; break;
case NODE_CONFIG_DEVICE: { case NODE_CONFIG_DEVICE: {
previousPage = MenuPage::NODE_CONFIG;
items.push_back(MenuItem("Back", previousPage)); items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
const char *role = DisplayFormatters::getDeviceRole(config.device.role); const char *role = DisplayFormatters::getDeviceRole(config.device.role);
nodeConfigLabels.emplace_back("Role: " + std::string(role)); nodeConfigLabels.emplace_back("Role: " + std::string(role));
@@ -997,8 +981,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_POSITION: { case NODE_CONFIG_POSITION: {
previousPage = MenuPage::NODE_CONFIG; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Back", previousPage));
#if !MESHTASTIC_EXCLUDE_GPS && HAS_GPS #if !MESHTASTIC_EXCLUDE_GPS && HAS_GPS
const auto mode = config.position.gps_mode; const auto mode = config.position.gps_mode;
if (mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { if (mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) {
@@ -1013,8 +996,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_POWER: { case NODE_CONFIG_POWER: {
previousPage = MenuPage::NODE_CONFIG; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Back", previousPage));
#if defined(ARCH_ESP32) #if defined(ARCH_ESP32)
items.push_back(MenuItem("Powersave", MenuAction::TOGGLE_POWER_SAVE, MenuPage::EXIT, &config.power.is_power_saving)); items.push_back(MenuItem("Powersave", MenuAction::TOGGLE_POWER_SAVE, MenuPage::EXIT, &config.power.is_power_saving));
#endif #endif
@@ -1047,8 +1029,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_POWER_ADC_CAL: { case NODE_CONFIG_POWER_ADC_CAL: {
previousPage = MenuPage::NODE_CONFIG_POWER; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG_POWER));
items.push_back(MenuItem("Back", previousPage));
// Instruction text (header-style, non-selectable) // Instruction text (header-style, non-selectable)
items.push_back(MenuItem::Header("Run on full charge Only")); items.push_back(MenuItem::Header("Run on full charge Only"));
@@ -1061,8 +1042,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_NETWORK: { case NODE_CONFIG_NETWORK: {
previousPage = MenuPage::NODE_CONFIG; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Back", previousPage));
const char *wifiLabel = config.network.wifi_enabled ? "WiFi: On" : "WiFi: Off"; const char *wifiLabel = config.network.wifi_enabled ? "WiFi: On" : "WiFi: Off";
@@ -1119,8 +1099,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_DISPLAY: { case NODE_CONFIG_DISPLAY: {
previousPage = MenuPage::NODE_CONFIG; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Back", previousPage));
items.push_back(MenuItem("12-Hour Clock", MenuAction::TOGGLE_12H_CLOCK, MenuPage::NODE_CONFIG_DISPLAY, items.push_back(MenuItem("12-Hour Clock", MenuAction::TOGGLE_12H_CLOCK, MenuPage::NODE_CONFIG_DISPLAY,
&config.display.use_12h_clock)); &config.display.use_12h_clock));
@@ -1135,8 +1114,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_BLUETOOTH: { case NODE_CONFIG_BLUETOOTH: {
previousPage = MenuPage::NODE_CONFIG; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Back", previousPage));
const char *btLabel = config.bluetooth.enabled ? "Bluetooth: On" : "Bluetooth: Off"; const char *btLabel = config.bluetooth.enabled ? "Bluetooth: On" : "Bluetooth: Off";
items.push_back(MenuItem(btLabel, MenuAction::TOGGLE_BLUETOOTH, MenuPage::EXIT)); items.push_back(MenuItem(btLabel, MenuAction::TOGGLE_BLUETOOTH, MenuPage::EXIT));
@@ -1149,8 +1127,8 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_LORA: { case NODE_CONFIG_LORA: {
previousPage = MenuPage::NODE_CONFIG;
items.push_back(MenuItem("Back", previousPage)); items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
const char *region = myRegion ? myRegion->name : "Unset"; const char *region = myRegion ? myRegion->name : "Unset";
nodeConfigLabels.emplace_back("Region: " + std::string(region)); nodeConfigLabels.emplace_back("Region: " + std::string(region));
@@ -1172,8 +1150,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_CHANNELS: { case NODE_CONFIG_CHANNELS: {
previousPage = MenuPage::NODE_CONFIG; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Back", previousPage));
for (uint8_t i = 0; i < MAX_NUM_CHANNELS; i++) { for (uint8_t i = 0; i < MAX_NUM_CHANNELS; i++) {
meshtastic_Channel &ch = channels.getByIndex(i); meshtastic_Channel &ch = channels.getByIndex(i);
@@ -1204,8 +1181,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_CHANNEL_DETAIL: { case NODE_CONFIG_CHANNEL_DETAIL: {
previousPage = MenuPage::NODE_CONFIG_CHANNELS; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG_CHANNELS));
items.push_back(MenuItem("Back", previousPage));
meshtastic_Channel &ch = channels.getByIndex(selectedChannelIndex); meshtastic_Channel &ch = channels.getByIndex(selectedChannelIndex);
@@ -1250,8 +1226,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_CHANNEL_PRECISION: { case NODE_CONFIG_CHANNEL_PRECISION: {
previousPage = MenuPage::NODE_CONFIG_CHANNEL_DETAIL; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG_CHANNEL_DETAIL));
items.push_back(MenuItem("Back", previousPage));
meshtastic_Channel &ch = channels.getByIndex(selectedChannelIndex); meshtastic_Channel &ch = channels.getByIndex(selectedChannelIndex);
if (!ch.settings.has_module_settings || ch.settings.module_settings.position_precision == 0) { if (!ch.settings.has_module_settings || ch.settings.module_settings.position_precision == 0) {
items.push_back(MenuItem("Position is Off", MenuPage::NODE_CONFIG_CHANNEL_DETAIL)); items.push_back(MenuItem("Position is Off", MenuPage::NODE_CONFIG_CHANNEL_DETAIL));
@@ -1272,8 +1247,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case NODE_CONFIG_DEVICE_ROLE: { case NODE_CONFIG_DEVICE_ROLE: {
previousPage = MenuPage::NODE_CONFIG_DEVICE; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG_DEVICE));
items.push_back(MenuItem("Back", previousPage));
items.push_back(MenuItem("Client", MenuAction::SET_ROLE_CLIENT, MenuPage::EXIT)); items.push_back(MenuItem("Client", MenuAction::SET_ROLE_CLIENT, MenuPage::EXIT));
items.push_back(MenuItem("Client Mute", MenuAction::SET_ROLE_CLIENT_MUTE, MenuPage::EXIT)); items.push_back(MenuItem("Client Mute", MenuAction::SET_ROLE_CLIENT_MUTE, MenuPage::EXIT));
items.push_back(MenuItem("Router", MenuAction::SET_ROLE_ROUTER, MenuPage::EXIT)); items.push_back(MenuItem("Router", MenuAction::SET_ROLE_ROUTER, MenuPage::EXIT));
@@ -1283,8 +1257,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
case TIMEZONE: case TIMEZONE:
previousPage = MenuPage::NODE_CONFIG_DEVICE; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG_DEVICE));
items.push_back(MenuItem("Back", previousPage));
items.push_back(MenuItem("US/Hawaii", SET_TZ_US_HAWAII, MenuPage::NODE_CONFIG_DEVICE)); items.push_back(MenuItem("US/Hawaii", SET_TZ_US_HAWAII, MenuPage::NODE_CONFIG_DEVICE));
items.push_back(MenuItem("US/Alaska", SET_TZ_US_ALASKA, MenuPage::NODE_CONFIG_DEVICE)); items.push_back(MenuItem("US/Alaska", SET_TZ_US_ALASKA, MenuPage::NODE_CONFIG_DEVICE));
items.push_back(MenuItem("US/Pacific", SET_TZ_US_PACIFIC, MenuPage::NODE_CONFIG_DEVICE)); items.push_back(MenuItem("US/Pacific", SET_TZ_US_PACIFIC, MenuPage::NODE_CONFIG_DEVICE));
@@ -1306,8 +1279,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
break; break;
case REGION: case REGION:
previousPage = MenuPage::NODE_CONFIG_LORA; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG_LORA));
items.push_back(MenuItem("Back", previousPage));
items.push_back(MenuItem("US", MenuAction::SET_REGION_US, MenuPage::EXIT)); items.push_back(MenuItem("US", MenuAction::SET_REGION_US, MenuPage::EXIT));
items.push_back(MenuItem("EU 868", MenuAction::SET_REGION_EU_868, MenuPage::EXIT)); items.push_back(MenuItem("EU 868", MenuAction::SET_REGION_EU_868, MenuPage::EXIT));
items.push_back(MenuItem("EU 433", MenuAction::SET_REGION_EU_433, MenuPage::EXIT)); items.push_back(MenuItem("EU 433", MenuAction::SET_REGION_EU_433, MenuPage::EXIT));
@@ -1338,8 +1310,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
break; break;
case NODE_CONFIG_PRESET: { case NODE_CONFIG_PRESET: {
previousPage = MenuPage::NODE_CONFIG_LORA; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG_LORA));
items.push_back(MenuItem("Back", previousPage));
items.push_back(MenuItem("Long Moderate", MenuAction::SET_PRESET_LONG_MODERATE, MenuPage::EXIT)); items.push_back(MenuItem("Long Moderate", MenuAction::SET_PRESET_LONG_MODERATE, MenuPage::EXIT));
items.push_back(MenuItem("Long Fast", MenuAction::SET_PRESET_LONG_FAST, MenuPage::EXIT)); items.push_back(MenuItem("Long Fast", MenuAction::SET_PRESET_LONG_FAST, MenuPage::EXIT));
items.push_back(MenuItem("Medium Slow", MenuAction::SET_PRESET_MEDIUM_SLOW, MenuPage::EXIT)); items.push_back(MenuItem("Medium Slow", MenuAction::SET_PRESET_MEDIUM_SLOW, MenuPage::EXIT));
@@ -1352,8 +1323,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
} }
// Administration Section // Administration Section
case NODE_CONFIG_ADMIN_RESET: case NODE_CONFIG_ADMIN_RESET:
previousPage = MenuPage::NODE_CONFIG; items.push_back(MenuItem("Back", MenuAction::BACK, MenuPage::NODE_CONFIG));
items.push_back(MenuItem("Back", previousPage));
items.push_back(MenuItem("Reset All", MenuAction::RESET_NODEDB_ALL, MenuPage::EXIT)); items.push_back(MenuItem("Reset All", MenuAction::RESET_NODEDB_ALL, MenuPage::EXIT));
items.push_back(MenuItem("Keep Favorites Only", MenuAction::RESET_NODEDB_KEEP_FAVORITES, MenuPage::EXIT)); items.push_back(MenuItem("Keep Favorites Only", MenuAction::RESET_NODEDB_KEEP_FAVORITES, MenuPage::EXIT));
items.push_back(MenuItem("Exit", MenuPage::EXIT)); items.push_back(MenuItem("Exit", MenuPage::EXIT));
@@ -1391,14 +1361,8 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
currentPage = page; currentPage = page;
} }
void InkHUD::MenuApplet::onRender(bool full) void InkHUD::MenuApplet::onRender()
{ {
// Free text mode draws a text input field and skips the normal rendering
if (freeTextMode) {
drawInputField(0, fontSmall.lineHeight(), X(1.0), Y(1.0) - fontSmall.lineHeight() - 1, cm.freeTextItem.rawText);
return;
}
if (items.size() == 0) if (items.size() == 0)
LOG_ERROR("Empty Menu"); LOG_ERROR("Empty Menu");
@@ -1517,48 +1481,44 @@ void InkHUD::MenuApplet::onRender(bool full)
void InkHUD::MenuApplet::onButtonShortPress() void InkHUD::MenuApplet::onButtonShortPress()
{ {
if (!freeTextMode) { // Push the auto-close timer back
// Push the auto-close timer back OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
if (!settings->joystick.enabled) { if (!settings->joystick.enabled) {
if (!cursorShown) { if (!cursorShown) {
cursorShown = true; cursorShown = true;
cursor = 0; cursor = 0;
} else {
do {
cursor = (cursor + 1) % items.size();
} while (items.at(cursor).isHeader);
}
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} else { } else {
if (cursorShown) do {
execute(items.at(cursor)); cursor = (cursor + 1) % items.size();
else } while (items.at(cursor).isHeader);
showPage(MenuPage::EXIT);
if (!wantsToRender())
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} else {
if (cursorShown)
execute(items.at(cursor));
else
showPage(MenuPage::EXIT);
if (!wantsToRender())
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
} }
void InkHUD::MenuApplet::onButtonLongPress() void InkHUD::MenuApplet::onButtonLongPress()
{ {
if (!freeTextMode) { // Push the auto-close timer back
// Push the auto-close timer back OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
if (cursorShown) if (cursorShown)
execute(items.at(cursor)); execute(items.at(cursor));
else else
showPage(MenuPage::EXIT); // Special case: Peek at root-menu; longpress again to close showPage(MenuPage::EXIT); // Special case: Peek at root-menu; longpress again to close
// If we didn't already request a specialized update, when handling a menu action, // If we didn't already request a specialized update, when handling a menu action,
// then perform the usual fast update. // then perform the usual fast update.
// FAST keeps things responsive: important because we're dealing with user input // FAST keeps things responsive: important because we're dealing with user input
if (!wantsToRender()) if (!wantsToRender())
requestUpdate(Drivers::EInk::UpdateTypes::FAST); requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
} }
void InkHUD::MenuApplet::onExitShort() void InkHUD::MenuApplet::onExitShort()
@@ -1571,107 +1531,56 @@ void InkHUD::MenuApplet::onExitShort()
void InkHUD::MenuApplet::onNavUp() void InkHUD::MenuApplet::onNavUp()
{ {
if (!freeTextMode) { OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
if (!cursorShown) { if (!cursorShown) {
cursorShown = true; cursorShown = true;
cursor = 0; cursor = 0;
} else { } else {
do { do {
if (cursor == 0) if (cursor == 0)
cursor = items.size() - 1; cursor = items.size() - 1;
else else
cursor--; cursor--;
} while (items.at(cursor).isHeader); } while (items.at(cursor).isHeader);
}
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
void InkHUD::MenuApplet::onNavDown() void InkHUD::MenuApplet::onNavDown()
{ {
if (!freeTextMode) { OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
if (!cursorShown) { if (!cursorShown) {
cursorShown = true; cursorShown = true;
cursor = 0; cursor = 0;
} else { } else {
do { do {
cursor = (cursor + 1) % items.size(); cursor = (cursor + 1) % items.size();
} while (items.at(cursor).isHeader); } while (items.at(cursor).isHeader);
}
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
void InkHUD::MenuApplet::onNavLeft() void InkHUD::MenuApplet::onNavLeft()
{ {
if (!freeTextMode) { OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
// Go to the previous menu page // Go to the previous menu page
showPage(previousPage); showPage(previousPage);
requestUpdate(Drivers::EInk::UpdateTypes::FAST); requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
} }
void InkHUD::MenuApplet::onNavRight() void InkHUD::MenuApplet::onNavRight()
{ {
if (!freeTextMode) {
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
if (cursorShown)
execute(items.at(cursor));
if (!wantsToRender())
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
}
void InkHUD::MenuApplet::onFreeText(char c)
{
if (cm.freeTextItem.rawText.length() >= menuTextLimit && c != '\b')
return;
if (c == '\b') {
if (!cm.freeTextItem.rawText.empty())
cm.freeTextItem.rawText.pop_back();
} else {
cm.freeTextItem.rawText += c;
}
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
void InkHUD::MenuApplet::onFreeTextDone()
{
// Restart the auto-close timeout
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL); OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::enabled = true;
handleFreeText = false; if (cursorShown)
freeTextMode = false; execute(items.at(cursor));
if (!wantsToRender())
if (!cm.freeTextItem.rawText.empty()) { requestUpdate(Drivers::EInk::UpdateTypes::FAST);
cm.selectedMessageItem = &cm.freeTextItem;
showPage(MenuPage::CANNEDMESSAGE_RECIPIENT);
}
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
void InkHUD::MenuApplet::onFreeTextCancel()
{
// Restart the auto-close timeout
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
OSThread::enabled = true;
handleFreeText = false;
freeTextMode = false;
// Clear the free text message
cm.freeTextItem.rawText.erase();
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
// Dynamically create MenuItem entries for activating / deactivating Applets, for the "Applet Selection" submenu // Dynamically create MenuItem entries for activating / deactivating Applets, for the "Applet Selection" submenu
@@ -1726,10 +1635,6 @@ void InkHUD::MenuApplet::populateSendPage()
// Position / NodeInfo packet // Position / NodeInfo packet
items.push_back(MenuItem("Ping", MenuAction::SEND_PING, MenuPage::EXIT)); items.push_back(MenuItem("Ping", MenuAction::SEND_PING, MenuPage::EXIT));
// If joystick is available, include the Free Text option
if (settings->joystick.enabled)
items.push_back(MenuItem("Free Text", MenuAction::FREE_TEXT, MenuPage::SEND));
// One menu item for each canned message // One menu item for each canned message
uint8_t count = cm.store->size(); uint8_t count = cm.store->size();
for (uint8_t i = 0; i < count; i++) { for (uint8_t i = 0; i < count; i++) {
@@ -1829,48 +1734,6 @@ void InkHUD::MenuApplet::populateRecipientPage()
items.push_back(MenuItem("Exit", MenuPage::EXIT)); items.push_back(MenuItem("Exit", MenuPage::EXIT));
} }
void InkHUD::MenuApplet::drawInputField(uint16_t left, uint16_t top, uint16_t width, uint16_t height, std::string text)
{
setFont(fontSmall);
uint16_t wrapMaxH = 0;
// Draw the text, input box, and cursor
// Adjusting the box for screen height
while (wrapMaxH < height - fontSmall.lineHeight()) {
wrapMaxH += fontSmall.lineHeight();
}
// If the text is so long that it goes outside of the input box, the text is actually rendered off screen.
uint32_t textHeight = getWrappedTextHeight(0, width - 5, text);
if (!text.empty()) {
uint16_t textPadding = X(1.0) > Y(1.0) ? wrapMaxH - textHeight : wrapMaxH - textHeight + 1;
if (textHeight > wrapMaxH)
printWrapped(2, textPadding, width - 5, text);
else
printWrapped(2, top + 2, width - 5, text);
}
uint16_t textCursorX = text.empty() ? 1 : getCursorX();
uint16_t textCursorY = text.empty() ? fontSmall.lineHeight() + 2 : getCursorY() - fontSmall.lineHeight() + 3;
if (textCursorX + 1 > width - 5) {
textCursorX = getCursorX() - width + 5;
textCursorY += fontSmall.lineHeight();
}
fillRect(textCursorX + 1, textCursorY, 1, fontSmall.lineHeight(), BLACK);
// A white rectangle clears the top part of the screen for any text that's printed beyond the input box
fillRect(0, 0, X(1.0), top, WHITE);
// Draw character limit
std::string ftlen = std::to_string(text.length()) + "/" + to_string(menuTextLimit);
uint16_t textLen = getTextWidth(ftlen);
printAt(X(1.0) - textLen - 2, 0, ftlen);
// Draw the border
drawRect(0, top, width, wrapMaxH + 5, BLACK);
}
// Renders the panel shown at the top of the root menu. // Renders the panel shown at the top of the root menu.
// Displays the clock, and several other pieces of instantaneous system info, // Displays the clock, and several other pieces of instantaneous system info,
// which we'd prefer not to have displayed in a normal applet, as they update too frequently. // which we'd prefer not to have displayed in a normal applet, as they update too frequently.
@@ -2012,4 +1875,4 @@ void InkHUD::MenuApplet::freeCannedMessageResources()
cm.messageItems.clear(); cm.messageItems.clear();
cm.recipientItems.clear(); cm.recipientItems.clear();
} }
#endif // MESHTASTIC_INCLUDE_INKHUD #endif // MESHTASTIC_INCLUDE_INKHUD

View File

@@ -32,10 +32,7 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
void onNavDown() override; void onNavDown() override;
void onNavLeft() override; void onNavLeft() override;
void onNavRight() override; void onNavRight() override;
void onFreeText(char c) override; void onRender() override;
void onFreeTextDone() override;
void onFreeTextCancel() override;
void onRender(bool full) 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); void setStartPage(MenuPage page);
@@ -54,8 +51,6 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
void populateAutoshowPage(); // Dynamically create MenuItems for selecting which applets can autoshow void populateAutoshowPage(); // Dynamically create MenuItems for selecting which applets can autoshow
void populateRecentsPage(); // Create menu items: a choice of values for settings.recentlyActiveSeconds void populateRecentsPage(); // Create menu items: a choice of values for settings.recentlyActiveSeconds
void drawInputField(uint16_t left, uint16_t top, uint16_t width, uint16_t height,
std::string text); // Draw input field for free text
uint16_t getSystemInfoPanelHeight(); uint16_t getSystemInfoPanelHeight();
void drawSystemInfoPanel(int16_t left, int16_t top, uint16_t width, void drawSystemInfoPanel(int16_t left, int16_t top, uint16_t width,
uint16_t *height = nullptr); // Info panel at top of root menu uint16_t *height = nullptr); // Info panel at top of root menu
@@ -67,9 +62,8 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
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
bool cursorShown = false; // Is *any* item highlighted? (Root menu: no initial selection) bool cursorShown = false; // Is *any* item highlighted? (Root menu: no initial selection)
bool freeTextMode = false;
uint16_t systemInfoPanelHeight = 0; // Need to know before we render uint16_t systemInfoPanelHeight = 0; // Need to know before we render
uint16_t menuTextLimit = 200;
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 std::vector<std::string> nodeConfigLabels; // Persistent labels for Node Config pages
@@ -110,8 +104,6 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
// Cleared onBackground (when MenuApplet closes) // Cleared onBackground (when MenuApplet closes)
std::vector<MessageItem> messageItems; std::vector<MessageItem> messageItems;
std::vector<RecipientItem> recipientItems; std::vector<RecipientItem> recipientItems;
MessageItem freeTextItem;
} cm; } cm;
Applet *borrowedTileOwner = nullptr; // Which applet we have temporarily replaced while displaying menu Applet *borrowedTileOwner = nullptr; // Which applet we have temporarily replaced while displaying menu

View File

@@ -65,7 +65,7 @@ int InkHUD::NotificationApplet::onReceiveTextMessage(const meshtastic_MeshPacket
return 0; return 0;
} }
void InkHUD::NotificationApplet::onRender(bool full) void InkHUD::NotificationApplet::onRender()
{ {
// Clear the region beneath the tile // Clear the region beneath the tile
// Most applets are drawing onto an empty frame buffer and don't need to do this // Most applets are drawing onto an empty frame buffer and don't need to do this
@@ -139,47 +139,54 @@ void InkHUD::NotificationApplet::onForeground()
void InkHUD::NotificationApplet::onBackground() void InkHUD::NotificationApplet::onBackground()
{ {
handleInput = false; handleInput = false;
inkhud->forceUpdate(EInk::UpdateTypes::FULL, true);
} }
void InkHUD::NotificationApplet::onButtonShortPress() void InkHUD::NotificationApplet::onButtonShortPress()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::NotificationApplet::onButtonLongPress() void InkHUD::NotificationApplet::onButtonLongPress()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::NotificationApplet::onExitShort() void InkHUD::NotificationApplet::onExitShort()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::NotificationApplet::onExitLong() void InkHUD::NotificationApplet::onExitLong()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::NotificationApplet::onNavUp() void InkHUD::NotificationApplet::onNavUp()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::NotificationApplet::onNavDown() void InkHUD::NotificationApplet::onNavDown()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::NotificationApplet::onNavLeft() void InkHUD::NotificationApplet::onNavLeft()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
void InkHUD::NotificationApplet::onNavRight() void InkHUD::NotificationApplet::onNavRight()
{ {
dismiss(); dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
// Ask the WindowManager to check whether any displayed applets are already displaying the info from this notification // Ask the WindowManager to check whether any displayed applets are already displaying the info from this notification

View File

@@ -26,7 +26,7 @@ class NotificationApplet : public SystemApplet
public: public:
NotificationApplet(); NotificationApplet();
void onRender(bool full) override; void onRender() override;
void onForeground() override; void onForeground() override;
void onBackground() override; void onBackground() override;
void onButtonShortPress() override; void onButtonShortPress() override;

View File

@@ -9,7 +9,7 @@ InkHUD::PairingApplet::PairingApplet()
bluetoothStatusObserver.observe(&bluetoothStatus->onNewStatus); bluetoothStatusObserver.observe(&bluetoothStatus->onNewStatus);
} }
void InkHUD::PairingApplet::onRender(bool full) void InkHUD::PairingApplet::onRender()
{ {
// Header // Header
setFont(fontMedium); setFont(fontMedium);
@@ -45,7 +45,7 @@ void InkHUD::PairingApplet::onBackground()
// Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background // Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background
// Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case // Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case
inkhud->forceUpdate(EInk::UpdateTypes::FULL, true); inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
int InkHUD::PairingApplet::onBluetoothStatusUpdate(const meshtastic::Status *status) int InkHUD::PairingApplet::onBluetoothStatusUpdate(const meshtastic::Status *status)

View File

@@ -22,7 +22,7 @@ class PairingApplet : public SystemApplet
public: public:
PairingApplet(); PairingApplet();
void onRender(bool full) override; void onRender() override;
void onForeground() override; void onForeground() override;
void onBackground() override; void onBackground() override;

View File

@@ -4,7 +4,7 @@
using namespace NicheGraphics; using namespace NicheGraphics;
void InkHUD::PlaceholderApplet::onRender(bool full) void InkHUD::PlaceholderApplet::onRender()
{ {
// This placeholder applet fills its area with sparse diagonal lines // This placeholder applet fills its area with sparse diagonal lines
hatchRegion(0, 0, width(), height(), 8, BLACK); hatchRegion(0, 0, width(), height(), 8, BLACK);

View File

@@ -17,7 +17,7 @@ namespace NicheGraphics::InkHUD
class PlaceholderApplet : public SystemApplet class PlaceholderApplet : public SystemApplet
{ {
public: public:
void onRender(bool full) override; void onRender() override;
// Note: onForeground, onBackground, and wantsToRender are not meaningful for this applet. // Note: onForeground, onBackground, and wantsToRender are not meaningful for this applet.
// The window manager decides when and where it should be rendered // The window manager decides when and where it should be rendered

View File

@@ -45,7 +45,7 @@ InkHUD::TipsApplet::TipsApplet()
bringToForeground(); bringToForeground();
} }
void InkHUD::TipsApplet::onRender(bool full) void InkHUD::TipsApplet::onRender()
{ {
switch (tipQueue.front()) { switch (tipQueue.front()) {
case Tip::WELCOME: case Tip::WELCOME:
@@ -261,7 +261,7 @@ void InkHUD::TipsApplet::onBackground()
// Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background // Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background
// Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case // Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case
inkhud->forceUpdate(EInk::UpdateTypes::FULL, true); inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} }
// While our SystemApplet::handleInput flag is true // While our SystemApplet::handleInput flag is true
@@ -292,8 +292,9 @@ void InkHUD::TipsApplet::onButtonShortPress()
inkhud->persistence->saveSettings(); inkhud->persistence->saveSettings();
} }
// Close applet // Close applet and clean the screen
sendToBackground(); sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
} else { } else {
requestUpdate(); requestUpdate();
} }
@@ -305,4 +306,4 @@ void InkHUD::TipsApplet::onExitShort()
onButtonShortPress(); onButtonShortPress();
} }
#endif #endif

View File

@@ -33,7 +33,7 @@ class TipsApplet : public SystemApplet
public: public:
TipsApplet(); TipsApplet();
void onRender(bool full) override; void onRender() override;
void onForeground() override; void onForeground() override;
void onBackground() override; void onBackground() override;
void onButtonShortPress() override; void onButtonShortPress() override;

View File

@@ -34,7 +34,7 @@ int InkHUD::AllMessageApplet::onReceiveTextMessage(const meshtastic_MeshPacket *
return 0; return 0;
} }
void InkHUD::AllMessageApplet::onRender(bool full) void InkHUD::AllMessageApplet::onRender()
{ {
// Find newest message, regardless of whether DM or broadcast // Find newest message, regardless of whether DM or broadcast
MessageStore::Message *message; MessageStore::Message *message;

View File

@@ -30,7 +30,7 @@ class Applet;
class AllMessageApplet : public Applet class AllMessageApplet : public Applet
{ {
public: public:
void onRender(bool full) override; void onRender() override;
void onActivate() override; void onActivate() override;
void onDeactivate() override; void onDeactivate() override;

View File

@@ -37,7 +37,7 @@ int InkHUD::DMApplet::onReceiveTextMessage(const meshtastic_MeshPacket *p)
return 0; return 0;
} }
void InkHUD::DMApplet::onRender(bool full) void InkHUD::DMApplet::onRender()
{ {
// Abort if no text message // Abort if no text message
if (!latestMessage->dm.sender) { if (!latestMessage->dm.sender) {

View File

@@ -30,7 +30,7 @@ class Applet;
class DMApplet : public Applet class DMApplet : public Applet
{ {
public: public:
void onRender(bool full) override; void onRender() override;
void onActivate() override; void onActivate() override;
void onDeactivate() override; void onDeactivate() override;

View File

@@ -5,10 +5,10 @@
using namespace NicheGraphics; using namespace NicheGraphics;
void InkHUD::PositionsApplet::onRender(bool full) void InkHUD::PositionsApplet::onRender()
{ {
// Draw the usual map applet first // Draw the usual map applet first
MapApplet::onRender(full); MapApplet::onRender();
// Draw our latest "node of interest" as a special marker // Draw our latest "node of interest" as a special marker
// ------------------------------------------------------- // -------------------------------------------------------

View File

@@ -24,7 +24,7 @@ class PositionsApplet : public MapApplet, public SinglePortModule
{ {
public: public:
PositionsApplet() : SinglePortModule("PositionsApplet", meshtastic_PortNum_POSITION_APP) {} PositionsApplet() : SinglePortModule("PositionsApplet", meshtastic_PortNum_POSITION_APP) {}
void onRender(bool full) override; void onRender() override;
protected: protected:
ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override; ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;

View File

@@ -22,7 +22,7 @@ InkHUD::ThreadedMessageApplet::ThreadedMessageApplet(uint8_t channelIndex)
store = new MessageStore("ch" + to_string(channelIndex)); store = new MessageStore("ch" + to_string(channelIndex));
} }
void InkHUD::ThreadedMessageApplet::onRender(bool full) void InkHUD::ThreadedMessageApplet::onRender()
{ {
// ============= // =============
// Draw a header // Draw a header

View File

@@ -36,7 +36,7 @@ class ThreadedMessageApplet : public Applet, public SinglePortModule
explicit ThreadedMessageApplet(uint8_t channelIndex); explicit ThreadedMessageApplet(uint8_t channelIndex);
ThreadedMessageApplet() = delete; ThreadedMessageApplet() = delete;
void onRender(bool full) override; void onRender() override;
void onActivate() override; void onActivate() override;
void onDeactivate() override; void onDeactivate() override;

View File

@@ -238,39 +238,6 @@ void InkHUD::Events::onNavRight()
} }
} }
void InkHUD::Events::onFreeText(char c)
{
// Trigger the first system applet that wants to handle the new character
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleFreeText) {
sa->onFreeText(c);
break;
}
}
}
void InkHUD::Events::onFreeTextDone()
{
// Trigger the first system applet that wants to handle it
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleFreeText) {
sa->onFreeTextDone();
break;
}
}
}
void InkHUD::Events::onFreeTextCancel()
{
// Trigger the first system applet that wants to handle it
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleFreeText) {
sa->onFreeTextCancel();
break;
}
}
}
// Callback for deepSleepObserver // Callback for deepSleepObserver
// Returns 0 to signal that we agree to sleep now // Returns 0 to signal that we agree to sleep now
int InkHUD::Events::beforeDeepSleep(void *unused) int InkHUD::Events::beforeDeepSleep(void *unused)
@@ -299,7 +266,7 @@ int InkHUD::Events::beforeDeepSleep(void *unused)
// then prepared a final powered-off screen for us, which shows device shortname. // then prepared a final powered-off screen for us, which shows device shortname.
// We're updating to show that one now. // We're updating to show that one now.
inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL, true, false); inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL, false);
delay(1000); // Cooldown, before potentially yanking display power delay(1000); // Cooldown, before potentially yanking display power
// InkHUD shutdown complete // InkHUD shutdown complete

View File

@@ -37,11 +37,6 @@ class Events
void onNavLeft(); // Navigate left void onNavLeft(); // Navigate left
void onNavRight(); // Navigate right void onNavRight(); // Navigate right
// Free text typing events
void onFreeText(char c); // New freetext character input
void onFreeTextDone();
void onFreeTextCancel();
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
int onReceiveTextMessage(const meshtastic_MeshPacket *packet); // Store most recent text message int onReceiveTextMessage(const meshtastic_MeshPacket *packet); // Store most recent text message

View File

@@ -175,25 +175,6 @@ void InkHUD::InkHUD::navRight()
} }
} }
// Call this for keyboard input
// The Keyboard Applet also calls this
void InkHUD::InkHUD::freeText(char c)
{
events->onFreeText(c);
}
// Call this to complete a freetext input
void InkHUD::InkHUD::freeTextDone()
{
events->onFreeTextDone();
}
// Call this to cancel a freetext input
void InkHUD::InkHUD::freeTextCancel()
{
events->onFreeTextCancel();
}
// Cycle the next user applet to the foreground // Cycle the next user applet to the foreground
// Only activated applets are cycled // Only activated applets are cycled
// If user has a multi-applet layout, the applets will cycle on the "focused tile" // If user has a multi-applet layout, the applets will cycle on the "focused tile"
@@ -223,18 +204,6 @@ void InkHUD::InkHUD::openAlignStick()
windowManager->openAlignStick(); windowManager->openAlignStick();
} }
// Open the on-screen keyboard
void InkHUD::InkHUD::openKeyboard()
{
windowManager->openKeyboard();
}
// Close the on-screen keyboard
void InkHUD::InkHUD::closeKeyboard()
{
windowManager->closeKeyboard();
}
// In layouts where multiple applets are shown at once, change which tile is focused // In layouts where multiple applets are shown at once, change which tile is focused
// The focused tile in the one which cycles applets on button short press, and displays menu on long press // The focused tile in the one which cycles applets on button short press, and displays menu on long press
void InkHUD::InkHUD::nextTile() void InkHUD::InkHUD::nextTile()
@@ -283,11 +252,10 @@ void InkHUD::InkHUD::requestUpdate()
// Ignores all diplomacy: // Ignores all diplomacy:
// - the display *will* update // - the display *will* update
// - the specified update type *will* be used // - the specified update type *will* be used
// If the all parameter is true, the whole screen buffer is cleared and re-rendered
// If the async parameter is false, code flow is blocked while the update takes place // If the async parameter is false, code flow is blocked while the update takes place
void InkHUD::InkHUD::forceUpdate(EInk::UpdateTypes type, bool all, bool async) void InkHUD::InkHUD::forceUpdate(EInk::UpdateTypes type, bool async)
{ {
renderer->forceUpdate(type, all, async); renderer->forceUpdate(type, async);
} }
// Wait for any in-progress display update to complete before continuing // Wait for any in-progress display update to complete before continuing

View File

@@ -63,11 +63,6 @@ class InkHUD
void navLeft(); void navLeft();
void navRight(); void navRight();
// Freetext handlers
void freeText(char c);
void freeTextDone();
void freeTextCancel();
// Trigger UI changes // Trigger UI changes
// - called by various InkHUD components // - called by various InkHUD components
// - suitable(?) for use by aux button, connected in variant nicheGraphics.h // - suitable(?) for use by aux button, connected in variant nicheGraphics.h
@@ -76,8 +71,6 @@ class InkHUD
void prevApplet(); void prevApplet();
void openMenu(); void openMenu();
void openAlignStick(); void openAlignStick();
void openKeyboard();
void closeKeyboard();
void nextTile(); void nextTile();
void prevTile(); void prevTile();
void rotate(); void rotate();
@@ -91,8 +84,7 @@ class InkHUD
// - called by various InkHUD components // - called by various InkHUD components
void requestUpdate(); void requestUpdate();
void forceUpdate(Drivers::EInk::UpdateTypes type = Drivers::EInk::UpdateTypes::UNSPECIFIED, bool all = false, void forceUpdate(Drivers::EInk::UpdateTypes type = Drivers::EInk::UpdateTypes::UNSPECIFIED, bool async = true);
bool async = true);
void awaitUpdate(); void awaitUpdate();
// (Re)configuring WindowManager // (Re)configuring WindowManager

View File

@@ -56,16 +56,15 @@ void InkHUD::Renderer::setDisplayResilience(uint8_t fastPerFull, float stressMul
void InkHUD::Renderer::begin() void InkHUD::Renderer::begin()
{ {
forceUpdate(Drivers::EInk::UpdateTypes::FULL, true, false); forceUpdate(Drivers::EInk::UpdateTypes::FULL, false);
} }
// Set a flag, which will be picked up by runOnce, ASAP. // Set a flag, which will be picked up by runOnce, ASAP.
// Quite likely, multiple applets will all want to respond to one event (Observable, etc) // Quite likely, multiple applets will all want to respond to one event (Observable, etc)
// Each affected applet can independently call requestUpdate(), and all share the one opportunity to render, at next runOnce // Each affected applet can independently call requestUpdate(), and all share the one opportunity to render, at next runOnce
void InkHUD::Renderer::requestUpdate(bool all) void InkHUD::Renderer::requestUpdate()
{ {
requested = true; requested = true;
renderAll |= all;
// We will run the thread as soon as we loop(), // We will run the thread as soon as we loop(),
// after all Applets have had a chance to observe whatever event set this off // after all Applets have had a chance to observe whatever event set this off
@@ -80,11 +79,10 @@ void InkHUD::Renderer::requestUpdate(bool all)
// Sometimes, however, we will want to trigger a display update manually, in the absence of any sort of applet event // Sometimes, however, we will want to trigger a display update manually, in the absence of any sort of applet event
// Display health, for example. // Display health, for example.
// In these situations, we use forceUpdate // In these situations, we use forceUpdate
void InkHUD::Renderer::forceUpdate(Drivers::EInk::UpdateTypes type, bool all, bool async) void InkHUD::Renderer::forceUpdate(Drivers::EInk::UpdateTypes type, bool async)
{ {
requested = true; requested = true;
forced = true; forced = true;
renderAll |= all;
displayHealth.forceUpdateType(type); displayHealth.forceUpdateType(type);
// Normally, we need to start the timer, in case the display is busy and we briefly defer the update // Normally, we need to start the timer, in case the display is busy and we briefly defer the update
@@ -221,8 +219,7 @@ void InkHUD::Renderer::render(bool async)
Drivers::EInk::UpdateTypes updateType = decideUpdateType(); Drivers::EInk::UpdateTypes updateType = decideUpdateType();
// Render the new image // Render the new image
if (renderAll) clearBuffer();
clearBuffer();
renderUserApplets(); renderUserApplets();
renderPlaceholders(); renderPlaceholders();
renderSystemApplets(); renderSystemApplets();
@@ -250,7 +247,6 @@ void InkHUD::Renderer::render(bool async)
// Tidy up, ready for a new request // Tidy up, ready for a new request
requested = false; requested = false;
forced = false; forced = false;
renderAll = false;
} }
// Manually fill the image buffer with WHITE // Manually fill the image buffer with WHITE
@@ -263,76 +259,6 @@ void InkHUD::Renderer::clearBuffer()
memset(imageBuffer, 0xFF, imageBufferHeight * imageBufferWidth); memset(imageBuffer, 0xFF, imageBufferHeight * imageBufferWidth);
} }
// Manually clear the pixels below a tile
void InkHUD::Renderer::clearTile(Tile *t)
{
// Rotate the tile dimensions
int16_t left = 0;
int16_t top = 0;
uint16_t width = 0;
uint16_t height = 0;
switch (settings->rotation) {
case 0:
left = t->getLeft();
top = t->getTop();
width = t->getWidth();
height = t->getHeight();
break;
case 1:
left = driver->width - (t->getTop() + t->getHeight());
top = t->getLeft();
width = t->getHeight();
height = t->getWidth();
break;
case 2:
left = driver->width - (t->getLeft() + t->getWidth());
top = driver->height - (t->getTop() + t->getHeight());
width = t->getWidth();
height = t->getHeight();
break;
case 3:
left = t->getTop();
top = driver->height - (t->getLeft() + t->getWidth());
width = t->getHeight();
height = t->getWidth();
break;
}
// Calculate the bounds to clear
uint16_t xStart = (left < 0) ? 0 : left;
uint16_t yStart = (top < 0) ? 0 : top;
if (xStart >= driver->width || yStart >= driver->height || left + width < 0 || top + height < 0)
return; // the box is completely off the screen
uint16_t xEnd = left + width;
uint16_t yEnd = top + height;
if (xEnd > driver->width)
xEnd = driver->width;
if (yEnd > driver->height)
yEnd = driver->height;
// Clear the pixels
if (xStart == 0 && xEnd == driver->width) { // full width box is easier to clear
memset(imageBuffer + (yStart * imageBufferWidth), 0xFF, (yEnd - yStart) * imageBufferWidth);
} else {
const uint16_t byteStart = (xStart / 8) + 1;
const uint16_t byteEnd = xEnd / 8;
const uint8_t leadingByte = 0xFF >> (xStart - ((byteStart - 1) * 8));
const uint8_t trailingByte = (0xFF00 >> (xEnd - (byteEnd * 8))) & 0xFF;
for (uint16_t i = yStart * imageBufferWidth; i < yEnd * imageBufferWidth; i += imageBufferWidth) {
// Set the leading byte
imageBuffer[i + byteStart - 1] |= leadingByte;
// Set the continuous bytes
if (byteStart < byteEnd)
memset(imageBuffer + i + byteStart, 0xFF, byteEnd - byteStart);
// Set the trailing byte
if (byteEnd != imageBufferWidth)
imageBuffer[i + byteEnd] |= trailingByte;
}
}
}
void InkHUD::Renderer::checkLocks() void InkHUD::Renderer::checkLocks()
{ {
lockRendering = nullptr; lockRendering = nullptr;
@@ -397,12 +323,12 @@ Drivers::EInk::UpdateTypes InkHUD::Renderer::decideUpdateType()
if (!forced) { if (!forced) {
// User applets // User applets
for (Applet *ua : inkhud->userApplets) { for (Applet *ua : inkhud->userApplets) {
if (ua && ua->isForeground() && (ua->wantsToRender() || renderAll)) if (ua && ua->isForeground())
displayHealth.requestUpdateType(ua->wantsUpdateType()); displayHealth.requestUpdateType(ua->wantsUpdateType());
} }
// System Applets // System Applets
for (SystemApplet *sa : inkhud->systemApplets) { for (SystemApplet *sa : inkhud->systemApplets) {
if (sa && sa->isForeground() && (sa->wantsToRender() || sa->alwaysRender || renderAll)) if (sa && sa->isForeground())
displayHealth.requestUpdateType(sa->wantsUpdateType()); displayHealth.requestUpdateType(sa->wantsUpdateType());
} }
} }
@@ -420,16 +346,9 @@ void InkHUD::Renderer::renderUserApplets()
// Render any user applets which are currently visible // Render any user applets which are currently visible
for (Applet *ua : inkhud->userApplets) { for (Applet *ua : inkhud->userApplets) {
if (ua && ua->isActive() && ua->isForeground() && (ua->wantsToRender() || renderAll)) { if (ua && ua->isActive() && ua->isForeground()) {
// Clear the tile unless the applet wants to draw over its previous render
// or everything is getting re-rendered anyways
if (ua->wantsFullRender() && !renderAll)
clearTile(ua->getTile());
uint32_t start = millis(); uint32_t start = millis();
bool full = ua->wantsFullRender() || renderAll; ua->render(); // Draw!
ua->render(full); // Draw!
uint32_t stop = millis(); uint32_t stop = millis();
LOG_DEBUG("%s took %dms to render", ua->name, stop - start); LOG_DEBUG("%s took %dms to render", ua->name, stop - start);
} }
@@ -451,9 +370,6 @@ void InkHUD::Renderer::renderSystemApplets()
if (!sa->isForeground()) if (!sa->isForeground())
continue; continue;
if (!sa->wantsToRender() && !sa->alwaysRender && !renderAll)
continue;
// Skip if locked by another applet // Skip if locked by another applet
if (lockRendering && lockRendering != sa) if (lockRendering && lockRendering != sa)
continue; continue;
@@ -465,14 +381,8 @@ void InkHUD::Renderer::renderSystemApplets()
assert(sa->getTile()); assert(sa->getTile());
// Clear the tile unless the applet wants to draw over its previous render
// or everything is getting re-rendered anyways
if (sa->wantsFullRender() && !renderAll)
clearTile(sa->getTile());
// uint32_t start = millis(); // uint32_t start = millis();
bool full = sa->wantsFullRender() || renderAll; sa->render(); // Draw!
sa->render(full); // Draw!
// uint32_t stop = millis(); // uint32_t stop = millis();
// LOG_DEBUG("%s took %dms to render", sa->name, stop - start); // LOG_DEBUG("%s took %dms to render", sa->name, stop - start);
} }
@@ -499,10 +409,7 @@ void InkHUD::Renderer::renderPlaceholders()
// uint32_t start = millis(); // uint32_t start = millis();
for (Tile *t : emptyTiles) { for (Tile *t : emptyTiles) {
t->assignApplet(placeholder); t->assignApplet(placeholder);
// Clear the tile unless everything is getting re-rendered placeholder->render();
if (!renderAll)
clearTile(t);
placeholder->render(true); // full render
t->assignApplet(nullptr); t->assignApplet(nullptr);
} }
// uint32_t stop = millis(); // uint32_t stop = millis();

View File

@@ -37,8 +37,8 @@ class Renderer : protected concurrency::OSThread
// Call these to make the image change // Call these to make the image change
void requestUpdate(bool all = false); // Update display, if a foreground applet has info it wants to show void requestUpdate(); // Update display, if a foreground applet has info it wants to show
void forceUpdate(Drivers::EInk::UpdateTypes type = Drivers::EInk::UpdateTypes::UNSPECIFIED, bool all = false, void forceUpdate(Drivers::EInk::UpdateTypes type = Drivers::EInk::UpdateTypes::UNSPECIFIED,
bool async = true); // Update display, regardless of whether any applets requested this bool async = true); // Update display, regardless of whether any applets requested this
// Wait for an update to complete // Wait for an update to complete
@@ -65,7 +65,6 @@ class Renderer : protected concurrency::OSThread
// Steps of the rendering process // Steps of the rendering process
void clearBuffer(); void clearBuffer();
void clearTile(Tile *t);
void checkLocks(); void checkLocks();
bool shouldUpdate(); bool shouldUpdate();
Drivers::EInk::UpdateTypes decideUpdateType(); Drivers::EInk::UpdateTypes decideUpdateType();
@@ -86,7 +85,6 @@ class Renderer : protected concurrency::OSThread
bool requested = false; bool requested = false;
bool forced = false; bool forced = false;
bool renderAll = false;
// For convenience // For convenience
InkHUD *inkhud = nullptr; InkHUD *inkhud = nullptr;

View File

@@ -22,11 +22,9 @@ class SystemApplet : public Applet
public: public:
// System applets have the right to: // System applets have the right to:
bool handleInput = false; // - respond to input from the user button bool handleInput = false; // - respond to input from the user button
bool handleFreeText = false; // - respond to free text input bool lockRendering = false; // - prevent other applets from being rendered during an update
bool lockRendering = false; // - prevent other applets from being rendered during an update bool lockRequests = false; // - prevent other applets from triggering display updates
bool lockRequests = false; // - prevent other applets from triggering display updates
bool alwaysRender = false; // - render every time the screen is updated
virtual void onReboot() { onShutdown(); } // - handle reboot specially virtual void onReboot() { onShutdown(); } // - handle reboot specially
virtual void onApplyingChanges() {} virtual void onApplyingChanges() {}
@@ -43,4 +41,4 @@ class SystemApplet : public Applet
}; // namespace NicheGraphics::InkHUD }; // namespace NicheGraphics::InkHUD
#endif #endif

View File

@@ -18,7 +18,7 @@ static int32_t runtaskHighlight()
LOG_DEBUG("Dismissing Highlight"); LOG_DEBUG("Dismissing Highlight");
InkHUD::Tile::highlightShown = false; InkHUD::Tile::highlightShown = false;
InkHUD::Tile::highlightTarget = nullptr; InkHUD::Tile::highlightTarget = nullptr;
InkHUD::InkHUD::getInstance()->forceUpdate(Drivers::EInk::UpdateTypes::FAST, true); // Re-render, clearing the highlighting InkHUD::InkHUD::getInstance()->forceUpdate(Drivers::EInk::UpdateTypes::FAST); // Re-render, clearing the highlighting
return taskHighlight->disable(); return taskHighlight->disable();
} }
static void inittaskHighlight() static void inittaskHighlight()
@@ -190,18 +190,6 @@ void InkHUD::Tile::handleAppletPixel(int16_t x, int16_t y, Color c)
} }
} }
// Used in Renderer for clearing the tile
int16_t InkHUD::Tile::getLeft()
{
return left;
}
// Used in Renderer for clearing the tile
int16_t InkHUD::Tile::getTop()
{
return top;
}
// Called by Applet base class, when setting applet dimensions, immediately before render // Called by Applet base class, when setting applet dimensions, immediately before render
uint16_t InkHUD::Tile::getWidth() uint16_t InkHUD::Tile::getWidth()
{ {
@@ -232,7 +220,7 @@ void InkHUD::Tile::requestHighlight()
{ {
Tile::highlightTarget = this; Tile::highlightTarget = this;
Tile::highlightShown = false; Tile::highlightShown = false;
inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FAST, true); inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FAST);
} }
// Starts the timer which will automatically dismiss the highlighting, if the tile doesn't organically redraw first // Starts the timer which will automatically dismiss the highlighting, if the tile doesn't organically redraw first

View File

@@ -29,8 +29,6 @@ class Tile
void setRegion(uint8_t layoutSize, uint8_t tileIndex); // Assign region automatically, based on layout void setRegion(uint8_t layoutSize, uint8_t tileIndex); // Assign region automatically, based on layout
void setRegion(int16_t left, int16_t top, uint16_t width, uint16_t height); // Assign region manually void setRegion(int16_t left, int16_t top, uint16_t width, uint16_t height); // Assign region manually
void handleAppletPixel(int16_t x, int16_t y, Color c); // Receive px output from assigned applet void handleAppletPixel(int16_t x, int16_t y, Color c); // Receive px output from assigned applet
int16_t getLeft();
int16_t getTop();
uint16_t getWidth(); uint16_t getWidth();
uint16_t getHeight(); uint16_t getHeight();
static uint16_t maxDisplayDimension(); // Largest possible width / height any tile may ever encounter static uint16_t maxDisplayDimension(); // Largest possible width / height any tile may ever encounter

View File

@@ -4,7 +4,6 @@
#include "./Applets/System/AlignStick/AlignStickApplet.h" #include "./Applets/System/AlignStick/AlignStickApplet.h"
#include "./Applets/System/BatteryIcon/BatteryIconApplet.h" #include "./Applets/System/BatteryIcon/BatteryIconApplet.h"
#include "./Applets/System/Keyboard/KeyboardApplet.h"
#include "./Applets/System/Logo/LogoApplet.h" #include "./Applets/System/Logo/LogoApplet.h"
#include "./Applets/System/Menu/MenuApplet.h" #include "./Applets/System/Menu/MenuApplet.h"
#include "./Applets/System/Notification/NotificationApplet.h" #include "./Applets/System/Notification/NotificationApplet.h"
@@ -149,28 +148,6 @@ void InkHUD::WindowManager::openAlignStick()
} }
} }
void InkHUD::WindowManager::openKeyboard()
{
KeyboardApplet *keyboard = (KeyboardApplet *)inkhud->getSystemApplet("Keyboard");
if (keyboard) {
keyboard->bringToForeground();
keyboardOpen = true;
changeLayout();
}
}
void InkHUD::WindowManager::closeKeyboard()
{
KeyboardApplet *keyboard = (KeyboardApplet *)inkhud->getSystemApplet("Keyboard");
if (keyboard) {
keyboard->sendToBackground();
keyboardOpen = false;
changeLayout();
}
}
// On the currently focussed tile: cycle to the next available user applet // On the currently focussed tile: cycle to the next available user applet
// Applets available for this must be activated, and not already displayed on another tile // Applets available for this must be activated, and not already displayed on another tile
void InkHUD::WindowManager::nextApplet() void InkHUD::WindowManager::nextApplet()
@@ -295,6 +272,7 @@ void InkHUD::WindowManager::toggleBatteryIcon()
batteryIcon->sendToBackground(); batteryIcon->sendToBackground();
// Force-render // Force-render
// - redraw all applets
inkhud->forceUpdate(EInk::UpdateTypes::FAST); inkhud->forceUpdate(EInk::UpdateTypes::FAST);
} }
@@ -333,25 +311,9 @@ void InkHUD::WindowManager::changeLayout()
menu->show(ft); menu->show(ft);
} }
// Resize for the on-screen keyboard
if (keyboardOpen) {
// Send all user applets to the background
// User applets currently don't handle free text input
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++)
inkhud->userApplets.at(i)->sendToBackground();
// Find the first system applet that can handle freetext and resize it
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleFreeText) {
const uint16_t keyboardHeight = KeyboardApplet::getKeyboardHeight();
sa->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height() - keyboardHeight - 1);
break;
}
}
}
// Force-render // Force-render
// - redraw all applets // - redraw all applets
inkhud->forceUpdate(EInk::UpdateTypes::FAST, true); inkhud->forceUpdate(EInk::UpdateTypes::FAST);
} }
// Perform necessary reconfiguration when user activates or deactivates applets at run-time // Perform necessary reconfiguration when user activates or deactivates applets at run-time
@@ -385,7 +347,7 @@ void InkHUD::WindowManager::changeActivatedApplets()
// Force-render // Force-render
// - redraw all applets // - redraw all applets
inkhud->forceUpdate(EInk::UpdateTypes::FAST, true); inkhud->forceUpdate(EInk::UpdateTypes::FAST);
} }
// Some applets may be permitted to bring themselves to foreground, to show new data // Some applets may be permitted to bring themselves to foreground, to show new data
@@ -471,10 +433,8 @@ void InkHUD::WindowManager::createSystemApplets()
addSystemApplet("Logo", new LogoApplet, new Tile); addSystemApplet("Logo", new LogoApplet, new Tile);
addSystemApplet("Pairing", new PairingApplet, new Tile); addSystemApplet("Pairing", new PairingApplet, new Tile);
addSystemApplet("Tips", new TipsApplet, new Tile); addSystemApplet("Tips", new TipsApplet, new Tile);
if (settings->joystick.enabled) { if (settings->joystick.enabled)
addSystemApplet("AlignStick", new AlignStickApplet, new Tile); addSystemApplet("AlignStick", new AlignStickApplet, new Tile);
addSystemApplet("Keyboard", new KeyboardApplet, new Tile);
}
addSystemApplet("Menu", new MenuApplet, nullptr); addSystemApplet("Menu", new MenuApplet, nullptr);
@@ -497,13 +457,9 @@ void InkHUD::WindowManager::placeSystemTiles()
inkhud->getSystemApplet("Logo")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height()); inkhud->getSystemApplet("Logo")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
inkhud->getSystemApplet("Pairing")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height()); inkhud->getSystemApplet("Pairing")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
inkhud->getSystemApplet("Tips")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height()); inkhud->getSystemApplet("Tips")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
if (settings->joystick.enabled) { if (settings->joystick.enabled)
inkhud->getSystemApplet("AlignStick")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height()); inkhud->getSystemApplet("AlignStick")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
const uint16_t keyboardHeight = KeyboardApplet::getKeyboardHeight();
inkhud->getSystemApplet("Keyboard")
->getTile()
->setRegion(0, inkhud->height() - keyboardHeight, inkhud->width(), keyboardHeight);
}
inkhud->getSystemApplet("Notification")->getTile()->setRegion(0, 0, inkhud->width(), 20); inkhud->getSystemApplet("Notification")->getTile()->setRegion(0, 0, inkhud->width(), 20);
const uint16_t batteryIconHeight = Applet::getHeaderHeight() - 2 - 2; const uint16_t batteryIconHeight = Applet::getHeaderHeight() - 2 - 2;

View File

@@ -31,8 +31,6 @@ class WindowManager
void prevTile(); void prevTile();
void openMenu(); void openMenu();
void openAlignStick(); void openAlignStick();
void openKeyboard();
void closeKeyboard();
void nextApplet(); void nextApplet();
void prevApplet(); void prevApplet();
void rotate(); void rotate();
@@ -66,7 +64,6 @@ class WindowManager
void findOrphanApplets(); // Find any applets left-behind when layout changes void findOrphanApplets(); // Find any applets left-behind when layout changes
std::vector<Tile *> userTiles; // Tiles which can host user applets std::vector<Tile *> userTiles; // Tiles which can host user applets
bool keyboardOpen = false;
// For convenience // For convenience
InkHUD *inkhud = nullptr; InkHUD *inkhud = nullptr;

View File

@@ -174,7 +174,7 @@ class BasicExampleApplet : public Applet
// You must have an onRender() method // You must have an onRender() method
// All drawing happens here // All drawing happens here
void onRender(bool full) override; void onRender() override;
}; };
``` ```
@@ -183,7 +183,7 @@ The `onRender` method is called when the display image is redrawn. This can happ
```cpp ```cpp
// All drawing happens here // All drawing happens here
// Our basic example doesn't do anything useful. It just passively prints some text. // Our basic example doesn't do anything useful. It just passively prints some text.
void InkHUD::BasicExampleApplet::onRender(bool full) void InkHUD::BasicExampleApplet::onRender()
{ {
printAt(0, 0, "Hello, world!"); printAt(0, 0, "Hello, world!");
} }

View File

@@ -5,6 +5,7 @@
SerialKeyboard *globalSerialKeyboard = nullptr; SerialKeyboard *globalSerialKeyboard = nullptr;
#ifdef INPUTBROKER_SERIAL_TYPE #ifdef INPUTBROKER_SERIAL_TYPE
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
#if INPUTBROKER_SERIAL_TYPE == 1 // It's a Chatter #if INPUTBROKER_SERIAL_TYPE == 1 // It's a Chatter
// 3 SHIFT level (lower case, upper case, numbers), up to 4 repeated presses, button number // 3 SHIFT level (lower case, upper case, numbers), up to 4 repeated presses, button number

View File

@@ -354,9 +354,9 @@ void setup()
digitalWrite(LED_POWER, LED_STATE_ON); digitalWrite(LED_POWER, LED_STATE_ON);
#endif #endif
#ifdef LED_NOTIFICATION #ifdef USER_LED
pinMode(LED_NOTIFICATION, OUTPUT); pinMode(USER_LED, OUTPUT);
digitalWrite(LED_NOTIFICATION, HIGH ^ LED_STATE_ON); digitalWrite(USER_LED, HIGH ^ LED_STATE_ON);
#endif #endif
#ifdef WIFI_LED #ifdef WIFI_LED

View File

@@ -170,7 +170,7 @@ template <typename T> bool LR11x0Interface<T>::reconfigure()
if (err != RADIOLIB_ERR_NONE) if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it err = lora.setCodingRate(cr);
if (err != RADIOLIB_ERR_NONE) if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);

View File

@@ -824,10 +824,16 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 500; moduleConfig.external_notification.output_ms = 500;
moduleConfig.external_notification.nag_timeout = 2; moduleConfig.external_notification.nag_timeout = 2;
#endif #endif
#if defined(LED_NOTIFICATION) #if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) || \
defined(ELECROW_ThinkNode_M4) || defined(ELECROW_ThinkNode_M6)
// Default to PIN_LED2 for external notification output (LED color depends on device variant)
moduleConfig.external_notification.enabled = true; moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = LED_NOTIFICATION; moduleConfig.external_notification.output = PIN_LED2;
moduleConfig.external_notification.active = LED_STATE_ON; #if defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
moduleConfig.external_notification.active = false;
#else
moduleConfig.external_notification.active = true;
#endif
moduleConfig.external_notification.alert_message = true; moduleConfig.external_notification.alert_message = true;
moduleConfig.external_notification.output_ms = 1000; moduleConfig.external_notification.output_ms = 1000;
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs; moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
@@ -851,6 +857,15 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 100; moduleConfig.external_notification.output_ms = 100;
moduleConfig.external_notification.active = true; moduleConfig.external_notification.active = true;
#endif #endif
#ifdef ELECROW_ThinkNode_M1
// Default to Elecrow USER_LED (blue)
moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = USER_LED;
moduleConfig.external_notification.active = true;
moduleConfig.external_notification.alert_message = true;
moduleConfig.external_notification.output_ms = 1000;
moduleConfig.external_notification.nag_timeout = 60;
#endif
#ifdef T_LORA_PAGER #ifdef T_LORA_PAGER
moduleConfig.canned_message.updown1_enabled = true; moduleConfig.canned_message.updown1_enabled = true;
moduleConfig.canned_message.inputbroker_pin_a = ROTARY_A; moduleConfig.canned_message.inputbroker_pin_a = ROTARY_A;

View File

@@ -27,7 +27,7 @@
#include "platform/portduino/USBHal.h" #include "platform/portduino/USBHal.h"
#endif #endif
#ifdef ARCH_STM32WL #ifdef ARCH_STM32WL>
#include "STM32WLE5JCInterface.h" #include "STM32WLE5JCInterface.h"
#endif #endif

View File

@@ -620,19 +620,15 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
!(p->pki_encrypted != true && (strcasecmp(channels.getName(chIndex), Channels::serialChannel) == 0 || !(p->pki_encrypted != true && (strcasecmp(channels.getName(chIndex), Channels::serialChannel) == 0 ||
strcasecmp(channels.getName(chIndex), Channels::gpioChannel) == 0)) && strcasecmp(channels.getName(chIndex), Channels::gpioChannel) == 0)) &&
// Check for valid keys and single node destination // Check for valid keys and single node destination
config.security.private_key.size == 32 && !isBroadcast(p->to) && config.security.private_key.size == 32 && !isBroadcast(p->to) && node != nullptr &&
// Check for a known public key for the destination
(node->user.public_key.size == 32) &&
// Some portnums either make no sense to send with PKC // Some portnums either make no sense to send with PKC
p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP &&
p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) {
LOG_DEBUG("Use PKI!"); LOG_DEBUG("Use PKI!");
if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_PKC_OVERHEAD > MAX_LORA_PAYLOAD_LEN) if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_PKC_OVERHEAD > MAX_LORA_PAYLOAD_LEN)
return meshtastic_Routing_Error_TOO_LARGE; return meshtastic_Routing_Error_TOO_LARGE;
// Check for a known public key for the destination
if (node == nullptr || node->user.public_key.size != 32) {
LOG_WARN("Unknown public key for destination node 0x%08x (portnum %d), refusing to send legacy DM", p->to,
p->decoded.portnum);
return meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY;
}
if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) &&
memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) {
LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x", *p->public_key.bytes, LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x", *p->public_key.bytes,

View File

@@ -126,7 +126,7 @@ template <typename T> bool SX128xInterface<T>::reconfigure()
if (err != RADIOLIB_ERR_NONE) if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it err = lora.setCodingRate(cr);
if (err != RADIOLIB_ERR_NONE) if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);

View File

@@ -130,7 +130,8 @@ CannedMessageModule::CannedMessageModule()
: SinglePortModule("canned", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessage") : SinglePortModule("canned", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessage")
{ {
this->loadProtoForModule(); this->loadProtoForModule();
if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address == 0x00) && !INPUTBROKER_MATRIX_TYPE) { if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address == 0x00) && !INPUTBROKER_MATRIX_TYPE &&
!CANNED_MESSAGE_MODULE_ENABLE) {
LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled"); LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled");
this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED; this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED;
disable(); disable();

View File

@@ -27,6 +27,10 @@ enum CannedMessageModuleIconType { shift, backspace, space, enter };
#define CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT 50 #define CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT 50
#define CANNED_MESSAGE_MODULE_MESSAGES_SIZE 800 #define CANNED_MESSAGE_MODULE_MESSAGES_SIZE 800
#ifndef CANNED_MESSAGE_MODULE_ENABLE
#define CANNED_MESSAGE_MODULE_ENABLE 0
#endif
// ============================ // ============================
// Data Structures // Data Structures
// ============================ // ============================

View File

@@ -130,6 +130,7 @@ int32_t StatusLEDModule::runOnce()
#ifdef LED_CHARGE #ifdef LED_CHARGE
digitalWrite(LED_CHARGE, CHARGE_LED_state); digitalWrite(LED_CHARGE, CHARGE_LED_state);
#endif #endif
// digitalWrite(green_LED_PIN, LED_STATE_OFF);
#ifdef LED_PAIRING #ifdef LED_PAIRING
digitalWrite(LED_PAIRING, PAIRING_LED_state); digitalWrite(LED_PAIRING, PAIRING_LED_state);
#endif #endif

View File

@@ -26,7 +26,7 @@ bool RAK12035Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
sensor.get_sensor_version(&data); sensor.get_sensor_version(&data);
if (data != 0) { if (data != 0) {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
LOG_INFO("RAK12035Sensor Init Succeed \nSensor Firmware version: %i, Sensor Name: %s", data, sensorName); LOG_INFO("RAK12035Sensor Init Succeed \nSensor1 Firmware version: %i, Sensor Name: %s", data, sensorName);
status = true; status = true;
sensor.sensor_sleep(); sensor.sensor_sleep();
RESTORE_3V3_POWER(); RESTORE_3V3_POWER();
@@ -49,39 +49,33 @@ void RAK12035Sensor::setup()
// TODO:: Check for and run calibration check for up to 2 additional sensors if present. // TODO:: Check for and run calibration check for up to 2 additional sensors if present.
uint16_t zero_val = 0; uint16_t zero_val = 0;
uint16_t hundred_val = 0; uint16_t hundred_val = 0;
const uint16_t default_zero_val = 510; uint16_t default_zero_val = 550;
const uint16_t default_hundred_val = 390; uint16_t default_hundred_val = 420;
sensor.sensor_on(); sensor.sensor_on();
sensor.begin();
delay(200); delay(200);
sensor.get_dry_cal(&zero_val); sensor.get_dry_cal(&zero_val);
delay(200);
sensor.get_wet_cal(&hundred_val); sensor.get_wet_cal(&hundred_val);
delay(200); delay(200);
if (zero_val == 0 || zero_val <= hundred_val) {
bool calibrationReset = false; LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
if (zero_val == 0) { LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
LOG_INFO("Dry calibration not set, using default: %d", default_zero_val); "https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Dry Calibration: %d", default_zero_val);
sensor.set_dry_cal(default_zero_val); sensor.set_dry_cal(default_zero_val);
delay(200); sensor.get_dry_cal(&zero_val);
zero_val = default_zero_val; LOG_INFO("Dry calibration reset complete. New value is %d", zero_val);
calibrationReset = true;
} }
if (hundred_val == 0 || hundred_val >= zero_val) { if (hundred_val == 0 || hundred_val >= zero_val) {
LOG_INFO("Wet calibration not set, using default: %d", default_hundred_val); LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Wet Calibration: %d", default_hundred_val);
sensor.set_wet_cal(default_hundred_val); sensor.set_wet_cal(default_hundred_val);
delay(200); sensor.get_wet_cal(&hundred_val);
hundred_val = default_hundred_val; LOG_INFO("Wet calibration reset complete. New value is %d", hundred_val);
calibrationReset = true;
} }
if (calibrationReset) {
LOG_INFO("Default calibration values applied. Consider running the calibration sketch for better accuracy: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture");
}
LOG_INFO("Dry calibration value: %d, Wet calibration value: %d", zero_val, hundred_val);
sensor.sensor_sleep(); sensor.sensor_sleep();
RESTORE_3V3_POWER(); RESTORE_3V3_POWER();
delay(200); delay(200);

View File

@@ -33,6 +33,9 @@
#ifndef HAS_RADIO #ifndef HAS_RADIO
#define HAS_RADIO 1 #define HAS_RADIO 1
#endif #endif
#ifndef HAS_RTC
#define HAS_RTC 1
#endif
#ifndef HAS_CPU_SHUTDOWN #ifndef HAS_CPU_SHUTDOWN
#define HAS_CPU_SHUTDOWN 1 #define HAS_CPU_SHUTDOWN 1
#endif #endif

View File

@@ -17,6 +17,9 @@
#ifndef HAS_RADIO #ifndef HAS_RADIO
#define HAS_RADIO 1 #define HAS_RADIO 1
#endif #endif
#ifndef HAS_RTC
#define HAS_RTC 1
#endif
#ifndef HAS_TELEMETRY #ifndef HAS_TELEMETRY
#define HAS_TELEMETRY 1 #define HAS_TELEMETRY 1
#endif #endif

View File

@@ -241,6 +241,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
#ifdef PIN_POWER_EN #ifdef PIN_POWER_EN
digitalWrite(PIN_POWER_EN, LOW); digitalWrite(PIN_POWER_EN, LOW);
pinMode(PIN_POWER_EN, INPUT); // power off peripherals pinMode(PIN_POWER_EN, INPUT); // power off peripherals
// pinMode(PIN_POWER_EN1, INPUT_PULLDOWN);
#endif #endif
#ifdef RAK_WISMESH_TAP_V2 #ifdef RAK_WISMESH_TAP_V2

View File

@@ -98,6 +98,7 @@
#define KB_LOAD 21 // load values from the switch and store in shift register #define KB_LOAD 21 // load values from the switch and store in shift register
#define KB_CLK 22 // clock pin for serial data out #define KB_CLK 22 // clock pin for serial data out
#define KB_DATA 23 // data pin #define KB_DATA 23 // data pin
#define CANNED_MESSAGE_MODULE_ENABLE 1
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
// // // //

View File

@@ -10,6 +10,3 @@ build_flags =
-D EBYTE_E22 -D EBYTE_E22
-D EBYTE_E22_900M30S ; Assume Tx power curve is identical to 900M30S as there is no documentation -D EBYTE_E22_900M30S ; Assume Tx power curve is identical to 900M30S as there is no documentation
-I variants/esp32/diy/9m2ibr_aprs_lora_tracker -I variants/esp32/diy/9m2ibr_aprs_lora_tracker
build_src_filter =
${esp32_base.build_src_filter}
+<../variants/esp32/diy/9m2ibr_aprs_lora_tracker>

View File

@@ -1,8 +0,0 @@
#include "variant.h"
#include "Arduino.h"
void earlyInitVariant()
{
pinMode(USER_LED, OUTPUT);
digitalWrite(USER_LED, HIGH ^ LED_STATE_ON);
}

View File

@@ -30,6 +30,7 @@ build_flags =
-DTFT_BL=32 -DTFT_BL=32
-DSPI_FREQUENCY=40000000 -DSPI_FREQUENCY=40000000
-DSPI_READ_FREQUENCY=16000000 -DSPI_READ_FREQUENCY=16000000
-DDISABLE_ALL_LIBRARY_WARNINGS
lib_ignore = lib_ignore =
m5stack-core m5stack-core
lib_deps = lib_deps =

View File

@@ -15,6 +15,7 @@
// PCF8563 RTC Module // PCF8563 RTC Module
#define PCF8563_RTC 0x51 #define PCF8563_RTC 0x51
#define HAS_RTC 1
// Wheel // Wheel
// Down 37 // Down 37

View File

@@ -57,6 +57,7 @@
#ifndef TOUCH_IRQ #ifndef TOUCH_IRQ
#define TOUCH_IRQ -1 #define TOUCH_IRQ -1
#endif #endif
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define USE_VIRTUAL_KEYBOARD 1 #define USE_VIRTUAL_KEYBOARD 1
#define ST7796_NSS 25 #define ST7796_NSS 25

View File

@@ -10,6 +10,7 @@
#define LED_PIN 13 // 13 red, 2 blue, 15 red #define LED_PIN 13 // 13 red, 2 blue, 15 red
// #define HAS_BUTTON 0
#define BUTTON_PIN 0 #define BUTTON_PIN 0
#define BUTTON_NEED_PULLUP #define BUTTON_NEED_PULLUP

View File

@@ -164,6 +164,3 @@ build_flags =
${crowpanel_large_esp32s3_base.build_flags} ${crowpanel_large_esp32s3_base.build_flags}
-D VIEW_320x240 -D VIEW_320x240
-D DISPLAY_SIZE=800x480 ; landscape mode -D DISPLAY_SIZE=800x480 ; landscape mode
build_src_filter =
${esp32s3_base.build_src_filter}
+<../variants/esp32s3/elecrow_panel>

View File

@@ -1,21 +0,0 @@
// meshtastic/firmware/variants/elecrow_panel/variant.cpp
#include "variant.h"
#include "Arduino.h"
#include "Wire.h"
bool elecrow_v2 = false; // false = v1, true = v2
extern "C" {
void initVariant()
{
Wire.begin(I2C_SDA, I2C_SCL, 100000);
delay(50);
Wire.beginTransmission(0x30);
if (Wire.endTransmission() == 0) {
elecrow_v2 = true;
}
Wire.end();
}
}

View File

@@ -1,8 +1,6 @@
#define I2C_SDA 15 #define I2C_SDA 15
#define I2C_SCL 16 #define I2C_SCL 16
extern bool elecrow_v2; // false = v1, true = v2
#if CROW_SELECT == 1 #if CROW_SELECT == 1
#define WAKE_ON_TOUCH #define WAKE_ON_TOUCH
#define SCREEN_TOUCH_INT 47 #define SCREEN_TOUCH_INT 47
@@ -19,7 +17,7 @@ extern bool elecrow_v2; // false = v1, true = v2
#define DAC_I2S_DOUT 12 #define DAC_I2S_DOUT 12
#define DAC_I2S_MCLK 8 // don't use GPIO0 because it's assigned to LoRa or button #define DAC_I2S_MCLK 8 // don't use GPIO0 because it's assigned to LoRa or button
#else #else
#define PIN_BUZZER (elecrow_v2 ? 0 : 8) #define PIN_BUZZER 8
#endif #endif
// GPS via UART1 connector // GPS via UART1 connector
@@ -74,7 +72,7 @@ extern bool elecrow_v2; // false = v1, true = v2
#define SENSOR_POWER_ON LOW #define SENSOR_POWER_ON LOW
#else #else
// 4.3", 5.0", 7.0" // 4.3", 5.0", 7.0"
#define LORA_CS (elecrow_v2 ? 8 : 0) #define LORA_CS 0
#define LORA_SCK 5 #define LORA_SCK 5
#define LORA_MISO 4 #define LORA_MISO 4
#define LORA_MOSI 6 #define LORA_MOSI 6

View File

@@ -16,12 +16,22 @@
#define SLEEP_TIME 120 #define SLEEP_TIME 120
#define GPS_DEFAULT_NOT_PRESENT 1 #define GPS_DEFAULT_NOT_PRESENT 1
// #define GPS_RX_PIN 44
// #define GPS_TX_PIN 43
// #define BATTERY_PIN 4 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
// ratio of voltage divider = 2.0 (RD2=100k, RD3=100k)
// #define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
// #define ADC_CHANNEL ADC1_GPIO4_CHANNEL
// keyboard // keyboard
#define I2C_SDA 47 // I2C pins for this board #define I2C_SDA 47 // I2C pins for this board
#define I2C_SCL 14 #define I2C_SCL 14
// #define KB_POWERON -1 // must be set to HIGH
// #define KB_SLAVE_ADDRESS TDECK_KB_ADDR // 0x55
// #define KB_BL_PIN 46 // not used for now // #define KB_BL_PIN 46 // not used for now
#define KB_INT 13 #define KB_INT 13
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define TFT_DC 39 #define TFT_DC 39
#define TFT_CS 41 #define TFT_CS 41
@@ -34,9 +44,11 @@
#define LORA_MOSI 3 #define LORA_MOSI 3
#define LORA_CS 17 #define LORA_CS 17
// #define LORA_DIO0 -1 // a No connect on the SX1262 module
#define LORA_RESET 18 #define LORA_RESET 18
#define LORA_DIO1 16 // SX1262 IRQ #define LORA_DIO1 16 // SX1262 IRQ
#define LORA_DIO2 15 // SX1262 BUSY #define LORA_DIO2 15 // SX1262 BUSY
// #define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled
#define SX126X_CS LORA_CS #define SX126X_CS LORA_CS
#define SX126X_DIO1 LORA_DIO1 #define SX126X_DIO1 LORA_DIO1
@@ -46,5 +58,4 @@
#define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8 #define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define LED_NOTIFICATION 1 // #define LED_PIN 1
#define LED_STATE_ON 0

View File

@@ -12,7 +12,7 @@
#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use #define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
// Button A (44), B (43), R (12), U (13), L (11), D (18) // Button A (44), B (43), R (12), U (13), L (11), D (18)
#define BUTTON_PIN 43 // If defined, this will be used for user button presses #define BUTTON_PIN 44 // If defined, this will be used for user button presses
#define BUTTON_NEED_PULLUP #define BUTTON_NEED_PULLUP
#define USE_RF95 #define USE_RF95
@@ -20,19 +20,8 @@
#define LORA_MISO 7 #define LORA_MISO 7
#define LORA_MOSI 8 #define LORA_MOSI 8
#define LORA_CS 9 #define LORA_CS 9
#define LORA_DIO0 16 #define LORA_DIO0 16 // a No connect on the SX1262 module
#define LORA_RESET 4 #define LORA_RESET 4
#define LORA_DIO1 RADIOLIB_NC #define LORA_DIO1 RADIOLIB_NC
#define LORA_DIO2 RADIOLIB_NC #define LORA_DIO2 RADIOLIB_NC
// jk, its not really a trackball but we're gonna pretend!
#define HAS_TRACKBALL 1
#define TB_UP 13
#define TB_DOWN 18
#define TB_LEFT 11
#define TB_RIGHT 12
#define TB_PRESS 44 // BUTTON_PIN
#define TB_DIRECTION FALLING
#define ENABLE_AMBIENTLIGHTING

View File

@@ -52,6 +52,8 @@
// Picomputer gets a white on black display // Picomputer gets a white on black display
#define TFT_MESH_OVERRIDE COLOR565(255, 255, 255) #define TFT_MESH_OVERRIDE COLOR565(255, 255, 255)
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define INPUTBROKER_MATRIX_TYPE 1 #define INPUTBROKER_MATRIX_TYPE 1
#define KEYS_COLS \ #define KEYS_COLS \

View File

@@ -22,8 +22,9 @@
#define LED_BLUE 45 #define LED_BLUE 45
#define PIN_LED1 LED_GREEN #define PIN_LED1 LED_GREEN
#define LED_NOTIFICATION LED_BLUE #define PIN_LED2 LED_BLUE
#define LED_CONN LED_BLUE
#define LED_PIN LED_GREEN #define LED_PIN LED_GREEN
#define ledOff(pin) pinMode(pin, INPUT) #define ledOff(pin) pinMode(pin, INPUT)

View File

@@ -30,8 +30,9 @@
#define LED_BLUE 45 #define LED_BLUE 45
#define PIN_LED1 LED_GREEN #define PIN_LED1 LED_GREEN
#define LED_NOTIFICATION LED_BLUE #define PIN_LED2 LED_BLUE
#define LED_CONN LED_BLUE
#define LED_PIN LED_GREEN #define LED_PIN LED_GREEN
#define ledOff(pin) pinMode(pin, INPUT) #define ledOff(pin) pinMode(pin, INPUT)
@@ -46,8 +47,10 @@
#define SPI_MISO (10) #define SPI_MISO (10)
#define SPI_CS (12) #define SPI_CS (12)
#define HAS_BUTTON 1
#define BUTTON_PIN 0 #define BUTTON_PIN 0
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define USE_VIRTUAL_KEYBOARD 1 #define USE_VIRTUAL_KEYBOARD 1
#define BATTERY_PIN 1 #define BATTERY_PIN 1

View File

@@ -43,6 +43,7 @@
// TCA8418 keyboard // TCA8418 keyboard
#define KB_BL_PIN 42 #define KB_BL_PIN 42
#define CANNED_MESSAGE_MODULE_ENABLE 1
// microphone PCM5102A // microphone PCM5102A
#define PCM5102A_SCK 47 #define PCM5102A_SCK 47

View File

@@ -61,6 +61,7 @@
#define KB_POWERON 10 // must be set to HIGH #define KB_POWERON 10 // must be set to HIGH
#define KB_SLAVE_ADDRESS TDECK_KB_ADDR // 0x55 #define KB_SLAVE_ADDRESS TDECK_KB_ADDR // 0x55
#define KB_BL_PIN 46 // not used for now #define KB_BL_PIN 46 // not used for now
#define CANNED_MESSAGE_MODULE_ENABLE 1
// trackball // trackball
#define HAS_TRACKBALL 1 #define HAS_TRACKBALL 1

View File

@@ -45,6 +45,7 @@
// PCF8563 RTC Module // PCF8563 RTC Module
#define PCF8563_RTC 0x51 #define PCF8563_RTC 0x51
#define HAS_RTC 1
#define I2C_SDA 10 // For QMC6310 sensors and screens #define I2C_SDA 10 // For QMC6310 sensors and screens
#define I2C_SCL 11 // For QMC6310 sensors and screens #define I2C_SCL 11 // For QMC6310 sensors and screens

View File

@@ -55,6 +55,7 @@
// PCF8563 RTC Module // PCF8563 RTC Module
#define PCF8563_RTC 0x51 #define PCF8563_RTC 0x51
#define HAS_RTC 1
// Specify the PMU as Wire1. In the t-beam-s3 core, PCF8563 and PMU share the bus // Specify the PMU as Wire1. In the t-beam-s3 core, PCF8563 and PMU share the bus
#define PMU_USE_WIRE1 #define PMU_USE_WIRE1

View File

@@ -40,6 +40,7 @@
// PCF85063 RTC Module // PCF85063 RTC Module
#define PCF85063_RTC 0x51 #define PCF85063_RTC 0x51
#define HAS_RTC 1
// Rotary // Rotary
#define ROTARY_A (40) #define ROTARY_A (40)
@@ -60,6 +61,7 @@
#define I2C_NO_RESCAN #define I2C_NO_RESCAN
#define KB_BL_PIN 46 #define KB_BL_PIN 46
#define KB_INT 6 #define KB_INT 6
#define CANNED_MESSAGE_MODULE_ENABLE 1
// audio codec ES8311 // audio codec ES8311
#define HAS_I2S #define HAS_I2S

View File

@@ -78,6 +78,7 @@
// keyboard changes // keyboard changes
#define PIN_BUZZER 43 #define PIN_BUZZER 43
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define INPUTBROKER_MATRIX_TYPE 1 #define INPUTBROKER_MATRIX_TYPE 1

View File

@@ -102,6 +102,7 @@
// keyboard changes // keyboard changes
#define PIN_BUZZER 43 #define PIN_BUZZER 43
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define INPUTBROKER_MATRIX_TYPE 1 #define INPUTBROKER_MATRIX_TYPE 1

View File

@@ -79,6 +79,7 @@
// keyboard changes // keyboard changes
#define PIN_BUZZER 43 #define PIN_BUZZER 43
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define INPUTBROKER_MATRIX_TYPE 1 #define INPUTBROKER_MATRIX_TYPE 1

View File

@@ -1,5 +1,6 @@
#define HAS_SCREEN 1 #define HAS_SCREEN 1
#define USE_TFTDISPLAY 1 #define USE_TFTDISPLAY 1
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define HAS_GPS 1 #define HAS_GPS 1
#define MAX_RX_TOPHONE portduino_config.maxtophone #define MAX_RX_TOPHONE portduino_config.maxtophone
#define MAX_NUM_NODES portduino_config.MaxNodes #define MAX_NUM_NODES portduino_config.MaxNodes

View File

@@ -2,6 +2,7 @@
#define HAS_SCREEN 1 #define HAS_SCREEN 1
#endif #endif
#define USE_TFTDISPLAY 1 #define USE_TFTDISPLAY 1
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define HAS_GPS 1 #define HAS_GPS 1
#define MAX_RX_TOPHONE portduino_config.maxtophone #define MAX_RX_TOPHONE portduino_config.maxtophone
#define MAX_NUM_NODES portduino_config.MaxNodes #define MAX_NUM_NODES portduino_config.MaxNodes

View File

@@ -50,6 +50,8 @@ extern "C" {
#define RGBLED_BLUE (0 + 12) // Blue of RGB P0.12 #define RGBLED_BLUE (0 + 12) // Blue of RGB P0.12
#define RGBLED_CA // comment out this line if you have a common cathode type, as defined use common anode logic #define RGBLED_CA // comment out this line if you have a common cathode type, as defined use common anode logic
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1 #define LED_GREEN PIN_LED1
#define LED_BLUE PIN_LED2 #define LED_BLUE PIN_LED2

Some files were not shown because too many files have changed in this diff Show More