From 7f5184281d6d8a48161b664d3b053ae2da7a20c6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 11 May 2026 16:09:33 -0500 Subject: [PATCH 1/7] Make power status logging less chatty and track battery presence transitions (#10453) --- src/Power.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Power.cpp b/src/Power.cpp index 1ea3a64c2..f752e9461 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -878,7 +878,16 @@ void Power::readPowerStatus() // Notify any status instances that are observing us const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent); - if (millis() > lastLogTime + 50 * 1000) { + + // Log battery-presence transitions once; skip OptUnknown so we don't lie before the first probe. + static OptionalBool prevHasBattery = OptUnknown; + if (hasBattery != OptUnknown && hasBattery != prevHasBattery) { + LOG_INFO("Power: battery hardware %s", hasBattery == OptTrue ? "detected" : "absent (USB-only)"); + prevHasBattery = hasBattery; + } + + // Periodic telemetry only emits when a battery is actually present (otherwise values are constant -1/0). + if (hasBattery == OptTrue && !Throttle::isWithinTimespanMs(lastLogTime, 50 * 1000)) { LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); lastLogTime = millis(); From cd5d608e8dd9122bf5336ee07e13e38aa1825d23 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 06:07:09 -0500 Subject: [PATCH 2/7] Upgrade trunk (#10461) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 00649f7ff..ad264bd76 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -11,7 +11,7 @@ lint: - checkov@3.2.528 - renovate@43.150.0 - prettier@3.8.3 - - trufflehog@3.95.2 + - trufflehog@3.95.3 - yamllint@1.38.0 - bandit@1.9.4 - trivy@0.70.0 From 0a7b3c723e7abe021e9d01b0547c193cd74139ce Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 10:57:48 +0200 Subject: [PATCH 3/7] Update NeoPixel to v1.15.5 (#10466) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 71c362a79..d872780e5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -138,7 +138,7 @@ lib_deps = # renovate: datasource=github-tags depName=Adafruit GFX packageName=adafruit/Adafruit-GFX-Library https://github.com/adafruit/Adafruit-GFX-Library/archive/refs/tags/1.12.6.zip # renovate: datasource=github-tags depName=NeoPixel packageName=adafruit/Adafruit_NeoPixel - https://github.com/adafruit/Adafruit_NeoPixel/archive/refs/tags/1.15.4.zip + https://github.com/adafruit/Adafruit_NeoPixel/archive/1.15.5.zip # renovate: datasource=github-tags depName=Adafruit SSD1306 packageName=adafruit/Adafruit_SSD1306 https://github.com/adafruit/Adafruit_SSD1306/archive/refs/tags/2.5.16.zip # renovate: datasource=github-tags depName=Adafruit BMP280 packageName=adafruit/Adafruit_BMP280_Library From 59025e4820c25bb764a2d6644f429e3afc20af3a Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 13 May 2026 08:07:24 -0500 Subject: [PATCH 4/7] Add initial support for Station G3 variant (#10457) * Add initial support for Station G3 variant * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- bin/config.d/lora-station-g3.yaml | 18 ++++++++ boards/station-g3.json | 41 +++++++++++++++++++ src/platform/esp32/architecture.h | 2 + .../esp32s3/station-common/station_common.h | 38 +++++++++++++++++ variants/esp32s3/station-g2/platformio.ini | 3 +- variants/esp32s3/station-g2/variant.h | 32 +-------------- variants/esp32s3/station-g3/pins_arduino.h | 21 ++++++++++ variants/esp32s3/station-g3/platformio.ini | 32 +++++++++++++++ variants/esp32s3/station-g3/variant.h | 22 ++++++++++ 9 files changed, 177 insertions(+), 32 deletions(-) create mode 100644 bin/config.d/lora-station-g3.yaml create mode 100644 boards/station-g3.json create mode 100644 variants/esp32s3/station-common/station_common.h create mode 100644 variants/esp32s3/station-g3/pins_arduino.h create mode 100644 variants/esp32s3/station-g3/platformio.ini create mode 100644 variants/esp32s3/station-g3/variant.h diff --git a/bin/config.d/lora-station-g3.yaml b/bin/config.d/lora-station-g3.yaml new file mode 100644 index 000000000..79d0d7e09 --- /dev/null +++ b/bin/config.d/lora-station-g3.yaml @@ -0,0 +1,18 @@ +# Station G3 motherboard with a Raspberry Pi Zero 2W as the MCU daughterboard. +# Verify spidev / I2C device paths for your OS — they may differ. +Meta: + name: Station G3 + support: community + compatible: + - raspberry-pi + +Lora: + Module: sx1262 + IRQ: 22 # BCM pin — wiki spec + Reset: 16 # BCM pin — wiki spec + Busy: 24 # BCM pin — wiki spec + # CS: 8 # BCM 8 = SPI0 CE0 (default); uncomment only to override + DIO2_AS_RF_SWITCH: true + DIO3_TCXO_VOLTAGE: true + spidev: spidev0.0 + # SX126X_MAX_POWER: 19 # matches Station G2 firmware cap; raise carefully per PA jumper mode diff --git a/boards/station-g3.json b/boards/station-g3.json new file mode 100644 index 000000000..615f8bb40 --- /dev/null +++ b/boards/station-g3.json @@ -0,0 +1,41 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=0" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "station-g3" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "BQ Station G3", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "", + "vendor": "BQ Consulting" +} diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index cd3ac1f9d..e4ec807f8 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -158,6 +158,8 @@ #define HW_VENDOR meshtastic_HardwareModel_CHATTER_2 #elif defined(STATION_G2) #define HW_VENDOR meshtastic_HardwareModel_STATION_G2 +#elif defined(STATION_G3) +#define HW_VENDOR meshtastic_HardwareModel_STATION_G3 #elif defined(UNPHONE) #define HW_VENDOR meshtastic_HardwareModel_UNPHONE #elif defined(WIPHONE) diff --git a/variants/esp32s3/station-common/station_common.h b/variants/esp32s3/station-common/station_common.h new file mode 100644 index 000000000..9b759fa42 --- /dev/null +++ b/variants/esp32s3/station-common/station_common.h @@ -0,0 +1,38 @@ +// Shared pin/feature defines for BQ Station G2 and G3. +// Boards differ only in PA output power (SX126X_MAX_POWER) and the wiki URL. +#pragma once + +// Station G2/G3 may not have GPS installed, but have a GROVE GPS Socket for an optional GPS module +#define GPS_RX_PIN 7 +#define GPS_TX_PIN 15 + +// 1.3 inch OLED Screen +#define USE_SH1107_128_64 + +#define I2C_SDA 5 +#define I2C_SCL 6 + +#define BUTTON_PIN 38 // Program button +#define BUTTON_NEED_PULLUP + +#define USE_SX1262 + +#define LORA_MISO 14 +#define LORA_SCK 12 +#define LORA_MOSI 13 +#define LORA_CS 11 + +#define LORA_RESET 21 +#define LORA_DIO1 48 + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS // Compatibility alias; prefer LORA_CS in board definitions. +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY 47 +#define SX126X_RESET LORA_RESET + +// DIO2 controls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +// NOTE: SX126X_MAX_POWER is intentionally NOT defined here — each board sets it. +#endif diff --git a/variants/esp32s3/station-g2/platformio.ini b/variants/esp32s3/station-g2/platformio.ini index 4efb21a00..9c6b71cec 100644 --- a/variants/esp32s3/station-g2/platformio.ini +++ b/variants/esp32s3/station-g2/platformio.ini @@ -22,10 +22,11 @@ upload_speed = 921600 build_unflags = ${esp32s3_base.build_unflags} -DARDUINO_USB_MODE=0 -build_flags = +build_flags = ${esp32s3_base.build_flags} -D STATION_G2 -I variants/esp32s3/station-g2 + -I variants/esp32s3/station-common -DBOARD_HAS_PSRAM -DSTATION_G2 -DARDUINO_USB_MODE=1 diff --git a/variants/esp32s3/station-g2/variant.h b/variants/esp32s3/station-g2/variant.h index 8f0b4b220..96c7f176b 100644 --- a/variants/esp32s3/station-g2/variant.h +++ b/variants/esp32s3/station-g2/variant.h @@ -2,39 +2,9 @@ Board Information: https://wiki.uniteng.com/en/meshtastic/station-g2 */ -// Station G2 may not have GPS installed, but it has a GROVE GPS Socket for Optional GPS Module -#define GPS_RX_PIN 7 -#define GPS_TX_PIN 15 - -// Station G2 has 1.3 inch OLED Screen -#define USE_SH1107_128_64 - -#define I2C_SDA 5 // I2C pins for this board -#define I2C_SCL 6 - -#define BUTTON_PIN 38 // This is the Program Button -#define BUTTON_NEED_PULLUP - -#define USE_SX1262 - -#define LORA_MISO 14 -#define LORA_SCK 12 -#define LORA_MOSI 13 -#define LORA_CS 11 - -#define LORA_RESET 21 -#define LORA_DIO1 48 +#include "station_common.h" #ifdef USE_SX1262 -#define SX126X_CS LORA_CS // FIXME - we really should define LORA_CS instead -#define SX126X_DIO1 LORA_DIO1 -#define SX126X_BUSY 47 -#define SX126X_RESET LORA_RESET - -// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 -#define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - // Ensure the PA does not exceed the saturation output power. More // Info:https://wiki.uniteng.com/en/meshtastic/station-g2#summary-for-lora-power-amplifier-conduction-test #define SX126X_MAX_POWER 19 diff --git a/variants/esp32s3/station-g3/pins_arduino.h b/variants/esp32s3/station-g3/pins_arduino.h new file mode 100644 index 000000000..129d9f9e2 --- /dev/null +++ b/variants/esp32s3/station-g3/pins_arduino.h @@ -0,0 +1,21 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// GPIO48 Reference: https://github.com/espressif/arduino-esp32/pull/8600 + +// The default Wire will be mapped to Screen and Sensors +static const uint8_t SDA = 5; +static const uint8_t SCL = 6; + +// Default SPI will be mapped to Radio +static const uint8_t MISO = 14; +static const uint8_t SCK = 12; +static const uint8_t MOSI = 13; +static const uint8_t SS = 11; + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32s3/station-g3/platformio.ini b/variants/esp32s3/station-g3/platformio.ini new file mode 100644 index 000000000..f94809ddf --- /dev/null +++ b/variants/esp32s3/station-g3/platformio.ini @@ -0,0 +1,32 @@ +[env:station-g3] +custom_meshtastic_hw_model = 134 +custom_meshtastic_hw_model_slug = STATION_G3 +custom_meshtastic_architecture = esp32-s3 +custom_meshtastic_actively_supported = true +custom_meshtastic_support_level = 2 +custom_meshtastic_display_name = Station G3 +custom_meshtastic_images = station-g3.svg +custom_meshtastic_tags = B&Q +custom_meshtastic_requires_dfu = true +custom_meshtastic_partition_scheme = 16MB + +extends = esp32s3_base +board = station-g3 +board_level = pr +board_check = true +board_build.partitions = default_16MB.csv +board_build.mcu = esp32s3 +upload_protocol = esptool +;upload_port = /dev/ttyACM0 +upload_speed = 921600 +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=0 +build_flags = + ${esp32s3_base.build_flags} + -D STATION_G3 + -I variants/esp32s3/station-g3 + -I variants/esp32s3/station-common + -DBOARD_HAS_PSRAM + -DSTATION_G3 + -DARDUINO_USB_MODE=1 diff --git a/variants/esp32s3/station-g3/variant.h b/variants/esp32s3/station-g3/variant.h new file mode 100644 index 000000000..98441ea99 --- /dev/null +++ b/variants/esp32s3/station-g3/variant.h @@ -0,0 +1,22 @@ +#include "station_common.h" + +#ifdef USE_SX1262 +// Station G3 reuses the same Fast-Transient DC-DC PA design as G2 (BQ35LORA900V1M). +// PA Operating Mode is set in hardware via the PA-PL1 / PA-PL2 jumpers. +// 19 matches the G2 cap (SX1262 19 dBm in → ~31 dBm PA out at Power Level 1, ISM compliant). +// Raise (max 22) only if running a higher PA Power Level and you can stay within local band limits. +#define SX126X_MAX_POWER 19 +#endif + +/* +#define BATTERY_PIN 4 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC1_GPIO4_CHANNEL +#define ADC_MULTIPLIER 4 +#define BATTERY_SENSE_SAMPLES 15 // Set the number of samples, It has an effect of increasing sensitivity. +#define BAT_FULLVOLT 8400 +#define BAT_EMPTYVOLT 5000 +#define BAT_CHARGINGVOLT 8400 +#define BAT_NOBATVOLT 4460 +#define CELL_TYPE_LION // same curve for liion/lipo +#define NUM_CELLS 2 +*/ From 4c3ba612bb8508e3a128be98332d54f174f352c8 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 13 May 2026 10:25:11 -0400 Subject: [PATCH 5/7] VSCode: Prepare for pioarduino transition (#10471) Start reccomending the pioarduino VS Code extension instead of the PlatformIO extension. pioarduino-based builds cannot complete correctly using the platformio extension. Normal platformio builds (nrf52, stm32) are unaffected//still work correctly. Devs may need to delete their ~.platformio and .pio directories once after install in order to build properly. --- .devcontainer/devcontainer.json | 10 +++++++--- .vscode/extensions.json | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e3f076ce0..a631832cb 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,17 +8,21 @@ "features": { "ghcr.io/devcontainers/features/python:1": { "installTools": true, - "version": "3.14" + "version": "3.13" } }, "customizations": { "vscode": { "extensions": [ "ms-vscode.cpptools", - "platformio.platformio-ide", + "Jason2866.esp-decoder", + "pioarduino.pioarduino-ide", "Trunk.io" ], - "unwantedRecommendations": ["ms-azuretools.vscode-docker"], + "unwantedRecommendations": [ + "ms-azuretools.vscode-docker", + "platformio.platformio-ide" + ], "settings": { "extensions.ignoreRecommendations": true } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 080e70d08..66d8356e5 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,10 +1,10 @@ { - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format "recommendations": [ - "platformio.platformio-ide" + "Jason2866.esp-decoder", + "pioarduino.pioarduino-ide" ], "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" + "ms-vscode.cpptools-extension-pack", + "platformio.platformio-ide" ] } From c756bbe2c1136838b54c56571dc2509929512552 Mon Sep 17 00:00:00 2001 From: Andros Fenollosa Date: Wed, 13 May 2026 13:43:36 +0200 Subject: [PATCH 6/7] Fix WiFi TCP/HTTP services not starting without USB serial connected (#10460) Move WiFi.onEvent(WiFiEvent) registration before createSSLCert() to prevent a race where the ESP32 auto-reconnects during cert generation and fires GOT_IP before the handler is attached, causing onNetworkConnected() to never run and the TCP/HTTP API services to never initialize when booting without USB serial. Also call onNetworkConnected() from reconnectWiFi() on all platforms (not just RP2040) as a safety net; it is already guarded by APStartupComplete so it only runs once. --- src/mesh/wifi/WiFiAPClient.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index be25e6865..f1c9f888a 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -291,9 +291,7 @@ static int32_t reconnectWiFi() #endif return 1000; // check once per second } else { -#ifdef ARCH_RP2040 - onNetworkConnected(); // will only do anything once -#endif + onNetworkConnected(); // will only do anything once (guarded by APStartupComplete) return 300000; // every 5 minutes } } @@ -343,9 +341,6 @@ bool initWifi() const char *wifiPsw = config.network.wifi_psk; #ifndef ARCH_RP2040 -#if !MESHTASTIC_EXCLUDE_WEBSERVER - createSSLCert(); // For WebServer -#endif WiFi.persistent(false); // Disable flash storage for WiFi credentials #endif if (!*wifiPsw) // Treat empty password as no password @@ -370,6 +365,9 @@ bool initWifi() #endif } #ifdef ARCH_ESP32 + // Register WiFi event handler BEFORE createSSLCert() to prevent race condition: + // Without this, WiFi can auto-reconnect during cert generation and fire GOT_IP + // before the handler is registered, causing onNetworkConnected() to never run. WiFi.onEvent(WiFiEvent); WiFi.setAutoReconnect(true); WiFi.setSleep(false); @@ -391,6 +389,12 @@ bool initWifi() wifiDisconnectReason = info.wifi_sta_disconnected.reason; }, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); +#endif + +#ifndef ARCH_RP2040 +#if !MESHTASTIC_EXCLUDE_WEBSERVER + createSSLCert(); // For WebServer - called after WiFi.onEvent() to avoid race condition +#endif #endif LOG_DEBUG("JOINING WIFI soon: ssid=%s", wifiName); wifiReconnect = new Periodic("WifiConnect", reconnectWiFi); From 1ae4a538f50cebac5b13e8fcaff4bac123b5cb51 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 13 May 2026 06:44:51 -0500 Subject: [PATCH 7/7] Trunk --- src/mesh/wifi/WiFiAPClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index f1c9f888a..9930d0a55 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -292,7 +292,7 @@ static int32_t reconnectWiFi() return 1000; // check once per second } else { onNetworkConnected(); // will only do anything once (guarded by APStartupComplete) - return 300000; // every 5 minutes + return 300000; // every 5 minutes } }