diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 30dec205a..49b2ba8e8 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,10 +9,10 @@ plugins: lint: enabled: - checkov@3.2.497 - - renovate@42.78.2 + - renovate@42.81.2 - prettier@3.7.4 - trufflehog@3.92.4 - - yamllint@1.37.1 + - yamllint@1.38.0 - bandit@1.9.2 - trivy@0.68.2 - taplo@0.10.0 diff --git a/boards/t-watch-s3.json b/boards/t-watch-s3.json index bae4f47b0..f3c0bea8e 100644 --- a/boards/t-watch-s3.json +++ b/boards/t-watch-s3.json @@ -9,7 +9,7 @@ "-DBOARD_HAS_PSRAM", "-DT_WATCH_S3", "-DARDUINO_USB_CDC_ON_BOOT=1", - "-DARDUINO_USB_MODE=0", + "-DARDUINO_USB_MODE=1", "-DARDUINO_RUNNING_CORE=1", "-DARDUINO_EVENT_RUNNING_CORE=1" ], diff --git a/platformio.ini b/platformio.ini index 6923c65b9..4c19136af 100644 --- a/platformio.ini +++ b/platformio.ini @@ -119,7 +119,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/12f8cddc1e2908e1988da21e3500c695668e8d92.zip + https://github.com/meshtastic/device-ui/archive/5a870c623a4e9ab7a7abe3d02950536f107d1a31.zip ; Common libs for environmental measurements in telemetry module [environmental_base] @@ -129,7 +129,7 @@ lib_deps = # renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor adafruit/Adafruit Unified Sensor@1.1.15 # renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library - adafruit/Adafruit BMP280 Library@2.6.8 + adafruit/Adafruit BMP280 Library@3.0.0 # renovate: datasource=custom.pio depName=Adafruit BMP085 packageName=adafruit/library/Adafruit BMP085 Library adafruit/Adafruit BMP085 Library@1.2.4 # renovate: datasource=custom.pio depName=Adafruit BME280 packageName=adafruit/library/Adafruit BME280 Library @@ -142,8 +142,6 @@ lib_deps = adafruit/Adafruit INA260 Library@1.5.3 # renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219 adafruit/Adafruit INA219@1.2.3 - # renovate: datasource=custom.pio depName=Adafruit PM25 AQI Sensor packageName=adafruit/library/Adafruit PM25 AQI Sensor - adafruit/Adafruit PM25 AQI Sensor@2.0.0 # renovate: datasource=custom.pio depName=Adafruit MPU6050 packageName=adafruit/library/Adafruit MPU6050 adafruit/Adafruit MPU6050@2.2.6 # renovate: datasource=custom.pio depName=Adafruit LIS3DH packageName=adafruit/library/Adafruit LIS3DH diff --git a/protobufs b/protobufs index 61219de74..4b9f104a1 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 61219de7480ac8ddf27256f405667d2f416ee1bd +Subproject commit 4b9f104a18ea43b1b2091ee2b48899fe43ad8a0b diff --git a/src/Power.cpp b/src/Power.cpp index c7d7c5d8b..e320f0557 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -476,7 +476,9 @@ class AnalogBatteryLevel : public HasBatteryLevel return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse; } #endif -#ifdef EXT_CHRG_DETECT +#if defined(ELECROW_ThinkNode_M6) + return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value || isVbusIn(); +#elif EXT_CHRG_DETECT return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value; #elif defined(BATTERY_CHARGING_INV) return !digitalRead(BATTERY_CHARGING_INV); diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index 45b96ad07..39436f18e 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -54,7 +54,7 @@ size_t SafeFile::write(const uint8_t *buffer, size_t size) } /** - * Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches + * Atomically close the file (overwriting any old version) and readback the contents to confirm the hash matches * * @return false for failure */ @@ -73,15 +73,7 @@ bool SafeFile::close() if (!testReadback()) return false; - { // Scope for lock - concurrency::LockGuard g(spiLock); - // brief window of risk here ;-) - if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) { - LOG_ERROR("Can't remove old pref file"); - return false; - } - } - + // Rename or overwrite (atomic operation) String filenameTmp = filename; filenameTmp += ".tmp"; if (!renameFile(filenameTmp.c_str(), filename.c_str())) { diff --git a/src/configuration.h b/src/configuration.h index ec1b9acc2..cbadedf3f 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -176,7 +176,8 @@ along with this program. If not, see . #define SSD1306_ADDRESS 0x3D #define USE_SH1106 #else -#define SSD1306_ADDRESS 0x3C +#define SSD1306_ADDRESS_L 0x3C //Addr = 0 +#define SSD1306_ADDRESS_H 0x3D //Addr = 1 #endif #define ST7567_ADDRESS 0x3F @@ -205,7 +206,7 @@ along with this program. If not, see . #define INA_ADDR_WAVESHARE_UPS 0x43 #define INA3221_ADDR 0x42 #define MAX1704X_ADDR 0x36 -#define QMC6310_ADDR 0x1C +#define QMC6310U_ADDR 0x1C #define QMI8658_ADDR 0x6B #define QMC5883L_ADDR 0x0D #define HMC5883L_ADDR 0x1E @@ -214,7 +215,7 @@ along with this program. If not, see . #define LPS22HB_ADDR_ALT 0x5D #define SHT31_4x_ADDR 0x44 #define SHT31_4x_ADDR_ALT 0x45 -#define PMSA0031_ADDR 0x12 +#define PMSA003I_ADDR 0x12 #define QMA6100P_ADDR 0x12 #define AHT10_ADDR 0x38 #define RCWL9620_ADDR 0x57 @@ -480,6 +481,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_AUDIO 1 #define MESHTASTIC_EXCLUDE_DETECTIONSENSOR 1 #define MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR 1 +#define MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR 1 #define MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY 1 #define MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION 1 #define MESHTASTIC_EXCLUDE_PAXCOUNTER 1 diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp index 83a455de7..4795d2abc 100644 --- a/src/detect/ScanI2C.cpp +++ b/src/detect/ScanI2C.cpp @@ -43,7 +43,7 @@ ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const ScanI2C::FoundDevice ScanI2C::firstAQI() const { - ScanI2C::DeviceType types[] = {PMSA0031, SCD4X}; + ScanI2C::DeviceType types[] = {PMSA003I, SCD4X}; return firstOfOrNONE(2, types); } diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 3a79d97c5..dffcd8fb6 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -35,11 +35,12 @@ class ScanI2C SHT4X, SHTC3, LPS22HB, - QMC6310, + QMC6310U, + QMC6310N, QMI8658, QMC5883L, HMC5883L, - PMSA0031, + PMSA003I, QMA6100P, MPU6050, LIS3DH, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 2be9212cf..7a263cd52 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -63,6 +63,10 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const if (i2cBus->available()) { r = i2cBus->read(); } + if(r == 0x80){ + LOG_INFO("QMC6310N found at address 0x%02X", addr.address); + return ScanI2C::DeviceType::QMC6310N; + } r &= 0x0f; if (r == 0x08 || r == 0x00) { @@ -106,7 +110,7 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation if (i2cBus->available()) i2cBus->read(); } - LOG_DEBUG("Register value: 0x%x", value); + LOG_DEBUG("Register value from 0x%x: 0x%x", registerLocation.i2cAddress.address, value); return value; } @@ -175,7 +179,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) type = NONE; if (err == 0) { switch (addr.address) { - case SSD1306_ADDRESS: + case SSD1306_ADDRESS_H: + case SSD1306_ADDRESS_L: type = probeOLED(addr); break; @@ -382,11 +387,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) } case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR - registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2); - if (registerValue == 0x5449) { + if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) { type = OPT3001; logFoundDevice("OPT3001", (uint8_t)addr.address); - } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2) != 0) { // unique SHT4x serial number + } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 6) != 0) { // unique SHT4x serial number (6 bytes inc. CRC) type = SHT4X; logFoundDevice("SHT4X", (uint8_t)addr.address); } else { @@ -412,7 +416,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) case LPS22HB_ADDR_ALT: SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB", (uint8_t)addr.address) - SCAN_SIMPLE_CASE(QMC6310_ADDR, QMC6310, "QMC6310", (uint8_t)addr.address) + SCAN_SIMPLE_CASE(QMC6310U_ADDR, QMC6310U, "QMC6310U", (uint8_t)addr.address) case QMI8658_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0A), 1); // get ID @@ -442,7 +446,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) #ifdef HAS_QMA6100P SCAN_SIMPLE_CASE(QMA6100P_ADDR, QMA6100P, "QMA6100P", (uint8_t)addr.address) #else - SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031", (uint8_t)addr.address) + SCAN_SIMPLE_CASE(PMSA003I_ADDR, PMSA003I, "PMSA003I", (uint8_t)addr.address) #endif case BMA423_ADDR: // this can also be LIS3DH_ADDR_ALT registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2); diff --git a/src/detect/reClockI2C.h b/src/detect/reClockI2C.h new file mode 100644 index 000000000..689e88d6f --- /dev/null +++ b/src/detect/reClockI2C.h @@ -0,0 +1,41 @@ +#ifdef CAN_RECLOCK_I2C +#include "ScanI2CTwoWire.h" + +uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus) +{ + + uint32_t currentClock; + + /* See https://github.com/arduino/Arduino/issues/11457 + Currently, only ESP32 can getClock() + While all cores can setClock() + https://github.com/sandeepmistry/arduino-nRF5/blob/master/libraries/Wire/Wire.h#L50 + https://github.com/earlephilhower/arduino-pico/blob/master/libraries/Wire/src/Wire.h#L60 + https://github.com/stm32duino/Arduino_Core_STM32/blob/main/libraries/Wire/src/Wire.h#L103 + For cases when I2C speed is different to the ones defined by sensors (see defines in sensor classes) + we need to reclock I2C and set it back to the previous desired speed. + Only for cases where we can know OR predefine the speed, we can do this. + */ + +#ifdef ARCH_ESP32 + currentClock = i2cBus->getClock(); +#elif defined(ARCH_NRF52) + // TODO add getClock function or return a predefined clock speed per variant? + return 0; +#elif defined(ARCH_RP2040) + // TODO add getClock function or return a predefined clock speed per variant + return 0; +#elif defined(ARCH_STM32WL) + // TODO add getClock function or return a predefined clock speed per variant + return 0; +#else + return 0; +#endif + + if (currentClock != desiredClock) { + LOG_DEBUG("Changing I2C clock to %u", desiredClock); + i2cBus->setClock(desiredClock); + } + return currentClock; +} +#endif diff --git a/src/graphics/EInkDisplay2.h b/src/graphics/EInkDisplay2.h index 9975527aa..f5418b069 100644 --- a/src/graphics/EInkDisplay2.h +++ b/src/graphics/EInkDisplay2.h @@ -9,6 +9,15 @@ #include "GxEPD2Multi.h" #endif +// Limit how often we push a full E-Ink refresh. T-Deck Pro needs faster updates for typing. +#ifndef EINK_FORCE_DISPLAY_THROTTLE_MS +#if defined(T_DECK_PRO) +#define EINK_FORCE_DISPLAY_THROTTLE_MS 200 +#else +#define EINK_FORCE_DISPLAY_THROTTLE_MS 1000 +#endif +#endif + /** * An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation. * @@ -42,7 +51,7 @@ class EInkDisplay : public OLEDDisplay * * @return true if we did draw the screen */ - virtual bool forceDisplay(uint32_t msecLimit = 1000); + virtual bool forceDisplay(uint32_t msecLimit = EINK_FORCE_DISPLAY_THROTTLE_MS); /** * Run any code needed to complete an update, after the physical refresh has completed. diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 28f17f962..8bf69b7a0 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -825,7 +825,7 @@ int32_t Screen::runOnce() #endif } #endif - if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) { + if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0 && !suppressRebootBanner) { showSimpleBanner("Rebooting...", 0); } diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 13e7d0dd2..5c459d984 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -2374,7 +2374,8 @@ void menuHandler::FrameToggles_menu() lora, clock, show_favorites, - show_telemetry, + show_env_telemetry, + show_aq_telemetry, show_power, enumEnd }; @@ -2419,8 +2420,11 @@ void menuHandler::FrameToggles_menu() optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites"; optionsEnumArray[options++] = show_favorites; - optionsArray[options] = moduleConfig.telemetry.environment_screen_enabled ? "Hide Telemetry" : "Show Telemetry"; - optionsEnumArray[options++] = show_telemetry; + optionsArray[options] = moduleConfig.telemetry.environment_screen_enabled ? "Hide Env. Telemetry" : "Show Env. Telemetry"; + optionsEnumArray[options++] = show_env_telemetry; + + optionsArray[options] = moduleConfig.telemetry.air_quality_screen_enabled ? "Hide AQ Telemetry" : "Show AQ Telemetry"; + optionsEnumArray[options++] = show_aq_telemetry; optionsArray[options] = moduleConfig.telemetry.power_screen_enabled ? "Hide Power" : "Show Power"; optionsEnumArray[options++] = show_power; @@ -2483,10 +2487,14 @@ void menuHandler::FrameToggles_menu() screen->toggleFrameVisibility("show_favorites"); menuHandler::menuQueue = menuHandler::FrameToggles; screen->runNow(); - } else if (selected == show_telemetry) { + } else if (selected == show_env_telemetry) { moduleConfig.telemetry.environment_screen_enabled = !moduleConfig.telemetry.environment_screen_enabled; menuHandler::menuQueue = menuHandler::FrameToggles; screen->runNow(); + } else if (selected == show_aq_telemetry) { + moduleConfig.telemetry.air_quality_screen_enabled = !moduleConfig.telemetry.air_quality_screen_enabled; + menuHandler::menuQueue = menuHandler::FrameToggles; + screen->runNow(); } else if (selected == show_power) { moduleConfig.telemetry.power_screen_enabled = !moduleConfig.telemetry.power_screen_enabled; menuHandler::menuQueue = menuHandler::FrameToggles; diff --git a/src/input/RotaryEncoderInterruptBase.cpp b/src/input/RotaryEncoderInterruptBase.cpp index c315f23d9..80ac08175 100644 --- a/src/input/RotaryEncoderInterruptBase.cpp +++ b/src/input/RotaryEncoderInterruptBase.cpp @@ -93,6 +93,8 @@ int32_t RotaryEncoderInterruptBase::runOnce() if (!pressDetected) { this->action = ROTARY_ACTION_NONE; + } else if (now - pressStartTime < LONG_PRESS_DURATION) { + return (20); // keep checking for long/short until time expires } return INT32_MAX; diff --git a/src/main.cpp b/src/main.cpp index d77767736..2961f6041 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -574,6 +574,7 @@ void setup() Wire.setSCL(I2C_SCL); Wire.begin(); #elif defined(I2C_SDA) && !defined(ARCH_RP2040) + LOG_INFO("Starting Bus with (SDA) %d and (SCL) %d: ", I2C_SDA, I2C_SCL); Wire.begin(I2C_SDA, I2C_SCL); #elif defined(ARCH_PORTDUINO) if (portduino_config.i2cdev != "") { @@ -758,11 +759,12 @@ void setup() scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310U, meshtastic_TelemetrySensorType_QMC6310); + //TODO: Types need to be added meshtastic_TelemetrySensorType_QMC6310N + // scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310N, meshtastic_TelemetrySensorType_QMC6310N); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102); @@ -1541,8 +1543,9 @@ void setup() } #endif -uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes) -uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client) +uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes) +uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client) +bool suppressRebootBanner; // If true, suppress "Rebooting..." overlay (used for OTA handoff) // If a thread does something that might need for it to be rescheduled ASAP it can set this flag // This will suppress the current delay and instead try to run ASAP. diff --git a/src/main.h b/src/main.h index 7ca14d825..c3528a63d 100644 --- a/src/main.h +++ b/src/main.h @@ -81,6 +81,7 @@ extern uint32_t timeLastPowered; extern uint32_t rebootAtMsec; extern uint32_t shutdownAtMsec; +extern bool suppressRebootBanner; extern uint32_t serialSinceMsec; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index c51c184c0..375bc76e3 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -53,7 +53,7 @@ #endif #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI -#include +#include #endif NodeDB *nodeDB = nullptr; @@ -756,8 +756,8 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) config.display.compass_orientation = COMPASS_ORIENTATION; #endif #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI - if (WiFiOTA::isUpdated()) { - WiFiOTA::recoverConfig(&config.network); + if (MeshtasticOTA::isUpdated()) { + MeshtasticOTA::recoverConfig(&config.network); } #endif diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index d4ef5bee4..d93f6fafa 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -66,7 +66,7 @@ typedef enum _meshtastic_Config_DeviceConfig_Role { but should not be given priority over other routers in order to avoid unnecessaraily consuming hops. */ meshtastic_Config_DeviceConfig_Role_ROUTER_LATE = 11, - /* Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT. + /* Description: Treats packets from or to favorited nodes as ROUTER_LATE, and all other packets as CLIENT. Technical Details: Used for stronger attic/roof nodes to distribute messages more widely from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node. */ diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index e0dd9c58b..68552ede5 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -298,6 +298,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_MESHSTICK_1262 = 121, /* LilyGo T-Beam 1W */ meshtastic_HardwareModel_TBEAM_1_WATT = 122, + /* LilyGo T5 S3 ePaper Pro (V1 and V2) */ + meshtastic_HardwareModel_T5_S3_EPAPER_PRO = 123, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 5eac64a62..990ca0f46 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -9,11 +9,8 @@ #include "meshUtils.h" #include #include // for better whitespace handling -#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH -#include "BleOta.h" -#endif #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI -#include "WiFiOTA.h" +#include "MeshtasticOTA.h" #endif #include "Router.h" #include "configuration.h" @@ -236,26 +233,27 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta reboot(r->reboot_seconds); break; } - case meshtastic_AdminMessage_reboot_ota_seconds_tag: { - int32_t s = r->reboot_ota_seconds; + case meshtastic_AdminMessage_ota_request_tag: { #if defined(ARCH_ESP32) -#if !MESHTASTIC_EXCLUDE_BLUETOOTH - if (!BleOta::getOtaAppVersion().isEmpty()) { - if (screen) - screen->startFirmwareUpdateScreen(); - BleOta::switchToOtaApp(); - LOG_INFO("Rebooting to BLE OTA"); + if (r->ota_request.ota_hash.size != 32) { + suppressRebootBanner = true; + LOG_INFO("OTA Failed: Invalid `ota_hash` provided"); + break; } -#endif -#if !MESHTASTIC_EXCLUDE_WIFI - if (WiFiOTA::trySwitchToOTA()) { + + meshtastic_OTAMode mode = r->ota_request.reboot_ota_mode; + if (MeshtasticOTA::trySwitchToOTA()) { + LOG_INFO("OTA Requested"); + suppressRebootBanner = true; if (screen) screen->startFirmwareUpdateScreen(); - WiFiOTA::saveConfig(&config.network); + MeshtasticOTA::saveConfig(&config.network, mode, r->ota_request.ota_hash.bytes); LOG_INFO("Rebooting to WiFi OTA"); + } else { + LOG_INFO("WIFI OTA Failed"); } #endif -#endif + int s = 1; // Reboot in 1 second, hard coded LOG_INFO("Reboot in %d seconds", s); rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); break; diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 63392f7e4..e17868baf 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -252,9 +252,9 @@ void setupModules() (moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) { new EnvironmentTelemetryModule(); } -#if __has_include("Adafruit_PM25AQI.h") - if (moduleConfig.has_telemetry && moduleConfig.telemetry.air_quality_enabled && - nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) { +#if HAS_TELEMETRY && HAS_SENSOR && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR + if (moduleConfig.has_telemetry && + (moduleConfig.telemetry.air_quality_enabled || moduleConfig.telemetry.air_quality_screen_enabled)) { new AirQualityTelemetryModule(); } #endif diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 21a563b9d..01f5da2c6 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -1,6 +1,6 @@ #include "configuration.h" -#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include("Adafruit_PM25AQI.h") +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "AirQualityTelemetry.h" @@ -10,27 +10,54 @@ #include "PowerFSM.h" #include "RTC.h" #include "Router.h" -#include "detect/ScanI2CTwoWire.h" +#include "Sensor/AddI2CSensorTemplate.h" +#include "UnitConversions.h" +#include "graphics/ScreenFonts.h" +#include "graphics/SharedUIDisplay.h" +#include "graphics/images.h" #include "main.h" +#include "sleep.h" #include -#ifndef PMSA003I_WARMUP_MS -// from the PMSA003I datasheet: -// "Stable data should be got at least 30 seconds after the sensor wakeup -// from the sleep mode because of the fan’s performance." -#define PMSA003I_WARMUP_MS 30000 -#endif +// Sensors +#include "Sensor/PMSA003ISensor.h" -int32_t AirQualityTelemetryModule::runOnce() +void AirQualityTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner) { + if (!moduleConfig.telemetry.air_quality_enabled && !AIR_QUALITY_TELEMETRY_MODULE_ENABLE) { + return; + } + LOG_INFO("Air Quality Telemetry adding I2C devices..."); + /* Uncomment the preferences below if you want to use the module without having to configure it from the PythonAPI or WebUI. + Note: this was previously on runOnce, which didnt take effect + as other modules already had already been initialized (screen) */ // moduleConfig.telemetry.air_quality_enabled = 1; + // moduleConfig.telemetry.air_quality_screen_enabled = 1; + // moduleConfig.telemetry.air_quality_interval = 15; - if (!(moduleConfig.telemetry.air_quality_enabled)) { + // order by priority of metrics/values (low top, high bottom) + addSensor(i2cScanner, ScanI2C::DeviceType::PMSA003I); +} + +int32_t AirQualityTelemetryModule::runOnce() +{ + if (sleepOnNextExecution == true) { + sleepOnNextExecution = false; + uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval, + default_telemetry_broadcast_interval_secs); + LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.", nightyNightMs); + doDeepSleep(nightyNightMs, true, false); + } + + uint32_t result = UINT32_MAX; + + if (!(moduleConfig.telemetry.air_quality_enabled || moduleConfig.telemetry.air_quality_screen_enabled || + AIR_QUALITY_TELEMETRY_MODULE_ENABLE)) { // If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it return disable(); } @@ -42,82 +69,152 @@ int32_t AirQualityTelemetryModule::runOnce() if (moduleConfig.telemetry.air_quality_enabled) { LOG_INFO("Air quality Telemetry: init"); -#ifdef PMSA003I_ENABLE_PIN - // put the sensor to sleep on startup - pinMode(PMSA003I_ENABLE_PIN, OUTPUT); - digitalWrite(PMSA003I_ENABLE_PIN, LOW); -#endif /* PMSA003I_ENABLE_PIN */ - - if (!aqi.begin_I2C()) { -#ifndef I2C_NO_RESCAN - LOG_WARN("Could not establish i2c connection to AQI sensor. Rescan"); - // rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty. - uint8_t i2caddr_scan[] = {PMSA0031_ADDR}; - uint8_t i2caddr_asize = 1; - auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire()); -#if defined(I2C_SDA1) - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize); -#endif - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize); - auto found = i2cScanner->find(ScanI2C::DeviceType::PMSA0031); - if (found.type != ScanI2C::DeviceType::NONE) { - nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address; - nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second = - i2cScanner->fetchI2CBus(found.address); - return setStartDelay(); - } -#endif - return disable(); + // check if we have at least one sensor + if (!sensors.empty()) { + result = DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } - return setStartDelay(); } - return disable(); + + // it's possible to have this module enabled, only for displaying values on the screen. + // therefore, we should only enable the sensor loop if measurement is also enabled + return result == UINT32_MAX ? disable() : setStartDelay(); } else { // if we somehow got to a second run of this module with measurement disabled, then just wait forever - if (!moduleConfig.telemetry.air_quality_enabled) - return disable(); - - switch (state) { -#ifdef PMSA003I_ENABLE_PIN - case State::IDLE: - // sensor is in standby; fire it up and sleep - LOG_DEBUG("runOnce(): state = idle"); - digitalWrite(PMSA003I_ENABLE_PIN, HIGH); - state = State::ACTIVE; - - return PMSA003I_WARMUP_MS; -#endif /* PMSA003I_ENABLE_PIN */ - case State::ACTIVE: - // sensor is already warmed up; grab telemetry and send it - LOG_DEBUG("runOnce(): state = active"); - - if (((lastSentToMesh == 0) || - !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( - moduleConfig.telemetry.air_quality_interval, - default_telemetry_broadcast_interval_secs, numOnlineNodes))) && - airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && - airTime->isTxAllowedAirUtil()) { - sendTelemetry(); - lastSentToMesh = millis(); - } else if (service->isToPhoneQueueEmpty()) { - // Just send to phone when it's not our time to send to mesh yet - // Only send while queue is empty (phone assumed connected) - sendTelemetry(NODENUM_BROADCAST, true); - } - -#ifdef PMSA003I_ENABLE_PIN - // put sensor back to sleep - digitalWrite(PMSA003I_ENABLE_PIN, LOW); - state = State::IDLE; -#endif /* PMSA003I_ENABLE_PIN */ - - return sendToPhoneIntervalMs; - default: + if (!moduleConfig.telemetry.air_quality_enabled && !AIR_QUALITY_TELEMETRY_MODULE_ENABLE) { return disable(); } + + // Wake up the sensors that need it + LOG_INFO("Waking up sensors"); + for (TelemetrySensor *sensor : sensors) { + if (!sensor->isActive()) { + return sensor->wakeUp(); + } + } + + if (((lastSentToMesh == 0) || + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.air_quality_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && + airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && + airTime->isTxAllowedAirUtil()) { + sendTelemetry(); + lastSentToMesh = millis(); + } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) && + (service->isToPhoneQueueEmpty())) { + // Just send to phone when it's not our time to send to mesh yet + // Only send while queue is empty (phone assumed connected) + sendTelemetry(NODENUM_BROADCAST, true); + lastSentToPhone = millis(); + } + + // Send to sleep sensors that consume power + LOG_INFO("Sending sensors to sleep"); + for (TelemetrySensor *sensor : sensors) { + sensor->sleep(); + } } + return min(sendToPhoneIntervalMs, result); } +bool AirQualityTelemetryModule::wantUIFrame() +{ + return moduleConfig.telemetry.air_quality_screen_enabled; +} + +#if HAS_SCREEN +void AirQualityTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // === Setup display === + display->clear(); + display->setFont(FONT_SMALL); + display->setTextAlignment(TEXT_ALIGN_LEFT); + int line = 1; + + // === Set Title + const char *titleStr = (graphics::currentResolution == graphics::ScreenResolution::High) ? "Air Quality" : "AQ."; + + // === Header === + graphics::drawCommonHeader(display, x, y, titleStr); + + // === Row spacing setup === + const int rowHeight = FONT_HEIGHT_SMALL - 4; + int currentY = graphics::getTextPositions(display)[line++]; + + // === Show "No Telemetry" if no data available === + if (!lastMeasurementPacket) { + display->drawString(x, currentY, "No Telemetry"); + return; + } + + // Decode the telemetry message from the latest received packet + const meshtastic_Data &p = lastMeasurementPacket->decoded; + meshtastic_Telemetry telemetry; + if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &telemetry)) { + display->drawString(x, currentY, "No Telemetry"); + return; + } + + const auto &m = telemetry.variant.air_quality_metrics; + + // Check if any telemetry field has valid data + bool hasAny = m.has_pm10_standard || m.has_pm25_standard || m.has_pm100_standard || m.has_pm10_environmental || + m.has_pm25_environmental || m.has_pm100_environmental; + + if (!hasAny) { + display->drawString(x, currentY, "No Telemetry"); + return; + } + + // === First line: Show sender name + time since received (left), and first metric (right) === + const char *sender = getSenderShortName(*lastMeasurementPacket); + uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket); + String agoStr = (agoSecs > 864000) ? "?" + : (agoSecs > 3600) ? String(agoSecs / 3600) + "h" + : (agoSecs > 60) ? String(agoSecs / 60) + "m" + : String(agoSecs) + "s"; + + String leftStr = String(sender) + " (" + agoStr + ")"; + display->drawString(x, currentY, leftStr); // Left side: who and when + + // === Collect sensor readings as label strings (no icons) === + std::vector entries; + + if (m.has_pm10_standard) + entries.push_back("PM1: " + String(m.pm10_standard) + "ug/m3"); + if (m.has_pm25_standard) + entries.push_back("PM2.5: " + String(m.pm25_standard) + "ug/m3"); + if (m.has_pm100_standard) + entries.push_back("PM10: " + String(m.pm100_standard) + "ug/m3"); + + // === Show first available metric on top-right of first line === + if (!entries.empty()) { + String valueStr = entries.front(); + int rightX = SCREEN_WIDTH - display->getStringWidth(valueStr); + display->drawString(rightX, currentY, valueStr); + entries.erase(entries.begin()); // Remove from queue + } + + // === Advance to next line for remaining telemetry entries === + currentY += rowHeight; + + // === Draw remaining entries in 2-column format (left and right) === + for (size_t i = 0; i < entries.size(); i += 2) { + // Left column + display->drawString(x, currentY, entries[i]); + + // Right column if it exists + if (i + 1 < entries.size()) { + int rightX = SCREEN_WIDTH / 2; + display->drawString(rightX, currentY, entries[i + 1]); + } + + currentY += rowHeight; + } + graphics::drawCommonFooter(display, x, y); +} +#endif + bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) { if (t->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { @@ -144,35 +241,21 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m) { - if (!aqi.read(&data)) { - LOG_WARN("Skip send measurements. Could not read AQIn"); - return false; - } - + bool valid = true; + bool hasSensor = false; m->time = getTime(); m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag; - m->variant.air_quality_metrics.has_pm10_standard = true; - m->variant.air_quality_metrics.pm10_standard = data.pm10_standard; - m->variant.air_quality_metrics.has_pm25_standard = true; - m->variant.air_quality_metrics.pm25_standard = data.pm25_standard; - m->variant.air_quality_metrics.has_pm100_standard = true; - m->variant.air_quality_metrics.pm100_standard = data.pm100_standard; + m->variant.air_quality_metrics = meshtastic_AirQualityMetrics_init_zero; - m->variant.air_quality_metrics.has_pm10_environmental = true; - m->variant.air_quality_metrics.pm10_environmental = data.pm10_env; - m->variant.air_quality_metrics.has_pm25_environmental = true; - m->variant.air_quality_metrics.pm25_environmental = data.pm25_env; - m->variant.air_quality_metrics.has_pm100_environmental = true; - m->variant.air_quality_metrics.pm100_environmental = data.pm100_env; + // TODO - Should we check for sensor state here? + // If a sensor is sleeping, we should know and check to wake it up + for (TelemetrySensor *sensor : sensors) { + LOG_INFO("Reading AQ sensors"); + valid = valid && sensor->getMetrics(m); + hasSensor = true; + } - LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard, - m->variant.air_quality_metrics.pm25_standard, m->variant.air_quality_metrics.pm100_standard); - - LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i", - m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental, - m->variant.air_quality_metrics.pm100_environmental); - - return true; + return valid && hasSensor; } meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply() @@ -206,7 +289,15 @@ meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply() bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; + m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag; + m.time = getTime(); if (getAirQualityTelemetry(&m)) { + LOG_INFO("Send: pm10_standard=%u, pm25_standard=%u, pm100_standard=%u, \ + pm10_environmental=%u, pm25_environmental=%u, pm100_environmental=%u", + m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard, + m.variant.air_quality_metrics.pm100_standard, m.variant.air_quality_metrics.pm10_environmental, + m.variant.air_quality_metrics.pm25_environmental, m.variant.air_quality_metrics.pm100_environmental); + meshtastic_MeshPacket *p = allocDataProtobuf(m); p->to = dest; p->decoded.want_response = false; @@ -221,16 +312,44 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { - LOG_INFO("Send packet to phone"); + LOG_INFO("Sending packet to phone"); service->sendToPhone(p); } else { - LOG_INFO("Send packet to mesh"); + LOG_INFO("Sending packet to mesh"); service->sendToMesh(p, RX_SRC_LOCAL, true); + + if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { + meshtastic_ClientNotification *notification = clientNotificationPool.allocZeroed(); + notification->level = meshtastic_LogRecord_Level_INFO; + notification->time = getValidTime(RTCQualityFromNet); + sprintf(notification->message, "Sending telemetry and sleeping for %us interval in a moment", + Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval, + default_telemetry_broadcast_interval_secs) / + 1000U); + service->sendClientNotification(notification); + sleepOnNextExecution = true; + LOG_DEBUG("Start next execution in 5s, then sleep"); + setIntervalFromNow(FIVE_SECONDS_MS); + } } return true; } - return false; } +AdminMessageHandleResult AirQualityTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp, + meshtastic_AdminMessage *request, + meshtastic_AdminMessage *response) +{ + AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED; + + for (TelemetrySensor *sensor : sensors) { + result = sensor->handleAdminMessage(mp, request, response); + if (result != AdminMessageHandleResult::NOT_HANDLED) + return result; + } + + return result; +} + #endif \ No newline at end of file diff --git a/src/modules/Telemetry/AirQualityTelemetry.h b/src/modules/Telemetry/AirQualityTelemetry.h index 0142ee686..2b88b74ba 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.h +++ b/src/modules/Telemetry/AirQualityTelemetry.h @@ -1,14 +1,23 @@ #include "configuration.h" -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include("Adafruit_PM25AQI.h") +#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #pragma once + +#ifndef AIR_QUALITY_TELEMETRY_MODULE_ENABLE +#define AIR_QUALITY_TELEMETRY_MODULE_ENABLE 0 +#endif + #include "../mesh/generated/meshtastic/telemetry.pb.h" -#include "Adafruit_PM25AQI.h" #include "NodeDB.h" #include "ProtobufModule.h" +#include "detect/ScanI2CConsumer.h" +#include +#include -class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule +class AirQualityTelemetryModule : private concurrency::OSThread, + public ScanI2CConsumer, + public ProtobufModule { CallbackObserver nodeStatusObserver = CallbackObserver(this, @@ -16,22 +25,19 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf public: AirQualityTelemetryModule() - : concurrency::OSThread("AirQualityTelemetry"), + : concurrency::OSThread("AirQualityTelemetry"), ScanI2CConsumer(), ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { lastMeasurementPacket = nullptr; - setIntervalFromNow(10 * 1000); - aqi = Adafruit_PM25AQI(); nodeStatusObserver.observe(&nodeStatus->onNewStatus); - -#ifdef PMSA003I_ENABLE_PIN - // the PMSA003I sensor uses about 300mW on its own; support powering it off when it's not actively taking - // a reading - state = State::IDLE; -#else - state = State::ACTIVE; -#endif + setIntervalFromNow(10 * 1000); } + virtual bool wantUIFrame() override; +#if !HAS_SCREEN + void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); +#else + virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override; +#endif protected: /** Called to handle a particular incoming message @@ -49,19 +55,17 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf */ bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - private: - enum State { - IDLE = 0, - ACTIVE = 1, - }; + virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp, + meshtastic_AdminMessage *request, + meshtastic_AdminMessage *response) override; + void i2cScanFinished(ScanI2C *i2cScanner); - State state; - Adafruit_PM25AQI aqi; - PM25_AQI_Data data = {0}; + private: bool firstTime = true; meshtastic_MeshPacket *lastMeasurementPacket; uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute uint32_t lastSentToMesh = 0; + uint32_t lastSentToPhone = 0; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 843d7b8d5..ec6fe4799 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -141,37 +141,10 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true +#include "Sensor/AddI2CSensorTemplate.h" #include "graphics/ScreenFonts.h" #include -#include - -static std::forward_list sensors; - -template void addSensor(ScanI2C *i2cScanner, ScanI2C::DeviceType type) -{ - ScanI2C::FoundDevice dev = i2cScanner->find(type); - if (dev.type != ScanI2C::DeviceType::NONE || type == ScanI2C::DeviceType::NONE) { - TelemetrySensor *sensor = new T(); -#if WIRE_INTERFACES_COUNT > 1 - TwoWire *bus = ScanI2CTwoWire::fetchI2CBus(dev.address); - if (dev.address.port != ScanI2C::I2CPort::WIRE1 && sensor->onlyWire1()) { - // This sensor only works on Wire (Wire1 is not supported) - delete sensor; - return; - } -#else - TwoWire *bus = &Wire; -#endif - if (sensor->initDevice(bus, &dev)) { - sensors.push_front(sensor); - return; - } - // destroy sensor - delete sensor; - } -} - void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner) { if (!moduleConfig.telemetry.environment_measurement_enabled && !ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) { @@ -642,8 +615,6 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) LOG_INFO("Send: soil_temperature=%f, soil_moisture=%u", m.variant.environment_metrics.soil_temperature, m.variant.environment_metrics.soil_moisture); - sensor_read_error_count = 0; - meshtastic_MeshPacket *p = allocDataProtobuf(m); p->to = dest; p->decoded.want_response = false; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h index 6e4ce82e7..049ed6b77 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.h +++ b/src/modules/Telemetry/EnvironmentTelemetry.h @@ -67,7 +67,6 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute uint32_t lastSentToMesh = 0; uint32_t lastSentToPhone = 0; - uint32_t sensor_read_error_count = 0; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h b/src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h new file mode 100644 index 000000000..37d909d71 --- /dev/null +++ b/src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h @@ -0,0 +1,34 @@ +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR + +#include "TelemetrySensor.h" +#include "detect/ScanI2C.h" +#include "detect/ScanI2CTwoWire.h" +#include +#include + +static std::forward_list sensors; + +template void addSensor(ScanI2C *i2cScanner, ScanI2C::DeviceType type) +{ + ScanI2C::FoundDevice dev = i2cScanner->find(type); + if (dev.type != ScanI2C::DeviceType::NONE || type == ScanI2C::DeviceType::NONE) { + TelemetrySensor *sensor = new T(); +#if WIRE_INTERFACES_COUNT > 1 + TwoWire *bus = ScanI2CTwoWire::fetchI2CBus(dev.address); + if (dev.address.port != ScanI2C::I2CPort::WIRE1 && sensor->onlyWire1()) { + // This sensor only works on Wire (Wire1 is not supported) + delete sensor; + return; + } +#else + TwoWire *bus = &Wire; +#endif + if (sensor->initDevice(bus, &dev)) { + sensors.push_front(sensor); + return; + } + // destroy sensor + delete sensor; + } +} +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/PMSA003ISensor.cpp b/src/modules/Telemetry/Sensor/PMSA003ISensor.cpp new file mode 100644 index 000000000..2225a4d87 --- /dev/null +++ b/src/modules/Telemetry/Sensor/PMSA003ISensor.cpp @@ -0,0 +1,158 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR + +#include "../detect/reClockI2C.h" +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "PMSA003ISensor.h" +#include "TelemetrySensor.h" + +#include + +PMSA003ISensor::PMSA003ISensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PMSA003I, "PMSA003I") {} + +bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) +{ + LOG_INFO("Init sensor: %s", sensorName); +#ifdef PMSA003I_ENABLE_PIN + pinMode(PMSA003I_ENABLE_PIN, OUTPUT); +#endif + + _bus = bus; + _address = dev->address.address; + +#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) + uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus); + if (!currentClock) { + LOG_WARN("PMSA003I can't be used at this clock speed"); + return false; + } +#endif + + _bus->beginTransmission(_address); + if (_bus->endTransmission() != 0) { + LOG_WARN("PMSA003I not found on I2C at 0x12"); + return false; + } + +#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) + reClockI2C(currentClock, _bus); +#endif + + status = 1; + LOG_INFO("PMSA003I Enabled"); + + initI2CSensor(); + return true; +} + +bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement) +{ + if (!isActive()) { + LOG_WARN("PMSA003I is not active"); + return false; + } + +#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) + uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus); +#endif + + _bus->requestFrom(_address, PMSA003I_FRAME_LENGTH); + if (_bus->available() < PMSA003I_FRAME_LENGTH) { + LOG_WARN("PMSA003I read failed: incomplete data (%d bytes)", _bus->available()); + return false; + } + +#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) + reClockI2C(currentClock, _bus); +#endif + + for (uint8_t i = 0; i < PMSA003I_FRAME_LENGTH; i++) { + buffer[i] = _bus->read(); + } + + if (buffer[0] != 0x42 || buffer[1] != 0x4D) { + LOG_WARN("PMSA003I frame header invalid: 0x%02X 0x%02X", buffer[0], buffer[1]); + return false; + } + + auto read16 = [](uint8_t *data, uint8_t idx) -> uint16_t { return (data[idx] << 8) | data[idx + 1]; }; + + computedChecksum = 0; + + for (uint8_t i = 0; i < PMSA003I_FRAME_LENGTH - 2; i++) { + computedChecksum += buffer[i]; + } + receivedChecksum = read16(buffer, PMSA003I_FRAME_LENGTH - 2); + + if (computedChecksum != receivedChecksum) { + LOG_WARN("PMSA003I checksum failed: computed 0x%04X, received 0x%04X", computedChecksum, receivedChecksum); + return false; + } + + measurement->variant.air_quality_metrics.has_pm10_standard = true; + measurement->variant.air_quality_metrics.pm10_standard = read16(buffer, 4); + + measurement->variant.air_quality_metrics.has_pm25_standard = true; + measurement->variant.air_quality_metrics.pm25_standard = read16(buffer, 6); + + measurement->variant.air_quality_metrics.has_pm100_standard = true; + measurement->variant.air_quality_metrics.pm100_standard = read16(buffer, 8); + + // TODO - Add admin command to remove environmental metrics to save protobuf space + measurement->variant.air_quality_metrics.has_pm10_environmental = true; + measurement->variant.air_quality_metrics.pm10_environmental = read16(buffer, 10); + + measurement->variant.air_quality_metrics.has_pm25_environmental = true; + measurement->variant.air_quality_metrics.pm25_environmental = read16(buffer, 12); + + measurement->variant.air_quality_metrics.has_pm100_environmental = true; + measurement->variant.air_quality_metrics.pm100_environmental = read16(buffer, 14); + + // TODO - Add admin command to remove PN to save protobuf space + measurement->variant.air_quality_metrics.has_particles_03um = true; + measurement->variant.air_quality_metrics.particles_03um = read16(buffer, 16); + + measurement->variant.air_quality_metrics.has_particles_05um = true; + measurement->variant.air_quality_metrics.particles_05um = read16(buffer, 18); + + measurement->variant.air_quality_metrics.has_particles_10um = true; + measurement->variant.air_quality_metrics.particles_10um = read16(buffer, 20); + + measurement->variant.air_quality_metrics.has_particles_25um = true; + measurement->variant.air_quality_metrics.particles_25um = read16(buffer, 22); + + measurement->variant.air_quality_metrics.has_particles_50um = true; + measurement->variant.air_quality_metrics.particles_50um = read16(buffer, 24); + + measurement->variant.air_quality_metrics.has_particles_100um = true; + measurement->variant.air_quality_metrics.particles_100um = read16(buffer, 26); + + return true; +} + +bool PMSA003ISensor::isActive() +{ + return state == State::ACTIVE; +} + +void PMSA003ISensor::sleep() +{ +#ifdef PMSA003I_ENABLE_PIN + digitalWrite(PMSA003I_ENABLE_PIN, LOW); + state = State::IDLE; +#endif +} + +uint32_t PMSA003ISensor::wakeUp() +{ +#ifdef PMSA003I_ENABLE_PIN + LOG_INFO("Waking up PMSA003I"); + digitalWrite(PMSA003I_ENABLE_PIN, HIGH); + state = State::ACTIVE; + return PMSA003I_WARMUP_MS; +#endif + // No need to wait for warmup if already active + return 0; +} +#endif diff --git a/src/modules/Telemetry/Sensor/PMSA003ISensor.h b/src/modules/Telemetry/Sensor/PMSA003ISensor.h new file mode 100644 index 000000000..09b43d620 --- /dev/null +++ b/src/modules/Telemetry/Sensor/PMSA003ISensor.h @@ -0,0 +1,35 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" + +#define PMSA003I_I2C_CLOCK_SPEED 100000 +#define PMSA003I_FRAME_LENGTH 32 +#define PMSA003I_WARMUP_MS 30000 + +class PMSA003ISensor : public TelemetrySensor +{ + public: + PMSA003ISensor(); + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; + + virtual bool isActive() override; + virtual void sleep() override; + virtual uint32_t wakeUp() override; + + private: + enum class State { IDLE, ACTIVE }; + State state = State::ACTIVE; + + uint16_t computedChecksum = 0; + uint16_t receivedChecksum = 0; + + uint8_t buffer[PMSA003I_FRAME_LENGTH]{}; + TwoWire *_bus{}; + uint8_t _address{}; +}; + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/TelemetrySensor.cpp b/src/modules/Telemetry/Sensor/TelemetrySensor.cpp index d6e7d1fac..f854cb5fe 100644 --- a/src/modules/Telemetry/Sensor/TelemetrySensor.cpp +++ b/src/modules/Telemetry/Sensor/TelemetrySensor.cpp @@ -1,6 +1,6 @@ #include "configuration.h" -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "NodeDB.h" diff --git a/src/modules/Telemetry/Sensor/TelemetrySensor.h b/src/modules/Telemetry/Sensor/TelemetrySensor.h index 3c3e61808..af51ddfad 100644 --- a/src/modules/Telemetry/Sensor/TelemetrySensor.h +++ b/src/modules/Telemetry/Sensor/TelemetrySensor.h @@ -58,6 +58,11 @@ class TelemetrySensor // TODO: delete after migration bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; } + // Functions to sleep / wakeup sensors that support it + virtual void sleep(){}; + virtual uint32_t wakeUp() { return 0; } + // Return active by default, override per sensor + virtual bool isActive() { return true; } #if WIRE_INTERFACES_COUNT > 1 // Set to true if Implementation only works first I2C port (Wire) @@ -65,6 +70,7 @@ class TelemetrySensor #endif virtual int32_t runOnce() { return INT32_MAX; } virtual bool isInitialized() { return initialized; } + // TODO: is this used? virtual bool isRunning() { return status > 0; } virtual bool getMetrics(meshtastic_Telemetry *measurement) = 0; diff --git a/src/platform/esp32/BleOta.cpp b/src/platform/esp32/BleOta.cpp deleted file mode 100644 index 698336f69..000000000 --- a/src/platform/esp32/BleOta.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "BleOta.h" -#include "Arduino.h" -#include - -static const String MESHTASTIC_OTA_APP_PROJECT_NAME("Meshtastic-OTA"); - -const esp_partition_t *BleOta::findEspOtaAppPartition() -{ - const esp_partition_t *part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, nullptr); - - esp_app_desc_t app_desc; - esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc)); - - if (ret != ESP_OK || MESHTASTIC_OTA_APP_PROJECT_NAME != app_desc.project_name) { - part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, nullptr); - ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc)); - } - - if (ret == ESP_OK && MESHTASTIC_OTA_APP_PROJECT_NAME == app_desc.project_name) { - return part; - } else { - return nullptr; - } -} - -String BleOta::getOtaAppVersion() -{ - const esp_partition_t *part = findEspOtaAppPartition(); - esp_app_desc_t app_desc; - esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc)); - String version; - if (ret == ESP_OK) { - version = app_desc.version; - } - return version; -} - -bool BleOta::switchToOtaApp() -{ - bool success = false; - const esp_partition_t *part = findEspOtaAppPartition(); - if (part) { - success = (ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_set_boot_partition(part)) == ESP_OK); - } - return success; -} \ No newline at end of file diff --git a/src/platform/esp32/BleOta.h b/src/platform/esp32/BleOta.h deleted file mode 100644 index f4c510920..000000000 --- a/src/platform/esp32/BleOta.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef BLEOTA_H -#define BLEOTA_H - -#include -#include - -class BleOta -{ - public: - explicit BleOta(){}; - - static String getOtaAppVersion(); - static bool switchToOtaApp(); - - private: - String mUserAgent; - static const esp_partition_t *findEspOtaAppPartition(); -}; - -#endif // BLEOTA_H \ No newline at end of file diff --git a/src/platform/esp32/WiFiOTA.cpp b/src/platform/esp32/MeshtasticOTA.cpp similarity index 77% rename from src/platform/esp32/WiFiOTA.cpp rename to src/platform/esp32/MeshtasticOTA.cpp index 4cf157b4c..b8cb052ef 100644 --- a/src/platform/esp32/WiFiOTA.cpp +++ b/src/platform/esp32/MeshtasticOTA.cpp @@ -1,13 +1,13 @@ -#include "WiFiOTA.h" +#include "MeshtasticOTA.h" #include "configuration.h" #include #include -namespace WiFiOTA +namespace MeshtasticOTA { -static const char *nvsNamespace = "ota-wifi"; -static const char *appProjectName = "OTA-WiFi"; +static const char *nvsNamespace = "MeshtasticOTA"; +static const char *appProjectName = "MeshtasticOTA"; static bool updated = false; @@ -43,12 +43,14 @@ void recoverConfig(meshtastic_Config_NetworkConfig *network) strncpy(network->wifi_psk, psk.c_str(), sizeof(network->wifi_psk)); } -void saveConfig(meshtastic_Config_NetworkConfig *network) +void saveConfig(meshtastic_Config_NetworkConfig *network, meshtastic_OTAMode method, uint8_t *ota_hash) { LOG_INFO("Saving WiFi settings for upcoming OTA update"); Preferences prefs; prefs.begin(nvsNamespace); + prefs.putUChar("method", method); + prefs.putBytes("ota_hash", ota_hash, 32); prefs.putString("ssid", network->wifi_ssid); prefs.putString("psk", network->wifi_psk); prefs.putBool("updated", false); @@ -62,10 +64,14 @@ const esp_partition_t *getAppPartition() bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc) { - if (esp_ota_get_partition_description(part, app_desc) != ESP_OK) + if (esp_ota_get_partition_description(part, app_desc) != ESP_OK) { + LOG_INFO("esp_ota_get_partition_description failed"); return false; - if (strcmp(app_desc->project_name, appProjectName) != 0) + } + if (strcmp(app_desc->project_name, appProjectName) != 0) { + LOG_INFO("app_desc->project_name == 0"); return false; + } return true; } @@ -89,4 +95,4 @@ const char *getVersion() return app_desc.version; } -} // namespace WiFiOTA +} // namespace MeshtasticOTA diff --git a/src/platform/esp32/MeshtasticOTA.h b/src/platform/esp32/MeshtasticOTA.h new file mode 100644 index 000000000..001eba039 --- /dev/null +++ b/src/platform/esp32/MeshtasticOTA.h @@ -0,0 +1,18 @@ +#ifndef MESHTASTICOTA_H +#define MESHTASTICOTA_H + +#include "mesh-pb-constants.h" +#include + +namespace MeshtasticOTA +{ +void initialize(); +bool isUpdated(); + +void recoverConfig(meshtastic_Config_NetworkConfig *network); +void saveConfig(meshtastic_Config_NetworkConfig *network, meshtastic_OTAMode method, uint8_t *ota_hash); +bool trySwitchToOTA(); +const char *getVersion(); +} // namespace MeshtasticOTA + +#endif // MESHTASTICOTA_H diff --git a/src/platform/esp32/WiFiOTA.h b/src/platform/esp32/WiFiOTA.h deleted file mode 100644 index 5a7ee348a..000000000 --- a/src/platform/esp32/WiFiOTA.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef WIFIOTA_H -#define WIFIOTA_H - -#include "mesh-pb-constants.h" -#include - -namespace WiFiOTA -{ -void initialize(); -bool isUpdated(); - -void recoverConfig(meshtastic_Config_NetworkConfig *network); -void saveConfig(meshtastic_Config_NetworkConfig *network); -bool trySwitchToOTA(); -const char *getVersion(); -} // namespace WiFiOTA - -#endif // WIFIOTA_H diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 760964119..6667acf5c 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -5,11 +5,10 @@ #include "main.h" #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH -#include "BleOta.h" #include "nimble/NimbleBluetooth.h" #endif -#include +#include #if HAS_WIFI #include "mesh/wifi/WiFiAPClient.h" @@ -144,22 +143,14 @@ void esp32Setup() preferences.putUInt("hwVendor", HW_VENDOR); preferences.end(); LOG_DEBUG("Number of Device Reboots: %d", rebootCounter); -#if !MESHTASTIC_EXCLUDE_BLUETOOTH - String BLEOTA = BleOta::getOtaAppVersion(); - if (BLEOTA.isEmpty()) { - LOG_INFO("No BLE OTA firmware available"); - } else { - LOG_INFO("BLE OTA firmware version %s", BLEOTA.c_str()); - } -#endif #if !MESHTASTIC_EXCLUDE_WIFI - String version = WiFiOTA::getVersion(); + String version = MeshtasticOTA::getVersion(); if (version.isEmpty()) { - LOG_INFO("No WiFi OTA firmware available"); + LOG_INFO("MeshtasticOTA firmware not available"); } else { - LOG_INFO("WiFi OTA firmware version %s", version.c_str()); + LOG_INFO("MeshtasticOTA firmware version %s", version.c_str()); } - WiFiOTA::initialize(); + MeshtasticOTA::initialize(); #endif // enableModemSleep(); diff --git a/variants/esp32/esp32-common.ini b/variants/esp32/esp32-common.ini index 81a49223b..3ee2b9516 100644 --- a/variants/esp32/esp32-common.ini +++ b/variants/esp32/esp32-common.ini @@ -49,6 +49,7 @@ build_flags = -DLIBPAX_BLE -DHAS_UDP_MULTICAST=1 ;-DDEBUG_HEAP + -DCAN_RECLOCK_I2C lib_deps = ${arduino_base.lib_deps} diff --git a/variants/esp32/heltec_wireless_bridge/platformio.ini b/variants/esp32/heltec_wireless_bridge/platformio.ini index 93c3e3394..6f9de7a84 100644 --- a/variants/esp32/heltec_wireless_bridge/platformio.ini +++ b/variants/esp32/heltec_wireless_bridge/platformio.ini @@ -1,9 +1,9 @@ [env:heltec-wireless-bridge] -;build_type = debug ; to make it possible to step through our jtag debugger +;build_type = debug ; to make it possible to step through our jtag debugger extends = esp32_base board_level = extra board = heltec_wifi_lora_32 -build_flags = +build_flags = ${esp32_base.build_flags} -I variants/esp32/heltec_wireless_bridge -D HELTEC_WIRELESS_BRIDGE @@ -13,6 +13,7 @@ build_flags = -D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1 -D MESHTASTIC_EXCLUDE_DETECTIONSENSOR=1 -D MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1 + -D MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR=1 -D MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL=1 -D MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION=1 -D MESHTASTIC_EXCLUDE_GPS=1 diff --git a/variants/esp32s3/elecrow_panel/platformio.ini b/variants/esp32s3/elecrow_panel/platformio.ini index 2033ccb59..e0f6f0760 100644 --- a/variants/esp32s3/elecrow_panel/platformio.ini +++ b/variants/esp32s3/elecrow_panel/platformio.ini @@ -44,7 +44,7 @@ lib_deps = ${esp32s3_base.lib_deps} # renovate: datasource=custom.pio depName=ESP8266Audio packageName=earlephilhower/library/ESP8266Audio earlephilhower/ESP8266Audio@1.9.9 # renovate: datasource=custom.pio depName=ESP8266SAM packageName=earlephilhower/library/ESP8266SAM - earlephilhower/ESP8266SAM@1.0.1 + earlephilhower/ESP8266SAM@1.1.0 # renovate: datasource=custom.pio depName=TCA9534 packageName=hideakitai/library/TCA9534 hideakitai/TCA9534@0.1.1 lovyan03/LovyanGFX@1.2.0 ; note: v1.2.7 breaks the elecrow 7" display functionality diff --git a/variants/esp32s3/t-watch-s3/variant.h b/variants/esp32s3/t-watch-s3/variant.h index dfd219391..216dda589 100644 --- a/variants/esp32s3/t-watch-s3/variant.h +++ b/variants/esp32s3/t-watch-s3/variant.h @@ -53,10 +53,13 @@ #define HAS_BMA423 1 #define BMA4XX_INT 14 // Interrupt for BMA_423 axis sensor +#define HAS_GPS 1 #define GPS_DEFAULT_NOT_PRESENT 1 #define GPS_BAUDRATE 38400 -#define GPS_RX_PIN 42 -#define GPS_TX_PIN 41 +#define GPS_RX_PIN 41 +#define GPS_TX_PIN 42 + +#define BUTTON_PIN 0 // only for Plus version #define USE_SX1262 #define USE_SX1268 diff --git a/variants/esp32s3/tlora-pager/platformio.ini b/variants/esp32s3/tlora-pager/platformio.ini index 3a7afb016..5973db1d0 100644 --- a/variants/esp32s3/tlora-pager/platformio.ini +++ b/variants/esp32s3/tlora-pager/platformio.ini @@ -33,7 +33,7 @@ lib_deps = ${esp32s3_base.lib_deps} # renovate: datasource=custom.pio depName=ESP8266Audio packageName=earlephilhower/library/ESP8266Audio earlephilhower/ESP8266Audio@1.9.9 # renovate: datasource=custom.pio depName=ESP8266SAM packageName=earlephilhower/library/ESP8266SAM - earlephilhower/ESP8266SAM@1.0.1 + earlephilhower/ESP8266SAM@1.1.0 # renovate: datasource=custom.pio depName=Adafruit DRV2605 packageName=adafruit/library/Adafruit DRV2605 Library adafruit/Adafruit DRV2605 Library@1.2.4 # renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library @@ -41,7 +41,7 @@ lib_deps = ${esp32s3_base.lib_deps} # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib lewisxhe/SensorLib@0.3.3 # renovate: datasource=github-tags depName=pschatzmann_arduino-audio-driver packageName=pschatzmann/arduino-audio-driver - https://github.com/pschatzmann/arduino-audio-driver/archive/v0.1.3.zip + https://github.com/pschatzmann/arduino-audio-driver/archive/v0.2.0.zip # TODO renovate https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip # TODO renovate diff --git a/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h b/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h index 984f967d8..e46391207 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h +++ b/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h @@ -134,12 +134,14 @@ static const uint8_t A0 = PIN_A0; #define BATTERY_SENSE_RESOLUTION_BITS 12 #define BATTERY_SENSE_RESOLUTION 4096.0 #undef AREF_VOLTAGE -#define AREF_VOLTAGE 3.0 -#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define AREF_VOLTAGE 2.4 +#define VBAT_AR_INTERNAL AR_INTERNAL_2_4 #define ADC_MULTIPLIER (1.75F) #define HAS_SOLAR +#define OCV_ARRAY 4080, 3990, 3935, 3880, 3825, 3770, 3715, 3660, 3605, 3550, 3450 + #ifdef __cplusplus } #endif diff --git a/variants/stm32/CDEBYTE_E77-MBL/platformio.ini b/variants/stm32/CDEBYTE_E77-MBL/platformio.ini index c5af9a4a4..b4c0c958f 100644 --- a/variants/stm32/CDEBYTE_E77-MBL/platformio.ini +++ b/variants/stm32/CDEBYTE_E77-MBL/platformio.ini @@ -13,6 +13,7 @@ build_flags = -DPIN_SERIAL1_RX=PB7 -DPIN_SERIAL1_TX=PB6 -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1 + -DMESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR=1 -DMESHTASTIC_EXCLUDE_I2C=1 -DMESHTASTIC_EXCLUDE_GPS=1 diff --git a/variants/stm32/rak3172/platformio.ini b/variants/stm32/rak3172/platformio.ini index b9a4b8a04..4d96e98f9 100644 --- a/variants/stm32/rak3172/platformio.ini +++ b/variants/stm32/rak3172/platformio.ini @@ -12,6 +12,7 @@ build_flags = -DPIN_WIRE_SDA=PA11 -DPIN_WIRE_SCL=PA12 -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1 + -DMESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR=1 -DMESHTASTIC_EXCLUDE_I2C=1 -DMESHTASTIC_EXCLUDE_GPS=1