diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 6b48e8128b6..876d7e4ceb8 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -96,6 +96,7 @@ jobs:
pio_platform: ${{ matrix.check.platform }}
pio_env: ${{ matrix.check.board }}
pio_target: check
+ pio_opts: --fail-on-defect=low
build:
needs: [setup, version]
diff --git a/_codeql_detected_source_root b/_codeql_detected_source_root
new file mode 120000
index 00000000000..945c9b46d68
--- /dev/null
+++ b/_codeql_detected_source_root
@@ -0,0 +1 @@
+.
\ No newline at end of file
diff --git a/boards/station-g2.json b/boards/station-g2.json
index 871f067aa9f..f7ce5077911 100755
--- a/boards/station-g2.json
+++ b/boards/station-g2.json
@@ -8,7 +8,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
- "-DARDUINO_USB_MODE=0",
+ "-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=0"
],
diff --git a/platformio.ini b/platformio.ini
index 12eab550e44..ccc5c9755bd 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -184,8 +184,8 @@ lib_deps =
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
wollewald/BH1750_WE@1.1.10
-; (not included in native / portduino)
-[environmental_extra]
+; Common environmental sensor libraries (not included in native / portduino)
+[environmental_extra_common]
lib_deps =
# renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
adafruit/Adafruit BMP3XX Library@2.1.6
@@ -205,10 +205,6 @@ lib_deps =
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
closedcube/ClosedCube OPT3001@1.1.2
- # renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
- boschsensortec/bsec2@1.10.2610
- # renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
- boschsensortec/BME68x Sensor Library@1.3.40408
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
@@ -217,33 +213,21 @@ lib_deps =
sensirion/Sensirion I2C SCD4x@1.1.0
# renovate: datasource=custom.pio depName=Sensirion I2C SFA3x packageName=sensirion/library/Sensirion I2C SFA3x
sensirion/Sensirion I2C SFA3x@1.0.0
+ # renovate: datasource=custom.pio depName=Sensirion I2C SCD30 packageName=sensirion/library/Sensirion I2C SCD30
+ sensirion/Sensirion I2C SCD30@1.0.0
-; Same as environmental_extra but without BSEC (saves ~3.5KB DRAM for original ESP32 targets)
+; Environmental sensors with BSEC2 (Bosch proprietary IAQ)
+[environmental_extra]
+lib_deps =
+ ${environmental_extra_common.lib_deps}
+ # renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
+ boschsensortec/bsec2@1.10.2610
+ # renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
+ boschsensortec/BME68x Sensor Library@1.3.40408
+
+; Environmental sensors without BSEC (saves ~3.5KB DRAM for original ESP32 targets)
[environmental_extra_no_bsec]
lib_deps =
- # renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
- adafruit/Adafruit BMP3XX Library@2.1.6
- # renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
- adafruit/Adafruit MAX1704X@1.0.3
- # renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
- adafruit/Adafruit SHTC3 Library@1.0.2
- # renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
- adafruit/Adafruit LPS2X@2.0.6
- # renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
- adafruit/Adafruit SHT31 Library@2.2.2
- # renovate: datasource=custom.pio depName=Adafruit VEML7700 packageName=adafruit/library/Adafruit VEML7700 Library
- adafruit/Adafruit VEML7700 Library@2.1.6
- # renovate: datasource=custom.pio depName=Adafruit SHT4x packageName=adafruit/library/Adafruit SHT4x Library
- adafruit/Adafruit SHT4x Library@1.0.5
- # renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
- sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
- # renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
- closedcube/ClosedCube OPT3001@1.1.2
- # renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
- https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
- # renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
- sensirion/Sensirion Core@0.7.3
- # renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
- sensirion/Sensirion I2C SCD4x@1.1.0
- # renovate: datasource=custom.pio depName=Sensirion I2C SFA3x packageName=sensirion/library/Sensirion I2C SFA3x
- sensirion/Sensirion I2C SFA3x@1.0.0
+ ${environmental_extra_common.lib_deps}
+ # renovate: datasource=custom.pio depName=adafruit/Adafruit BME680 Library packageName=adafruit/library/Adafruit BME680
+ adafruit/Adafruit BME680 Library@^2.0.5
diff --git a/src/Power.cpp b/src/Power.cpp
index 6ee658f2b12..ea4fcf42a6b 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -704,11 +704,11 @@ bool Power::setup()
found = true;
} else if (analogInit()) {
found = true;
- }
-
+ } else {
#ifdef NRF_APM
- found = true;
+ found = true;
#endif
+ }
#ifdef EXT_PWR_DETECT
attachInterrupt(
EXT_PWR_DETECT,
@@ -846,8 +846,10 @@ void Power::readPowerStatus()
if (batteryLevel) {
hasBattery = batteryLevel->isBatteryConnect() ? OptTrue : OptFalse;
+#ifndef NRF_APM
usbPowered = batteryLevel->isVbusIn() ? OptTrue : OptFalse;
isChargingNow = batteryLevel->isCharging() ? OptTrue : OptFalse;
+#endif
if (hasBattery) {
batteryVoltageMv = batteryLevel->getBattVoltage();
// If the AXP192 returns a valid battery percentage, use it
diff --git a/src/configuration.h b/src/configuration.h
index 5ec856f88c7..53ae30d51d2 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -244,6 +244,7 @@ along with this program. If not, see .
#define BQ25896_ADDR 0x6B
#define LTR553ALS_ADDR 0x23
#define SEN5X_ADDR 0x69
+#define SCD30_ADDR 0x61
// -----------------------------------------------------------------------------
// ACCELEROMETER
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 16119276649..7b575dd6350 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -91,7 +91,8 @@ class ScanI2C
CST226SE,
SEN5X,
SFA30,
- CW2015
+ CW2015,
+ SCD30
} DeviceType;
// typedef uint8_t DeviceAddress;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 2802854abf2..c58a5851880 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -117,6 +117,25 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
return value;
}
+bool ScanI2CTwoWire::i2cCommandResponseLength(ScanI2C::DeviceAddress addr, uint16_t command, uint8_t expectedLength) const
+{
+ TwoWire *i2cBus = fetchI2CBus(addr);
+ i2cBus->beginTransmission(addr.address);
+ if (command > 0xFF) {
+ i2cBus->write((uint8_t)(command >> 8));
+ }
+ i2cBus->write((uint8_t)(command & 0xFF));
+ if (i2cBus->endTransmission() != 0) {
+ return false;
+ }
+ delay(20);
+ uint8_t received = i2cBus->requestFrom(addr.address, expectedLength);
+ bool match = (received == expectedLength);
+ while (i2cBus->available())
+ i2cBus->read();
+ return match;
+}
+
/// for SEN5X detection
// Note, this code needs to be called before setting the I2C bus speed
// for the screen at high speed. The speed needs to be at 100kHz, otherwise
@@ -432,8 +451,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
type = OPT3001;
logFoundDevice("OPT3001", (uint8_t)addr.address);
- } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 6) !=
- 0) { // unique SHT4x serial number (6 bytes inc. CRC)
+ } else if (i2cCommandResponseLength(addr, 0x89, 6)) { // SHT4x serial number (6 bytes inc. CRC)
type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address);
} else {
@@ -458,13 +476,19 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break;
case LPS22HB_ADDR_ALT:
- registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xD060), 48); // get device marking
- if (registerValue != 0) {
+ // SFA30 detection: send 2-byte command 0xD060 (Get Device Marking) and check for 48-byte response
+ if (i2cCommandResponseLength(addr, 0xD060, 48)) {
type = SFA30;
logFoundDevice("SFA30", (uint8_t)addr.address);
break;
}
- // TODO - What happens with these two?
+ // Fallback: LPS22HB detection at alternate address using WHO_AM_I register (0x0F == 0xB1)
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1);
+ if (registerValue == 0xB1) {
+ type = LPS22HB;
+ logFoundDevice("LPS22HB", (uint8_t)addr.address);
+ }
+ break;
SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB", (uint8_t)addr.address)
SCAN_SIMPLE_CASE(QMC6310U_ADDR, QMC6310U, "QMC6310U", (uint8_t)addr.address)
@@ -557,6 +581,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
+ SCAN_SIMPLE_CASE(SCD30_ADDR, SCD30, "SCD30", (uint8_t)addr.address);
case CST328_ADDR:
// Do we have the CST328 or the CST226SE
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1);
diff --git a/src/detect/ScanI2CTwoWire.h b/src/detect/ScanI2CTwoWire.h
index c5b79192027..841a8b946c4 100644
--- a/src/detect/ScanI2CTwoWire.h
+++ b/src/detect/ScanI2CTwoWire.h
@@ -55,6 +55,8 @@ class ScanI2CTwoWire : public ScanI2C
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth, bool) const;
+ bool i2cCommandResponseLength(DeviceAddress addr, uint16_t command, uint8_t expectedLength) const;
+
DeviceType probeOLED(ScanI2C::DeviceAddress) const;
static void logFoundDevice(const char *device, uint8_t address);
diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp
index 5e31de950ce..e67bef53e35 100644
--- a/src/gps/RTC.cpp
+++ b/src/gps/RTC.cpp
@@ -312,7 +312,7 @@ const char *RtcName(RTCQuality quality)
* @param t The time to potentially set the RTC to.
* @return True if the RTC was set to the provided time, false otherwise.
*/
-RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
+RTCSetResult perhapsSetRTC(RTCQuality q, const struct tm &t)
{
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
diff --git a/src/gps/RTC.h b/src/gps/RTC.h
index cf6db0239f4..16ecd824508 100644
--- a/src/gps/RTC.h
+++ b/src/gps/RTC.h
@@ -41,7 +41,7 @@ extern uint32_t lastSetFromPhoneNtpOrGps;
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
-RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t);
+RTCSetResult perhapsSetRTC(RTCQuality q, const struct tm &t);
/// Return a string name for the quality
const char *RtcName(RTCQuality quality);
diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp
index 195da09f9cb..f57c3951250 100644
--- a/src/graphics/draw/MenuHandler.cpp
+++ b/src/graphics/draw/MenuHandler.cpp
@@ -539,7 +539,7 @@ void menuHandler::messageResponseMenu()
// If viewing ALL chats, hide “Mute Chat”
if (mode != graphics::MessageRenderer::ThreadMode::ALL && mode != graphics::MessageRenderer::ThreadMode::DIRECT) {
const uint8_t chIndex = (threadChannel != 0) ? (uint8_t)threadChannel : channels.getPrimaryIndex();
- auto &chan = channels.getByIndex(chIndex);
+ const auto &chan = channels.getByIndex(chIndex);
optionsArray[options] = chan.settings.module_settings.is_muted ? "Unmute Channel" : "Mute Channel";
optionsEnumArray[options++] = MuteChannel;
@@ -831,7 +831,7 @@ void menuHandler::messageViewModeMenu()
// Gather unique peers
auto dms = messageStore.getDirectMessages();
std::vector uniquePeers;
- for (auto &m : dms) {
+ for (const auto &m : dms) {
uint32_t peer = (m.sender == nodeDB->getNodeNum()) ? m.dest : m.sender;
if (peer != nodeDB->getNodeNum() && std::find(uniquePeers.begin(), uniquePeers.end(), peer) == uniquePeers.end())
uniquePeers.push_back(peer);
@@ -1397,7 +1397,7 @@ void menuHandler::manageNodeMenu()
}
if (selected == Favorite) {
- auto n = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
+ const auto *n = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
if (!n) {
return;
}
@@ -2292,14 +2292,13 @@ void menuHandler::wifiToggleMenu()
void menuHandler::screenOptionsMenu()
{
// Check if brightness is supported
- bool hasSupportBrightness = false;
-#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
- hasSupportBrightness = true;
-#endif
-
#if defined(T_DECK)
// TDeck Doesn't seem to support brightness at all, at least not reliably
- hasSupportBrightness = false;
+ bool hasSupportBrightness = false;
+#elif defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
+ bool hasSupportBrightness = true;
+#else
+ bool hasSupportBrightness = false;
#endif
enum optionsNumbers { Back, Brightness, ScreenColor, FrameToggles, DisplayUnits, MessageBubbles };
@@ -2444,7 +2443,7 @@ void menuHandler::frameTogglesMenu()
nodelist_hopsignal,
nodelist_distance,
nodelist_bearings,
- gps,
+ gps_position,
lora,
clock,
show_favorites,
@@ -2482,7 +2481,7 @@ void menuHandler::frameTogglesMenu()
#endif
optionsArray[options] = screen->isFrameHidden("gps") ? "Show Position" : "Hide Position";
- optionsEnumArray[options++] = gps;
+ optionsEnumArray[options++] = gps_position;
#endif
optionsArray[options] = screen->isFrameHidden("lora") ? "Show LoRa" : "Hide LoRa";
@@ -2545,7 +2544,7 @@ void menuHandler::frameTogglesMenu()
screen->toggleFrameVisibility("nodelist_bearings");
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
- } else if (selected == gps) {
+ } else if (selected == gps_position) {
screen->toggleFrameVisibility("gps");
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp
index 9d678013068..b36a5057cbb 100644
--- a/src/graphics/draw/NodeListRenderer.cpp
+++ b/src/graphics/draw/NodeListRenderer.cpp
@@ -171,7 +171,7 @@ unsigned long getModeCycleIntervalMs()
int calculateMaxScroll(int totalEntries, int visibleRows)
{
- return std::max(0, (totalEntries - 1) / (visibleRows * 2));
+ return max(0, (totalEntries - 1) / (visibleRows * 2));
}
void drawColumnSeparator(OLEDDisplay *display, int16_t x, int16_t yStart, int16_t yEnd)
@@ -187,13 +187,12 @@ void drawScrollbar(OLEDDisplay *display, int visibleNodeRows, int totalEntries,
if (totalEntries <= visibleNodeRows * columns)
return;
- int scrollbarX = display->getWidth() - 2;
int scrollbarHeight = display->getHeight() - scrollStartY - 10;
- int thumbHeight = std::max(4, (scrollbarHeight * visibleNodeRows * columns) / totalEntries);
- int perPage = visibleNodeRows * columns;
- int maxScroll = std::max(0, (totalEntries - 1) / perPage);
- int thumbY = scrollStartY + (scrollIndex * (scrollbarHeight - thumbHeight)) / std::max(1, maxScroll);
+ int thumbHeight = max(4, (scrollbarHeight * visibleNodeRows * columns) / totalEntries);
+ int thumbY = scrollStartY + (scrollIndex * (scrollbarHeight - thumbHeight)) /
+ max(1, max(0, (totalEntries - 1) / (visibleNodeRows * columns)));
+ int scrollbarX = display->getWidth() - 2;
for (int i = 0; i < thumbHeight; i++) {
display->setPixel(scrollbarX, thumbY + i);
}
@@ -556,13 +555,13 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int maxScroll = 0;
if (perPage > 0) {
- maxScroll = std::max(0, (totalEntries - 1) / perPage);
+ maxScroll = max(0, (totalEntries - 1) / perPage);
}
if (scrollIndex > maxScroll)
scrollIndex = maxScroll;
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
- int endIndex = std::min(startIndex + visibleNodeRows * totalColumns, totalEntries);
+ int endIndex = min(startIndex + visibleNodeRows * totalColumns, totalEntries);
int yOffset = 0;
int col = 0;
int lastNodeY = y;
@@ -580,7 +579,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
if (extras)
extras(display, node, xPos, yPos, columnWidth, heading, lat, lon);
- lastNodeY = std::max(lastNodeY, yPos + FONT_HEIGHT_SMALL);
+ lastNodeY = max(lastNodeY, yPos + FONT_HEIGHT_SMALL);
yOffset += rowYOffset;
shownCount++;
rowCount++;
@@ -613,13 +612,11 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
if (millis() - popupTime < POPUP_DURATION_MS) {
popupTotal = totalEntries;
- int perPage = visibleNodeRows * totalColumns;
-
popupStart = startIndex + 1;
- popupEnd = std::min(startIndex + perPage, totalEntries);
+ popupEnd = min(startIndex + perPage, totalEntries);
popupPage = (scrollIndex + 1);
- popupMaxPage = std::max(1, (totalEntries + perPage - 1) / perPage);
+ popupMaxPage = max(1, (totalEntries + perPage - 1) / perPage);
char buf[32];
snprintf(buf, sizeof(buf), "%d-%d/%d Pg %d/%d", popupStart, popupEnd, popupTotal, popupPage, popupMaxPage);
diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp
index 922ca1028f1..25a70f16dae 100644
--- a/src/graphics/draw/UIRenderer.cpp
+++ b/src/graphics/draw/UIRenderer.cpp
@@ -288,7 +288,8 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
// **********************
// * Favorite Node Info *
// **********************
-void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
+// cppcheck-suppress constParameterPointer; signature must match FrameCallback typedef from OLEDDisplayUi library
+void UIRenderer::drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
if (favoritedNodes.empty())
return;
@@ -1388,6 +1389,7 @@ static int8_t lastFrameIndex = -1;
static uint32_t lastFrameChangeTime = 0;
constexpr uint32_t ICON_DISPLAY_DURATION_MS = 2000;
+// cppcheck-suppress constParameterPointer; signature must match OverlayCallback typedef from OLEDDisplayUi library
void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *state)
{
int currentFrame = state->currentFrame;
diff --git a/src/graphics/draw/UIRenderer.h b/src/graphics/draw/UIRenderer.h
index 6e37b68f253..8f0d0788195 100644
--- a/src/graphics/draw/UIRenderer.h
+++ b/src/graphics/draw/UIRenderer.h
@@ -49,7 +49,7 @@ class UIRenderer
// Navigation bar overlay
static void drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *state);
- static void drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y);
+ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
static void drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
diff --git a/src/graphics/niche/Drivers/Backlight/LatchingBacklight.cpp b/src/graphics/niche/Drivers/Backlight/LatchingBacklight.cpp
index 6d9b709b12b..ad92e28eaf1 100644
--- a/src/graphics/niche/Drivers/Backlight/LatchingBacklight.cpp
+++ b/src/graphics/niche/Drivers/Backlight/LatchingBacklight.cpp
@@ -42,7 +42,7 @@ int LatchingBacklight::beforeDeepSleep(void *unused)
{
// Contingency only
// - pin wasn't set
- if (pin != (uint8_t)-1) {
+ if (pin != static_cast(-1)) {
off();
pinMode(pin, INPUT); // High impedance - unnecessary?
} else
@@ -55,7 +55,7 @@ int LatchingBacklight::beforeDeepSleep(void *unused)
// The effect on the backlight is the same; peek and latch are separated to simplify short vs long press button handling
void LatchingBacklight::peek()
{
- assert(pin != (uint8_t)-1);
+ assert(pin != static_cast(-1));
digitalWrite(pin, logicActive); // On
on = true;
latched = false;
@@ -67,7 +67,7 @@ void LatchingBacklight::peek()
// The effect on the backlight is the same; peek and latch are separated to simplify short vs long press button handling
void LatchingBacklight::latch()
{
- assert(pin != (uint8_t)-1);
+ assert(pin != static_cast(-1));
// Blink if moving from peek to latch
// Indicates to user that the transition has taken place
@@ -89,7 +89,7 @@ void LatchingBacklight::latch()
// Suitable for ending both peek and latch
void LatchingBacklight::off()
{
- assert(pin != (uint8_t)-1);
+ assert(pin != static_cast(-1));
digitalWrite(pin, !logicActive); // Off
on = false;
latched = false;
diff --git a/src/graphics/niche/Drivers/Backlight/LatchingBacklight.h b/src/graphics/niche/Drivers/Backlight/LatchingBacklight.h
index 0097cae4c2e..87862ea1b0a 100644
--- a/src/graphics/niche/Drivers/Backlight/LatchingBacklight.h
+++ b/src/graphics/niche/Drivers/Backlight/LatchingBacklight.h
@@ -40,7 +40,7 @@ class LatchingBacklight
CallbackObserver deepSleepObserver =
CallbackObserver(this, &LatchingBacklight::beforeDeepSleep);
- uint8_t pin = (uint8_t)-1;
+ uint8_t pin = static_cast(-1);
bool logicActive = HIGH; // Is light active HIGH or active LOW
bool on = false; // Is light on (either peek or latched)
diff --git a/src/graphics/niche/InkHUD/Applet.cpp b/src/graphics/niche/InkHUD/Applet.cpp
index 0616d03cd81..0a9cd3add87 100644
--- a/src/graphics/niche/InkHUD/Applet.cpp
+++ b/src/graphics/niche/InkHUD/Applet.cpp
@@ -34,7 +34,7 @@ void InkHUD::Applet::drawPixel(int16_t x, int16_t y, uint16_t color)
{
// Only render pixels if they fall within user's cropped region
if (x >= cropLeft && x < (cropLeft + cropWidth) && y >= cropTop && y < (cropTop + cropHeight))
- assignedTile->handleAppletPixel(x, y, (Color)color);
+ assignedTile->handleAppletPixel(x, y, static_cast(color));
}
// Link our applet to a tile
@@ -144,6 +144,21 @@ void InkHUD::Applet::resetDrawingSpace()
setFont(fontSmall);
}
+// Sets one or more inputs to enabled/disabled for this applet and if they should be sent to it
+void InkHUD::Applet::setInputsSubscribed(uint8_t input, bool captured)
+{
+ if (captured)
+ subscribedInputs |= input;
+ else
+ subscribedInputs &= ~input;
+}
+
+// Checks if a specific input is enabled for this applet and should be sent to it
+bool InkHUD::Applet::isInputSubscribed(InputMask input)
+{
+ return (subscribedInputs & input) == input;
+}
+
// Tell InkHUD::Renderer that we want to render now
// Applets should internally listen for events they are interested in, via MeshModule, CallbackObserver etc
// When an applet decides it has heard something important, and wants to redraw, it calls this method
@@ -312,7 +327,7 @@ void InkHUD::Applet::printAt(int16_t x, int16_t y, const char *text, HorizontalA
}
// Print text, specifying the position of any edge / corner of the textbox
-void InkHUD::Applet::printAt(int16_t x, int16_t y, std::string text, HorizontalAlignment ha, VerticalAlignment va)
+void InkHUD::Applet::printAt(int16_t x, int16_t y, const std::string &text, HorizontalAlignment ha, VerticalAlignment va)
{
printAt(x, y, text.c_str(), ha, va);
}
@@ -334,7 +349,7 @@ InkHUD::AppletFont InkHUD::Applet::getFont()
// Parse any text which might have "special characters"
// Re-encodes UTF-8 characters to match our 8-bit encoded fonts
-std::string InkHUD::Applet::parse(std::string text)
+std::string InkHUD::Applet::parse(const std::string &text)
{
return getFont().decodeUTF8(text);
}
@@ -361,10 +376,10 @@ std::string InkHUD::Applet::parseShortName(meshtastic_NodeInfoLite *node)
}
// Determine if all characters of a string are printable using the current font
-bool InkHUD::Applet::isPrintable(std::string text)
+bool InkHUD::Applet::isPrintable(const std::string &text)
{
// Scan for SUB (0x1A), which is the value assigned by AppletFont::applyEncoding if a unicode character is not handled
- for (char &c : text) {
+ for (const char &c : text) {
if (c == '\x1A')
return false;
}
@@ -387,7 +402,7 @@ uint16_t InkHUD::Applet::getTextWidth(const char *text)
// Gets rendered width of a string
// Wrapper for getTextBounds
-uint16_t InkHUD::Applet::getTextWidth(std::string text)
+uint16_t InkHUD::Applet::getTextWidth(const std::string &text)
{
return getTextWidth(text.c_str());
}
@@ -435,7 +450,7 @@ std::string InkHUD::Applet::hexifyNodeNum(NodeNum num)
// Print text, with word wrapping
// Avoids splitting words in half, instead moving the entire word to a new line wherever possible
-void InkHUD::Applet::printWrapped(int16_t left, int16_t top, uint16_t width, std::string text)
+void InkHUD::Applet::printWrapped(int16_t left, int16_t top, uint16_t width, const std::string &text)
{
// Place the AdafruitGFX cursor to suit our "top" coord
setCursor(left, top + getFont().heightAboveCursor());
@@ -492,15 +507,15 @@ void InkHUD::Applet::printWrapped(int16_t left, int16_t top, uint16_t width, std
// Todo: rewrite making use of AdafruitGFX native text wrapping
char cstr[] = {0, 0};
- int16_t l, t;
- uint16_t w, h;
+ int16_t bx, by;
+ uint16_t bw, bh;
for (uint16_t c = 0; c < word.length(); c++) {
// Shove next char into a c string
cstr[0] = word[c];
- getTextBounds(cstr, getCursorX(), getCursorY(), &l, &t, &w, &h);
+ getTextBounds(cstr, getCursorX(), getCursorY(), &bx, &by, &bw, &bh);
// Manual newline, if next character will spill beyond screen edge
- if ((l + w) > left + width)
+ if ((bx + bw) > left + width)
setCursor(left, getCursorY() + getFont().lineHeight());
// Print next character
@@ -519,7 +534,7 @@ void InkHUD::Applet::printWrapped(int16_t left, int16_t top, uint16_t width, std
// Simulate running printWrapped, to determine how tall the block of text will be.
// This is a wasteful way of handling things. Maybe some way to optimize in future?
-uint32_t InkHUD::Applet::getWrappedTextHeight(int16_t left, uint16_t width, std::string text)
+uint32_t InkHUD::Applet::getWrappedTextHeight(int16_t left, uint16_t width, const std::string &text)
{
// Cache the current crop region
int16_t cL = cropLeft;
@@ -649,7 +664,7 @@ uint16_t InkHUD::Applet::getActiveNodeCount()
// For each node in db
for (uint16_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
- meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
+ const meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Check if heard recently, and not our own node
if (sinceLastSeen(node) < settings->recentlyActiveSeconds && node->num != nodeDB->getNodeNum())
@@ -702,7 +717,7 @@ std::string InkHUD::Applet::localizeDistance(uint32_t meters)
}
// Print text with a "faux bold" effect, by drawing it multiple times, offsetting slightly
-void InkHUD::Applet::printThick(int16_t xCenter, int16_t yCenter, std::string text, uint8_t thicknessX, uint8_t thicknessY)
+void InkHUD::Applet::printThick(int16_t xCenter, int16_t yCenter, const std::string &text, uint8_t thicknessX, uint8_t thicknessY)
{
// How many times to draw along x axis
int16_t xStart;
@@ -770,7 +785,7 @@ bool InkHUD::Applet::approveNotification(NicheGraphics::InkHUD::Notification &n)
│ │
└───────────────────────────────┘
*/
-void InkHUD::Applet::drawHeader(std::string text)
+void InkHUD::Applet::drawHeader(const std::string &text)
{
// Y position for divider
// - between header text and messages
diff --git a/src/graphics/niche/InkHUD/Applet.h b/src/graphics/niche/InkHUD/Applet.h
index 69d35a2348f..84fd8646589 100644
--- a/src/graphics/niche/InkHUD/Applet.h
+++ b/src/graphics/niche/InkHUD/Applet.h
@@ -89,6 +89,9 @@ class Applet : public GFX
virtual void onForeground() {}
virtual void onBackground() {}
virtual void onShutdown() {}
+
+ // Input Events
+
virtual void onButtonShortPress() {}
virtual void onButtonLongPress() {}
virtual void onExitShort() {}
@@ -100,6 +103,18 @@ class Applet : public GFX
virtual void onFreeText(char c) {}
virtual void onFreeTextDone() {}
virtual void onFreeTextCancel() {}
+ // List of inputs which can be subscribed to
+ enum InputMask { // | No Joystick | With Joystick |
+ BUTTON_SHORT = 1, // | Button Click | Joystick Center Click |
+ BUTTON_LONG = 2, // | Button Hold | Joystick Center Hold |
+ EXIT_SHORT = 4, // | no-op | Back Button Click |
+ EXIT_LONG = 8, // | no-op | Back Button Hold |
+ NAV_UP = 16, // | no-op | Joystick Up |
+ NAV_DOWN = 32, // | no-op | Joystick Down |
+ NAV_LEFT = 64, // | no-op | Joystick Left |
+ NAV_RIGHT = 128 // | no-op | Joystick Right |
+ };
+ bool isInputSubscribed(InputMask input); // Check if input should be handled by applet, this should not be overloaded.
virtual bool approveNotification(Notification &n); // Allow an applet to veto a notification
@@ -121,20 +136,28 @@ class Applet : public GFX
void setCrop(int16_t left, int16_t top, uint16_t width, uint16_t height); // Ignore pixels drawn outside a certain region
void resetCrop(); // Removes setCrop()
+ // User Input Handling
+
+ uint8_t subscribedInputs = 0b00000000; // Maybe uint16_t for futureproofing? other devices may need more inputs
+ void setInputsSubscribed(uint8_t input,
+ bool captured); // Set if an input should be handled by applet or not, this should not be
+ // overloaded. Can take multiple inputs at once if you OR/`|` them together
+
// Text
void setFont(AppletFont f);
AppletFont getFont();
- uint16_t getTextWidth(std::string text);
+ uint16_t getTextWidth(const std::string &text);
uint16_t getTextWidth(const char *text);
- uint32_t getWrappedTextHeight(int16_t left, uint16_t width, std::string text); // Result of printWrapped
+ uint32_t getWrappedTextHeight(int16_t left, uint16_t width, const std::string &text); // Result of printWrapped
void printAt(int16_t x, int16_t y, const char *text, HorizontalAlignment ha = LEFT, VerticalAlignment va = TOP);
- void printAt(int16_t x, int16_t y, std::string text, HorizontalAlignment ha = LEFT, VerticalAlignment va = TOP);
- void printThick(int16_t xCenter, int16_t yCenter, std::string text, uint8_t thicknessX, uint8_t thicknessY); // Faux bold
- void printWrapped(int16_t left, int16_t top, uint16_t width, std::string text); // Per-word line wrapping
+ void printAt(int16_t x, int16_t y, const std::string &text, HorizontalAlignment ha = LEFT, VerticalAlignment va = TOP);
+ void printThick(int16_t xCenter, int16_t yCenter, const std::string &text, uint8_t thicknessX,
+ uint8_t thicknessY); // Faux bold
+ void printWrapped(int16_t left, int16_t top, uint16_t width, const std::string &text); // Per-word line wrapping
void hatchRegion(int16_t x, int16_t y, uint16_t w, uint16_t h, uint8_t spacing, Color color); // Fill with sparse lines
- void drawHeader(std::string text); // Draw the standard applet header
+ void drawHeader(const std::string &text); // Draw the standard applet header
// Meshtastic Logo
@@ -150,9 +173,9 @@ class Applet : public GFX
std::string getTimeString(); // Current time, human readable
uint16_t getActiveNodeCount(); // Duration determined by user, in onscreen menu
std::string localizeDistance(uint32_t meters); // Human readable distance, imperial or metric
- std::string parse(std::string text); // Handle text which might contain special chars
+ std::string parse(const std::string &text); // Handle text which might contain special chars
std::string parseShortName(meshtastic_NodeInfoLite *node); // Get the shortname, or a substitute if has unprintable chars
- bool isPrintable(std::string); // Check for characters which the font can't print
+ bool isPrintable(const std::string &text); // Check for characters which the font can't print
// Convenient references
diff --git a/src/graphics/niche/InkHUD/AppletFont.cpp b/src/graphics/niche/InkHUD/AppletFont.cpp
index 93a621ee8e0..188671a0ed9 100644
--- a/src/graphics/niche/InkHUD/AppletFont.cpp
+++ b/src/graphics/niche/InkHUD/AppletFont.cpp
@@ -39,11 +39,11 @@ InkHUD::AppletFont::AppletFont(const GFXfont &adafruitGFXFont, Encoding encoding
// Caution: signed and unsigned types
int8_t glyphAscender = 0 - gfxFont->glyph[i].yOffset;
if (glyphAscender > 0)
- this->ascenderHeight = max(this->ascenderHeight, (uint8_t)glyphAscender);
+ this->ascenderHeight = max(this->ascenderHeight, static_cast(glyphAscender));
int8_t glyphDescender = gfxFont->glyph[i].height + gfxFont->glyph[i].yOffset;
if (glyphDescender > 0)
- this->descenderHeight = max(this->descenderHeight, (uint8_t)glyphDescender);
+ this->descenderHeight = max(this->descenderHeight, static_cast(glyphDescender));
}
// Apply any manual padding to grow or shrink the line size
@@ -52,7 +52,7 @@ InkHUD::AppletFont::AppletFont(const GFXfont &adafruitGFXFont, Encoding encoding
descenderHeight += paddingBottom;
// Find how far the cursor advances when we "print" a space character
- spaceCharWidth = gfxFont->glyph[(uint8_t)' ' - gfxFont->first].xAdvance;
+ spaceCharWidth = gfxFont->glyph[static_cast(' ') - gfxFont->first].xAdvance;
}
/*
@@ -98,7 +98,7 @@ uint8_t InkHUD::AppletFont::widthBetweenWords()
// Convert a unicode char from set of UTF-8 bytes to UTF-32
// Used by AppletFont::applyEncoding, which remaps unicode chars for extended ASCII fonts, based on their UTF-32 value
-uint32_t InkHUD::AppletFont::toUtf32(std::string utf8)
+uint32_t InkHUD::AppletFont::toUtf32(const std::string &utf8)
{
uint32_t utf32 = 0;
@@ -132,7 +132,7 @@ uint32_t InkHUD::AppletFont::toUtf32(std::string utf8)
// Process a string, collating UTF-8 bytes, and sending them off for re-encoding to extended ASCII
// Not all InkHUD text is passed through here, only text which could potentially contain non-ASCII chars
-std::string InkHUD::AppletFont::decodeUTF8(std::string encoded)
+std::string InkHUD::AppletFont::decodeUTF8(const std::string &encoded)
{
// Final processed output
std::string decoded;
@@ -141,7 +141,7 @@ std::string InkHUD::AppletFont::decodeUTF8(std::string encoded)
std::string utf8Char;
uint8_t utf8CharSize = 0;
- for (char &c : encoded) {
+ for (const char &c : encoded) {
// If first byte
if (utf8Char.empty()) {
@@ -178,7 +178,7 @@ std::string InkHUD::AppletFont::decodeUTF8(std::string encoded)
// Re-encode a single UTF-8 character to extended ASCII
// Target encoding depends on the font
-char InkHUD::AppletFont::applyEncoding(std::string utf8)
+char InkHUD::AppletFont::applyEncoding(const std::string &utf8)
{
// ##################################################### Syntactic Sugar #####################################################
#define REMAP(in, out) \
diff --git a/src/graphics/niche/InkHUD/AppletFont.h b/src/graphics/niche/InkHUD/AppletFont.h
index 02ba13c31d7..8374c7f617f 100644
--- a/src/graphics/niche/InkHUD/AppletFont.h
+++ b/src/graphics/niche/InkHUD/AppletFont.h
@@ -30,20 +30,21 @@ class AppletFont
};
AppletFont();
- AppletFont(const GFXfont &adafruitGFXFont, Encoding encoding = ASCII, int8_t paddingTop = 0, int8_t paddingBottom = 0);
+ explicit AppletFont(const GFXfont &adafruitGFXFont, Encoding encoding = ASCII, int8_t paddingTop = 0,
+ int8_t paddingBottom = 0);
uint8_t lineHeight();
uint8_t heightAboveCursor();
uint8_t heightBelowCursor();
uint8_t widthBetweenWords(); // Width of the space character
- std::string decodeUTF8(std::string encoded);
+ std::string decodeUTF8(const std::string &encoded);
- const GFXfont *gfxFont = NULL; // Default value: in-built AdafruitGFX font
+ const GFXfont *gfxFont = nullptr; // Default value: in-built AdafruitGFX font
private:
- uint32_t toUtf32(std::string utf8);
- char applyEncoding(std::string utf8);
+ uint32_t toUtf32(const std::string &utf8);
+ char applyEncoding(const std::string &utf8);
uint8_t height = 8; // Default value: in-built AdafruitGFX font
uint8_t ascenderHeight = 0; // Default value: in-built AdafruitGFX font
diff --git a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp
index 9794c3efbaf..a063c08b536 100644
--- a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp
@@ -81,25 +81,25 @@ ProcessMessage InkHUD::NodeListApplet::handleReceived(const meshtastic_MeshPacke
uint8_t InkHUD::NodeListApplet::maxCards()
{
// Cache result. Shouldn't change during execution
- static uint8_t cards = 0;
+ static uint8_t maxCardCount = 0;
- if (!cards) {
+ if (!maxCardCount) {
const uint16_t height = Tile::maxDisplayDimension();
// Use a loop instead of arithmetic, because it's easier for my brain to follow
// Add cards one by one, until the latest card extends below screen
uint16_t y = cardH; // First card: no margin above
- cards = 1;
+ maxCardCount = 1;
while (y < height) {
y += cardMarginH;
y += cardH;
- cards++;
+ maxCardCount++;
}
}
- return cards;
+ return maxCardCount;
}
// Draw, using info which derived applet placed into NodeListApplet::cards for us
@@ -137,12 +137,12 @@ void InkHUD::NodeListApplet::onRender(bool full)
// Gather info
// ========================================
- NodeNum &nodeNum = card->nodeNum;
+ const NodeNum &nodeNum = card->nodeNum;
SignalStrength &signal = card->signal;
std::string longName; // handled below
std::string shortName; // handled below
std::string distance; // handled below;
- uint8_t &hopsAway = card->hopsAway;
+ const uint8_t &hopsAway = card->hopsAway;
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum);
diff --git a/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp b/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp
new file mode 100644
index 00000000000..79133719ae6
--- /dev/null
+++ b/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp
@@ -0,0 +1,79 @@
+#ifdef MESHTASTIC_INCLUDE_INKHUD
+#include "./UserAppletInputExample.h"
+
+using namespace NicheGraphics;
+
+void InkHUD::UserAppletInputExampleApplet::onActivate()
+{
+ setGrabbed(false);
+}
+
+void InkHUD::UserAppletInputExampleApplet::onRender(bool full)
+{
+ drawHeader("Input Example");
+ uint16_t headerHeight = getHeaderHeight();
+
+ std::string buttonName;
+ if (settings->joystick.enabled)
+ buttonName = "joystick center button";
+ else
+ buttonName = "user button";
+
+ std::string additional = " | Control is grabbed, long press " + buttonName + " to release controls";
+ if (!isGrabbed)
+ additional = " | Control is released, long press " + buttonName + " to grab controls";
+
+ printWrapped(0, headerHeight, width(), "Last button: " + lastInput + additional);
+}
+
+void InkHUD::UserAppletInputExampleApplet::setGrabbed(bool grabbed)
+{
+ isGrabbed = grabbed;
+ setInputsSubscribed(BUTTON_SHORT | EXIT_SHORT | EXIT_LONG | NAV_UP | NAV_DOWN | NAV_LEFT | NAV_RIGHT,
+ grabbed); // Enables/disables grabbing all inputs
+ setInputsSubscribed(BUTTON_LONG, true); // Always grab this input
+}
+
+void InkHUD::UserAppletInputExampleApplet::onButtonShortPress()
+{
+ lastInput = "BUTTON_SHORT";
+ requestUpdate();
+}
+void InkHUD::UserAppletInputExampleApplet::onButtonLongPress()
+{
+ lastInput = "BUTTON_LONG";
+ setGrabbed(!isGrabbed);
+ requestUpdate();
+}
+void InkHUD::UserAppletInputExampleApplet::onExitShort()
+{
+ lastInput = "EXIT_SHORT";
+ requestUpdate();
+}
+void InkHUD::UserAppletInputExampleApplet::onExitLong()
+{
+ lastInput = "EXIT_LONG";
+ requestUpdate();
+}
+void InkHUD::UserAppletInputExampleApplet::onNavUp()
+{
+ lastInput = "NAV_UP";
+ requestUpdate();
+}
+void InkHUD::UserAppletInputExampleApplet::onNavDown()
+{
+ lastInput = "NAV_DOWN";
+ requestUpdate();
+}
+void InkHUD::UserAppletInputExampleApplet::onNavLeft()
+{
+ lastInput = "NAV_LEFT";
+ requestUpdate();
+}
+void InkHUD::UserAppletInputExampleApplet::onNavRight()
+{
+ lastInput = "NAV_RIGHT";
+ requestUpdate();
+}
+
+#endif
\ No newline at end of file
diff --git a/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h b/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h
new file mode 100644
index 00000000000..a99dec00c41
--- /dev/null
+++ b/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h
@@ -0,0 +1,36 @@
+#ifdef MESHTASTIC_INCLUDE_INKHUD
+
+#pragma once
+
+#include "configuration.h"
+
+#include "graphics/niche/InkHUD/Applet.h"
+
+namespace NicheGraphics::InkHUD
+{
+
+class UserAppletInputExampleApplet : public Applet
+{
+ public:
+ void onActivate() override;
+
+ void onRender(bool full) 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;
+
+ private:
+ std::string lastInput = "None";
+ bool isGrabbed = false;
+
+ void setGrabbed(bool grabbed);
+};
+
+} // namespace NicheGraphics::InkHUD
+
+#endif
\ No newline at end of file
diff --git a/src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.cpp
index 4fd01c3480b..c0850b74200 100644
--- a/src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.cpp
@@ -29,10 +29,10 @@ int InkHUD::BatteryIconApplet::onPowerStatusUpdate(const meshtastic::Status *sta
// If we get a different type of status, something has gone weird elsewhere
assert(status->getStatusType() == STATUS_TYPE_POWER);
- meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)status;
+ const meshtastic::PowerStatus *pwrStatus = (const meshtastic::PowerStatus *)status;
// Get the new state of charge %, and round to the nearest 10%
- uint8_t newSocRounded = ((powerStatus->getBatteryChargePercent() + 5) / 10) * 10;
+ uint8_t newSocRounded = ((pwrStatus->getBatteryChargePercent() + 5) / 10) * 10;
// If rounded value has changed, trigger a display update
// It's okay to requestUpdate before we store the new value, as the update won't run until next loop()
diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp
index 6a141f73e4d..520a6de976a 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp
@@ -1176,7 +1176,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
items.push_back(MenuItem("Back", previousPage));
for (uint8_t i = 0; i < MAX_NUM_CHANNELS; i++) {
- meshtastic_Channel &ch = channels.getByIndex(i);
+ const meshtastic_Channel &ch = channels.getByIndex(i);
if (!ch.has_settings)
continue;
@@ -1252,7 +1252,7 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
case NODE_CONFIG_CHANNEL_PRECISION: {
previousPage = MenuPage::NODE_CONFIG_CHANNEL_DETAIL;
items.push_back(MenuItem("Back", previousPage));
- meshtastic_Channel &ch = channels.getByIndex(selectedChannelIndex);
+ const meshtastic_Channel &ch = channels.getByIndex(selectedChannelIndex);
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));
break;
@@ -1524,7 +1524,15 @@ void InkHUD::MenuApplet::onButtonShortPress()
if (!settings->joystick.enabled) {
if (!cursorShown) {
cursorShown = true;
+ // Select the first item that isn't a header
cursor = 0;
+ while (cursor < items.size() && items.at(cursor).isHeader) {
+ cursor++;
+ }
+ if (cursor >= items.size()) {
+ cursorShown = false;
+ cursor = 0;
+ }
} else {
do {
cursor = (cursor + 1) % items.size();
@@ -1576,7 +1584,15 @@ void InkHUD::MenuApplet::onNavUp()
if (!cursorShown) {
cursorShown = true;
- cursor = 0;
+ // Select the last item that isn't a header
+ cursor = items.size() - 1;
+ while (items.at(cursor).isHeader) {
+ if (cursor == 0) {
+ cursorShown = false;
+ break;
+ }
+ cursor--;
+ }
} else {
do {
if (cursor == 0)
@@ -1597,7 +1613,15 @@ void InkHUD::MenuApplet::onNavDown()
if (!cursorShown) {
cursorShown = true;
+ // Select the first item that isn't a header
cursor = 0;
+ while (cursor < items.size() && items.at(cursor).isHeader) {
+ cursor++;
+ }
+ if (cursor >= items.size()) {
+ cursorShown = false;
+ cursor = 0;
+ }
} else {
do {
cursor = (cursor + 1) % items.size();
@@ -1759,7 +1783,7 @@ void InkHUD::MenuApplet::populateRecipientPage()
for (uint8_t i = 0; i < MAX_NUM_CHANNELS; i++) {
// Get the channel, and check if it's enabled
- meshtastic_Channel &channel = channels.getByIndex(i);
+ const meshtastic_Channel &channel = channels.getByIndex(i);
if (!channel.has_settings || channel.role == meshtastic_Channel_Role_DISABLED)
continue;
@@ -1829,7 +1853,7 @@ void InkHUD::MenuApplet::populateRecipientPage()
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)
+void InkHUD::MenuApplet::drawInputField(uint16_t left, uint16_t top, uint16_t width, uint16_t height, const std::string &text)
{
setFont(fontSmall);
uint16_t wrapMaxH = 0;
diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h
index 7b092153b60..b5c1c86e4c8 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h
+++ b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h
@@ -55,7 +55,7 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
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
+ const std::string &text); // Draw input field for free text
uint16_t getSystemInfoPanelHeight();
void drawSystemInfoPanel(int16_t left, int16_t top, uint16_t width,
uint16_t *height = nullptr); // Info panel at top of root menu
diff --git a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp
index 19cef4fbdab..6c8069c8b9d 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp
@@ -228,17 +228,17 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila
Notification::Type::NOTIFICATION_MESSAGE_BROADCAST)) {
// Although we are handling DM and broadcast notifications together, we do need to treat them slightly differently
- bool isBroadcast = currentNotification.type == Notification::Type::NOTIFICATION_MESSAGE_BROADCAST;
+ bool msgIsBroadcast = currentNotification.type == Notification::Type::NOTIFICATION_MESSAGE_BROADCAST;
// Pick source of message
- MessageStore::Message *message =
- isBroadcast ? &inkhud->persistence->latestMessage.broadcast : &inkhud->persistence->latestMessage.dm;
+ const MessageStore::Message *message =
+ msgIsBroadcast ? &inkhud->persistence->latestMessage.broadcast : &inkhud->persistence->latestMessage.dm;
// Find info about the sender
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(message->sender);
// Leading tag (channel vs. DM)
- text += isBroadcast ? "From:" : "DM: ";
+ text += msgIsBroadcast ? "From:" : "DM: ";
// Sender id
if (node && node->has_user)
@@ -252,7 +252,7 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila
text.clear();
// Leading tag (channel vs. DM)
- text += isBroadcast ? "Msg from " : "DM from ";
+ text += msgIsBroadcast ? "Msg from " : "DM from ";
// Sender id
if (node && node->has_user)
diff --git a/src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.cpp
index a09ff55d536..54515b2966d 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.cpp
@@ -55,12 +55,12 @@ int InkHUD::PairingApplet::onBluetoothStatusUpdate(const meshtastic::Status *sta
// We'll mimic that behavior, just to keep in line with the other Statuses,
// even though I'm not sure what the original reason for jumping through these extra hoops was.
assert(status->getStatusType() == STATUS_TYPE_BLUETOOTH);
- meshtastic::BluetoothStatus *bluetoothStatus = (meshtastic::BluetoothStatus *)status;
+ const auto *btStatus = static_cast(status);
// When pairing begins
- if (bluetoothStatus->getConnectionState() == meshtastic::BluetoothStatus::ConnectionState::PAIRING) {
+ if (btStatus->getConnectionState() == meshtastic::BluetoothStatus::ConnectionState::PAIRING) {
// Store the passkey for rendering
- passkey = bluetoothStatus->getPasskey();
+ passkey = btStatus->getPasskey();
// Show pairing screen
bringToForeground();
diff --git a/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.cpp
new file mode 100644
index 00000000000..6963df54afb
--- /dev/null
+++ b/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.cpp
@@ -0,0 +1,111 @@
+#ifdef MESHTASTIC_INCLUDE_INKHUD
+
+#include "./FavoritesMapApplet.h"
+#include "NodeDB.h"
+
+using namespace NicheGraphics;
+
+bool InkHUD::FavoritesMapApplet::shouldDrawNode(meshtastic_NodeInfoLite *node)
+{
+ // Keep our own node available as map anchor/center; all others must be favorited.
+ return node && (node->num == nodeDB->getNodeNum() || node->is_favorite);
+}
+
+void InkHUD::FavoritesMapApplet::onRender(bool full)
+{
+ // Custom empty state text for favorites-only map.
+ if (!enoughMarkers()) {
+ printAt(X(0.5), Y(0.5) - (getFont().lineHeight() / 2), "Favorite node position", CENTER, MIDDLE);
+ printAt(X(0.5), Y(0.5) + (getFont().lineHeight() / 2), "will appear here", CENTER, MIDDLE);
+ return;
+ }
+
+ // Draw the usual map applet first.
+ MapApplet::onRender(full);
+
+ // Draw our latest "node of interest" as a special marker.
+ meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(lastFrom);
+ if (node && node->is_favorite && nodeDB->hasValidPosition(node) && enoughMarkers())
+ drawLabeledMarker(node);
+}
+
+// Determine if we need to redraw the map, when we receive a new position packet.
+ProcessMessage InkHUD::FavoritesMapApplet::handleReceived(const meshtastic_MeshPacket &mp)
+{
+ // If applet is not active, we shouldn't be handling any data.
+ if (!isActive())
+ return ProcessMessage::CONTINUE;
+
+ // Try decode a position from the packet.
+ bool hasPosition = false;
+ float lat;
+ float lng;
+ if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.decoded.portnum == meshtastic_PortNum_POSITION_APP) {
+ meshtastic_Position position = meshtastic_Position_init_default;
+ if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Position_msg, &position)) {
+ if (position.has_latitude_i && position.has_longitude_i // Actually has position
+ && (position.latitude_i != 0 || position.longitude_i != 0)) // Position isn't "null island"
+ {
+ hasPosition = true;
+ lat = position.latitude_i * 1e-7; // Convert from Meshtastic's internal int32_t format
+ lng = position.longitude_i * 1e-7;
+ }
+ }
+ }
+
+ // Skip if we didn't get a valid position.
+ if (!hasPosition)
+ return ProcessMessage::CONTINUE;
+
+ const int8_t hopsAway = getHopsAway(mp);
+ const bool hasHopsAway = hopsAway >= 0;
+
+ // Determine if the position packet would change anything on-screen.
+ bool somethingChanged = false;
+
+ // If our own position.
+ if (isFromUs(&mp)) {
+ // Ignore tiny local movement to reduce update spam.
+ if (GeoCoord::latLongToMeter(ourLastLat, ourLastLng, lat, lng) > 50) {
+ somethingChanged = true;
+ ourLastLat = lat;
+ ourLastLng = lng;
+ }
+ } else {
+ // For non-local packets, this applet only reacts to favorited nodes.
+ meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(mp.from);
+ if (!sender || !sender->is_favorite)
+ return ProcessMessage::CONTINUE;
+
+ // Check if this position is from someone different than our previous position packet.
+ if (mp.from != lastFrom) {
+ somethingChanged = true;
+ lastFrom = mp.from;
+ lastLat = lat;
+ lastLng = lng;
+ lastHopsAway = hopsAway;
+ }
+
+ // Same sender: check if position changed.
+ else if (GeoCoord::latLongToMeter(lastLat, lastLng, lat, lng) > 10) {
+ somethingChanged = true;
+ lastLat = lat;
+ lastLng = lng;
+ }
+
+ // Same sender, same position: check if hops changed.
+ else if (hasHopsAway && (hopsAway != lastHopsAway)) {
+ somethingChanged = true;
+ lastHopsAway = hopsAway;
+ }
+ }
+
+ if (somethingChanged) {
+ requestAutoshow();
+ requestUpdate();
+ }
+
+ return ProcessMessage::CONTINUE;
+}
+
+#endif
diff --git a/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h b/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h
new file mode 100644
index 00000000000..da5fb0dc3ae
--- /dev/null
+++ b/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h
@@ -0,0 +1,44 @@
+#ifdef MESHTASTIC_INCLUDE_INKHUD
+
+/*
+
+Plots position of favorited nodes from DB, with North facing up.
+Scaled to fit the most distant node.
+Size of marker represents hops away.
+The favorite node which most recently sent a position will be labeled.
+
+*/
+
+#pragma once
+
+#include "configuration.h"
+
+#include "graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h"
+
+#include "SinglePortModule.h"
+
+namespace NicheGraphics::InkHUD
+{
+
+class FavoritesMapApplet : public MapApplet, public SinglePortModule
+{
+ public:
+ FavoritesMapApplet() : SinglePortModule("FavoritesMapApplet", meshtastic_PortNum_POSITION_APP) {}
+ void onRender(bool full) override;
+
+ protected:
+ bool shouldDrawNode(meshtastic_NodeInfoLite *node) override;
+ ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
+
+ NodeNum lastFrom = 0; // Sender of most recent favorited (non-local) position packet
+ float lastLat = 0.0;
+ float lastLng = 0.0;
+ float lastHopsAway = 0;
+
+ float ourLastLat = 0.0; // Info about most recent local position
+ float ourLastLng = 0.0;
+};
+
+} // namespace NicheGraphics::InkHUD
+
+#endif
diff --git a/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp
index 5a659c60679..a7fd094e6ca 100644
--- a/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp
@@ -69,9 +69,10 @@ void InkHUD::HeardApplet::populateFromNodeDB()
}
// Sort the collection by age
- std::sort(ordered.begin(), ordered.end(), [](meshtastic_NodeInfoLite *top, meshtastic_NodeInfoLite *bottom) -> bool {
- return (top->last_heard > bottom->last_heard);
- });
+ std::sort(ordered.begin(), ordered.end(),
+ [](const meshtastic_NodeInfoLite *top, const meshtastic_NodeInfoLite *bottom) -> bool {
+ return (top->last_heard > bottom->last_heard);
+ });
// Keep the most recent entries only
// Just enough to fill the screen
diff --git a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp
index f16721357db..01bdc2224ba 100644
--- a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp
@@ -69,7 +69,7 @@ void InkHUD::ThreadedMessageApplet::onRender(bool full)
while (msgB >= (0 - fontSmall.lineHeight()) && i < store->messages.size()) {
// Grab data for message
- MessageStore::Message &m = store->messages.at(i);
+ const MessageStore::Message &m = store->messages.at(i);
bool outgoing = (m.sender == 0) || (m.sender == myNodeInfo.my_node_num); // Own NodeNum if canned message
std::string bodyText = parse(m.text); // Parse any non-ascii chars in the message
diff --git a/src/graphics/niche/InkHUD/Events.cpp b/src/graphics/niche/InkHUD/Events.cpp
index e6c16d3500f..577a773bb49 100644
--- a/src/graphics/niche/InkHUD/Events.cpp
+++ b/src/graphics/niche/InkHUD/Events.cpp
@@ -59,10 +59,16 @@ void InkHUD::Events::onButtonShort()
if (consumer) {
consumer->onButtonShortPress();
} else if (!dismissedExt) { // Don't change applet if this button press silenced the external notification module
- if (!settings->joystick.enabled)
- inkhud->nextApplet();
- else
- inkhud->openMenu();
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::BUTTON_SHORT))
+ userConsumer->onButtonShortPress();
+ else {
+ if (!settings->joystick.enabled)
+ inkhud->nextApplet();
+ else
+ inkhud->openMenu();
+ }
}
}
@@ -84,8 +90,14 @@ void InkHUD::Events::onButtonLong()
// If no system applet is handling input, default behavior instead is to open the menu
if (consumer)
consumer->onButtonLongPress();
- else
- inkhud->openMenu();
+ else {
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::BUTTON_LONG))
+ userConsumer->onButtonLongPress();
+ else
+ inkhud->openMenu();
+ }
}
void InkHUD::Events::onExitShort()
@@ -110,8 +122,14 @@ void InkHUD::Events::onExitShort()
// If no system applet is handling input, default behavior instead is change tiles
if (consumer)
consumer->onExitShort();
- else if (!dismissedExt) // Don't change tile if this button press silenced the external notification module
- inkhud->nextTile();
+ else if (!dismissedExt) { // Don't change tile if this button press silenced the external notification module
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::EXIT_SHORT))
+ userConsumer->onExitShort();
+ else
+ inkhud->nextTile();
+ }
}
}
@@ -133,6 +151,13 @@ void InkHUD::Events::onExitLong()
if (consumer)
consumer->onExitLong();
+ else {
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::EXIT_LONG))
+ userConsumer->onExitLong();
+ // Nothing uses exit long yet
+ }
}
}
@@ -157,6 +182,12 @@ void InkHUD::Events::onNavUp()
if (consumer)
consumer->onNavUp();
+ else if (!dismissedExt) {
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::NAV_UP))
+ userConsumer->onNavUp();
+ }
}
}
@@ -181,6 +212,12 @@ void InkHUD::Events::onNavDown()
if (consumer)
consumer->onNavDown();
+ else if (!dismissedExt) {
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::NAV_DOWN))
+ userConsumer->onNavDown();
+ }
}
}
@@ -206,8 +243,14 @@ void InkHUD::Events::onNavLeft()
// If no system applet is handling input, default behavior instead is to cycle applets
if (consumer)
consumer->onNavLeft();
- else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
- inkhud->prevApplet();
+ else if (!dismissedExt) { // Don't change applet if this button press silenced the external notification module
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::NAV_LEFT))
+ userConsumer->onNavLeft();
+ else
+ inkhud->prevApplet();
+ }
}
}
@@ -233,8 +276,14 @@ void InkHUD::Events::onNavRight()
// If no system applet is handling input, default behavior instead is to cycle applets
if (consumer)
consumer->onNavRight();
- else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
- inkhud->nextApplet();
+ else if (!dismissedExt) { // Don't change applet if this button press silenced the external notification module
+ Applet *userConsumer = inkhud->getActiveApplet();
+
+ if (userConsumer != nullptr && userConsumer->isInputSubscribed(Applet::NAV_RIGHT))
+ userConsumer->onNavRight();
+ else
+ inkhud->nextApplet();
+ }
}
}
diff --git a/src/graphics/niche/InkHUD/InkHUD.cpp b/src/graphics/niche/InkHUD/InkHUD.cpp
index 5fab6763998..edffda6b776 100644
--- a/src/graphics/niche/InkHUD/InkHUD.cpp
+++ b/src/graphics/niche/InkHUD/InkHUD.cpp
@@ -210,6 +210,12 @@ void InkHUD::InkHUD::prevApplet()
windowManager->prevApplet();
}
+// Returns the currently active applet
+InkHUD::Applet *InkHUD::InkHUD::getActiveApplet()
+{
+ return windowManager->getActiveApplet();
+}
+
// Show the menu (on the the focused tile)
// The applet previously displayed there will be restored once the menu closes
void InkHUD::InkHUD::openMenu()
diff --git a/src/graphics/niche/InkHUD/InkHUD.h b/src/graphics/niche/InkHUD/InkHUD.h
index ae029137eae..0e25b09000b 100644
--- a/src/graphics/niche/InkHUD/InkHUD.h
+++ b/src/graphics/niche/InkHUD/InkHUD.h
@@ -74,6 +74,7 @@ class InkHUD
void nextApplet();
void prevApplet();
+ NicheGraphics::InkHUD::Applet *getActiveApplet();
void openMenu();
void openAlignStick();
void openKeyboard();
diff --git a/src/graphics/niche/InkHUD/MessageStore.cpp b/src/graphics/niche/InkHUD/MessageStore.cpp
index 94e0aa66157..44a1ef63310 100644
--- a/src/graphics/niche/InkHUD/MessageStore.cpp
+++ b/src/graphics/niche/InkHUD/MessageStore.cpp
@@ -12,7 +12,7 @@ using namespace NicheGraphics;
constexpr uint8_t MAX_MESSAGES_SAVED = 10;
constexpr uint32_t MAX_MESSAGE_SIZE = 250;
-InkHUD::MessageStore::MessageStore(std::string label)
+InkHUD::MessageStore::MessageStore(const std::string &label)
{
filename = "";
filename += "/NicheGraphics";
@@ -50,12 +50,13 @@ void InkHUD::MessageStore::saveToFlash()
// For each message
for (uint8_t i = 0; i < messages.size() && i < MAX_MESSAGES_SAVED; i++) {
Message &m = messages.at(i);
- f.write((uint8_t *)&m.timestamp, sizeof(m.timestamp)); // Write timestamp. 4 bytes
- f.write((uint8_t *)&m.sender, sizeof(m.sender)); // Write sender NodeId. 4 Bytes
- f.write((uint8_t *)&m.channelIndex, sizeof(m.channelIndex)); // Write channel index. 1 Byte
- f.write((uint8_t *)m.text.c_str(), min(MAX_MESSAGE_SIZE, m.text.size())); // Write message text. Variable length
- f.write('\0'); // Append null term
- LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", (uint32_t)i, min(MAX_MESSAGE_SIZE, m.text.size()), m.text.c_str());
+ f.write(reinterpret_cast(&m.timestamp), sizeof(m.timestamp)); // Write timestamp. 4 bytes
+ f.write(reinterpret_cast(&m.sender), sizeof(m.sender)); // Write sender NodeId. 4 Bytes
+ f.write(reinterpret_cast(&m.channelIndex), sizeof(m.channelIndex)); // Write channel index. 1 Byte
+ f.write(reinterpret_cast(m.text.c_str()), min(MAX_MESSAGE_SIZE, m.text.size())); // Write message text
+ f.write('\0'); // Append null term
+ LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", static_cast(i), min(MAX_MESSAGE_SIZE, m.text.size()),
+ m.text.c_str());
}
// Release firmware's SPI lock, because SafeFile::close needs it
@@ -111,17 +112,17 @@ void InkHUD::MessageStore::loadFromFlash()
// First byte: how many messages are in the flash store
uint8_t flashMessageCount = 0;
- f.readBytes((char *)&flashMessageCount, 1);
- LOG_DEBUG("Messages available: %u", (uint32_t)flashMessageCount);
+ f.readBytes(reinterpret_cast(&flashMessageCount), 1);
+ LOG_DEBUG("Messages available: %u", static_cast(flashMessageCount));
// For each message
for (uint8_t i = 0; i < flashMessageCount && i < MAX_MESSAGES_SAVED; i++) {
Message m;
// Read meta data (fixed width)
- f.readBytes((char *)&m.timestamp, sizeof(m.timestamp));
- f.readBytes((char *)&m.sender, sizeof(m.sender));
- f.readBytes((char *)&m.channelIndex, sizeof(m.channelIndex));
+ f.readBytes(reinterpret_cast(&m.timestamp), sizeof(m.timestamp));
+ f.readBytes(reinterpret_cast(&m.sender), sizeof(m.sender));
+ f.readBytes(reinterpret_cast(&m.channelIndex), sizeof(m.channelIndex));
// Read characters until we find a null term
char c;
@@ -136,7 +137,8 @@ void InkHUD::MessageStore::loadFromFlash()
// Store in RAM
messages.push_back(m);
- LOG_DEBUG("#%u, timestamp=%u, sender(num)=%u, text=\"%s\"", (uint32_t)i, m.timestamp, m.sender, m.text.c_str());
+ LOG_DEBUG("#%u, timestamp=%u, sender(num)=%u, text=\"%s\"", static_cast(i), m.timestamp, m.sender,
+ m.text.c_str());
}
f.close();
diff --git a/src/graphics/niche/InkHUD/MessageStore.h b/src/graphics/niche/InkHUD/MessageStore.h
index 745c3b2ebd3..55fb9b8cc0b 100644
--- a/src/graphics/niche/InkHUD/MessageStore.h
+++ b/src/graphics/niche/InkHUD/MessageStore.h
@@ -31,7 +31,7 @@ class MessageStore
};
MessageStore() = delete;
- explicit MessageStore(std::string label); // Label determines filename in flash
+ explicit MessageStore(const std::string &label); // Label determines filename in flash
void saveToFlash();
void loadFromFlash();
diff --git a/src/graphics/niche/InkHUD/PlatformioConfig.ini b/src/graphics/niche/InkHUD/PlatformioConfig.ini
index b985f9f7708..67ad5098fa4 100644
--- a/src/graphics/niche/InkHUD/PlatformioConfig.ini
+++ b/src/graphics/niche/InkHUD/PlatformioConfig.ini
@@ -8,5 +8,5 @@ build_flags =
-D MESHTASTIC_EXCLUDE_INPUTBROKER ; Suppress default input handling
-D HAS_BUTTON=0 ; Suppress default ButtonThread
lib_deps =
- # TODO renovate
- https://github.com/ZinggJM/GFX_Root#2.0.0 ; Used by InkHUD as a "slimmer" version of AdafruitGFX
+ # renovate: datasource=github-tags depName=GFX_Root packageName=ZinggJM/GFX_Root
+ https://github.com/ZinggJM/GFX_Root/archive/3195764e352a0d2567c8d277ac408ca7293a99b0.zip ; Used by InkHUD as a "slimmer" version of AdafruitGFX
diff --git a/src/graphics/niche/InkHUD/Renderer.cpp b/src/graphics/niche/InkHUD/Renderer.cpp
index 89a83c9325c..a73e209ff7f 100644
--- a/src/graphics/niche/InkHUD/Renderer.cpp
+++ b/src/graphics/niche/InkHUD/Renderer.cpp
@@ -269,42 +269,42 @@ 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;
+ uint16_t tileW = 0;
+ uint16_t tileH = 0;
switch (settings->rotation) {
case 0:
left = t->getLeft();
top = t->getTop();
- width = t->getWidth();
- height = t->getHeight();
+ tileW = t->getWidth();
+ tileH = t->getHeight();
break;
case 1:
left = driver->width - (t->getTop() + t->getHeight());
top = t->getLeft();
- width = t->getHeight();
- height = t->getWidth();
+ tileW = t->getHeight();
+ tileH = 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();
+ tileW = t->getWidth();
+ tileH = t->getHeight();
break;
case 3:
left = t->getTop();
top = driver->height - (t->getLeft() + t->getWidth());
- width = t->getHeight();
- height = t->getWidth();
+ tileW = t->getHeight();
+ tileH = 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)
+ if (xStart >= driver->width || yStart >= driver->height || left + tileW < 0 || top + tileH < 0)
return; // the box is completely off the screen
- uint16_t xEnd = left + width;
- uint16_t yEnd = top + height;
+ uint16_t xEnd = left + tileW;
+ uint16_t yEnd = top + tileH;
if (xEnd > driver->width)
xEnd = driver->width;
if (yEnd > driver->height)
diff --git a/src/graphics/niche/InkHUD/WindowManager.cpp b/src/graphics/niche/InkHUD/WindowManager.cpp
index cec72ce8f5f..fce3c97707f 100644
--- a/src/graphics/niche/InkHUD/WindowManager.cpp
+++ b/src/graphics/niche/InkHUD/WindowManager.cpp
@@ -273,6 +273,12 @@ void InkHUD::WindowManager::prevApplet()
inkhud->forceUpdate(EInk::UpdateTypes::FAST); // bringToForeground already requested, but we're manually forcing FAST
}
+// Returns active applet
+NicheGraphics::InkHUD::Applet *InkHUD::WindowManager::getActiveApplet()
+{
+ return userTiles.at(settings->userTiles.focused)->getAssignedApplet();
+}
+
// Rotate the display image by 90 degrees
void InkHUD::WindowManager::rotate()
{
@@ -396,7 +402,7 @@ void InkHUD::WindowManager::autoshow()
{
// Don't perform autoshow if a system applet has exclusive use of the display right now
// Note: lockRequests prevents autoshow attempting to hide menuApplet
- for (SystemApplet *sa : inkhud->systemApplets) {
+ for (const SystemApplet *sa : inkhud->systemApplets) {
if (sa->lockRendering || sa->lockRequests)
return;
}
diff --git a/src/graphics/niche/InkHUD/WindowManager.h b/src/graphics/niche/InkHUD/WindowManager.h
index 948ef61319f..a11688cf5de 100644
--- a/src/graphics/niche/InkHUD/WindowManager.h
+++ b/src/graphics/niche/InkHUD/WindowManager.h
@@ -29,6 +29,7 @@ class WindowManager
void nextTile();
void prevTile();
+ Applet *getActiveApplet();
void openMenu();
void openAlignStick();
void openKeyboard();
diff --git a/src/graphics/niche/Utils/CannedMessageStore.cpp b/src/graphics/niche/Utils/CannedMessageStore.cpp
index 50998930d79..182b7e1f8ff 100644
--- a/src/graphics/niche/Utils/CannedMessageStore.cpp
+++ b/src/graphics/niche/Utils/CannedMessageStore.cpp
@@ -146,7 +146,7 @@ void CannedMessageStore::handleGet(meshtastic_AdminMessage *response)
std::string merged;
if (!messages.empty()) { // Don't run if no messages: error on pop_back with size=0
merged.reserve(201);
- for (std::string &s : messages) {
+ for (const std::string &s : messages) {
merged += s;
merged += '|';
}
diff --git a/src/input/HackadayCommunicatorKeyboard.cpp b/src/input/HackadayCommunicatorKeyboard.cpp
index c6a9e0ae877..b096c74d297 100644
--- a/src/input/HackadayCommunicatorKeyboard.cpp
+++ b/src/input/HackadayCommunicatorKeyboard.cpp
@@ -106,8 +106,8 @@ static unsigned char HackadayCommunicatorTapMap[_TCA8418_NUM_KEYS][2] = {{},
{}};
HackadayCommunicatorKeyboard::HackadayCommunicatorKeyboard()
- : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
- last_tap(0L), char_idx(0), tap_interval(0)
+ : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(UINT8_MAX),
+ next_key(UINT8_MAX), last_tap(0L), char_idx(0), tap_interval(0)
{
reset();
}
@@ -147,7 +147,6 @@ void HackadayCommunicatorKeyboard::pressed(uint8_t key)
modifierFlag = 0;
}
- uint8_t next_key = 0;
int row = (key - 1) / 10;
int col = (key - 1) % 10;
if (row >= _TCA8418_ROWS || col >= _TCA8418_COLS) {
@@ -187,8 +186,8 @@ void HackadayCommunicatorKeyboard::released()
return;
}
- if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
- last_key = -1;
+ if (last_key >= _TCA8418_NUM_KEYS) {
+ last_key = UINT8_MAX;
state = Idle;
return;
}
diff --git a/src/input/HackadayCommunicatorKeyboard.h b/src/input/HackadayCommunicatorKeyboard.h
index 8316bed7230..cbba5c12f49 100644
--- a/src/input/HackadayCommunicatorKeyboard.h
+++ b/src/input/HackadayCommunicatorKeyboard.h
@@ -18,8 +18,8 @@ class HackadayCommunicatorKeyboard : public TCA8418KeyboardBase
private:
uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
uint32_t last_modifier_time; // Timestamp of the last modifier key press
- int8_t last_key;
- int8_t next_key;
+ uint8_t last_key;
+ uint8_t next_key;
uint32_t last_tap;
uint8_t char_idx;
int32_t tap_interval;
diff --git a/src/input/MPR121Keyboard.cpp b/src/input/MPR121Keyboard.cpp
index 9bca6801d35..ec37cfbaacd 100644
--- a/src/input/MPR121Keyboard.cpp
+++ b/src/input/MPR121Keyboard.cpp
@@ -91,7 +91,7 @@ MPR121Keyboard::MPR121Keyboard() : m_wire(nullptr), m_addr(0), readCallback(null
{
// LOG_DEBUG("MPR121 @ %02x", m_addr);
state = Init;
- last_key = -1;
+ last_key = UINT8_MAX;
last_tap = 0L;
char_idx = 0;
queue = "";
@@ -359,8 +359,8 @@ void MPR121Keyboard::released()
return;
}
// would clear longpress callback... later.
- if (last_key < 0 || last_key > _NUM_KEYS) { // reset to idle if last_key out of bounds
- last_key = -1;
+ if (last_key >= _NUM_KEYS) { // reset to idle if last_key out of bounds
+ last_key = UINT8_MAX;
state = Idle;
return;
}
diff --git a/src/input/MPR121Keyboard.h b/src/input/MPR121Keyboard.h
index 6349750cef2..ec3f56e8792 100644
--- a/src/input/MPR121Keyboard.h
+++ b/src/input/MPR121Keyboard.h
@@ -14,7 +14,7 @@ class MPR121Keyboard
MPR121States state;
- int8_t last_key;
+ uint8_t last_key;
uint32_t last_tap;
uint8_t char_idx;
diff --git a/src/input/TCA8418Keyboard.cpp b/src/input/TCA8418Keyboard.cpp
index bd8338acf5a..822885a9ff4 100644
--- a/src/input/TCA8418Keyboard.cpp
+++ b/src/input/TCA8418Keyboard.cpp
@@ -43,8 +43,8 @@ static unsigned char TCA8418LongPressMap[_TCA8418_NUM_KEYS] = {
};
TCA8418Keyboard::TCA8418Keyboard()
- : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), last_key(-1), next_key(-1), last_tap(0L), char_idx(0), tap_interval(0),
- should_backspace(false)
+ : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), last_key(UINT8_MAX), next_key(UINT8_MAX), last_tap(0L), char_idx(0),
+ tap_interval(0), should_backspace(false)
{
}
@@ -63,7 +63,6 @@ void TCA8418Keyboard::pressed(uint8_t key)
if (state == Init || state == Busy) {
return;
}
- uint8_t next_key = 0;
int row = (key - 1) / 10;
int col = (key - 1) % 10;
@@ -72,7 +71,7 @@ void TCA8418Keyboard::pressed(uint8_t key)
}
// Compute key index based on dynamic row/column
- next_key = row * _TCA8418_COLS + col;
+ next_key = (uint8_t)(row * _TCA8418_COLS + col);
// LOG_DEBUG("TCA8418: Key %u -> Next Key %u", key, next_key);
@@ -106,8 +105,8 @@ void TCA8418Keyboard::released()
return;
}
- if (last_key < 0 || last_key > _TCA8418_NUM_KEYS) { // reset to idle if last_key out of bounds
- last_key = -1;
+ if (last_key >= _TCA8418_NUM_KEYS) { // reset to idle if last_key out of bounds
+ last_key = UINT8_MAX;
state = Idle;
return;
}
diff --git a/src/input/TCA8418Keyboard.h b/src/input/TCA8418Keyboard.h
index b76916643ba..0e882126015 100644
--- a/src/input/TCA8418Keyboard.h
+++ b/src/input/TCA8418Keyboard.h
@@ -14,8 +14,8 @@ class TCA8418Keyboard : public TCA8418KeyboardBase
void pressed(uint8_t key) override;
void released(void) override;
- int8_t last_key;
- int8_t next_key;
+ uint8_t last_key;
+ uint8_t next_key;
uint32_t last_tap;
uint8_t char_idx;
int32_t tap_interval;
diff --git a/src/input/TDeckProKeyboard.cpp b/src/input/TDeckProKeyboard.cpp
index eeafe49496f..b83f0c6aed8 100644
--- a/src/input/TDeckProKeyboard.cpp
+++ b/src/input/TDeckProKeyboard.cpp
@@ -62,8 +62,8 @@ static unsigned char TDeckProTapMap[_TCA8418_NUM_KEYS][5] = {
};
TDeckProKeyboard::TDeckProKeyboard()
- : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
- last_tap(0L), char_idx(0), tap_interval(0)
+ : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(UINT8_MAX),
+ next_key(UINT8_MAX), last_tap(0L), char_idx(0), tap_interval(0)
{
}
@@ -101,7 +101,6 @@ void TDeckProKeyboard::pressed(uint8_t key)
modifierFlag = 0;
}
- uint8_t next_key = 0;
int row = (key - 1) / 10;
int col = (key - 1) % 10;
@@ -142,8 +141,8 @@ void TDeckProKeyboard::released()
return;
}
- if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
- last_key = -1;
+ if (last_key >= _TCA8418_NUM_KEYS) {
+ last_key = UINT8_MAX;
state = Idle;
return;
}
diff --git a/src/input/TDeckProKeyboard.h b/src/input/TDeckProKeyboard.h
index 617f3f20ba5..3ef97fc3d26 100644
--- a/src/input/TDeckProKeyboard.h
+++ b/src/input/TDeckProKeyboard.h
@@ -19,8 +19,8 @@ class TDeckProKeyboard : public TCA8418KeyboardBase
private:
uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
uint32_t last_modifier_time; // Timestamp of the last modifier key press
- int8_t last_key;
- int8_t next_key;
+ uint8_t last_key;
+ uint8_t next_key;
uint32_t last_tap;
uint8_t char_idx;
int32_t tap_interval;
diff --git a/src/input/TLoraPagerKeyboard.cpp b/src/input/TLoraPagerKeyboard.cpp
index 9a4fd867902..4efa5d6e250 100644
--- a/src/input/TLoraPagerKeyboard.cpp
+++ b/src/input/TLoraPagerKeyboard.cpp
@@ -65,8 +65,8 @@ static unsigned char TLoraPagerTapMap[_TCA8418_NUM_KEYS][3] = {{'q', 'Q', '1'},
{' ', 0x00, Key::BL_TOGGLE}};
TLoraPagerKeyboard::TLoraPagerKeyboard()
- : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
- last_tap(0L), char_idx(0), tap_interval(0)
+ : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(UINT8_MAX),
+ next_key(UINT8_MAX), last_tap(0L), char_idx(0), tap_interval(0)
{
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
ledcAttach(KB_BL_PIN, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
@@ -129,7 +129,6 @@ void TLoraPagerKeyboard::pressed(uint8_t key)
modifierFlag = 0;
}
- uint8_t next_key = 0;
int row = (key - 1) / 10;
int col = (key - 1) % 10;
@@ -170,8 +169,8 @@ void TLoraPagerKeyboard::released()
return;
}
- if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
- last_key = -1;
+ if (last_key >= _TCA8418_NUM_KEYS) {
+ last_key = UINT8_MAX;
state = Idle;
return;
}
diff --git a/src/input/TLoraPagerKeyboard.h b/src/input/TLoraPagerKeyboard.h
index f04d2ce6af6..06a4c7b6382 100644
--- a/src/input/TLoraPagerKeyboard.h
+++ b/src/input/TLoraPagerKeyboard.h
@@ -21,8 +21,8 @@ class TLoraPagerKeyboard : public TCA8418KeyboardBase
private:
uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
uint32_t last_modifier_time; // Timestamp of the last modifier key press
- int8_t last_key;
- int8_t next_key;
+ uint8_t last_key;
+ uint8_t next_key;
uint32_t last_tap;
uint8_t char_idx;
int32_t tap_interval;
diff --git a/src/main.cpp b/src/main.cpp
index d5e652a9589..f70b3975e07 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -248,9 +248,8 @@ uint32_t timeLastPowered = 0;
static OSThread *powerFSMthread;
OSThread *ambientLightingThread;
-#ifdef ARCH_PORTDUINO
+RadioInterface *rIf = NULL;
RadioLibHal *RadioLibHAL = NULL;
-#endif
/**
* Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep.
@@ -724,17 +723,15 @@ void setup()
playStartMelody();
#if HAS_SCREEN
- // fixed screen override?
- if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO)
- screen_model = config.display.oled;
-
+ // fixed screen override?
#if defined(USE_SH1107)
screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // set dimension of 128x128
screen_geometry = GEOMETRY_128_128;
-#endif
-
-#if defined(USE_SH1107_128_64)
+#elif defined(USE_SH1107_128_64)
screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // keep dimension of 128x64
+#else
+ if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO)
+ screen_model = config.display.oled;
#endif
#endif
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 913d45b4181..a52fa447804 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -814,26 +814,28 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.has_store_forward = true;
moduleConfig.has_telemetry = true;
moduleConfig.has_external_notification = true;
-#if defined(PIN_BUZZER)
+#if defined(PIN_BUZZER) || defined(PIN_VIBRATION) || defined(LED_NOTIFICATION)
moduleConfig.external_notification.enabled = true;
+#endif
+#if defined(PIN_BUZZER)
moduleConfig.external_notification.output_buzzer = PIN_BUZZER;
moduleConfig.external_notification.use_pwm = true;
moduleConfig.external_notification.alert_message_buzzer = true;
- moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
#endif
#if defined(PIN_VIBRATION)
- moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output_vibra = PIN_VIBRATION;
moduleConfig.external_notification.alert_message_vibra = true;
moduleConfig.external_notification.output_ms = 500;
- moduleConfig.external_notification.nag_timeout = 2;
#endif
#if defined(LED_NOTIFICATION)
- moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = LED_NOTIFICATION;
moduleConfig.external_notification.active = LED_STATE_ON;
moduleConfig.external_notification.alert_message = true;
moduleConfig.external_notification.output_ms = 1000;
+#endif
+#if defined(PIN_VIBRATION)
+ moduleConfig.external_notification.nag_timeout = 2;
+#elif defined(PIN_BUZZER) || defined(LED_NOTIFICATION)
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
#endif
diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp
index 34393d25963..845a936d4cd 100644
--- a/src/mesh/PacketHistory.cpp
+++ b/src/mesh/PacketHistory.cpp
@@ -439,7 +439,7 @@ void PacketHistory::removeRelayer(const uint8_t relayer, const uint32_t id, cons
}
// Getters and setters for hop limit fields packed in hop_limit
-inline uint8_t PacketHistory::getHighestHopLimit(PacketRecord &r)
+inline uint8_t PacketHistory::getHighestHopLimit(const PacketRecord &r)
{
return r.hop_limit & HOP_LIMIT_HIGHEST_MASK;
}
@@ -449,7 +449,7 @@ inline void PacketHistory::setHighestHopLimit(PacketRecord &r, uint8_t hopLimit)
r.hop_limit = (r.hop_limit & ~HOP_LIMIT_HIGHEST_MASK) | (hopLimit & HOP_LIMIT_HIGHEST_MASK);
}
-inline uint8_t PacketHistory::getOurTxHopLimit(PacketRecord &r)
+inline uint8_t PacketHistory::getOurTxHopLimit(const PacketRecord &r)
{
return (r.hop_limit & HOP_LIMIT_OUR_TX_MASK) >> HOP_LIMIT_OUR_TX_SHIFT;
}
diff --git a/src/mesh/PacketHistory.h b/src/mesh/PacketHistory.h
index 5fbad2dc949..9b6a9328099 100644
--- a/src/mesh/PacketHistory.h
+++ b/src/mesh/PacketHistory.h
@@ -43,9 +43,9 @@ class PacketHistory
* @return true if node was indeed a relayer, false if not */
bool wasRelayer(const uint8_t relayer, const PacketRecord &r, bool *wasSole = nullptr);
- uint8_t getHighestHopLimit(PacketRecord &r);
+ uint8_t getHighestHopLimit(const PacketRecord &r);
void setHighestHopLimit(PacketRecord &r, uint8_t hopLimit);
- uint8_t getOurTxHopLimit(PacketRecord &r);
+ uint8_t getOurTxHopLimit(const PacketRecord &r);
void setOurTxHopLimit(PacketRecord &r, uint8_t hopLimit);
PacketHistory(const PacketHistory &); // non construction-copyable
diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index ab3e1d8a067..e8202d9b0b9 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -237,9 +237,9 @@ std::unique_ptr initLoRa()
std::unique_ptr rIf = nullptr;
#if ARCH_PORTDUINO
- SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
+ SPISettings loraSpiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
#else
- SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
+ SPISettings loraSpiSettings(4000000, MSBFIRST, SPI_MODE0);
#endif
#ifdef ARCH_PORTDUINO
@@ -280,7 +280,7 @@ std::unique_ptr initLoRa()
delete RadioLibHAL;
RadioLibHAL = nullptr;
}
- RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
+ RadioLibHAL = new LockingArduinoHal(SPI, loraSpiSettings);
}
rIf =
loraModuleInterface((LockingArduinoHal *)RadioLibHAL, portduino_config.lora_cs_pin.pin, portduino_config.lora_irq_pin.pin,
@@ -295,16 +295,18 @@ std::unique_ptr initLoRa()
}
#elif defined(HW_SPI1_DEVICE)
- LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings);
+ LockingArduinoHal *loraHal = new LockingArduinoHal(SPI1, loraSpiSettings);
+ RadioLibHAL = loraHal;
#else // HW_SPI1_DEVICE
- LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
+ LockingArduinoHal *loraHal = new LockingArduinoHal(SPI, loraSpiSettings);
+ RadioLibHAL = loraHal;
#endif
// radio init MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
#if defined(USE_STM32WLx)
if (!rIf) {
rIf = std::unique_ptr(
- new STM32WLE5JCInterface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
+ new STM32WLE5JCInterface(loraHal, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
if (!rIf->init()) {
LOG_WARN("No STM32WL radio");
rIf = nullptr;
@@ -317,7 +319,7 @@ std::unique_ptr initLoRa()
#if defined(RF95_IRQ) && RADIOLIB_EXCLUDE_SX127X != 1
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
- rIf = std::unique_ptr(new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1));
+ rIf = std::unique_ptr(new RF95Interface(loraHal, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1));
if (!rIf->init()) {
LOG_WARN("No RF95 radio");
rIf = nullptr;
@@ -331,7 +333,7 @@ std::unique_ptr initLoRa()
#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL) && RADIOLIB_EXCLUDE_SX126X != 1
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
auto sxIf =
- std::unique_ptr(new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
+ std::unique_ptr(new SX1262Interface(loraHal, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
#ifdef SX126X_DIO3_TCXO_VOLTAGE
sxIf->setTCXOVoltage(SX126X_DIO3_TCXO_VOLTAGE);
#endif
@@ -350,7 +352,7 @@ std::unique_ptr initLoRa()
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
// try using the specified TCXO voltage
auto sxIf =
- std::unique_ptr(new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
+ std::unique_ptr(new SX1262Interface(loraHal, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
sxIf->setTCXOVoltage(SX126X_DIO3_TCXO_VOLTAGE);
if (!sxIf->init()) {
LOG_WARN("No SX1262 radio with TCXO, Vref %fV", SX126X_DIO3_TCXO_VOLTAGE);
@@ -364,8 +366,7 @@ std::unique_ptr initLoRa()
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
// If specified TCXO voltage fails, attempt to use DIO3 as a reference instead
- rIf =
- std::unique_ptr(new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
+ rIf = std::unique_ptr(new SX1262Interface(loraHal, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
if (!rIf->init()) {
LOG_WARN("No SX1262 radio with XTAL, Vref 0.0V");
rIf = nullptr;
@@ -381,7 +382,7 @@ std::unique_ptr initLoRa()
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
// try using the specified TCXO voltage
auto sxIf =
- std::unique_ptr(new SX1268Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
+ std::unique_ptr(new SX1268Interface(loraHal, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
sxIf->setTCXOVoltage(SX126X_DIO3_TCXO_VOLTAGE);
if (!sxIf->init()) {
LOG_WARN("No SX1268 radio with TCXO, Vref %fV", SX126X_DIO3_TCXO_VOLTAGE);
@@ -394,8 +395,7 @@ std::unique_ptr initLoRa()
}
#endif
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
- rIf =
- std::unique_ptr(new SX1268Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
+ rIf = std::unique_ptr(new SX1268Interface(loraHal, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
if (!rIf->init()) {
LOG_WARN("No SX1268 radio");
rIf = nullptr;
@@ -408,8 +408,7 @@ std::unique_ptr initLoRa()
#if defined(USE_LLCC68)
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
- rIf =
- std::unique_ptr(new LLCC68Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
+ rIf = std::unique_ptr(new LLCC68Interface(loraHal, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY));
if (!rIf->init()) {
LOG_WARN("No LLCC68 radio");
rIf = nullptr;
@@ -423,7 +422,7 @@ std::unique_ptr initLoRa()
#if defined(USE_LR1110) && RADIOLIB_EXCLUDE_LR11X0 != 1
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
rIf = std::unique_ptr(
- new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESET_PIN, LR1110_BUSY_PIN));
+ new LR1110Interface(loraHal, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESET_PIN, LR1110_BUSY_PIN));
if (!rIf->init()) {
LOG_WARN("No LR1110 radio");
rIf = nullptr;
@@ -437,7 +436,7 @@ std::unique_ptr initLoRa()
#if defined(USE_LR1120) && RADIOLIB_EXCLUDE_LR11X0 != 1
if (!rIf) {
rIf = std::unique_ptr(
- new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESET_PIN, LR1120_BUSY_PIN));
+ new LR1120Interface(loraHal, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESET_PIN, LR1120_BUSY_PIN));
if (!rIf->init()) {
LOG_WARN("No LR1120 radio");
rIf = nullptr;
@@ -451,7 +450,7 @@ std::unique_ptr initLoRa()
#if defined(USE_LR1121) && RADIOLIB_EXCLUDE_LR11X0 != 1
if (!rIf) {
rIf = std::unique_ptr(
- new LR1121Interface(RadioLibHAL, LR1121_SPI_NSS_PIN, LR1121_IRQ_PIN, LR1121_NRESET_PIN, LR1121_BUSY_PIN));
+ new LR1121Interface(loraHal, LR1121_SPI_NSS_PIN, LR1121_IRQ_PIN, LR1121_NRESET_PIN, LR1121_BUSY_PIN));
if (!rIf->init()) {
LOG_WARN("No LR1121 radio");
rIf = nullptr;
@@ -464,8 +463,7 @@ std::unique_ptr initLoRa()
#if defined(USE_SX1280) && RADIOLIB_EXCLUDE_SX128X != 1
if (!rIf) {
- rIf =
- std::unique_ptr(new SX1280Interface(RadioLibHAL, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY));
+ rIf = std::unique_ptr(new SX1280Interface(loraHal, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY));
if (!rIf->init()) {
LOG_WARN("No SX1280 radio");
rIf = nullptr;
diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp
index 20026767e30..2d9230a2178 100644
--- a/src/mesh/StreamAPI.cpp
+++ b/src/mesh/StreamAPI.cpp
@@ -27,7 +27,7 @@ int32_t StreamAPI::runOncePart(char *buf, uint16_t bufLen)
/**
* Read any rx chars from the link and call handleRecStream
*/
-int32_t StreamAPI::readStream(char *buf, uint16_t bufLen)
+int32_t StreamAPI::readStream(const char *buf, uint16_t bufLen)
{
if (bufLen < 1) {
// Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time
@@ -56,7 +56,7 @@ void StreamAPI::writeStream()
}
}
-int32_t StreamAPI::handleRecStream(char *buf, uint16_t bufLen)
+int32_t StreamAPI::handleRecStream(const char *buf, uint16_t bufLen)
{
uint16_t index = 0;
while (bufLen > index) { // Currently we never want to block
diff --git a/src/mesh/StreamAPI.h b/src/mesh/StreamAPI.h
index 4ca2c197fc3..97e231f23f7 100644
--- a/src/mesh/StreamAPI.h
+++ b/src/mesh/StreamAPI.h
@@ -57,8 +57,8 @@ class StreamAPI : public PhoneAPI
* Read any rx chars from the link and call handleToRadio
*/
int32_t readStream();
- int32_t readStream(char *buf, uint16_t bufLen);
- int32_t handleRecStream(char *buf, uint16_t bufLen);
+ int32_t readStream(const char *buf, uint16_t bufLen);
+ int32_t handleRecStream(const char *buf, uint16_t bufLen);
/**
* call getFromRadio() and deliver encapsulated packets to the Stream
diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp
index 420d80e9a3b..5ed7ff92838 100644
--- a/src/mesh/aes-ccm.cpp
+++ b/src/mesh/aes-ccm.cpp
@@ -33,7 +33,7 @@ static int constant_time_compare(const void *a_, const void *b_, size_t len)
d |= (a[i] ^ b[i]);
}
/* Constant time bit arithmetic to convert d > 0 to -1 and d = 0 to 0. */
- return (1 & ((d - 1) >> 8)) - 1;
+ return (1 & (((unsigned int)d - 1) >> 8)) - 1;
}
static void WPA_PUT_BE16(uint8_t *a, uint16_t val)
diff --git a/src/mesh/api/PacketAPI.h b/src/mesh/api/PacketAPI.h
index fc08ab209a1..357eb05c207 100644
--- a/src/mesh/api/PacketAPI.h
+++ b/src/mesh/api/PacketAPI.h
@@ -17,7 +17,7 @@ class PacketAPI : public PhoneAPI, public concurrency::OSThread
virtual int32_t runOnce();
protected:
- PacketAPI(PacketServer *_server);
+ explicit PacketAPI(PacketServer *_server);
// Check the current underlying physical queue to see if the client is fetching packets
bool checkIsConnected() override;
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 912cb467ebd..281ece46483 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -623,7 +623,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
}
// Helper lambda to create JSON array and clean up memory properly
- auto createJSONArrayFromLog = [](uint32_t *logArray, int count) -> JSONValue * {
+ auto createJSONArrayFromLog = [](const uint32_t *logArray, int count) -> JSONValue * {
JSONArray tempArray;
for (int i = 0; i < count; i++) {
tempArray.push_back(new JSONValue((int)logArray[i]));
diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp
index 7d7b3cdb15f..c7eb1b15b67 100644
--- a/src/modules/CannedMessageModule.cpp
+++ b/src/modules/CannedMessageModule.cpp
@@ -1209,14 +1209,13 @@ int32_t CannedMessageModule::runOnce()
this->cursor = 0;
// Tell Screen to jump straight to the TextMessage frame
- UIFrameEvent e;
e.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE;
this->notifyObservers(&e);
// Now deactivate this module
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
- return INT32_MAX; // don’t fall back into canned list
+ return INT32_MAX; // don't fall back into canned list
} else {
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
}
@@ -1237,14 +1236,13 @@ int32_t CannedMessageModule::runOnce()
this->cursor = 0;
// Tell Screen to jump straight to the TextMessage frame
- UIFrameEvent e;
e.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE;
this->notifyObservers(&e);
// Now deactivate this module
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
- return INT32_MAX; // don’t fall back into canned list
+ return INT32_MAX; // don't fall back into canned list
}
} else {
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
@@ -1255,11 +1253,10 @@ int32_t CannedMessageModule::runOnce()
this->freetext = "";
this->cursor = 0;
- UIFrameEvent e;
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
this->notifyObservers(&e);
- // Immediately stop, don’t linger on canned screen
+ // Immediately stop, don't linger on canned screen
return INT32_MAX;
}
// Highlight [Select Destination] initially when entering the message list
@@ -2070,7 +2067,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
// Draw lines with emotes
int rowHeight = FONT_HEIGHT_SMALL;
int yLine = inputY;
- for (auto &line : lines) {
+ for (const auto &line : lines) {
int nextX = x;
for (const auto &token : line) {
if (token.first) {
diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp
index 0301b2ac34b..cc7124f0e7d 100644
--- a/src/modules/ExternalNotificationModule.cpp
+++ b/src/modules/ExternalNotificationModule.cpp
@@ -367,7 +367,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
}
}
- meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(mp.from);
+ const meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(mp.from);
meshtastic_Channel ch = channels.getByIndex(mp.channel ? mp.channel : channels.getPrimaryIndex());
// If we receive a broadcast message, apply channel mute setting
diff --git a/src/modules/StatusLEDModule.cpp b/src/modules/StatusLEDModule.cpp
index 457935688e2..f828f4a1691 100644
--- a/src/modules/StatusLEDModule.cpp
+++ b/src/modules/StatusLEDModule.cpp
@@ -23,7 +23,6 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
{
switch (arg->getStatusType()) {
case STATUS_TYPE_POWER: {
- meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg;
if (powerStatus->getHasUSB() || powerStatus->getIsCharging()) {
power_state = charging;
if (powerStatus->getBatteryChargePercent() >= 100) {
@@ -39,7 +38,6 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
break;
}
case STATUS_TYPE_BLUETOOTH: {
- meshtastic::BluetoothStatus *bluetoothStatus = (meshtastic::BluetoothStatus *)arg;
switch (bluetoothStatus->getConnectionState()) {
case meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED: {
ble_state = unpaired;
@@ -199,33 +197,30 @@ void StatusLEDModule::setPowerLED(bool LEDon)
PMU->setChargingLedMode(LEDon ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
#endif
- if (LEDon)
- LEDon = LED_STATE_ON;
- else
- LEDon = LED_STATE_OFF;
+ uint8_t ledState = LEDon ? LED_STATE_ON : LED_STATE_OFF;
#ifdef PCA_LED_POWER
- io.digitalWrite(PCA_LED_POWER, LEDon);
+ io.digitalWrite(PCA_LED_POWER, ledState);
#endif
#ifdef PCA_LED_ENABLE
- io.digitalWrite(PCA_LED_ENABLE, LEDon);
+ io.digitalWrite(PCA_LED_ENABLE, ledState);
#endif
#ifdef LED_POWER
- digitalWrite(LED_POWER, LEDon);
+ digitalWrite(LED_POWER, ledState);
#endif
#ifdef LED_PAIRING
- digitalWrite(LED_PAIRING, LEDon);
+ digitalWrite(LED_PAIRING, ledState);
#endif
#ifdef Battery_LED_1
- digitalWrite(Battery_LED_1, LEDon);
+ digitalWrite(Battery_LED_1, ledState);
#endif
#ifdef Battery_LED_2
- digitalWrite(Battery_LED_2, LEDon);
+ digitalWrite(Battery_LED_2, ledState);
#endif
#ifdef Battery_LED_3
- digitalWrite(Battery_LED_3, LEDon);
+ digitalWrite(Battery_LED_3, ledState);
#endif
#ifdef Battery_LED_4
- digitalWrite(Battery_LED_4, LEDon);
+ digitalWrite(Battery_LED_4, ledState);
#endif
}
diff --git a/src/modules/StoreForwardModule.cpp b/src/modules/StoreForwardModule.cpp
index 023a1c79825..135d9f6eb9c 100644
--- a/src/modules/StoreForwardModule.cpp
+++ b/src/modules/StoreForwardModule.cpp
@@ -131,9 +131,7 @@ void StoreForwardModule::historySend(uint32_t secAgo, uint32_t to)
uint32_t StoreForwardModule::getNumAvailablePackets(NodeNum dest, uint32_t last_time)
{
uint32_t count = 0;
- if (lastRequest.find(dest) == lastRequest.end()) {
- lastRequest.emplace(dest, 0);
- }
+ lastRequest.emplace(dest, 0);
for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
// Client is only interested in packets not from itself and only in broadcast packets or packets towards it.
diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp
index d7127bb0162..5ffe4d9926c 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.cpp
+++ b/src/modules/Telemetry/AirQualityTelemetry.cpp
@@ -29,6 +29,9 @@
#if __has_include()
#include "Sensor/SFA30Sensor.h"
#endif
+#if __has_include()
+#include "Sensor/SCD30Sensor.h"
+#endif
void AirQualityTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
{
@@ -57,6 +60,9 @@ void AirQualityTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
#if __has_include()
addSensor(i2cScanner, ScanI2C::DeviceType::SFA30);
#endif
+#if __has_include()
+ addSensor(i2cScanner, ScanI2C::DeviceType::SCD30);
+#endif
}
int32_t AirQualityTelemetryModule::runOnce()
diff --git a/src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h b/src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h
index 37d909d71f1..b7029986bea 100644
--- a/src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h
+++ b/src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h
@@ -8,7 +8,7 @@
static std::forward_list sensors;
-template void addSensor(ScanI2C *i2cScanner, ScanI2C::DeviceType type)
+template void addSensor(const ScanI2C *i2cScanner, ScanI2C::DeviceType type)
{
ScanI2C::FoundDevice dev = i2cScanner->find(type);
if (dev.type != ScanI2C::DeviceType::NONE || type == ScanI2C::DeviceType::NONE) {
diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp
index 3a1eb95321b..c202028e182 100644
--- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp
@@ -8,6 +8,10 @@
#include "SPILock.h"
#include "TelemetrySensor.h"
+#if __has_include()
+#include
+#endif
+
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
#if __has_include()
@@ -96,8 +100,28 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.environment_metrics.temperature = bme680->readTemperature();
measurement->variant.environment_metrics.relative_humidity = bme680->readHumidity();
measurement->variant.environment_metrics.barometric_pressure = bme680->readPressure() / 100.0F;
- measurement->variant.environment_metrics.gas_resistance = bme680->readGas() / 1000.0;
+ float gasRaw = bme680->readGas();
+ measurement->variant.environment_metrics.gas_resistance = gasRaw / 1000.0;
+
+ // IAQ approximation: humidity-compensated logarithmic mapping of gas resistance
+ // Gas sensor resistance drops with humidity; compensate to a 40% RH reference baseline
+ // Map compensated gas resistance (Ohms) to IAQ 0-500 using log-linear interpolation
+ // Clean air reference ~400 kOhm, polluted reference ~5 kOhm
+ if (gasRaw > 0.0f && !isfinite(gasRaw)) {
+
+ static constexpr float LOG_UPPER = 12.899219f; // log(400k)
+ static constexpr float LOG_RANGE_INV = 1.0f / (12.899219f - 8.517193f); // 1 / (log(400k) - log(5k))
+ measurement->variant.environment_metrics.has_iaq = true;
+ measurement->variant.environment_metrics.iaq = (uint16_t)(fminf(
+ fmaxf(((LOG_UPPER -
+ logf(fmaxf(gasRaw * expf(0.035f * (measurement->variant.environment_metrics.relative_humidity - 40.0f)),
+ 1.0f))) *
+ LOG_RANGE_INV) *
+ 500.0f,
+ 0.0f),
+ 500.0f));
+ }
#endif
return true;
}
diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.h b/src/modules/Telemetry/Sensor/BME680Sensor.h
index eaeceb84810..1134f04d9d7 100644
--- a/src/modules/Telemetry/Sensor/BME680Sensor.h
+++ b/src/modules/Telemetry/Sensor/BME680Sensor.h
@@ -28,7 +28,7 @@ class BME680Sensor : public TelemetrySensor
#else
using BME680Ptr = std::unique_ptr;
- static BME680Ptr makeBME680(TwoWire *bus) { return std::make_unique(bus); }
+ static BME680Ptr makeBME680(TwoWire *bus) { return BME680Ptr(new Adafruit_BME680(bus)); }
BME680Ptr bme680;
#endif
diff --git a/src/modules/Telemetry/Sensor/PMSA003ISensor.cpp b/src/modules/Telemetry/Sensor/PMSA003ISensor.cpp
index a752f2ab807..e34b70a1ff8 100644
--- a/src/modules/Telemetry/Sensor/PMSA003ISensor.cpp
+++ b/src/modules/Telemetry/Sensor/PMSA003ISensor.cpp
@@ -67,7 +67,7 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
#endif /* CAN_RECLOCK_I2C */
#endif /* PMSA003I_I2C_CLOCK_SPEED */
- _bus->requestFrom(_address, PMSA003I_FRAME_LENGTH);
+ _bus->requestFrom(_address, (uint8_t)PMSA003I_FRAME_LENGTH);
if (_bus->available() < PMSA003I_FRAME_LENGTH) {
LOG_WARN("%s read failed: incomplete data (%d bytes)", sensorName, _bus->available());
return false;
@@ -86,7 +86,7 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
return false;
}
- auto read16 = [](uint8_t *data, uint8_t idx) -> uint16_t { return (data[idx] << 8) | data[idx + 1]; };
+ auto read16 = [](const uint8_t *data, uint8_t idx) -> uint16_t { return (data[idx] << 8) | data[idx + 1]; };
computedChecksum = 0;
diff --git a/src/modules/Telemetry/Sensor/SCD30Sensor.cpp b/src/modules/Telemetry/Sensor/SCD30Sensor.cpp
new file mode 100644
index 00000000000..0478b6651bd
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/SCD30Sensor.cpp
@@ -0,0 +1,511 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include()
+
+#include "../detect/reClockI2C.h"
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "SCD30Sensor.h"
+
+#define SCD30_NO_ERROR 0
+
+SCD30Sensor::SCD30Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD30, "SCD30") {}
+
+bool SCD30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
+{
+ LOG_INFO("Init sensor: %s", sensorName);
+
+ _bus = bus;
+ _address = dev->address.address;
+
+#ifdef SCD30_I2C_CLOCK_SPEED
+#ifdef CAN_RECLOCK_I2C
+ uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
+#elif !HAS_SCREEN
+ reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
+#else
+ LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
+ return false;
+#endif /* CAN_RECLOCK_I2C */
+#endif /* SCD30_I2C_CLOCK_SPEED */
+
+ scd30.begin(*_bus, _address);
+
+ if (!startMeasurement()) {
+ LOG_ERROR("%s: Failed to start periodic measurement", sensorName);
+#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
+ reClockI2C(currentClock, _bus, false);
+#endif
+ return false;
+ }
+
+ if (!getASC(ascActive)) {
+ LOG_WARN("%s: Could not determine ASC state", sensorName);
+ }
+
+#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
+ reClockI2C(currentClock, _bus, false);
+#endif
+
+ if (state == SCD30_MEASUREMENT) {
+ status = 1;
+ } else {
+ status = 0;
+ }
+
+ initI2CSensor();
+
+ return true;
+}
+
+bool SCD30Sensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+ float co2, temperature, humidity;
+
+#ifdef SCD30_I2C_CLOCK_SPEED
+#ifdef CAN_RECLOCK_I2C
+ uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
+#elif !HAS_SCREEN
+ reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
+#else
+ LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
+ return false;
+#endif /* CAN_RECLOCK_I2C */
+#endif /* SCD30_I2C_CLOCK_SPEED */
+
+ if (scd30.readMeasurementData(co2, temperature, humidity) != SCD30_NO_ERROR) {
+ LOG_ERROR("SCD30: Failed to read measurement data.");
+#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
+ reClockI2C(currentClock, _bus, false);
+#endif
+ return false;
+ }
+
+#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
+ reClockI2C(currentClock, _bus, false);
+#endif
+
+ if (co2 == 0) {
+ LOG_ERROR("SCD30: Invalid CO₂ reading.");
+ return false;
+ }
+
+ measurement->variant.air_quality_metrics.has_co2 = true;
+ measurement->variant.air_quality_metrics.has_co2_temperature = true;
+ measurement->variant.air_quality_metrics.has_co2_humidity = true;
+ measurement->variant.air_quality_metrics.co2 = (uint32_t)co2;
+ measurement->variant.air_quality_metrics.co2_temperature = temperature;
+ measurement->variant.air_quality_metrics.co2_humidity = humidity;
+
+ LOG_DEBUG("Got %s readings: co2=%u, co2_temp=%.2f, co2_hum=%.2f", sensorName, (uint32_t)co2, temperature, humidity);
+
+ return true;
+}
+
+bool SCD30Sensor::setMeasurementInterval(uint16_t measInterval)
+{
+ uint16_t error;
+
+ LOG_INFO("%s: setting measurement interval at %us", sensorName, measInterval);
+ error = scd30.setMeasurementInterval(measInterval);
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to set measurement interval. Error code: %u", sensorName, error);
+ return false;
+ }
+
+ // Restart measuring so we don't need to wait the current interval to finish
+ // (useful when you come from very long intervals)
+ scd30.stopPeriodicMeasurement();
+ scd30.startPeriodicMeasurement(0);
+
+ getMeasurementInterval(measurementInterval);
+ return true;
+}
+
+bool SCD30Sensor::getMeasurementInterval(uint16_t &measInterval)
+{
+ uint16_t error;
+
+ LOG_INFO("%s: getting measurement interval", sensorName);
+ error = scd30.getMeasurementInterval(measInterval);
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to get measurement interval. Error code: %u", sensorName, error);
+ return false;
+ }
+
+ LOG_INFO("%s: measurement interval is %us", sensorName, measInterval);
+
+ return true;
+}
+
+/**
+ * @brief Start measurement mode
+ * @note This function should not change the clock
+ */
+bool SCD30Sensor::startMeasurement()
+{
+ uint16_t error;
+
+ if (state == SCD30_MEASUREMENT) {
+ LOG_DEBUG("%s: Already in measurement mode", sensorName);
+ return true;
+ }
+
+ error = scd30.startPeriodicMeasurement(0);
+
+ if (error == SCD30_NO_ERROR) {
+ LOG_INFO("%s: Started measurement mode", sensorName);
+
+ state = SCD30_MEASUREMENT;
+ return true;
+ } else {
+ LOG_ERROR("%s: Couldn't start measurement mode", sensorName);
+ return false;
+ }
+}
+
+/**
+ * @brief Stop measurement mode
+ * @note This function should not change the clock
+ */
+bool SCD30Sensor::stopMeasurement()
+{
+ uint16_t error;
+
+ error = scd30.stopPeriodicMeasurement();
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to stop measurement", sensorName);
+ return false;
+ }
+
+ state = SCD30_IDLE;
+ return true;
+}
+
+bool SCD30Sensor::performFRC(uint16_t targetCO2)
+{
+ uint16_t error;
+
+ LOG_INFO("%s: Issuing FRC. Ensure device has been working at least 3 minutes in stable target environment", sensorName);
+
+ LOG_INFO("%s: Target CO2: %u ppm", sensorName, targetCO2);
+ error = scd30.forceRecalibration((uint16_t)targetCO2);
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to perform forced recalibration.", sensorName);
+ return false;
+ }
+
+ LOG_INFO("%s: FRC Correction successful.", sensorName);
+
+ return true;
+}
+
+bool SCD30Sensor::setASC(bool ascEnabled)
+{
+ uint16_t error;
+
+ LOG_INFO("%s: %s ASC", sensorName, ascEnabled ? "Enabling" : "Disabling");
+
+ error = scd30.activateAutoCalibration((uint16_t)ascEnabled);
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to send command.", sensorName);
+ return false;
+ }
+
+ if (!getASC(ascActive)) {
+ LOG_ERROR("%s: Unable to check if ASC is enabled", sensorName);
+ return false;
+ }
+
+ return true;
+}
+
+bool SCD30Sensor::getASC(uint16_t &_ascActive)
+{
+ uint16_t error;
+ // LOG_INFO("%s: Getting ASC", sensorName);
+
+ error = scd30.getAutoCalibrationStatus(_ascActive);
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to send command.", sensorName);
+ return false;
+ }
+
+ LOG_INFO("%s: ASC is %s", sensorName, _ascActive ? "enabled" : "disabled");
+
+ return true;
+}
+
+/**
+ * @brief Set the temperature reference. Unit ℃.
+ *
+ * The on-board RH/T sensor is influenced by thermal self-heating of SCD30
+ * and other electrical components. Design-in alters the thermal properties
+ * of SCD30 such that temperature and humidity offsets may occur when
+ * operating the sensor in end-customer devices. Compensation of those
+ * effects is achievable by writing the temperature offset found in
+ * continuous operation of the device into the sensor. Temperature offset
+ * value is saved in non-volatile memory. The last set value will be used
+ * for temperature offset compensation after repowering.
+ *
+ * @param[in] tempReference
+ * @note this function is certainly confusing and it's not recommended
+ */
+bool SCD30Sensor::setTemperature(float tempReference)
+{
+ uint16_t error;
+ uint16_t updatedTempOffset;
+ float tempOffset;
+ uint16_t _tempOffset;
+ float co2;
+ float temperature;
+ float humidity;
+
+ if (tempReference == 100) {
+ // Requesting the value of 100 will restore the temperature offset
+ LOG_INFO("%s: Setting reference temperature at 0degC", sensorName);
+ _tempOffset = 0;
+ } else {
+
+ LOG_INFO("%s: Setting reference temperature at: %.2f", sensorName, tempReference);
+
+ error = scd30.readMeasurementData(co2, temperature, humidity);
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to read current temperature. Error code: %u", sensorName, error);
+ return false;
+ }
+
+ LOG_INFO("%s: Current sensor temperature: %.2f", sensorName, temperature);
+
+ tempOffset = (temperature - tempReference);
+ if (tempOffset < 0) {
+ LOG_ERROR("%s temperature offset is only positive", sensorName);
+ return false;
+ }
+
+ tempOffset *= 100;
+ _tempOffset = static_cast(tempOffset);
+ }
+
+ LOG_INFO("%s: Setting temperature offset: %u (*100)", sensorName, _tempOffset);
+
+ error = scd30.setTemperatureOffset(_tempOffset);
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to set temperature offset. Error code: %u", sensorName, error);
+ return false;
+ }
+
+ scd30.getTemperatureOffset(updatedTempOffset);
+ LOG_INFO("%s: Updated sensor temperature offset: %u (*100)", sensorName, updatedTempOffset);
+
+ return true;
+}
+
+bool SCD30Sensor::setAltitude(uint16_t altitude)
+{
+ uint16_t error;
+
+ LOG_INFO("%s: setting altitude at %um", sensorName, altitude);
+
+ error = scd30.setAltitudeCompensation(altitude);
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to set altitude. Error code: %u", sensorName, error);
+ return false;
+ }
+
+ uint16_t newAltitude;
+ getAltitude(newAltitude);
+
+ return true;
+}
+
+bool SCD30Sensor::getAltitude(uint16_t &altitude)
+{
+ uint16_t error;
+ // LOG_INFO("%s: Getting altitude", sensorName);
+
+ error = scd30.getAltitudeCompensation(altitude);
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to get altitude. Error code: %u", sensorName, error);
+ return false;
+ }
+ LOG_INFO("%s: Sensor altitude: %u", sensorName, altitude);
+
+ return true;
+}
+
+bool SCD30Sensor::softReset()
+{
+ uint16_t error;
+
+ LOG_INFO("%s: Requesting soft reset", sensorName);
+
+ error = scd30.softReset();
+
+ if (error != SCD30_NO_ERROR) {
+ LOG_ERROR("%s: Unable to do soft reset. Error code: %u", sensorName, error);
+ return false;
+ }
+
+ LOG_INFO("%s: soft reset successful", sensorName);
+
+ return true;
+}
+
+/**
+ * @brief Check if sensor is in measurement mode
+ */
+bool SCD30Sensor::isActive()
+{
+ return state == SCD30_MEASUREMENT;
+}
+
+/**
+ * @brief Start measurement mode
+ * @note Not used in admin comands, getMetrics or init, can change clock.
+ */
+uint32_t SCD30Sensor::wakeUp()
+{
+
+#ifdef SCD30_I2C_CLOCK_SPEED
+#ifdef CAN_RECLOCK_I2C
+ uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
+#elif !HAS_SCREEN
+ reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
+#else
+ LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
+ return 0;
+#endif /* CAN_RECLOCK_I2C */
+#endif /* SCD30_I2C_CLOCK_SPEED */
+
+ startMeasurement();
+
+#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
+ reClockI2C(currentClock, _bus, false);
+#endif
+
+ return 0;
+}
+
+/**
+ * @brief Stop measurement mode
+ * @note Not used in admin comands, getMetrics or init, can change clock.
+ */
+void SCD30Sensor::sleep()
+{
+#ifdef SCD30_I2C_CLOCK_SPEED
+#ifdef CAN_RECLOCK_I2C
+ uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
+#elif !HAS_SCREEN
+ reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
+#else
+ LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
+ return;
+#endif /* CAN_RECLOCK_I2C */
+#endif /* SCD30_I2C_CLOCK_SPEED */
+
+ stopMeasurement();
+
+#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
+ reClockI2C(currentClock, _bus, false);
+#endif
+}
+
+bool SCD30Sensor::canSleep()
+{
+ return false;
+}
+
+int32_t SCD30Sensor::wakeUpTimeMs()
+{
+ return 0;
+}
+
+int32_t SCD30Sensor::pendingForReadyMs()
+{
+ return 0;
+}
+
+AdminMessageHandleResult SCD30Sensor::handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response)
+{
+ AdminMessageHandleResult result;
+
+#ifdef SCD30_I2C_CLOCK_SPEED
+#ifdef CAN_RECLOCK_I2C
+ uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
+#elif !HAS_SCREEN
+ reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
+#else
+ LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
+ return AdminMessageHandleResult::NOT_HANDLED;
+#endif /* CAN_RECLOCK_I2C */
+#endif /* SCD30_I2C_CLOCK_SPEED */
+
+ switch (request->which_payload_variant) {
+ case meshtastic_AdminMessage_sensor_config_tag:
+ // Check for ASC-FRC request first
+ if (!request->sensor_config.has_scd30_config) {
+ result = AdminMessageHandleResult::NOT_HANDLED;
+ break;
+ }
+
+ if (request->sensor_config.scd30_config.has_soft_reset) {
+ LOG_DEBUG("%s: Requested soft reset", sensorName);
+ this->softReset();
+ } else {
+
+ if (request->sensor_config.scd30_config.has_set_asc) {
+ this->setASC(request->sensor_config.scd30_config.set_asc);
+ if (request->sensor_config.scd30_config.set_asc == false) {
+ LOG_DEBUG("%s: Request for FRC", sensorName);
+ if (request->sensor_config.scd30_config.has_set_target_co2_conc) {
+ this->performFRC(request->sensor_config.scd30_config.set_target_co2_conc);
+ } else {
+ // FRC requested but no target CO2 provided
+ LOG_ERROR("%s: target CO2 not provided", sensorName);
+ result = AdminMessageHandleResult::NOT_HANDLED;
+ break;
+ }
+ }
+ }
+
+ // Check for temperature offset
+ // NOTE: this requires to have a sensor working on stable environment
+ // And to make it between readings
+ if (request->sensor_config.scd30_config.has_set_temperature) {
+ this->setTemperature(request->sensor_config.scd30_config.set_temperature);
+ }
+
+ // Check for altitude
+ if (request->sensor_config.scd30_config.has_set_altitude) {
+ this->setAltitude(request->sensor_config.scd30_config.set_altitude);
+ }
+
+ // Check for set measuremen interval
+ if (request->sensor_config.scd30_config.has_set_measurement_interval) {
+ this->setMeasurementInterval(request->sensor_config.scd30_config.set_measurement_interval);
+ }
+ }
+
+ result = AdminMessageHandleResult::HANDLED;
+ break;
+
+ default:
+ result = AdminMessageHandleResult::NOT_HANDLED;
+ }
+
+#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
+ reClockI2C(currentClock, _bus, false);
+#endif
+
+ return result;
+}
+
+#endif
diff --git a/src/modules/Telemetry/Sensor/SCD30Sensor.h b/src/modules/Telemetry/Sensor/SCD30Sensor.h
new file mode 100644
index 00000000000..6e03e2dda60
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/SCD30Sensor.h
@@ -0,0 +1,53 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include()
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+#include
+
+#define SCD30_I2C_CLOCK_SPEED 100000
+
+class SCD30Sensor : public TelemetrySensor
+{
+ private:
+ SensirionI2cScd30 scd30;
+ TwoWire *_bus{};
+ uint8_t _address{};
+
+ bool performFRC(uint16_t targetCO2);
+ bool setASC(bool ascEnabled);
+ bool getASC(uint16_t &ascEnabled);
+ bool setTemperature(float tempReference);
+ bool getAltitude(uint16_t &altitude);
+ bool setAltitude(uint16_t altitude);
+ bool softReset(); //
+ bool setMeasurementInterval(uint16_t measInterval);
+ bool getMeasurementInterval(uint16_t &measInterval);
+ bool startMeasurement();
+ bool stopMeasurement();
+
+ // Parameters
+ uint16_t ascActive = 1;
+ uint16_t measurementInterval = 2;
+
+ public:
+ SCD30Sensor();
+ virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
+ virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+
+ enum SCD30State { SCD30_OFF, SCD30_IDLE, SCD30_MEASUREMENT };
+ SCD30State state = SCD30_OFF;
+
+ virtual bool isActive() override;
+
+ virtual void sleep() override; // Stops measurement (measurement -> idle)
+ virtual uint32_t wakeUp() override; // Starts measurement (idle -> measurement)
+ virtual bool canSleep() override;
+ virtual int32_t wakeUpTimeMs() override;
+ virtual int32_t pendingForReadyMs() override;
+ AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response) override;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/SCD4XSensor.cpp b/src/modules/Telemetry/Sensor/SCD4XSensor.cpp
index 6572ef9b100..c6ab7bb043b 100644
--- a/src/modules/Telemetry/Sensor/SCD4XSensor.cpp
+++ b/src/modules/Telemetry/Sensor/SCD4XSensor.cpp
@@ -111,7 +111,7 @@ bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
bool dataReady;
error = scd4x.getDataReadyStatus(dataReady);
- if (!dataReady) {
+ if (error != SCD4X_NO_ERROR || !dataReady) {
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
@@ -217,7 +217,7 @@ bool SCD4XSensor::startMeasurement()
state = SCD4X_MEASUREMENT;
return true;
} else {
- LOG_ERROR("%s: Couldn't start measurement mode", sensorName);
+ LOG_ERROR("%s: Unable to start measurement mode", sensorName);
return false;
}
}
@@ -232,7 +232,7 @@ bool SCD4XSensor::stopMeasurement()
error = scd4x.stopPeriodicMeasurement();
if (error != SCD4X_NO_ERROR) {
- LOG_ERROR("%s: Unable to set idle mode on SCD4X.", sensorName);
+ LOG_ERROR("%s: Unable to stop measurement.", sensorName);
return false;
}
@@ -283,11 +283,7 @@ bool SCD4XSensor::getASC(uint16_t &_ascActive)
return false;
}
- if (_ascActive) {
- LOG_INFO("%s: ASC is enabled", sensorName);
- } else {
- LOG_INFO("%s: FRC is enabled", sensorName);
- }
+ LOG_INFO("%s ASC is %s", sensorName, _ascActive ? "enabled" : "disabled");
return true;
}
@@ -305,11 +301,7 @@ bool SCD4XSensor::setASC(bool ascEnabled)
{
uint16_t error;
- if (ascEnabled) {
- LOG_INFO("%s: Enabling ASC", sensorName);
- } else {
- LOG_INFO("%s: Disabling ASC", sensorName);
- }
+ LOG_INFO("%s %s ASC", sensorName, ascEnabled ? "Enabling" : "Disabling");
if (!stopMeasurement()) {
return false;
@@ -351,7 +343,6 @@ bool SCD4XSensor::setASC(bool ascEnabled)
*/
bool SCD4XSensor::setASCBaseline(uint32_t targetCO2)
{
- // TODO - Remove?
// Available in library, but not described in datasheet.
uint16_t error;
LOG_INFO("%s: Setting ASC baseline to: %u", sensorName, targetCO2);
@@ -419,7 +410,7 @@ bool SCD4XSensor::setTemperature(float tempReference)
LOG_INFO("%s: Setting reference temperature at: %.2f", sensorName, tempReference);
error = scd4x.getDataReadyStatus(dataReady);
- if (!dataReady) {
+ if (error != SCD4X_NO_ERROR || !dataReady) {
LOG_ERROR("%s: Data is not ready", sensorName);
return false;
}
diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.cpp b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp
index 0a9db4dff9e..b0f0f90711e 100644
--- a/src/modules/Telemetry/Sensor/SEN5XSensor.cpp
+++ b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp
@@ -182,7 +182,7 @@ uint8_t SEN5XSensor::readBuffer(uint8_t *buffer, uint8_t byteNumber)
return receivedBytes;
}
-uint8_t SEN5XSensor::sen5xCRC(uint8_t *buffer)
+uint8_t SEN5XSensor::sen5xCRC(const uint8_t *buffer)
{
// This code is based on Sensirion's own implementation
// https://github.com/Sensirion/arduino-core/blob/41fd02cacf307ec4945955c58ae495e56809b96c/src/SensirionCrc.cpp
@@ -205,7 +205,6 @@ uint8_t SEN5XSensor::sen5xCRC(uint8_t *buffer)
void SEN5XSensor::sleep()
{
- // TODO Check this works
idle(true);
}
@@ -230,41 +229,43 @@ bool SEN5XSensor::idle(bool checkState)
// Check if we have time, and store it
uint32_t now; // If time is RTCQualityNone, it will return zero
now = getValidTime(RTCQuality::RTCQualityDevice);
+ // Check if state is valid (non-zero)
if (now) {
- // Check if state is valid (non-zero)
vocTime = now;
}
}
- if (vocStateStable() && vocValid) {
- saveState();
- } else {
- LOG_INFO("SEN5X: Not stopping measurement, vocState is not stable yet!");
+ if (!(vocStateStable() && vocValid)) {
+ LOG_INFO("%s: Not stopping measurement, vocState is not stable yet!", sensorName);
return true;
}
}
+ // Save state and prefs (on all models)
+ saveState();
}
if (!oneShotMode) {
- LOG_INFO("SEN5X: Not stopping measurement, continuous mode!");
+ LOG_INFO("%s: Not stopping measurement, continuous mode!", sensorName);
return true;
+ } else {
+ LOG_INFO("%s: One shot mode enabled", sensorName);
}
// Switch to low-power based on the model
if (model == SEN50) {
if (!sendCommand(SEN5X_STOP_MEASUREMENT)) {
- LOG_ERROR("SEN5X: Error stopping measurement");
+ LOG_ERROR("%s: Error stopping measurement", sensorName);
return false;
}
state = SEN5X_IDLE;
- LOG_INFO("SEN5X: Stop measurement mode");
+ LOG_INFO("%s: Stop measurement mode", sensorName);
} else {
if (!sendCommand(SEN5X_START_MEASUREMENT_RHT_GAS)) {
- LOG_ERROR("SEN5X: Error switching to RHT/Gas measurement");
+ LOG_ERROR("%s: Error switching to RHT/Gas measurement", sensorName);
return false;
}
state = SEN5X_RHTGAS_ONLY;
- LOG_INFO("SEN5X: Switch to RHT/Gas only measurement mode");
+ LOG_INFO("%s: Switch to RHT/Gas only measurement mode", sensorName);
}
delay(200); // From Sensirion Datasheet
@@ -289,10 +290,10 @@ bool SEN5XSensor::vocStateValid()
{
if (!vocState[0] && !vocState[1] && !vocState[2] && !vocState[3] && !vocState[4] && !vocState[5] && !vocState[6] &&
!vocState[7]) {
- LOG_DEBUG("SEN5X: VOC state is all 0, invalid");
+ LOG_DEBUG("%s: VOC state is all 0, invalid", sensorName);
return false;
} else {
- LOG_DEBUG("SEN5X: VOC state is valid");
+ LOG_DEBUG("%s: VOC state is valid", sensorName);
return true;
}
}
@@ -618,7 +619,7 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
}
} else {
// TODO - Should this actually ignore? We could end up never cleaning...
- LOG_INFO("SEN5X: Not enough RTCQuality, ignoring saved state. Trying again later");
+ LOG_INFO("SEN5X: Not enough RTCQuality, ignoring saved cleaning and VOC state");
}
idle(false);
@@ -965,4 +966,4 @@ AdminMessageHandleResult SEN5XSensor::handleAdminMessage(const meshtastic_MeshPa
return result;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.h b/src/modules/Telemetry/Sensor/SEN5XSensor.h
index 46f8c70e98d..ef5ad5c2922 100644
--- a/src/modules/Telemetry/Sensor/SEN5XSensor.h
+++ b/src/modules/Telemetry/Sensor/SEN5XSensor.h
@@ -114,7 +114,7 @@ See: https://sensirion.com/resource/application_note/low_power_mode/sen5x
bool sendCommand(uint16_t command);
bool sendCommand(uint16_t command, uint8_t *buffer, uint8_t byteNumber = 0);
uint8_t readBuffer(uint8_t *buffer, uint8_t byteNumber); // Return number of bytes received
- uint8_t sen5xCRC(uint8_t *buffer);
+ uint8_t sen5xCRC(const uint8_t *buffer);
bool startCleaning();
uint8_t getMeasurements();
// bool readRawValues();
diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp
index 41dc02cd190..3371c405dd5 100644
--- a/src/modules/TraceRouteModule.cpp
+++ b/src/modules/TraceRouteModule.cpp
@@ -266,7 +266,7 @@ void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtasti
}
}
-void TraceRouteModule::updateNextHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
+void TraceRouteModule::updateNextHops(const meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
{
// E.g. if the route is A->B->C->D and we are B, we can set C as next-hop for C and D
// Similarly, if we are C, we can set D as next-hop for D
diff --git a/src/modules/TraceRouteModule.h b/src/modules/TraceRouteModule.h
index a40ed77334e..db94b9d9bb8 100644
--- a/src/modules/TraceRouteModule.h
+++ b/src/modules/TraceRouteModule.h
@@ -62,7 +62,7 @@ class TraceRouteModule : public ProtobufModule,
void appendMyIDandSNR(meshtastic_RouteDiscovery *r, float snr, bool isTowardsDestination, bool SNRonly);
// Update next-hops in the routing table based on the returned route
- void updateNextHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r);
+ void updateNextHops(const meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r);
// Helper to update next-hop for a single node
void maybeSetNextHop(NodeNum target, uint8_t nextHopByte);
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 18a4f913e16..2c10c4b2b4d 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -754,10 +754,8 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
// For uplinking other's packets, check if it's not OK to MQTT or if it's an older packet without the bitfield
bool dontUplink = !mp_decoded.decoded.has_bitfield || !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK);
- // check for the lowest bit of the data bitfield set false, and the use of one of the default keys.
- if (!isFromUs(&mp_decoded) && !isMqttServerAddressPrivate && dontUplink &&
- (ch.settings.psk.size < 2 || (ch.settings.psk.size == 16 && memcmp(ch.settings.psk.bytes, defaultpsk, 16)) ||
- (ch.settings.psk.size == 32 && memcmp(ch.settings.psk.bytes, eventpsk, 32)))) {
+ // Respect the DontMqttMeBro flag for other nodes' packets on public MQTT servers
+ if (!isFromUs(&mp_decoded) && !isMqttServerAddressPrivate && dontUplink) {
LOG_INFO("MQTT onSend - Not forwarding packet due to DontMqttMeBro flag");
return;
}
diff --git a/src/platform/esp32/MeshtasticOTA.cpp b/src/platform/esp32/MeshtasticOTA.cpp
index 4ca0747234f..20a3c59cc50 100644
--- a/src/platform/esp32/MeshtasticOTA.cpp
+++ b/src/platform/esp32/MeshtasticOTA.cpp
@@ -75,7 +75,7 @@ bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc)
return true;
}
-bool checkOTACapability(esp_app_desc_t *app_desc, uint8_t method)
+bool checkOTACapability(const esp_app_desc_t *app_desc, uint8_t method)
{
// Combined loader supports all (both) transports, BLE and WiFi
if (strcmp(app_desc->project_name, combinedAppProjectName) == 0) {
diff --git a/src/platform/esp32/MeshtasticOTA.h b/src/platform/esp32/MeshtasticOTA.h
index 7c158775f98..ce5a7e86b77 100644
--- a/src/platform/esp32/MeshtasticOTA.h
+++ b/src/platform/esp32/MeshtasticOTA.h
@@ -16,7 +16,7 @@ void initialize();
bool isUpdated();
const esp_partition_t *getAppPartition();
bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc);
-bool checkOTACapability(esp_app_desc_t *app_desc, uint8_t method);
+bool checkOTACapability(const esp_app_desc_t *app_desc, uint8_t method);
void recoverConfig(meshtastic_Config_NetworkConfig *network);
void saveConfig(meshtastic_Config_NetworkConfig *network, meshtastic_OTAMode method, uint8_t *ota_hash);
bool trySwitchToOTA();
diff --git a/src/platform/nrf52/AsyncUDP.h b/src/platform/nrf52/AsyncUDP.h
index 718277309c1..ecbcb12fb9f 100644
--- a/src/platform/nrf52/AsyncUDP.h
+++ b/src/platform/nrf52/AsyncUDP.h
@@ -38,7 +38,7 @@ class AsyncUDP : public Print, private concurrency::OSThread
class AsyncUDPPacket
{
public:
- AsyncUDPPacket(EthernetUDP &source);
+ explicit AsyncUDPPacket(EthernetUDP &source);
IPAddress remoteIP();
uint16_t length();
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index 9579bef456c..3f0b2147c7a 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -496,7 +496,7 @@ void portduinoSetup()
randomSeed(time(NULL));
std::string defaultGpioChipName = gpioChipName + std::to_string(portduino_config.lora_default_gpiochip);
- for (auto i : portduino_config.all_pins) {
+ for (const auto *i : portduino_config.all_pins) {
if (i->enabled && i->pin > max_GPIO)
max_GPIO = i->pin;
}
@@ -510,7 +510,7 @@ void portduinoSetup()
// Need to bind all the configured GPIO pins so they're not simulated
// TODO: If one of these fails, we should log and terminate
- for (auto i : portduino_config.all_pins) {
+ for (const auto *i : portduino_config.all_pins) {
// In the case of a ch341 Lora device, we don't want to touch the system GPIO lines for Lora
// Those GPIO are handled in our usermode driver instead.
if (i->config_section == "Lora" && portduino_config.lora_spi_dev == "ch341") {
@@ -572,7 +572,7 @@ void portduinoSetup()
return;
}
-int initGPIOPin(int pinNum, const std::string gpioChipName, int line)
+int initGPIOPin(int pinNum, const std::string &gpioChipName, int line)
{
#ifdef PORTDUINO_LINUX_HARDWARE
std::string gpio_name = "GPIO" + std::to_string(pinNum);
@@ -643,7 +643,7 @@ bool loadConfig(const char *configPath)
if (yamlConfig["Lora"]) {
if (yamlConfig["Lora"]["Module"]) {
- for (auto &loraModule : portduino_config.loraModules) {
+ for (const auto &loraModule : portduino_config.loraModules) {
if (yamlConfig["Lora"]["Module"].as("") == loraModule.second) {
portduino_config.lora_module = loraModule.first;
break;
@@ -777,7 +777,7 @@ bool loadConfig(const char *configPath)
}
if (yamlConfig["Display"]) {
- for (auto &screen_name : portduino_config.screen_names) {
+ for (const auto &screen_name : portduino_config.screen_names) {
if (yamlConfig["Display"]["Panel"].as("") == screen_name.second)
portduino_config.displayPanel = screen_name.first;
}
@@ -907,7 +907,7 @@ bool loadConfig(const char *configPath)
}
if (checkConfigPort) {
portduino_config.api_port = (yamlConfig["General"]["APIPort"]).as(-1);
- if (portduino_config.api_port != -1 && portduino_config.api_port > 1023 && portduino_config.api_port < 65536) {
+ if (portduino_config.api_port > 1023 && portduino_config.api_port < 65536) {
TCPPort = (portduino_config.api_port);
}
}
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index 990803e7914..aa8847fd7a7 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -58,7 +58,7 @@ extern std::ofstream traceFile;
extern std::ofstream JSONFile;
extern Ch341Hal *ch341Hal;
-int initGPIOPin(int pinNum, std::string gpioChipname, int line);
+int initGPIOPin(int pinNum, const std::string &gpioChipname, int line);
bool loadConfig(const char *configPath);
static bool ends_with(std::string_view str, std::string_view suffix);
void getMacAddr(uint8_t *dmac);
@@ -224,7 +224,7 @@ extern struct portduino_config_struct {
out << YAML::Key << "Lora" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "Module" << YAML::Value << loraModules[lora_module];
- for (auto lora_pin : all_pins) {
+ for (const auto *lora_pin : all_pins) {
if (lora_pin->config_section == "Lora" && lora_pin->enabled) {
out << YAML::Key << lora_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << lora_pin->pin;
@@ -350,11 +350,11 @@ extern struct portduino_config_struct {
// Display
if (displayPanel != no_screen) {
out << YAML::Key << "Display" << YAML::Value << YAML::BeginMap;
- for (auto &screen_name : screen_names) {
+ for (const auto &screen_name : screen_names) {
if (displayPanel == screen_name.first)
out << YAML::Key << "Module" << YAML::Value << screen_name.second;
}
- for (auto display_pin : all_pins) {
+ for (const auto *display_pin : all_pins) {
if (display_pin->config_section == "Display" && display_pin->enabled) {
out << YAML::Key << display_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << display_pin->pin;
@@ -402,7 +402,7 @@ extern struct portduino_config_struct {
case ft5x06:
out << YAML::Key << "Module" << YAML::Value << "FT5x06";
}
- for (auto touchscreen_pin : all_pins) {
+ for (const auto *touchscreen_pin : all_pins) {
if (touchscreen_pin->config_section == "Touchscreen" && touchscreen_pin->enabled) {
out << YAML::Key << touchscreen_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << touchscreen_pin->pin;
@@ -425,7 +425,7 @@ extern struct portduino_config_struct {
if (pointerDevice != "")
out << YAML::Key << "PointerDevice" << YAML::Value << pointerDevice;
- for (auto input_pin : all_pins) {
+ for (const auto *input_pin : all_pins) {
if (input_pin->config_section == "Input" && input_pin->enabled) {
out << YAML::Key << input_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << input_pin->pin;
diff --git a/src/platform/portduino/USBHal.h b/src/platform/portduino/USBHal.h
index 441f75b1091..1725763f209 100644
--- a/src/platform/portduino/USBHal.h
+++ b/src/platform/portduino/USBHal.h
@@ -29,7 +29,7 @@ class Ch341Hal : public RadioLibHal
{
public:
// default constructor - initializes the base HAL and any needed private members
- explicit Ch341Hal(uint8_t spiChannel, std::string serial = "", uint32_t vid = 0x1A86, uint32_t pid = 0x5512,
+ explicit Ch341Hal(uint8_t spiChannel, const std::string &serial = "", uint32_t vid = 0x1A86, uint32_t pid = 0x5512,
uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING)
{
diff --git a/variants/esp32/diy/9m2ibr_aprs_lora_tracker/platformio.ini b/variants/esp32/diy/9m2ibr_aprs_lora_tracker/platformio.ini
index 2ddc5a2dbf7..3fdb738fc07 100644
--- a/variants/esp32/diy/9m2ibr_aprs_lora_tracker/platformio.ini
+++ b/variants/esp32/diy/9m2ibr_aprs_lora_tracker/platformio.ini
@@ -10,6 +10,7 @@ build_flags =
-D EBYTE_E22
-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
+ -ULED_BUILTIN
build_src_filter =
${esp32_base.build_src_filter}
+<../variants/esp32/diy/9m2ibr_aprs_lora_tracker>
\ No newline at end of file
diff --git a/variants/esp32c6/m5stack_unitc6l/platformio.ini b/variants/esp32c6/m5stack_unitc6l/platformio.ini
index 37221c10346..4c04d0c26c7 100644
--- a/variants/esp32c6/m5stack_unitc6l/platformio.ini
+++ b/variants/esp32c6/m5stack_unitc6l/platformio.ini
@@ -43,4 +43,4 @@ lib_ignore =
NonBlockingRTTTL
libpax
build_src_filter =
- ${esp32c6_base.build_src_filter} +<../variants/esp32c6/m5stack_unitc6l>
\ No newline at end of file
+ ${esp32c6_base.build_src_filter} +<../variants/esp32c6/m5stack_unitc6l>
diff --git a/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h b/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h
index 1b129142426..fb0744bc3b6 100644
--- a/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h
+++ b/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -87,6 +88,7 @@ void setupNicheGraphics()
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // -
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
@@ -114,4 +116,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h b/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h
index 61b08c7405a..a90500b150a 100644
--- a/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h
+++ b/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h
@@ -24,6 +24,7 @@ Different NicheGraphics UIs and different hardware variants will each have their
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -84,6 +85,7 @@ void setupNicheGraphics()
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // -
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
@@ -111,4 +113,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h b/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h
index 445b577142e..9e84a541e18 100644
--- a/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h
+++ b/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -87,6 +88,7 @@ void setupNicheGraphics()
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); // -
// Start running InkHUD
inkhud->begin();
@@ -107,4 +109,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/esp32s3/station-g2/platformio.ini b/variants/esp32s3/station-g2/platformio.ini
index 091b35f0037..4efb21a0098 100755
--- a/variants/esp32s3/station-g2/platformio.ini
+++ b/variants/esp32s3/station-g2/platformio.ini
@@ -21,11 +21,11 @@ upload_protocol = esptool
upload_speed = 921600
build_unflags =
${esp32s3_base.build_unflags}
- -DARDUINO_USB_MODE=1
+ -DARDUINO_USB_MODE=0
build_flags =
${esp32s3_base.build_flags}
-D STATION_G2
-I variants/esp32s3/station-g2
-DBOARD_HAS_PSRAM
-DSTATION_G2
- -DARDUINO_USB_MODE=0
+ -DARDUINO_USB_MODE=1
diff --git a/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h b/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h
index 8f5e63653ba..73cc2e23573 100644
--- a/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h
+++ b/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -67,6 +68,7 @@ void setupNicheGraphics()
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // -
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0
@@ -86,4 +88,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/esp32s3/unphone/platformio.ini b/variants/esp32s3/unphone/platformio.ini
index f52fcc09a72..1d4af52f307 100644
--- a/variants/esp32s3/unphone/platformio.ini
+++ b/variants/esp32s3/unphone/platformio.ini
@@ -38,8 +38,8 @@ build_src_filter =
lib_deps = ${esp32s3_base.lib_deps}
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
lovyan03/LovyanGFX@1.2.19
- # TODO renovate
- https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0
+ # TODO renovate https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0
+ https://gitlab.com/hamishcunningham/unphonelibrary/-/archive/meshtastic/unphonelibrary-meshtastic.zip
# renovate: datasource=custom.pio depName=NeoPixel packageName=adafruit/library/Adafruit NeoPixel
adafruit/Adafruit NeoPixel@1.15.4
diff --git a/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h b/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h
index f64de9d0760..242e5ae495b 100644
--- a/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h
+++ b/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -71,13 +72,14 @@ void setupNicheGraphics()
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
- inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
- inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
- inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
- inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
- inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
- inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
- inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
+ inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown
+ inkhud->addApplet("DMs", new InkHUD::DMApplet); // -
+ inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
+ inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
+ inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
+ inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
+ inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); // -
// Start running InkHUD
inkhud->begin();
@@ -115,4 +117,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h
index 8f30a244f8a..0a01b613e9a 100644
--- a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h
+++ b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -72,7 +73,8 @@ void setupNicheGraphics()
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, false, 3); // Default on tile 3
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 2); // Default on tile 2
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1));
- inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true, false, 1); // Default on tile 1
+ inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true, false, 1); // Default on tile 1
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet, true, false, 0); // Default on tile 0
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true); // Background
@@ -92,4 +94,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h b/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h
index b6be70ff4a0..ad17e745727 100644
--- a/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h
+++ b/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -73,7 +74,8 @@ void setupNicheGraphics()
inkhud->addApplet("DMs", new InkHUD::DMApplet, true, false, 3); // Default on tile 3
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, false, 2); // Default on tile 2
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1));
- inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true, false, 1); // Default on tile 1
+ inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true, false, 1); // Default on tile 1
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet, true, false, 0); // Default on tile 0
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true); // Background
@@ -93,4 +95,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h b/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h
index 10f628d565a..187022ea71c 100644
--- a/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h
+++ b/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -67,6 +68,7 @@ void setupNicheGraphics()
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // -
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
@@ -87,4 +89,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h b/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h
index 125f50590a6..0f413191679 100644
--- a/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h
+++ b/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -67,6 +68,7 @@ void setupNicheGraphics()
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // -
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
@@ -87,4 +89,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini b/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini
index 07d763df3cf..9b15e668aa7 100644
--- a/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini
+++ b/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini
@@ -24,8 +24,8 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/rak4631_nomadstar_meteor_pro> + +
lib_deps =
${nrf52840_base.lib_deps}
- # TODO renovate
- https://github.com/NomadStar-outdoor/IOBoard-RGB-LP5562-Library.git#9c366c8
+ # renovate: datasource=git-refs depName=IOBoard-RGB-LP5562-Library packageName=NomadStar-outdoor/IOBoard-RGB-LP5562-Library gitBranch=master
+ https://github.com/NomadStar-outdoor/IOBoard-RGB-LP5562-Library/archive/9c366c875e1e8103ed97b5d4c09f3878345da80a.zip
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds
diff --git a/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h b/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h
index 98aeb870039..2a2967f5ece 100644
--- a/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h
+++ b/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h
@@ -9,8 +9,10 @@
#include "graphics/niche/InkHUD/InkHUD.h"
// Applets
+#include "graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h"
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -63,6 +65,7 @@ void setupNicheGraphics()
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side
+ inkhud->persistence->settings.optionalFeatures.batteryIcon = true;
// Pick applets
// Note: order of applets determines priority of "auto-show" feature
@@ -74,6 +77,7 @@ void setupNicheGraphics()
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); // -
// Start running InkHUD
inkhud->begin();
diff --git a/variants/nrf52840/t-echo-plus/nicheGraphics.h b/variants/nrf52840/t-echo-plus/nicheGraphics.h
index 483e16ea4b8..73067d7a752 100644
--- a/variants/nrf52840/t-echo-plus/nicheGraphics.h
+++ b/variants/nrf52840/t-echo-plus/nicheGraphics.h
@@ -8,6 +8,7 @@
#include "graphics/niche/Drivers/EInk/GDEY0154D67.h"
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -43,6 +44,7 @@ void setupNicheGraphics()
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0));
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1));
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true);
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet);
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet);
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0);
diff --git a/variants/nrf52840/t-echo/nicheGraphics.h b/variants/nrf52840/t-echo/nicheGraphics.h
index c89d816b998..c0b24dea77e 100644
--- a/variants/nrf52840/t-echo/nicheGraphics.h
+++ b/variants/nrf52840/t-echo/nicheGraphics.h
@@ -11,6 +11,7 @@
// Applets
#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h"
#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h"
+#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h"
#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h"
#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h"
@@ -79,6 +80,7 @@ void setupNicheGraphics()
inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // -
inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // -
inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated
+ inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // -
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
@@ -123,4 +125,4 @@ void setupNicheGraphics()
buttons->start();
}
-#endif
\ No newline at end of file
+#endif