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