feat: consolidate full companion runtime profiles

This commit is contained in:
mikecarper committed 2026-08-28 08:47:26 -07:00
1 parent d17c250137
commit c6476b8ba8
42 files changed
+731 -561

No files matched your search

+118 -48
View File
@@ -114,7 +114,7 @@ Commands:
build-room-server-firmwares: Build all chat room server firmwares for all build targets.
build-sensor-firmwares: Build all sensor firmwares for all build targets.
build-kiss-radio-firmwares: Build all KISS radio firmwares for all build targets.
get-companion-firmwares-to-build: List canonical attached companion targets for release automation; dual-CDC boards use Full Companion instead of separate transport artifacts.
get-companion-firmwares-to-build: List canonical attached companion targets for release automation; Full Companion replaces separate transport artifacts where qualified.
get-repeater-firmwares-to-build: List standard and specialized external-storage repeater targets for release automation.
get-room-server-firmwares-to-build: List standard room-server targets for release automation.
@@ -430,11 +430,15 @@ for section, options in data:
M5Stack_Unit_C6L_companion_radio_ble
Heltec_Wireless_Tracker_companion_radio_ble
LilyGo_T3S3_sx1276_companion_radio_ble
LilyGo_Tlora_C6_companion_radio_ble_
Heltec_ct62_companion_radio_ble
Meshadventurer_sx1262_companion_radio_ble
Meshadventurer_sx1268_companion_radio_ble
Heltec_Wireless_Paper_companion_radio_ble
Heltec_E213_companion_radio_ble
Meshimi_companion_radio_ble_
WHY2025_badge_companion_radio_ble_
Xiao_C6_companion_radio_ble_
Xiao_S3_companion_radio_ble
LilyGo_TETH_Elite_sx1262_companion_radio_ble
LilyGo_T3S3_sx1262_companion_radio_ble
@@ -443,6 +447,19 @@ for section, options in data:
Tbeam_SX1262_companion_radio_ble
Tbeam_SX1276_companion_radio_ble
T_Beam_S3_Supreme_SX1262_companion_radio_ble
heltec_v4_expansionkit_tft_companion_radio_ble_femon
)
# A few older targets predate the companion_radio_<transport> naming
# convention. Keep their established PlatformIO recipe as the build base,
# but publish a consistently named Full target. SenseCAP Indicator keeps
# separate ESP-NOW and LoRa images because those are different physical
# radio layouts, not transport-only variants of one image.
local -a qualified_esp32_named_full_companion_specs=(
'Generic_ESPNOW_comp_radio_usb|Generic_ESPNOW_companion_radio_full'
'Heltec_E290_companion_usb_ble|Heltec_E290_companion_radio_full'
'Heltec_T190_companion_radio_usb_ble_|Heltec_T190_companion_radio_full_'
'SenseCapIndicator-ESPNow_comp_radio_usb|SenseCapIndicator-ESPNow_companion_radio_full'
'SenseCapIndicator-LoRa_comp_radio_usb_wifi|SenseCapIndicator-LoRa_companion_radio_full'
)
local -a qualified_nrf52_full_companion_bases=(
GAT562_Mesh_Watch13_companion_radio_ble
@@ -472,6 +489,28 @@ for section, options in data:
PIO_ENV_BUILD_BASE_BY_NAME["$full_env"]="$env_name"
done
local full_spec
for full_spec in "${qualified_esp32_named_full_companion_specs[@]}"; do
IFS='|' read -r env_name full_env <<<"$full_spec"
if [ -n "${PIO_ENV_PLATFORM_BY_NAME[$full_env]+x}" ]; then
continue
fi
if [ "${PIO_ENV_PLATFORM_BY_NAME[$env_name]:-}" != "ESP32_PLATFORM" ]; then
echo "Qualified Full Companion base is missing or not ESP32: ${env_name}" >&2
return 1
fi
SUPPORTED_PIO_ENVS+=("$full_env")
PIO_ENV_PLATFORM_BY_NAME["$full_env"]="ESP32_PLATFORM"
PIO_ENV_BOARD_BY_NAME["$full_env"]="${PIO_ENV_BOARD_BY_NAME[$env_name]}"
PIO_ENV_MQTT_BY_NAME["$full_env"]=0
PIO_ENV_OTA_BY_NAME["$full_env"]=1
PIO_ENV_SD_OTA_BY_NAME["$full_env"]=0
PIO_ENV_QSPI_OTA_BY_NAME["$full_env"]=0
PIO_ENV_FULL_BUILD_BY_NAME["$full_env"]=0
PIO_ENV_FULL_WIFI_OTA_BY_NAME["$full_env"]="${PIO_ENV_FULL_WIFI_OTA_BY_NAME[$env_name]:-0}"
PIO_ENV_BUILD_BASE_BY_NAME["$full_env"]="$env_name"
done
for env_name in "${qualified_nrf52_full_companion_bases[@]}"; do
full_env=${env_name/companion_radio_ble/companion_radio_full}
if [ -n "${PIO_ENV_PLATFORM_BY_NAME[$full_env]+x}" ]; then
@@ -492,6 +531,17 @@ for section, options in data:
PIO_ENV_FULL_WIFI_OTA_BY_NAME["$full_env"]=0
PIO_ENV_BUILD_BASE_BY_NAME["$full_env"]="$env_name"
done
# Full Companion may compile the direct WiFi MQTT bridge as a saved,
# runtime-optional capability. It is still the canonical all-transport
# image, not an MQTT-only profile which the global MQTT build selector may
# replace or discard.
for env_name in "${SUPPORTED_PIO_ENVS[@]}"; do
if [ "${PIO_ENV_PLATFORM_BY_NAME[$env_name]:-}" = "ESP32_PLATFORM" ] \
&& is_companion_radio_full_target "$env_name"; then
PIO_ENV_MQTT_BY_NAME["$env_name"]=0
fi
done
fi
}
@@ -610,7 +660,7 @@ prompt_for_build_mode() {
"Build all canonical firmwares (legacy setting aliases remain direct-build only)"
"Build the canonical release matrix with unified FULL ESP32 USB + WiFi output (plus logging fallbacks where WiFi is unavailable)"
"Build all repeater firmwares"
"Build canonical companion firmwares (dual-CDC Full replaces separate transports; power saving and FEM/RX gain are runtime configurable)"
"Build canonical companion firmwares (Full replaces separate transports; power saving and FEM/RX gain are runtime configurable)"
"Build all chat room server firmwares"
"Build all sensor firmwares"
"Build FULL ESP32 firmwares (all features, USB logging, WiFi MQTT where available, and LoRa OTA)"
@@ -2195,7 +2245,17 @@ normalize_resolved_targets_for_mqtt() {
for target in "${candidates[@]}"; do
candidate=""
if [ "${MQTT_BRIDGE_OVERRIDE,,}" == "on" ]; then
if is_companion_radio_full_target "$target"; then
# Full is already the board's one runtime-configurable transport image,
# so never swap it for a second artifact or compile a different feature
# set under the same name. Keep it for MQTT=off; for MQTT=on, keep only
# recipes which were explicitly qualified with direct MQTT support.
if [ "${MQTT_BRIDGE_OVERRIDE,,}" != "on" ] \
|| pio_env_option_contains "$(get_pio_build_env "$target")" \
build_flags "WITH_MQTT_BRIDGE"; then
candidate=$target
fi
elif [ "${MQTT_BRIDGE_OVERRIDE,,}" == "on" ]; then
candidate=$(get_mqtt_enabled_target "$target") || candidate=""
else
candidate=$(get_mqtt_disabled_target "$target") || candidate=""
@@ -2242,6 +2302,16 @@ disable_debug_flags() {
}
apply_mqtt_bridge_override() {
local env_name=${1:-}
# Full Companion is one immutable release artifact whose compiled
# capabilities are controlled by its qualified board recipe and then
# enabled or disabled at runtime. A matrix-wide MQTT choice must not mutate
# that artifact into another binary under the same filename.
if [ -n "$env_name" ] && is_companion_radio_full_target "$env_name"; then
return 0
fi
case "${MQTT_BRIDGE_OVERRIDE,,}" in
on)
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DWITH_MQTT_BRIDGE=1"
@@ -2338,8 +2408,8 @@ disable_usb_logging_for_mqtt() {
local env_name=$1
# Full Companion always compiles diagnostics behind a saved runtime gate.
# Dual-CDC boards write them to CDC 1. Single-TTY boards first switch CDC 0
# into an input-capable terminal so plaintext cannot mix with framed traffic.
# ESP32 uses one TTY and switches it into an input-capable logging terminal,
# so plaintext cannot mix with framed traffic. nRF52 retains dedicated CDC 1.
if is_companion_radio_full_target "$env_name"; then
return 0
fi
@@ -2393,36 +2463,13 @@ is_nrf52_companion_radio_full_target() {
&& is_companion_radio_full_target "$1"
}
is_esp32_dual_cdc_companion_radio_full_target() {
is_esp32_companion_radio_full_target "$1" || return 1
case "${1,,}" in
lilygo_tbeam_1w_companion_radio_full|\
station_g2_companion_radio_full|\
station_g3_esp32_companion_radio_full|\
xiao_s3_wio_companion_radio_full|\
heltec_tracker_v2_companion_radio_full_femon|\
meshnology_w12_companion_radio_full|\
nibble_screen_connect_companion_radio_full_|\
nibble_zero_connect_companion_radio_full_|\
heltec_v4_2_v4_3_companion_radio_full_femon|\
heltec_v4_3_companion_radio_full_femoff|\
heltec_v4_tft_companion_radio_full_femon|\
heltec_v4_3_tft_companion_radio_full_femoff|\
heltec_v4_r8_companion_radio_full|\
heltec_v4_r8_tft_companion_radio_full)
return 0
;;
*) return 1 ;;
esac
}
requires_esp32_arduino3_framework() {
local env_name=$1
local pio_env_name=$2
if is_esp32_dual_cdc_companion_radio_full_target "$env_name"; then
return 0
fi
# RC32 still uses its board-qualified Arduino 3 package independently of
# the removed dual-CDC Full profile. ESP32-C6 also inherently uses A3;
# ordinary ESP32/S3 Full images return to the shared A2 platform.
case "${env_name,,}:${pio_env_name,,}" in
heltec_rc32_*:*|*:heltec_rc32_*) return 0 ;;
*) return 1 ;;
@@ -2694,8 +2741,7 @@ declare_build_capability_contract() {
record_build_expectation "companion.bluetooth" \
"Companion: starting Bluetooth"
record_build_expectation "companion.usb_logging" "get usb.logging"
if is_nrf52_companion_radio_full_target "$env_name" \
|| is_esp32_dual_cdc_companion_radio_full_target "$env_name"; then
if is_nrf52_companion_radio_full_target "$env_name"; then
record_build_expectation "companion.dedicated_usb_logging" \
"get usb.logging"
fi
@@ -2705,6 +2751,11 @@ declare_build_capability_contract() {
record_build_expectation "companion.wifi_ota_seeder" \
"OTA seeder listening on :"
record_build_expectation "web.webconfig" "start webconfig"
pio_env_name=$(get_pio_build_env "$env_name")
if pio_env_option_contains "$pio_env_name" build_flags \
"WITH_MQTT_BRIDGE"; then
record_build_expectation "companion.direct_mqtt" "mqtt.status"
fi
else
record_build_expectation "companion.usb_mota_source" "ota folder on"
record_build_expectation "companion.ble_mota_source" \
@@ -3137,7 +3188,8 @@ apply_companion_radio_full_profile() {
# command to Binary Companion. `motatool serve --serial` switches it into
# an exclusive host-folder mode with its existing `ota folder on` preamble.
# CDC 1 is a write-only plaintext packet/debug logging stream; BLE remains
# an independent Companion link.
# an independent Companion link. ESP32 intentionally does not use this
# dual-CDC capability.
append_platformio_build_unflags "-UOTA_FOLDER_SERIAL"
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DOTA_FOLDER_SERIAL=1 -DCOMPANION_FEATURE_USB_MOTA_SOURCE=1 -DCOMPANION_FEATURE_BLE_MOTA_SOURCE=1 -DCOMPANION_FEATURE_DEDICATED_USB_LOGGING=1 -DCFG_TUD_CDC=2 -DMESH_DUAL_CDC_LOGGING=1 -DMESH_DEBUG=1 -DMESH_PACKET_LOGGING=1"
@@ -3151,16 +3203,6 @@ apply_companion_radio_full_profile() {
return 0
fi
if is_esp32_dual_cdc_companion_radio_full_target "$env_name"; then
# Qualified ESP32-S3 boards route their data connector to the native USB
# peripheral. Arduino-ESP32 3.x supplies two real TinyUSB CDC ACM
# instances: CDC 0 carries framed Companion traffic and accepts the
# flashing reboot gesture; CDC 1 is a write-only plaintext diagnostics
# stream and cannot reboot the board.
append_platformio_build_unflags "-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0"
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=1 -DCOMPANION_FEATURE_DEDICATED_USB_LOGGING=1 -DMESH_DUAL_CDC_LOGGING=1 -DMESH_DEBUG=1 -DMESH_PACKET_LOGGING=1"
fi
# ESP32 keeps both source transports. TCP remains the normal unattended
# path; a host may explicitly hold the native USB port in Companion terminal
# mode when WiFi is unavailable and serve the same folder protocol there.
@@ -3636,7 +3678,7 @@ build_firmware() {
export PLATFORMIO_BUILD_FLAGS="${original_platformio_build_flags} -DFIRMWARE_BUILD_DATE='\"${firmware_build_date}\"' -DFIRMWARE_BUILD_EPOCH=${firmware_build_epoch} -DFIRMWARE_VERSION='\"${embedded_version_string}\"' -DOTA_VARIANT='\"${env_name}\"'${mota_target_flag}"
disable_debug_flags "$env_name"
apply_debug_overrides "$env_name"
apply_mqtt_bridge_override
apply_mqtt_bridge_override "$env_name"
disable_usb_logging_for_mqtt "$env_name"
apply_merged_standard_usb_logging_profile "$env_name"
apply_lora_ota_override "$env_name"
@@ -3838,17 +3880,45 @@ get_esp32_full_companion_replacement() {
[ "${PIO_ENV_PLATFORM_BY_NAME[$1]:-}" = "ESP32_PLATFORM" ] || return 1
case "$env_name" in
heltec_v3_companion_radio_wifi_mqtt|\
heltec_v4_companion_radio_wifi_mqtt_femon|\
heltec_v4_3_companion_radio_wifi_mqtt_femoff) ;;
*companion_radio_wifi_mqtt*) return 1 ;;
*companion_radio_usb*|*companion_radio_ble*|*companion_radio_wifi*|\
*companion_radio_serial*|*companion_radio_ethernet*) ;;
*companion_radio_serial*|*companion_radio_ethernet*|*comp_radio_usb*|\
heltec_e290_companion_ble|heltec_e290_companion_usb|\
heltec_e290_companion_usb_ble) ;;
*) return 1 ;;
esac
# Full Companion includes USB, BLE, and ordinary WiFi Companion. Map legacy
# FEM-default aliases to the one runtime-configurable image for each physical
# V4 display/radio layout. Expansion-kit targets intentionally do not match:
# they have distinct sensor wiring and no corresponding Full recipe.
# V4 display/radio layout. The expansion-kit TFT remains a distinct Full
# recipe because it has different sensor wiring from both base display
# layouts.
case "$env_name" in
heltec_v3_companion_radio_wifi_mqtt)
full_env=Heltec_v3_companion_radio_full
;;
generic_espnow_comp_radio_*)
full_env=Generic_ESPNOW_companion_radio_full
;;
heltec_e290_companion_*)
full_env=Heltec_E290_companion_radio_full
;;
heltec_t190_companion_radio_*)
full_env=Heltec_T190_companion_radio_full_
;;
sensecapindicator-espnow_comp_radio_*)
full_env=SenseCapIndicator-ESPNow_companion_radio_full
;;
sensecapindicator-lora_comp_radio_*)
full_env=SenseCapIndicator-LoRa_companion_radio_full
;;
heltec_v4_3_expansionkit_tft_companion_radio_*|\
heltec_v4_expansionkit_tft_companion_radio_*)
full_env=heltec_v4_expansionkit_tft_companion_radio_full_femon
;;
heltec_v4_r8_tft_companion_radio_*)
full_env=heltec_v4_r8_tft_companion_radio_full
;;
+8 -20
View File
@@ -388,22 +388,9 @@
if (!profile || profile.role !== "companion" || profile.mode !== "full") {
return false;
}
if (profile.installKinds.includes("zip")) return true;
const target = String(profile.target || "").toLowerCase();
if (/^heltec_v4(?:_2_v4_3|_3)?(?:_r8)?(?:_tft)?_companion_radio_full(?:_|$)/
.test(target)) {
return true;
}
return [
"lilygo_tbeam_1w_companion_radio_full",
"station_g2_companion_radio_full",
"station_g3_esp32_companion_radio_full",
"xiao_s3_wio_companion_radio_full",
"heltec_tracker_v2_companion_radio_full_femon",
"meshnology_w12_companion_radio_full",
"nibble_screen_connect_companion_radio_full_",
"nibble_zero_connect_companion_radio_full_",
].includes(target);
// Native nRF52 Serial DFU packages use .zip. ESP32 Full Companion always
// has one USB TTY and never receives the dedicated-port capability.
return profile.installKinds.includes("zip");
}
function isFullCompanion(profile) {
@@ -487,11 +474,12 @@
return (profiles || []).map(function (profile) {
if (!isFullCompanion(profile)) return profile;
// Every Full Companion has a runtime USB logging mode. Dual-CDC targets
// add an independent plaintext interface after reboot; single-TTY
// targets switch their existing port between framed and plaintext modes.
// Every Full Companion has a runtime USB logging mode. nRF52 adds an
// independent plaintext interface after reboot. Every ESP32 target
// switches its one TTY between framed and plaintext modes.
profile.logging = "usb-runtime";
profile.loggingModes = ["none", "usb"];
profile.dedicatedUsbLogging = false;
if (isDualCdcFullCompanion(profile)) {
profile.dedicatedUsbLogging = true;
}
@@ -830,7 +818,7 @@
);
} else {
extra.push(
"Full Companion starts with USB logging off and Binary Companion on its single TTY. Enter the text terminal and use set usb.logging on for plaintext logs; that TTY still accepts set usb.logging off and automatically returns to Binary Companion after the reply."
"Full Companion starts in its ASCII terminal with USB logging off. Use set usb.logging on for plaintext logs; set usb.logging off stops those logs but stays in normal ASCII mode. Then send +++MESHCORE-TERM-STOP or let a Companion app send a valid framed probe to switch the TTY to Binary Companion."
);
}
}
+9 -8
View File
@@ -72,8 +72,8 @@ retain 50 because their MQTT discovery tables are constrained by internal DRAM.
| ESP32 MQTT observer or ESP-NOW bridge | Always uses the expanded FULL partition profile. The build never substitutes a reduced CLI to fit the legacy application slot. |
| FULL ESP32 USB + WiFi | Uses the matching MQTT target with packet logging on, verbose debug off, and the complete command surface supported by that role and hardware. `get/set logging.output off\|usb\|wifi\|both` selects and persists the active output paths. |
| FULL ESP32 logging fallback | Uses the matching non-MQTT target only when no WiFi MQTT sibling exists, with debug and packet logging enabled and the complete command surface supported by that role and hardware. Its persistent USB gate also covers output-off operation, avoiding a second FULL ESP-NOW image. |
| Dual-CDC Full Companion | nRF52 and qualified native-USB ESP32-S3 Full images use one physical USB connection. Fresh installs expose only interface `00`; it starts as an ASCII terminal and automatically hands a complete `<` frame to framed Companion. The same interface also carries exclusive serial mOTA traffic. Enabling logging and rebooting adds interface `02` for plaintext logs. They also provide BLE and source-only LoRa OTA; ESP32 additionally provides WiFi. `get/set usb.logging` persistently controls whether the logging interface is present. |
| Single-TTY Full Companion | ESP32 Full images without dual CDC start with the ASCII terminal on their one TTY and automatically hand a complete `<` frame to framed Companion. `set usb.logging on` switches it to an input-capable plaintext logging terminal; `set usb.logging off` replies and then restores framed Companion automatically. A saved logging-on setting starts directly in that logging terminal and disables automatic frame detection. BLE, WiFi, and source-only LoRa OTA remain available. |
| nRF52 dual-CDC Full Companion | Fresh installs expose only interface `00`; it starts as an ASCII terminal and automatically hands a complete `<` frame to framed Companion. The same interface also carries exclusive serial mOTA traffic. Enabling logging and rebooting adds interface `02` for plaintext logs. BLE and source-only LoRa OTA remain available. `get/set usb.logging` persistently controls whether the logging interface is present. |
| ESP32 single-TTY Full Companion | Every ESP32 Full image starts with the ASCII terminal on its one USB TTY and automatically hands a complete `<` frame to framed Companion. `set usb.logging on` switches that TTY to an input-capable plaintext logging terminal and makes framed Companion unavailable on USB; `set usb.logging off` stops logging but leaves the TTY in normal ASCII mode. The terminal stop token or a valid incoming framed probe then performs the ordinary switch to Binary Companion. A saved logging-on setting starts directly in that logging terminal and disables automatic frame detection. BLE, WiFi, and source-only LoRa OTA remain available. ESP32 Full uses Arduino-ESP32 2.x where supported; RC32 and ESP32-C6 keep their board-required Arduino 3.x platform but still expose only one TTY. |
| `no_external_sensors` | Removes optional external-sensor drivers and their settings; it does not remove core repeater discovery, routing, or runtime RS-232 commands. RAK3401 and RAK4631 profiles retain the four common INA I2C voltage/current monitors. GPS-preserving RAK nRF52 OTA profiles retain their GPS commands and provider; RAK4631 defaults the bridge to UART 2 because GPS uses UART 1. Legacy target suffixes remain stable for OTA identity compatibility. |
`logging`, `OTA`, and `FULL` describe independent build features in historical
@@ -98,10 +98,11 @@ available from a canonical image:
separate USB, BLE, ordinary WiFi, hardware-serial, Ethernet Companion, and
USB packet-logging Companion artifacts.
It provides BLE and source-only LoRa OTA; ESP32 also provides ordinary WiFi.
Dual-CDC builds keep framed traffic on interface `00` and add logging on
interface `02` after a reboot. Single-TTY builds switch interface `00` into
an input-capable logging terminal and restore Binary Companion when that
terminal session ends.
nRF52 keeps framed traffic on interface `00` and can add logging on interface
`02` after a reboot. Every ESP32 Full build instead switches its one USB TTY
into an input-capable logging terminal. Turning logging off restores the
normal ASCII terminal; the usual stop token or a valid framed probe then
switches it to Binary Companion.
Its LoRa OTA support is source-only: it can serve a host file to another node
but has no staging store and cannot update itself over LoRa.
Installing an ESP32 Full Companion may require one merged-image erase/flash
@@ -127,8 +128,8 @@ The old aliases still work with `build-firmware` and
collapsed; they retain their exact storage, bootloader, role, and target
identity contracts. Companion boards keep transport-specific canonical images
only when no exact Full recipe has passed the combined flash/RAM qualification.
Dual CDC is not required: a qualified single-TTY Full image safely makes Binary
Companion and plaintext USB logging mutually exclusive.
ESP32 deliberately uses one TTY: Binary Companion and plaintext USB logging
are mutually exclusive there. nRF52 retains its optional second CDC port.
## Complete CLI policy
+2 -2
View File
@@ -72,7 +72,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
| Statistics | [`set telemetry.gps`; `get/set/send telemetry.tx`](cli_commands.md#read-repeater-telemetry-history) | Non-STM32 repeater; GPS setting requires a provider; remote access requires administrator | Yes | Yes | Yes |
| Logging | [`log start`; `log stop`; `log erase`](cli_commands.md#logging) | Storage-backed roles retain data; other roles can return empty data | Yes | Yes | Yes |
| Logging | [`log`](cli_commands.md#print-the-captured-log-to-the-serial-terminal) | Local serial | Serial | Serial | Serial |
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Ordinary safe-USB artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; Full Companion uses either a reboot-controlled second CDC or an input-capable single-TTY logging terminal | Yes | Yes | No |
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Ordinary safe-USB artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; nRF52 Full Companion can add a reboot-controlled second CDC | Yes | Yes | No |
| Radio | [`get radio`; `set radio ...`](cli_commands.md#view-or-change-this-nodes-radio-parameters) | All text CLI roles | Yes | Yes | Yes |
| Radio | [`get tx`; `set tx <dbm>`](cli_commands.md#view-or-change-this-nodes-transmit-power) | Board TX-power limits apply | Yes | Yes | Yes |
| Radio | [`tempradio ...`; `normalradio`](cli_commands.md#change-the-radio-parameters-for-a-set-duration) | Full parser | Yes | Yes | Yes |
@@ -236,7 +236,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
| Statistics | [`set telemetry.gps`; `get/set/send telemetry.tx`](cli_commands.md#read-repeater-telemetry-history) | Non-STM32 repeater; GPS setting requires a provider; remote access requires administrator | Yes | Yes | Yes | Yes | Yes |
| Logging | [`log start`; `log stop`; `log erase`](cli_commands.md#logging) | Storage-backed roles retain data | Yes | Yes | Yes | Yes | Yes |
| Logging | [`log`](cli_commands.md#print-the-captured-log-to-the-serial-terminal) | Local serial | Serial | Serial | Serial | Serial | Serial |
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Ordinary safe-USB artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; Full Companion uses either a reboot-controlled second CDC or an input-capable single-TTY logging terminal | Yes | Yes | No | No | Yes |
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Ordinary safe-USB artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; every ESP32 Full Companion uses an input-capable single-TTY logging terminal with framed USB Companion disabled while logging | Yes | Yes | No | No | Yes |
| Radio | [`get radio`; `set radio ...`](cli_commands.md#view-or-change-this-nodes-radio-parameters) | All text CLI roles | Yes | Yes | Yes | Yes | Yes |
| Radio | [`get tx`; `set tx <dbm>`](cli_commands.md#view-or-change-this-nodes-transmit-power) | Board TX-power limits apply | Yes | Yes | Yes | Yes | Yes |
| Radio | [`tempradio ...`; `normalradio`](cli_commands.md#change-the-radio-parameters-for-a-set-duration) | Full parser | Yes | Yes | Yes | Yes | Yes |
+19 -16
View File
@@ -624,26 +624,29 @@ Binary `get/set` parameter namespace. Open interface `00`, send
`meshcli ... get usb.logging` directly can instead return
`Unknown var usb.logging` because that is a different protocol operation.
Dual-CDC Full Companion instead defaults logging to off and enumerates only
USB interface `00`, which carries Companion, terminal, and serial mOTA traffic.
Enabling logging adds interface `02`, its dedicated plaintext logging port, on
the next boot. Disabling it removes interface `02` on the next boot. A command
without the optional `reboot` argument saves the choice and reports that a
reboot is required when the USB interface count must change. The exact
nRF52 Full Companion defaults logging to off and enumerates only USB interface
`00`, which carries Companion, terminal, and serial mOTA traffic. Enabling
logging adds interface `02`, its dedicated plaintext logging port, on the next
boot. Disabling it removes interface `02` on the next boot. A command without
the optional `reboot` argument saves the choice and reports that a reboot is
required when the USB interface count must change. The exact
`set usb.logging on reboot` and `set usb.logging off reboot` forms save the
choice, send their reply, and reboot one second later only when needed. This
behavior includes nRF52 and dual-CDC native-USB ESP32-S3 Full images.
choice, send their reply, and reboot one second later only when needed.
On a single-TTY ESP32 Full Companion, enter the USB text terminal and use
On every ESP32 Full Companion, enter the USB text terminal and use
`set usb.logging on` to turn that TTY into a logging-repeater-style plaintext
stream. It remains an input-capable CLI, so `set usb.logging off` works on the
same TTY and automatically restores Binary Companion after its reply. No
reboot is needed because the USB interface count does not change.
stream. Framed Binary Companion is unavailable on USB while logging is on. The
TTY remains an input-capable CLI, so `set usb.logging off` works on the same
TTY. After its reply, logging stops and the TTY remains in the normal ASCII
terminal, just as it does after a fresh Full installation. Send
`+++MESHCORE-TERM-STOP`, or let a Companion app send a valid framed probe, to
switch it to Binary Companion. No reboot is needed because the USB interface
count does not change.
Turning USB logging off does not disable CLI replies. Dual-CDC builds keep
Companion frames active on interface `00`; single-TTY builds resume frames when
their terminal session ends. This setting does not change the node-storage
capture controlled by `log start` and `log stop`.
Turning USB logging off does not disable CLI replies. nRF52 keeps Companion
frames active on interface `00`; ESP32 resumes the ordinary ASCII/Binary
switcher after the logging terminal turns logging off. This setting does not
change the node-storage capture controlled by `log start` and `log stop`.
Unified ESP32 FULL builds add one saved selector for both output paths:
+56 -44
View File
@@ -11,7 +11,7 @@ firmware as an mOTA image.
| USB Binary Companion | Yes | Yes |
| BLE Binary Companion | Yes | Yes |
| USB ASCII terminal | Yes | Yes |
| Dedicated USB plaintext logging | Qualified native-USB ESP32-S3 profiles | Yes |
| Dedicated USB plaintext logging | No - shares the one USB TTY | Yes |
| Host-backed LoRa mOTA source | WiFi TCP 5001 | Exclusive USB mode or encrypted BLE |
| WiFi Companion/WebConfig | Yes | No - nRF52840 has no WiFi |
| Hardware serial Companion | On targets with assigned serial pins | On targets with assigned serial pins |
@@ -29,13 +29,15 @@ passed the combined-transport size check:
same board can safely add its platform's remaining transports. It never
substitutes the pin map or peripherals from another board.
The measured ESP32 additions are M5Stack Unit C6L, Heltec Wireless Tracker,
Wireless Paper, E213, and CT62; LilyGo T3S3 SX1262/SX1276, T-Deck, TETH Elite,
classic T-Beam SX1262/SX1276, and T-Beam S3 Supreme; Ebyte EoRa-S3;
Meshadventurer SX1262/SX1268; and XIAO S3. The measured nRF52 additions are
GAT562 Mesh Watch13, LilyGo T-Echo Lite, LilyGo T-Impulse Plus, and Wio Tracker
L1 E-Ink. Their old transport-specific names remain available for explicit
compatibility builds, but the Full image is the canonical release artifact.
The measured ESP32 additions are M5Stack Unit C6L; XIAO C6/S3, Meshimi,
WHY2025 Badge, LilyGo T-LoRa C6, T3S3 SX1262/SX1276, T-Deck, TETH Elite,
classic T-Beam SX1262/SX1276, and T-Beam S3 Supreme; Heltec Wireless Tracker,
Wireless Paper, E213, E290, T190, CT62, and V4 expansion-kit TFT; Generic
ESP-NOW; SenseCAP Indicator ESP-NOW/LoRa; Ebyte EoRa-S3; and Meshadventurer
SX1262/SX1268. The measured nRF52 additions are GAT562 Mesh Watch13, LilyGo
T-Echo Lite, LilyGo T-Impulse Plus, and Wio Tracker L1 E-Ink. Their old
transport-specific names remain available for explicit compatibility builds,
but the Full image is the canonical release artifact.
List the available targets:
@@ -69,8 +71,8 @@ hardware-serial, Ethernet Companion, Terminal Chat, and USB-only packet-logging
release artifacts whenever the exact board supports those combined transports.
Direct builds of the legacy targets remain available. RAK4631 repeater and room
server Ethernet builds remain separate because they are different standalone
roles, not Companion transports. Dual-CDC builds separate framed traffic and logs;
single-TTY builds make those modes mutually exclusive. In WebConfig, use the
roles, not Companion transports. nRF52 separates framed traffic and logs;
ESP32 makes those modes mutually exclusive on its one USB TTY. In WebConfig, use the
**FEM RX boost** switch. From the text terminal (USB, or TCP 5002 on ESP32), use:
```text
@@ -103,8 +105,15 @@ set display.rotation 0
`0` resets the screen to that board's compiled default orientation.
Heltec E290 and T190 use one `usb_ble` Companion artifact for simultaneous USB
and BLE rather than publishing separate USB-only and BLE-only images.
Heltec E290 and T190 now use their Full Companion artifacts for simultaneous
USB, BLE, and WiFi. Their older `usb_ble`, USB-only, and BLE-only names remain
available only as explicit compatibility builds.
Heltec V3 and base OLED V4 Full Companion also include the former direct WiFi
MQTT Companion capability. Configure and enable MQTT at runtime through
WebConfig; the canonical release therefore publishes the Full image instead of
a second `companion_radio_wifi_mqtt` image. V4 TFT and expansion-kit layouts
remain separate hardware images because their display and I2C wiring differs.
Device power saving is separate from LoRa RXPS. It can be changed in WebConfig
with the **Device power saving** switch or from the text terminal:
@@ -196,8 +205,8 @@ started before WiFi on any combined ESP32 WiFi+BLE Companion to avoid heap
fragmentation. Compile-time prerequisite checks reject inconsistent feature
flags. After linking, the capability sidecar verifies USB, BLE, the OTA CLI,
TempRadio, and each platform's host-folder transport; it also verifies the TCP
terminal, WebConfig, and WiFi seeder on ESP32, plus dedicated logging whenever
the board uses dual CDC.
terminal, WebConfig, and WiFi seeder on ESP32, plus dedicated logging on
nRF52.
On 4 MB ESP32 boards, the full target uses a single 3 MB application partition
so WiFi, BLE, WebConfig, and source-only mOTA fit together. The T-Beam 1W Full
@@ -318,23 +327,31 @@ WiFi credential, status, WebConfig, CLI-tab, and power-save controls. Every
Full Companion provides persistent `get/set usb.logging` and starts with
logging off on a fresh installation.
### Single USB serial port
### ESP32 single USB serial port
On an ESP32 Full Companion without dual CDC, interface `00` has two exclusive
modes. It starts as the ASCII terminal unless a saved logging-on preference
boots directly into the logging terminal. If it is already binary, enter its
text terminal with `+++MESHCORE-TERM-START`, then run `set usb.logging on`; the same TTY emits
plaintext packet/debug logs and continues accepting CLI commands, including
`set usb.logging off`. Turning it off sends the command reply and then returns
that TTY to Binary Companion automatically, including on USB-UART bridges that
cannot detect a cable disconnect. A saved logging-on preference boots directly
into this input-capable logging terminal. BLE and Wi-Fi Companion remain
available while USB is logging.
Every ESP32 Full Companion exposes one USB TTY with two exclusive modes. It
starts as the ASCII terminal unless a saved logging-on preference boots
directly into the logging terminal. If it is already binary, enter its text
terminal with `+++MESHCORE-TERM-START`, then run `set usb.logging on`; the same
TTY emits plaintext packet/debug logs and continues accepting CLI commands,
including `set usb.logging off`. Framed Binary Companion is unavailable on USB
while logging owns the TTY. Turning logging off sends the command reply, stops
the logs, and leaves that TTY in the normal ASCII terminal, matching a fresh
Full installation. Send `+++MESHCORE-TERM-STOP`, or let a Companion app send a
valid framed probe, to switch it to Binary Companion afterward. A saved
logging-on preference boots directly into this input-capable logging terminal.
BLE and Wi-Fi Companion remain available while USB is logging.
### Dual USB serial ports
ESP32 Full Companion uses the repository's Arduino-ESP32 2.x platform base
where the board supports it. RC32 and ESP32-C6 retain their board-required
Arduino 3.x platform, but follow the same one-TTY policy. No ESP32 Full image
creates an optional second CDC interface. The single-TTY behavior applies to
native-USB ESP32-S3 boards and boards using a USB-UART bridge.
Current nRF52 Full Companion and qualified native-USB ESP32-S3 Full Companion
firmware can expose two CDC ACM serial interfaces on one physical USB cable:
### nRF52 dual USB serial ports
Current nRF52 Full Companion firmware can expose two CDC ACM serial interfaces
on one physical USB cable:
- USB interface `00` is the primary Companion, text-terminal, and serial-mOTA
source port. It is always present, starts in ASCII after boot, and
@@ -362,35 +379,30 @@ When logging is on, Linux normally shows two `/dev/ttyACM*` devices. Match the s
`ID_USB_INTERFACE_NUM`, rather than assuming which tty number is assigned. On
Windows they appear as two COM ports; identify them by USB interface instead of
depending on a particular COM number. The nRF52 bootloader temporarily exposes
its normal DFU serial interface during an update. Qualified S3 boards
temporarily expose the ESP32-S3 ROM USB-JTAG serial port during a wired flash.
its normal DFU serial interface during an update.
Opening the logging port prints `MeshCore USB logging port` followed by its
portable identity, `USB CDC 1; interface 02; Linux stable suffix: -if02`.
Opening the nRF52 logging port prints `MeshCore USB logging port` followed by
its portable identity, `USB CDC 1; interface 02; Linux stable suffix: -if02`.
`get usb.logging` reports the same endpoint. Firmware cannot print the exact
`/dev/ttyACM*` or `COM*` name because Linux, macOS, or Windows assigns that name
after USB enumeration; use the `*-if02` link on Linux to obtain the exact path.
For example, `readlink -f /dev/serial/by-id/*-if02` prints the host-assigned
`/dev/ttyACM*` name.
Dual-CDC ESP32-S3 targets are Heltec V4, T-Beam 1W, Station G2/G3, XIAO S3
WIO, Heltec Tracker V2, Meshnology W12, and Nibble Screen/Zero Connect. The
base Heltec V4 profile has completed live two-interface, ROM-flashing, and
logging-off one-interface validation. Full recipes with only one usable TTY
still replace separate transport images; they use the exclusive terminal/log
mode above because firmware cannot add a second interface to a USB-UART bridge
or a single-port USB peripheral.
Every ESP32-S3 Full Companion image uses DIO flash mode, including the RAK3112
and RC32 profiles that do not expose dual CDC. The S3 ROM supports DIO while
and RC32 profiles. The S3 ROM supports DIO while
loading the software bootloader, and some flash configurations fail before the
application starts when a merged image inherits QIO. DIO trades some maximum
flash-read throughput for compatibility; it does not change a board's PSRAM
type or any ordinary non-Full firmware profile.
Point MeshCore Companion software, `meshcli`, and `motatool` at interface `00`.
When enabled and rebooted, point a plaintext reader or USB-connected MQTT
service at interface `02`.
On nRF52, point MeshCore Companion software, `meshcli`, and `motatool` at
interface `00`. When enabled and rebooted, point a plaintext reader or
USB-connected MQTT service at interface `02`. On ESP32, use its only USB TTY
for Binary Companion while logging is off, or for the plaintext CLI/logger
while logging is on; close one consumer before switching modes. Turning
logging off returns to ASCII, so the normal stop token or a valid framed probe
is still required before Binary Companion owns the port.
ESP32 Full Companion exposes this same text terminal on TCP port 5002. Connect
with `nc DEVICE_IP 5002`; no USB control token is needed. USB terminal mode and
+31 -26
View File
@@ -178,12 +178,18 @@ board-qualified serial or Ethernet Companion transport. Bulk builds therefore
omit separate attached-transport, Terminal Chat, and USB-logging artifacts
whenever the exact Full recipe exists. RAK4631 repeater and room-server
Ethernet images remain separate roles. Fresh installs default to logging off.
Heltec V3 and base OLED V4 Full images also contain the former direct Wi-Fi
MQTT Companion capability, configured at runtime through WebConfig, so their
separate `companion_radio_wifi_mqtt` artifacts are omitted from canonical
builds as well.
When Full Companion does not fit but a matching USB Companion does, that USB
artifact also supplies Terminal Chat and replaces its standalone release image.
Heltec E290 and T190 publish a combined USB + BLE Companion. SSD1306 Full
Companion builds use `set display.rotation 90|180|270`; `0` restores the board
default, so a separate rotated release image is not recommended.
Heltec E290 and T190 now publish one Full USB + BLE + WiFi Companion; their old
combined and single-transport names are explicit-build compatibility aliases.
SSD1306 Full Companion builds use `set display.rotation 90|180|270`; `0`
restores the board default, so a separate rotated release image is not
recommended.
Ordinary non-OTA roles also use one artifact for normal operation and USB
logging. Select the saved mode with `set usb.logging off|on`; no `-logging-`
@@ -191,23 +197,22 @@ artifact is emitted. KISS, BLE-only Companion, and constrained LoRa-OTA
receiver images retain their protocol/partition contracts and do not inherit
plaintext USB logging.
Dual-CDC nRF52 and qualified native-USB ESP32-S3 builds keep the multi-role
primary interface on `00`; it starts as an ASCII terminal and automatically
hands a complete `<` frame to Binary Companion. `set usb.logging on reboot`
adds plaintext interface `02`.
Single-TTY ESP32 builds instead use `set usb.logging on` to switch that TTY to
an input-capable plaintext logging terminal. `set usb.logging off` stops the
logs and returns the TTY to Binary Companion after its reply. BLE and Wi-Fi
remain usable while the USB TTY is logging.
nRF52 Full Companion keeps the multi-role primary interface on `00`; it starts
as an ASCII terminal and automatically hands a complete `<` frame to Binary
Companion. `set usb.logging on reboot` adds its plaintext interface `02`.
The dual-CDC ESP32-S3 subset includes Heltec V4, T-Beam 1W, Station G2/G3,
XIAO S3 WIO, Heltec Tracker V2, Meshnology W12, and Nibble Screen/Zero Connect
layouts. Existing single-TTY Full recipes include RAK3112, Heltec RC32,
Heltec V3/WSL3, ThinkNode M2/M5/M7/M9, Heltec V2, LilyGo T-LoRa V2.1.1.6,
and XIAO C3. Additional qualified single-TTY ESP32 profiles include M5Stack
Unit C6L, Heltec Wireless Tracker/Paper/E213/CT62, LilyGo T3S3
SX1262/SX1276, T-Deck, TETH Elite, classic T-Beam SX1262/SX1276, T-Beam S3
Supreme, Ebyte EoRa-S3, Meshadventurer SX1262/SX1268, and XIAO S3.
Every ESP32 Full Companion instead exposes one USB TTY. Logging is off by
default, so the TTY serves the ASCII/Binary Companion switcher. Use
`set usb.logging on` to turn that same TTY into an input-capable plaintext
CLI/logging stream; framed Binary Companion is unavailable on USB while
logging owns it. `set usb.logging off` stops the logs and leaves the TTY in
the normal ASCII terminal, matching a fresh Full installation. Send
`+++MESHCORE-TERM-STOP`, or let a Companion app send a valid framed probe, to
switch from there to Binary Companion. BLE and Wi-Fi Companion remain usable
while the USB TTY is logging. ESP32 Full builds use the repository's
Arduino-ESP32 2.x base where the board supports it; RC32 and ESP32-C6 retain
their board-required Arduino 3.x platform but still expose only one USB TTY.
A second ESP32 CDC interface is not part of the release profile.
## Installation methods
@@ -244,11 +249,11 @@ saving, controllable FEM receive gain, and radio-chip receive gain are saved
settings rather than separate recommended firmware files. Do not substitute a
similarly named physical target.
For hardware with a dual-CDC Full Companion image, the picker recommends that
one normal image instead of separate USB, BLE, ordinary WiFi, and USB-logging
images. Full Companion provides the attached transports and a dedicated
plaintext logging port when enabled without mixing logs into framed Companion
traffic. Logging is off by default, so only primary interface `00` appears; it
starts in ASCII and automatically changes to framed Companion when a complete
`<` frame arrives. Enabling logging and rebooting adds interface `02`.
The picker recommends one Full Companion image instead of separate USB, BLE,
ordinary WiFi, and USB-logging images. On ESP32, logging is off by default so
the one USB TTY starts in ASCII and automatically changes to framed Companion
when a complete `<` frame arrives. Enabling logging gives that TTY to the
plaintext CLI/logger and disables framed USB Companion until logging is turned
off and the normal ASCII-to-binary mode switch occurs. nRF52 Full Companion
retains its optional dedicated interface `02`.
Exact filename search still finds old aliases from earlier releases.
+14 -6
View File
@@ -62,6 +62,7 @@ boot ------> ASCII terminal Binary Companion
ASCII terminal -- +++MESHCORE-TERM-STOP ------> Binary Companion
ASCII terminal -- observable USB disconnect ---> Binary Companion
ESP32 logging terminal -- usb.logging off -----> ASCII terminal
any mode ------- reboot ------------------------> ASCII terminal
```
@@ -144,14 +145,21 @@ mOTA traffic on the same stream.
| Situation | Primary USB behavior |
| --- | --- |
| Full Companion after boot | ASCII; a complete `<` frame switches to Binary Companion |
| Dual-CDC logging enabled | Primary interface still follows the switcher; logs use the optional second interface |
| Single-TTY logging enabled at boot | Logging terminal owns primary USB; automatic `<` detection is disabled |
| nRF52 logging enabled | Primary interface still follows the switcher; logs use the optional second interface |
| ESP32 logging enabled at boot | Logging terminal owns the only USB TTY; automatic `<` detection is disabled |
| nRF52 USB serial mOTA active | mOTA owns primary USB; ASCII and Binary Companion are unavailable there |
| BLE/WiFi/Ethernet/hardware serial | Always Binary Companion and unaffected by the USB mode |
On a single-TTY build, use `set usb.logging off` in its logging terminal before
trying to use primary USB with an app. BLE and WiFi Companion transports remain
available while primary USB is logging.
On every ESP32 Full Companion, use `set usb.logging off` in its logging terminal
before trying to use USB with an app. That command stops logging and returns to
the ordinary ASCII terminal; it does not select Binary Companion. The app's
first valid framed probe can perform the normal automatic switch, or a human
can send `+++MESHCORE-TERM-STOP` first. The framed/binary Companion parser is
disabled while logging owns that TTY. BLE and WiFi Companion transports remain
available while USB is logging. ESP32 Full builds use the Arduino-ESP32 2.x
base where supported; RC32 and ESP32-C6 keep their board-required Arduino 3.x
platform but follow the same one-TTY policy. No ESP32 Full build enumerates a
second CDC interface.
On nRF52, serial `motatool` is also text-first. Its exact initial
`ota folder on` line is recognized in either startup ASCII or Binary Companion
@@ -235,7 +243,7 @@ prefix it with another character if it is needed as command text.
If `meshcli` cannot connect:
1. close every terminal or logging reader using the primary interface;
2. confirm that saved single-TTY logging is off;
2. on ESP32, confirm that saved USB logging is off;
3. reboot and let `meshcli` be the first process to open the data interface;
4. use the stable `/dev/serial/by-id/*-if00` path on Linux when available;
5. if a prompt appears after one second, the client's first frame was not
+7 -4
View File
@@ -120,10 +120,13 @@ set usb.logging {on|off} [reboot]
Shows or changes persistent live USB debug and packet output in an ordinary
USB-loggable Companion or Full Companion. USB Companion and Full start off on
a fresh install with logging disabled to protect framed traffic.
Dual-CDC Full changes its interface count after a reboot and keeps Companion on
interface `00`. Single-TTY Full needs no reboot: logging uses the active text
terminal, which continues accepting `set usb.logging off` and automatically
restores Binary Companion after that reply.
nRF52 Full changes its interface count after a reboot and keeps Companion on
interface `00`. Every ESP32 Full Companion has one TTY and needs no reboot:
logging uses the active text terminal, disables framed Binary Companion on
USB, and continues accepting `set usb.logging off`. Turning logging off leaves
the port in the normal ASCII terminal, matching fresh firmware. Send
`+++MESHCORE-TERM-STOP`, or let a Companion app send a valid framed probe, to
switch it to Binary Companion afterward.
```
reboot
+1 -1
View File
@@ -86,7 +86,7 @@
#if COMPANION_FEATURE_DEDICATED_USB_LOGGING \
&& !(defined(MESH_DUAL_CDC_LOGGING) \
&& defined(ENABLE_USB_INTERFACE))
#error "COMPANION_FEATURE_DEDICATED_USB_LOGGING requires dual-CDC USB"
#error "COMPANION_FEATURE_DEDICATED_USB_LOGGING requires nRF52 dual-CDC USB"
#endif
#if defined(COMPANION_RADIO_FULL) && !defined(BLE_PIN_CODE)
+1 -1
View File
@@ -1500,7 +1500,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.cad_max_duration_ms = 0;
#if defined(ENABLE_USB_INTERFACE)
// Keep a USB Companion's primary stream exclusively framed on a fresh
// install. Full dual-CDC builds can add a diagnostics port; single-TTY
// install. nRF52 Full can add a diagnostics port; ESP32 and other single-TTY
// builds switch the primary stream into the text terminal before logs.
_prefs.usb_logging_enabled = 0;
#else
+19 -10
View File
@@ -596,8 +596,8 @@ static void serviceUsbTerminal() {
// logging repeater: plaintext diagnostics plus an input-capable CLI. Put the
// Companion interface into passthrough before it can mix framed traffic with
// logs. `set usb.logging off` remains available here and returns this TTY to
// Binary Companion after its command reply, even on USB-UART bridges that
// cannot detect a host disconnect.
// the normal ASCII terminal, matching a fresh Full install. Binary Companion
// starts only after the usual terminal-stop token or framed startup probe.
#if MESH_USB_LOGGING_AVAILABLE
if (!mesh::hasDedicatedUsbLoggingPort()) {
if (mesh::isUsbLoggingEnabled()) {
@@ -607,8 +607,15 @@ static void serviceUsbTerminal() {
}
usb_logging_terminal_mode = true;
} else if (usb_logging_terminal_mode && the_mesh.isTerminalMode()) {
leaveUsbTerminalMode(true);
return;
// Logging may also be disabled over BLE/WiFi. Stop treating this session
// as the logging terminal, but keep the ordinary ASCII terminal active;
// do not silently change the USB protocol underneath an idle host. A
// remote mode change also cancels any partially typed USB command before
// drawing a fresh prompt.
usb_logging_terminal_mode = false;
clearUsbTerminalLine();
usb_terminal_discard_line = false;
Serial.print("\r\nUSB logging off; ASCII terminal active\r\n> ");
}
}
#endif
@@ -698,8 +705,10 @@ static void serviceUsbTerminal() {
#if MESH_USB_LOGGING_AVAILABLE
if (usb_logging_terminal_mode
&& !mesh::isUsbLoggingEnabled()) {
leaveUsbTerminalMode(true);
return;
// The command reply belongs to the ASCII session. Leave that session
// active so a separate, observable mode switch is required before the
// port accepts framed Binary Companion traffic again.
usb_logging_terminal_mode = false;
}
#endif
Serial.print("> ");
@@ -1422,8 +1431,8 @@ void setup() {
#endif
// nRF52 cannot decide whether to add its optional logging CDC interface
// until the saved Companion preferences above are available. ESP32 already
// fixed its descriptor from the early NVS mirror, so this is harmless there.
// until the saved Companion preferences above are available. Single-TTY
// platforms have no separate port, so this is a harmless no-op there.
mesh::beginUsbLoggingPort();
// Load WiFi state before bringing up either wireless stack.
@@ -1576,8 +1585,8 @@ void loop() {
#if defined(NRF52_PLATFORM)
board.feedWatchdog();
#endif
// Identify CDC 1 when a terminal opens it. Doing this on the connection edge
// avoids losing the marker during boot before the host has opened the port.
// Identify nRF52 CDC 1 when a terminal opens it. Doing this on the connection
// edge avoids losing the marker before the host has opened the port.
mesh::serviceUsbLoggingPort();
#if defined(ENABLE_USB_INTERFACE) && defined(COMPANION_RADIO_FULL)
expireUsbBinaryStartupProbeBeforeDispatch();
@@ -141,9 +141,10 @@ def update_catalog(catalog: dict, release_files: dict[str, list[Path]], args: ar
"provide USB packet logging and direct WiFi MQTT in one image with a "
"persistent off/USB/WiFi/both selector. Host software can consume the "
"USB serial log and publish it separately. Full Companion replaces "
"older USB-logging images. Dual-CDC builds add a separate plaintext "
"interface after reboot; single-TTY builds use an input-capable "
"logging terminal on their existing port. "
"older USB-logging images. nRF52 Full Companion can add a separate "
"plaintext interface after reboot; ESP32 Full Companion uses its "
"one USB TTY as an input-capable logging terminal and disables "
"framed USB Companion while logging. "
"Open Release notes for role, hardware, installation, and partition "
"requirements."
)
@@ -279,19 +280,11 @@ def update_catalog(catalog: dict, release_files: dict[str, list[Path]], args: ar
notes = common.normalize_nrf52_full_companion_metadata(
firmware, notes
)
elif device_type == "esp32" and any(
common.ESP32_DUAL_CDC_FULL_RE.match(identity)
is not None
for identity in identities
):
notes = common.normalize_esp32_dual_cdc_full_companion_metadata(
firmware, notes
)
elif device_type == "esp32" and any(
"companion_radio_full" in identity.lower()
for identity in identities
):
notes = common.normalize_esp32_single_tty_full_companion_metadata(
notes = common.normalize_esp32_full_companion_metadata(
firmware, notes
)
if any("-logging" in identity.lower() for identity in identities):
+16 -86
View File
@@ -19,16 +19,6 @@ VERSION_LABEL_RE = re.compile(r"v\d+(?:\.\d+)+")
CATALOG_VERSION_TEXT_RE = re.compile(
r"Keymind Cascade MeshCore v[0-9][A-Za-z0-9_.-]*"
)
ESP32_DUAL_CDC_FULL_RE = re.compile(
r"^(?:"
r"heltec_v4(?:_2_v4_3|_3)?(?:_r8)?(?:_tft)?|"
r"LilyGo_TBeam_1W|Station_G2|Station_G3_ESP32|Xiao_S3_WIO|"
r"heltec_tracker_v2|meshnology_w12|"
r"nibble_(?:screen|zero)_connect"
r")_companion_radio_full(?:_|$)",
flags=re.IGNORECASE,
)
LEGACY_PORTABLE_CEILING_EXCEPTIONS = {
"heltec_ct62_repeater_lora_ota_no_external_sensors",
"heltec_tracker_v1_1_repeater_observer_mqtt",
@@ -344,78 +334,25 @@ def normalize_nrf52_full_companion_metadata(
return "\n\n".join(paragraphs)
def normalize_esp32_dual_cdc_full_companion_metadata(
def normalize_esp32_full_companion_metadata(
firmware: dict, notes: str
) -> str:
"""Describe a native-USB ESP32-S3 Full Companion accurately."""
"""Describe the canonical single-TTY ESP32 Full Companion accurately."""
firmware["title"] = "Full Companion"
firmware["subTitle"] = (
"USB Companion + optional USB logging + BLE + Wi-Fi + LoRa OTA source"
"USB Companion or logging + BLE + Wi-Fi + LoRa OTA source"
)
profile = (
"PROFILE - Native-USB ESP32-S3 Full Companion: one USB cable always "
"exposes interface 00 for Binary Companion, the text terminal, flashing, and "
"serial mOTA source traffic. Fresh installs default USB logging off and "
"do not enumerate interface 02. Enabling logging and rebooting adds "
"interface 02 as a separate plaintext packet/debug port. BLE, Wi-Fi "
"Companion on TCP 5000, "
"WebConfig, TCP mOTA seeding on 5001, and the text terminal on 5002 "
"remain available. Use set usb.logging on reboot or set usb.logging "
"off reboot to save and apply the interface count."
)
logging_use = (
"LOGGING USE - Point Companion software, meshcli, motatool, and wired "
"flashing at USB interface 00. After enabling logging and rebooting, "
"point a USB-connected MQTT/logging reader at interface 02. Match the "
"USB interface number rather than assuming a tty or COM port number. "
"Input received on interface 02 is ignored and cannot reboot the board."
)
selection = (
"SELECTION - One Full image for this exact native-USB hardware layout "
"replaces separate USB, BLE, ordinary Wi-Fi, and USB-logging images."
)
paragraphs: list[str] = []
profile_added = False
selection_added = False
for paragraph in notes.split("\n\n"):
if paragraph.startswith("PROFILE "):
if not profile_added:
paragraphs.append(profile)
profile_added = True
continue
if paragraph.startswith("LOGGING USE "):
continue
if paragraph.startswith("SELECTION "):
if not selection_added:
paragraphs.append(selection)
selection_added = True
continue
paragraphs.append(paragraph)
if not profile_added:
paragraphs.append(profile)
if not selection_added:
paragraphs.append(selection)
paragraphs.append(logging_use)
return "\n\n".join(paragraphs)
def normalize_esp32_single_tty_full_companion_metadata(
firmware: dict, notes: str
) -> str:
"""Describe an ESP32 Full Companion whose USB path has one TTY."""
firmware["title"] = "Full Companion"
firmware["subTitle"] = (
"USB Companion/logging + BLE + Wi-Fi + LoRa OTA source"
)
profile = (
"PROFILE - Single-TTY ESP32 Full Companion: the USB TTY defaults to "
"Binary Companion on a fresh install. BLE, Wi-Fi Companion on TCP "
"PROFILE - ESP32 Full Companion: its single USB TTY starts with USB "
"logging off and uses the ASCII/Binary Companion switcher. BLE, "
"Wi-Fi Companion on TCP "
"5000, WebConfig, TCP mOTA seeding on 5001, and the text terminal on "
"5002 remain available. Enter the USB text terminal and use set "
"usb.logging on to turn that TTY into an input-capable plaintext "
"packet/debug stream. Use set usb.logging off to stop diagnostics; "
"after its reply, the TTY returns to Binary Companion automatically. "
"after its reply, the TTY remains in the normal ASCII terminal, matching "
"fresh Full firmware. Then use the normal terminal stop token or let a "
"Companion app send a valid framed probe to select Binary Companion. "
"The saved logging choice is restored at boot."
)
logging_use = (
@@ -483,9 +420,9 @@ def resolve_release_identity(
if candidate in release_files:
return candidate, False
# Full Companion replaces the old USB-only logging artifact. Dual-CDC
# hardware uses its second interface; single-TTY hardware safely switches
# its primary port into an input-capable plaintext logging terminal.
# Full Companion replaces the old USB-only logging artifact. ESP32 safely
# switches its one TTY between framed Companion and an input-capable
# plaintext logging terminal; nRF52 can expose a second interface.
for candidate in candidates:
if not candidate.endswith("-logging"):
continue
@@ -777,9 +714,9 @@ def update_catalog(catalog: dict, release_files: dict[str, list[Path]], args: ar
"USB + Wi-Fi observer and ESP-NOW bridge builds. Unified observers "
"provide persistent off/USB/WiFi/both output selection. Full Companion "
"replaces separate BLE, USB, ordinary Wi-Fi, and USB-logging choices "
"with one image. Dual-CDC builds can add a separate plaintext port; "
"single-TTY builds switch that port into an input-capable logging "
"terminal. Open Release notes for role, "
"with one image. nRF52 Full Companion can add a separate plaintext "
"port; ESP32 Full Companion switches its one USB TTY into an "
"input-capable logging terminal. Open Release notes for role, "
"hardware, installation, and partition requirements."
)
@@ -874,18 +811,11 @@ def update_catalog(catalog: dict, release_files: dict[str, list[Path]], args: ar
notes = normalize_nrf52_full_companion_metadata(
firmware, notes
)
elif device_type == "esp32" and any(
ESP32_DUAL_CDC_FULL_RE.match(identity) is not None
for identity in resolved_identities
):
notes = normalize_esp32_dual_cdc_full_companion_metadata(
firmware, notes
)
elif device_type == "esp32" and any(
"companion_radio_full" in identity.lower()
for identity in resolved_identities
):
notes = normalize_esp32_single_tty_full_companion_metadata(
notes = normalize_esp32_full_companion_metadata(
firmware, notes
)
-22
View File
@@ -111,28 +111,6 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/
[esp32_s3_full]
board_build.flash_mode = dio
; Full Companion profiles on boards whose connector is wired to the ESP32-S3
; native USB peripheral use Arduino-ESP32 3.x TinyUSB. This supplies two real
; CDC ACM interfaces: CDC 0 for framed Companion traffic and optional CDC 1
; for plaintext diagnostics. Do not apply this section to USB-UART-bridge
; boards; firmware cannot add an interface to an external bridge chip.
[esp32_s3_dual_cdc_full]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.311/platform-espressif32.zip
; Pin the matching Arduino core as an explicit project dependency. Standard
; ESP32 profiles use Arduino 2.x under the same PlatformIO package name, so an
; alternating build otherwise can leave this Arduino 3.x profile resolving the
; wrong global framework directory (or no compatible directory at all).
platform_packages =
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32/releases/download/3.3.11/esp32-core-3.3.11.tar.xz
framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.3.11/esp32-core-3.3.11-libs.tar.xz
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags =
-D ARDUINO_USB_MODE=1
-D ARDUINO_USB_CDC_ON_BOOT=0
build_flags =
-D ARDUINO_USB_MODE=0
-D ARDUINO_USB_CDC_ON_BOOT=1
; ----------------- NRF52 ---------------------
[nrf52_base]
+7
View File
@@ -15,6 +15,13 @@ def parse_flash_size(value):
if os.environ.get("MESHCORE_ESP32_FULL_BUILD") == "1":
board = env.BoardConfig()
flash_size = parse_flash_size(board.get("upload.flash_size", "4MB"))
if str(board.get("build.mcu", "")).lower() == "esp32s3":
# Every S3 Full image must remain ROM-bootable across the supported
# flash parts. Some board manifests default to QIO even though their
# ROM/bootloader chain only starts a merged image reliably in DIO.
# PSRAM mode is an independent setting and is intentionally untouched.
board.update("build.flash_mode", "dio")
print("ESP32-S3 FULL build: forcing ROM-compatible DIO flash mode")
companion_radio_full = (
os.environ.get("MESHCORE_COMPANION_RADIO_FULL") == "1"
)
+2 -1
View File
@@ -6,6 +6,7 @@
#include "TxtDataHelpers.h"
#include "AdvertDataHelpers.h"
#include "AlertReporter.h" // for alertReporterBannedChannelMatch()
#include "sensors/EnvironmentI2CConfig.h"
#if defined(NRF52_PLATFORM)
#include "AtomicFileWriter.h"
#include "ota/OtaFlashLayout_nrf52.h"
@@ -2785,7 +2786,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
}
} else if (memcmp(command, "sensor", 6) == 0) {
// I2C
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
#if ENV_HAS_SECONDARY_I2C
sprintf(reply, "I2C Wire1: SDA=%s,SCL=%s\r\n", STR(ENV_PIN_SDA), STR(ENV_PIN_SCL));
#elif defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
sprintf(reply, "I2C Wire: SDA=%s, SCL=%s\r\n", STR(PIN_BOARD_SDA), STR(PIN_BOARD_SCL));
+6 -113
View File
@@ -3,32 +3,18 @@
#if defined(ARDUINO)
#include <Arduino.h>
#include <atomic>
#include <new>
#if defined(MESH_DUAL_CDC_LOGGING)
#if !defined(COMPANION_FEATURE_DEDICATED_USB_LOGGING) || \
!COMPANION_FEATURE_DEDICATED_USB_LOGGING
#error "MESH_DUAL_CDC_LOGGING requires its dedicated Companion capability"
#endif
#if defined(NRF52_PLATFORM)
#define MESH_NRF52_DUAL_CDC_LOGGING 1
#include <Adafruit_TinyUSB.h>
#if !defined(CFG_TUD_CDC) || CFG_TUD_CDC < 2
#error "MESH_DUAL_CDC_LOGGING requires CFG_TUD_CDC >= 2"
#endif
#elif defined(ESP32_PLATFORM) && defined(SOC_USB_OTG_SUPPORTED) && \
SOC_USB_OTG_SUPPORTED && !ARDUINO_USB_MODE
#define MESH_ESP32_DUAL_CDC_LOGGING 1
#include <USB.h>
#include <USBCDC.h>
#include <nvs.h>
#include <nvs_flash.h>
#if !defined(CONFIG_TINYUSB_CDC_MAX_PORTS) || \
CONFIG_TINYUSB_CDC_MAX_PORTS < 2
#error "MESH_DUAL_CDC_LOGGING requires two ESP32 TinyUSB CDC ports"
#endif
#else
#error "MESH_DUAL_CDC_LOGGING requires nRF52 or ESP32 native TinyUSB"
#if !defined(NRF52_PLATFORM)
#error "MESH_DUAL_CDC_LOGGING is supported only by nRF52 Full Companion"
#endif
#include <Adafruit_TinyUSB.h>
#if !defined(CFG_TUD_CDC) || CFG_TUD_CDC < 2
#error "MESH_DUAL_CDC_LOGGING requires CFG_TUD_CDC >= 2"
#endif
#endif
@@ -60,68 +46,7 @@ static NullUsbLoggingStream null_usb_logging_stream;
static bool dedicated_usb_logging_port_configured = false;
static bool dedicated_usb_logging_port_started = false;
static bool dedicated_usb_logging_port_connected = false;
#if defined(MESH_NRF52_DUAL_CDC_LOGGING)
static Adafruit_USBD_CDC dedicated_usb_logging_port;
#elif defined(MESH_ESP32_DUAL_CDC_LOGGING)
static constexpr char USB_LOGGING_NVS_NAMESPACE[] = "mesh_usb";
static constexpr char USB_LOGGING_NVS_KEY[] = "log_port";
alignas(USBCDC) static uint8_t dedicated_usb_logging_port_storage[
sizeof(USBCDC)];
static USBCDC* dedicated_usb_logging_port = nullptr;
static bool readEsp32UsbLoggingBootPreference() {
// Missing or unreadable state must stay protocol-safe: Full Companion then
// exposes only its primary framed CDC interface. The normal preference load
// below mirrors an explicitly enabled setting for the following reboot.
if (nvs_flash_init() != ESP_OK) return false;
nvs_handle_t handle;
if (nvs_open(USB_LOGGING_NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
return false;
}
uint8_t value = 0;
const esp_err_t result = nvs_get_u8(handle, USB_LOGGING_NVS_KEY, &value);
nvs_close(handle);
return result == ESP_OK && value != 0;
}
static bool writeEsp32UsbLoggingBootPreference(bool enabled) {
if (nvs_flash_init() != ESP_OK) return false;
nvs_handle_t handle;
if (nvs_open(USB_LOGGING_NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
return false;
}
uint8_t current = 0;
esp_err_t result = nvs_get_u8(handle, USB_LOGGING_NVS_KEY, &current);
if (result == ESP_OK && current == (enabled ? 1 : 0)) {
nvs_close(handle);
return true;
}
result = nvs_set_u8(handle, USB_LOGGING_NVS_KEY, enabled ? 1 : 0);
if (result == ESP_OK) result = nvs_commit(handle);
nvs_close(handle);
return result == ESP_OK;
}
class Esp32UsbLoggingBootstrap {
public:
Esp32UsbLoggingBootstrap() {
const bool enabled = readEsp32UsbLoggingBootPreference();
usb_logging_enabled.store(enabled, std::memory_order_relaxed);
usb_logging_preference_known.store(true, std::memory_order_relaxed);
if (!enabled) return;
dedicated_usb_logging_port = new (dedicated_usb_logging_port_storage)
USBCDC(1);
dedicated_usb_logging_port_configured = true;
}
};
static Esp32UsbLoggingBootstrap esp32_usb_logging_bootstrap;
#endif
#endif
bool isUsbLoggingEnabled() {
@@ -134,12 +59,8 @@ void setUsbLoggingEnabled(bool enabled) {
}
bool saveUsbLoggingBootPreference(bool enabled) {
#if defined(MESH_ESP32_DUAL_CDC_LOGGING)
return writeEsp32UsbLoggingBootPreference(enabled);
#else
(void)enabled;
return true;
#endif
}
void beginUsbLoggingPort() {
@@ -150,27 +71,13 @@ void beginUsbLoggingPort() {
return;
}
#if defined(MESH_ESP32_DUAL_CDC_LOGGING)
// ESP32 registers its descriptor before app_main starts native USB. If the
// saved boot mirror omitted CDC 1, enabling logging takes effect after the
// next reboot instead of trying to mutate a live USB descriptor.
if (dedicated_usb_logging_port == nullptr) return;
// Only the Companion interface may use the CDC control-line reboot gesture.
// A terminal opening or closing the diagnostics interface must never reboot
// the node or put it into the ROM downloader.
dedicated_usb_logging_port->enableReboot(false);
dedicated_usb_logging_port->setTxTimeoutMs(0);
dedicated_usb_logging_port->begin(115200);
#elif defined(MESH_NRF52_DUAL_CDC_LOGGING)
dedicated_usb_logging_port.begin(115200);
// `begin()` installs the core's generic "TinyUSB Serial" name. Replace it
// before (re-)enumeration so host USB tools can distinguish the log endpoint.
dedicated_usb_logging_port.setStringDescriptor("MeshCore Logging");
dedicated_usb_logging_port_configured = true;
#endif
dedicated_usb_logging_port_started = true;
#if defined(MESH_NRF52_DUAL_CDC_LOGGING)
// The nRF52 core starts TinyUSB before setup(). If the host completed
// enumeration in that small window, reconnect once so it reads the expanded
// two-CDC descriptor. Usually enumeration has not completed and no reconnect
@@ -181,20 +88,13 @@ void beginUsbLoggingPort() {
TinyUSBDevice.attach();
}
#endif
#endif
}
void serviceUsbLoggingPort() {
#if defined(MESH_DUAL_CDC_LOGGING)
bool connected = false;
#if defined(MESH_NRF52_DUAL_CDC_LOGGING)
connected = dedicated_usb_logging_port_started
&& dedicated_usb_logging_port.dtr();
#elif defined(MESH_ESP32_DUAL_CDC_LOGGING)
connected = dedicated_usb_logging_port_started
&& dedicated_usb_logging_port != nullptr
&& (bool)*dedicated_usb_logging_port;
#endif
if (connected && !dedicated_usb_logging_port_connected
&& isUsbLoggingEnabled()) {
@@ -208,16 +108,9 @@ void serviceUsbLoggingPort() {
Stream& usbLoggingPort() {
#if defined(MESH_DUAL_CDC_LOGGING)
#if defined(MESH_NRF52_DUAL_CDC_LOGGING)
if (isUsbLoggingEnabled() && dedicated_usb_logging_port_started) {
return dedicated_usb_logging_port;
}
#elif defined(MESH_ESP32_DUAL_CDC_LOGGING)
if (isUsbLoggingEnabled() && dedicated_usb_logging_port_started
&& dedicated_usb_logging_port != nullptr) {
return *dedicated_usb_logging_port;
}
#endif
return null_usb_logging_stream;
#else
if (!isUsbLoggingEnabled()) return null_usb_logging_stream;
+4 -4
View File
@@ -21,13 +21,13 @@ namespace mesh {
bool isUsbLoggingEnabled();
void setUsbLoggingEnabled(bool enabled);
// ESP32 native USB starts before setup(), so its next-boot interface count is
// mirrored outside the normal role preferences. Other platforms need no
// mirror. Returns false only when that mirror could not be saved.
// Kept as a platform-neutral persistence hook. Single-TTY builds need no
// descriptor mirror; nRF52 selects its optional second interface after loading
// the normal role preferences.
bool saveUsbLoggingBootPreference(bool enabled);
// Start the optional dedicated USB logging interface. Ordinary and single-TTY
// builds use Serial; dual-CDC Full Companion builds use a second CDC ACM port.
// builds use Serial; nRF52 Full Companion uses a second CDC ACM port.
void beginUsbLoggingPort();
// Emit a short identity marker whenever a host opens the dedicated logging
// endpoint. The device can report its USB interface, but the host alone chooses
+3 -3
View File
@@ -3,6 +3,8 @@
#include <stddef.h>
#include <stdint.h>
#include "sensors/EnvironmentI2CConfig.h"
// Simulator targets do not include RadioLib, but board pin macros can still
// use its sentinel value. Keep the fallback local to this header.
#ifndef RADIOLIB_NC
@@ -150,10 +152,8 @@ inline bool isFirmwareReserved(uint8_t pin) {
#ifdef PIN_WIRE_SCL
PIN_WIRE_SCL,
#endif
#ifdef ENV_PIN_SDA
#if ENV_HAS_SECONDARY_I2C
ENV_PIN_SDA,
#endif
#ifdef ENV_PIN_SCL
ENV_PIN_SCL,
#endif
#ifdef I2C_SDA
+7 -1
View File
@@ -1292,7 +1292,11 @@ void MQTTBridge::initializeWiFiInTask() {
// Enable automatic reconnection - ESP32 will handle reconnection automatically
WiFi.setAutoReconnect(true);
#if !defined(ESP_ARDUINO_VERSION_MAJOR) || ESP_ARDUINO_VERSION_MAJOR < 3
// Arduino-ESP32 3.x removed setAutoConnect(); begin() plus
// setAutoReconnect() is the supported equivalent there.
WiFi.setAutoConnect(true);
#endif
// Set up WiFi event handlers for better diagnostics and immediate disconnection
// detection. Register ONCE - the bridge is reused across restarts (e.g. stopped
@@ -4098,7 +4102,9 @@ bool MQTTBridge::syncTimeWithNTP(bool force, bool primary_only) {
for (int s = 0; s < server_count && !ntp_ok; s++) {
const char* server = servers[s];
MQTT_DEBUG_PRINTLN("SNTP fallback trying %s...", server);
if (sntp_enabled()) sntp_stop();
// configTime() already stops and reconfigures an active SNTP client on
// both Arduino-ESP32 2.x and 3.x. Avoid the legacy unprefixed stop APIs,
// which are no longer exported by esp_sntp.h in Arduino 3.x.
sntp_set_sync_status(SNTP_SYNC_STATUS_RESET);
configTime(0, 0, server);
for (int i = 0; i < 20; i++) {
@@ -0,0 +1,11 @@
#pragma once
// ENV_PIN_SDA / ENV_PIN_SCL select a dedicated environmental-sensor bus.
// GPIO 0 is valid on supported targets, while negative values are the common
// disconnected-pin sentinel. Keep every consumer on the same validity rule.
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL) \
&& (ENV_PIN_SDA >= 0) && (ENV_PIN_SCL >= 0)
#define ENV_HAS_SECONDARY_I2C 1
#else
#define ENV_HAS_SECONDARY_I2C 0
#endif
@@ -1,8 +1,9 @@
#include "EnvironmentSensorManager.h"
#include "EnvironmentI2CConfig.h"
#include <Wire.h>
#if ENV_PIN_SDA && ENV_PIN_SCL
#if ENV_HAS_SECONDARY_I2C
#define TELEM_WIRE &Wire1 // Use Wire1 as the I2C bus for Environment Sensors
#else
#define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors
@@ -707,7 +708,7 @@ bool EnvironmentSensorManager::begin() {
#endif
#endif
#if ENV_PIN_SDA && ENV_PIN_SCL
#if ENV_HAS_SECONDARY_I2C
#ifdef NRF52_PLATFORM
Wire1.setPins(ENV_PIN_SDA, ENV_PIN_SCL);
Wire1.setClock(100000);
+182
View File
@@ -106,6 +106,61 @@ require("Heltec_E290_companion_usb_ble", "build_flags", "ENABLE_USB_INTERFACE")
require("Heltec_E290_companion_usb_ble", "build_flags", "BLE_PIN_CODE=123456")
require("Heltec_T190_companion_radio_usb_ble_", "build_flags", "ENABLE_USB_INTERFACE")
require("Heltec_T190_companion_radio_usb_ble_", "build_flags", "BLE_PIN_CODE=123456")
require("heltec_v4_expansionkit_tft_companion_radio_full_femon", "platform", "platformio/espressif32@6.11.0")
require("heltec_v4_expansionkit_tft_companion_radio_full_femon", "build_flags", "ARDUINO_USB_MODE=1")
reject("heltec_v4_expansionkit_tft_companion_radio_full_femon", "build_flags", "ARDUINO_USB_MODE=0")
reject("heltec_v4_expansionkit_tft_companion_radio_full_femon", "platform", "55.03.311")
reject("heltec_v4_expansionkit_tft_companion_radio_full_femon", "platform_packages", "esp32-core-3.3.11")
# ESP32 Full uses the board-specific Arduino-ESP32 2.x recipe and one USB TTY. The
# independently qualified RC32 stays on Arduino 3.x, but it must not enable a
# second CDC interface either.
for env_name in (
"LilyGo_TBeam_1W_companion_radio_full",
"RAK_3112_companion_radio_full",
"Station_G2_companion_radio_full",
"ThinkNode_M2_companion_radio_full",
"ThinkNode_M5_companion_radio_full",
"ThinkNode_M7_companion_radio_full",
"Xiao_S3_WIO_companion_radio_full",
"heltec_v4_r8_companion_radio_full",
"heltec_v4_r8_tft_companion_radio_full",
"meshnology_w12_companion_radio_full",
"heltec_v4_2_v4_3_companion_radio_full_femon",
"heltec_v4_3_companion_radio_full_femoff",
"heltec_v4_expansionkit_tft_companion_radio_full_femon",
"heltec_v4_tft_companion_radio_full_femon",
"heltec_v4_3_tft_companion_radio_full_femoff",
"nibble_zero_connect_companion_radio_full_",
"nibble_screen_connect_companion_radio_full_",
"Station_G3_ESP32_companion_radio_full",
"Heltec_v3_companion_radio_full",
"heltec_tracker_v2_companion_radio_full_femon",
):
require(env_name, "platform", "platformio/espressif32@6.11.0")
reject(env_name, "platform", "55.03.311")
reject(env_name, "build_flags", "MESH_DUAL_CDC_LOGGING")
reject(env_name, "build_flags", "COMPANION_FEATURE_DEDICATED_USB_LOGGING")
reject(env_name, "build_flags", "CFG_TUD_CDC=2")
for env_name in (
"heltec_rc32_without_display_companion_radio_full",
"heltec_rc32_companion_radio_full",
):
require(env_name, "platform", "55.03.311")
reject(env_name, "build_flags", "MESH_DUAL_CDC_LOGGING")
reject(env_name, "build_flags", "COMPANION_FEATURE_DEDICATED_USB_LOGGING")
reject(env_name, "build_flags", "CFG_TUD_CDC=2")
# V3/V4 Full are the one canonical Companion image for each base OLED layout,
# including the former separately published direct-WiFi-MQTT capability.
for env_name in (
"Heltec_v3_companion_radio_full",
"heltec_v4_2_v4_3_companion_radio_full_femon",
):
require(env_name, "build_flags", "WITH_MQTT_BRIDGE=1")
require(env_name, "build_src_filter", "helpers/bridges/MQTTBridge.cpp")
require(env_name, "lib_deps", "PsychicMqttClient")
rc32_repeater = "heltec_rc32_repeater"
require(rc32_repeater, "platform", "55.03.311")
@@ -115,6 +170,55 @@ require(rc32_repeater, "build_flags", "RC32_PERIPHERAL_WARMUP_MS=100")
require(rc32_repeater, "build_flags", "SX126X_ALLOW_RECOVERABLE_INIT_STATUS=1")
reject(rc32_repeater, "build_flags", "ESP32_CPU_FREQ=160")
# Environmental telemetry must follow the verified connector bus without
# creating a second Wire instance on the same pins as the board bus.
require("Heltec_t096_companion_radio_usb_femon", "build_flags", "ENV_PIN_SDA=PIN_WIRE1_SDA")
require("Heltec_t096_companion_radio_usb_femon", "build_flags", "ENV_PIN_SCL=PIN_WIRE1_SCL")
require("Heltec_v3_companion_radio_wifi", "build_flags", "ENV_PIN_SDA=33")
require("Heltec_v3_companion_radio_wifi", "build_flags", "ENV_PIN_SCL=34")
for env_name in (
"heltec_rc32_without_display_sensor",
"heltec_rc32_sensor",
):
require(env_name, "build_flags", "PIN_BOARD_SDA=21")
require(env_name, "build_flags", "PIN_BOARD_SCL=18")
reject(env_name, "build_flags", "ENV_PIN_")
require("meshnology_w12_sensor", "build_flags", "PIN_BOARD_SDA=17")
require("meshnology_w12_sensor", "build_flags", "PIN_BOARD_SCL=18")
reject("meshnology_w12_sensor", "build_flags", "ENV_PIN_")
for env_name in (
"heltec_v4_sensor",
"heltec_v4_2_v4_3_companion_radio_full_femon",
):
require(env_name, "build_flags", "PIN_BOARD_SDA=17")
require(env_name, "build_flags", "PIN_BOARD_SCL=18")
require(env_name, "build_flags", "ENV_PIN_SDA=4")
require(env_name, "build_flags", "ENV_PIN_SCL=3")
for env_name in (
"heltec_v4_tft_sensor",
"heltec_v4_expansionkit_tft_companion_radio_ble_femon",
"heltec_v4_expansionkit_tft_companion_radio_full_femon",
"heltec_v4_tft_companion_radio_full_femon",
):
require(env_name, "build_flags", "PIN_BOARD_SDA=4")
require(env_name, "build_flags", "PIN_BOARD_SCL=3")
reject(env_name, "build_flags", "ENV_PIN_")
for env_name in (
"RAK_3112_sensor",
"RAK_3112_companion_radio_full",
):
require(env_name, "build_flags", "PIN_BOARD_SDA=9")
require(env_name, "build_flags", "PIN_BOARD_SCL=40")
require(env_name, "build_flags", "ENV_PIN_SDA=17")
require(env_name, "build_flags", "ENV_PIN_SCL=18")
reject(env_name, "build_flags", "ENV_PIN_SDA=33")
reject(env_name, "build_flags", "ENV_PIN_SCL=34")
for env_name in (
"Heltec_t114_without_display_repeater",
"Heltec_t114_repeater",
@@ -159,6 +263,72 @@ require(rak_usb, "build_flags", "RAK_BOARD")
require(rak_usb, "build_flags", "FORCE_GPS_ALIVE")
'
# Every qualified ESP32 Companion layout publishes one Full image even when
# its historical recipe had no separately named WiFi target. The transport
# aliases remain directly buildable, but canonical release resolution must
# replace each with that exact board's Full target.
init_project_context >/dev/null
while IFS='|' read -r source_env full_env build_base; do
[ "${PIO_ENV_PLATFORM_BY_NAME[$full_env]:-}" = ESP32_PLATFORM ] \
|| fail "$full_env was not registered as ESP32 Full Companion"
[ "$(get_pio_build_env "$full_env")" = "$build_base" ] \
|| fail "$full_env did not retain its exact board recipe"
[ "$(get_esp32_full_companion_replacement "$source_env")" = "$full_env" ] \
|| fail "$source_env did not map to $full_env"
is_redundant_bulk_build_target "$source_env" \
|| fail "$source_env remained beside its Full Companion"
done <<'FULL_COMPANION_SPECS'
LilyGo_Tlora_C6_companion_radio_ble_|LilyGo_Tlora_C6_companion_radio_full_|LilyGo_Tlora_C6_companion_radio_ble_
Meshimi_companion_radio_ble_|Meshimi_companion_radio_full_|Meshimi_companion_radio_ble_
WHY2025_badge_companion_radio_ble_|WHY2025_badge_companion_radio_full_|WHY2025_badge_companion_radio_ble_
Xiao_C6_companion_radio_ble_|Xiao_C6_companion_radio_full_|Xiao_C6_companion_radio_ble_
heltec_v4_3_expansionkit_tft_companion_radio_ble_femoff|heltec_v4_expansionkit_tft_companion_radio_full_femon|heltec_v4_expansionkit_tft_companion_radio_full_femon
Generic_ESPNOW_comp_radio_usb|Generic_ESPNOW_companion_radio_full|Generic_ESPNOW_comp_radio_usb
Heltec_E290_companion_usb_ble|Heltec_E290_companion_radio_full|Heltec_E290_companion_usb_ble
Heltec_T190_companion_radio_usb_ble_|Heltec_T190_companion_radio_full_|Heltec_T190_companion_radio_usb_ble_
SenseCapIndicator-ESPNow_comp_radio_usb|SenseCapIndicator-ESPNow_companion_radio_full|SenseCapIndicator-ESPNow_comp_radio_usb
SenseCapIndicator-LoRa_comp_radio_usb_wifi|SenseCapIndicator-LoRa_companion_radio_full|SenseCapIndicator-LoRa_comp_radio_usb_wifi
FULL_COMPANION_SPECS
while IFS='|' read -r source_env full_env; do
[ "$(get_esp32_full_companion_replacement "$source_env")" = "$full_env" ] \
|| fail "$source_env did not collapse into $full_env"
is_redundant_bulk_build_target "$source_env" \
|| fail "$source_env remained beside its MQTT-capable Full Companion"
[ "${PIO_ENV_MQTT_BY_NAME[$full_env]:-1}" = 0 ] \
|| fail "$full_env was incorrectly classified as an MQTT-only profile"
done <<'MQTT_FULL_COMPANION_SPECS'
Heltec_v3_companion_radio_wifi_mqtt|Heltec_v3_companion_radio_full
heltec_v4_companion_radio_wifi_mqtt_femon|heltec_v4_2_v4_3_companion_radio_full_femon
heltec_v4_3_companion_radio_wifi_mqtt_femoff|heltec_v4_2_v4_3_companion_radio_full_femon
MQTT_FULL_COMPANION_SPECS
for mqtt_override in on off; do
MQTT_BRIDGE_OVERRIDE=$mqtt_override
RESOLVED_BUILD_TARGETS=(Heltec_v3_companion_radio_full)
normalize_resolved_targets_for_mqtt build-companion-firmwares >/dev/null
[ "${RESOLVED_BUILD_TARGETS[*]}" = Heltec_v3_companion_radio_full ] \
|| fail "MQTT=${mqtt_override} replaced or discarded canonical Full Companion"
PLATFORMIO_BUILD_FLAGS="-DKEEP_FULL_RECIPE=1"
apply_mqtt_bridge_override Heltec_v3_companion_radio_full
[ "$PLATFORMIO_BUILD_FLAGS" = "-DKEEP_FULL_RECIPE=1" ] \
|| fail "MQTT=${mqtt_override} mutated canonical Full Companion flags"
done
MQTT_BRIDGE_OVERRIDE=on
RESOLVED_BUILD_TARGETS=(Station_G2_companion_radio_full)
if normalize_resolved_targets_for_mqtt build-companion-firmwares >/dev/null; then
fail "MQTT=on retained a Full recipe without direct MQTT capability"
fi
MQTT_BRIDGE_OVERRIDE=""
while IFS= read -r env_name; do
if [ "${PIO_ENV_PLATFORM_BY_NAME[$env_name]:-}" = ESP32_PLATFORM ] \
&& ! is_companion_radio_full_target "$env_name"; then
fail "canonical ESP32 Companion inventory still contains $env_name"
fi
done < <(resolve_companion_firmwares)
# Synthetic inventory: one ESP32 target qualified for expanded Full and one
# nRF52 target which must attempt complete LoRa OTA in its current partition.
SUPPORTED_PIO_ENVS=(
@@ -506,6 +676,18 @@ expectations=" ${BUILD_EXPECTATIONS[*]} "
[[ "$expectations" != *"Full Companion terminal listening"* ]] \
|| fail "ESP32 Full contract still depends on optional debug logging"
pio_env_option_contains() {
[ "$1" = Heltec_v3_companion_radio_full ] \
&& [ "$2" = build_flags ] \
&& [ "$3" = WITH_MQTT_BRIDGE ]
}
BUILD_EXPECTATIONS=()
declare_build_capability_contract \
Heltec_v3_companion_radio_full ESP32_PLATFORM
expectations=" ${BUILD_EXPECTATIONS[*]} "
[[ "$expectations" == *"companion.direct_mqtt=mqtt.status"* ]] \
|| fail "MQTT-capable ESP32 Full contract omitted direct MQTT"
PIO_ENV_PLATFORM_BY_NAME[RAK_3401_companion_radio_full]=NRF52_PLATFORM
BUILD_CAPABILITIES=()
BUILD_REDUCTIONS=()
+3 -3
View File
@@ -142,7 +142,7 @@ assert.strictEqual(companionFull.role, "companion");
assert.strictEqual(companionFull.mode, "full");
assert.strictEqual(companionFull.logging, "usb-runtime");
assert.deepStrictEqual(companionFull.loggingModes, ["none", "usb"]);
assert.strictEqual(companionFull.dedicatedUsbLogging, true);
assert.strictEqual(companionFull.dedicatedUsbLogging, false);
assert.strictEqual(companionFull.ota, "lora-source");
assert.strictEqual(companionFull.feature, "full");
assert.strictEqual(companionFull.variant, "default");
@@ -186,7 +186,7 @@ assert.deepStrictEqual(standardBesideFull[0].loggingModes, ["none", "usb"]);
const v4Full = profile("heltec_v4_2_v4_3_companion_radio_full_femon");
assert.strictEqual(v4Full.logging, "usb-runtime");
assert.deepStrictEqual(v4Full.loggingModes, ["none", "usb"]);
assert.strictEqual(v4Full.dedicatedUsbLogging, true);
assert.strictEqual(v4Full.dedicatedUsbLogging, false);
["usb", "ble", "wifi"].forEach(function (mode) {
assert(!catalog.profiles.some(function (item) {
return item.hardware === "heltec_v4" && item.role === "companion" &&
@@ -207,7 +207,7 @@ assert.strictEqual(v4Full.dedicatedUsbLogging, true);
const classified = picker.applyFullCompanionCapabilities(candidates);
assert.strictEqual(classified[0].logging, "usb-runtime");
assert.deepStrictEqual(classified[0].loggingModes, ["none", "usb"]);
assert.strictEqual(classified[0].dedicatedUsbLogging, undefined);
assert.strictEqual(classified[0].dedicatedUsbLogging, false);
assert.deepStrictEqual(
picker.omitTransportsReplacedByFull(classified).map(function (item) {
return item.target;
+104 -47
View File
@@ -92,8 +92,9 @@ def test_full_esp32_profile_unifies_usb_logging_and_wifi_mqtt():
assert 'uses_merged_standard_usb_logging "$target"' in matrix
assert 'run_full_esp32_profile "FULL unified pass"' in matrix
assert 'run_full_esp32_profile "FULL logging fallback pass"' in matrix
assert "Single-TTY boards first switch CDC 0" in mqtt_gate
assert "input-capable terminal" in mqtt_gate
assert "ESP32 uses one TTY" in mqtt_gate
assert "input-capable logging terminal" in mqtt_gate
assert "plaintext cannot mix with framed traffic" in mqtt_gate
def test_espnow_tx_power_matches_cli_callback_contract():
@@ -201,17 +202,24 @@ def test_canonical_bulk_matrix_omits_runtime_and_transport_aliases():
assert "resolve_all_firmwares" in logging_matrix
assert "print_nrf52_usb_logging_source_targets" not in build
full = build.split("apply_companion_radio_full_profile()", 1)[1]
full = build.split("apply_companion_radio_full_profile() {", 1)[1]
full = full.split("get_firmware_filename()", 1)[0]
assert "-DOTA_SEEDER_ONLY=1" in full
assert "-DMOTA_TARGET_ID=0" in full
assert "-UOTA_FLASH_STORE" in full
assert "-UOTA_SD_STORE" in full
assert "-UWEBCONFIG_DISABLED" in full
assert "-DCFG_TUD_CDC=2" in full
assert "-DMESH_DUAL_CDC_LOGGING=1" in full
assert "-DMESH_DEBUG=1" in full
assert "-DMESH_PACKET_LOGGING=1" in full
nrf52_full = full.split(
'if is_nrf52_companion_radio_full_target "$env_name"', 1
)[1].split("return 0", 1)[0]
assert "-DCFG_TUD_CDC=2" in nrf52_full
assert "-DMESH_DUAL_CDC_LOGGING=1" in nrf52_full
esp32_full = full.split("return 0\n fi", 1)[1]
assert "-DCFG_TUD_CDC=2" not in esp32_full
assert "-DMESH_DUAL_CDC_LOGGING=1" not in esp32_full
assert "-DCOMPANION_FEATURE_DEDICATED_USB_LOGGING=1" not in esp32_full
assert 'disable_debug_flags "$env_name"' in build
assert 'apply_debug_overrides "$env_name"' in build
@@ -228,13 +236,7 @@ def test_canonical_bulk_matrix_omits_runtime_and_transport_aliases():
assert 'is_companion_radio_full_target "$env_name"' in disable_debug
assert 'usb_logging_undefs=""' in disable_debug
esp32_dual = build.split(
"is_esp32_dual_cdc_companion_radio_full_target()", 1
)[1].split("requires_esp32_companion_full_ota_fallback()", 1)[0]
assert "heltec_v4_2_v4_3_companion_radio_full_femon" in esp32_dual
assert "rak_3112_companion_radio_full" not in esp32_dual
assert "heltec_rc32_companion_radio_full" not in esp32_dual
assert "heltec_rc32_without_display_companion_radio_full" not in esp32_dual
assert "is_esp32_dual_cdc_companion_radio_full_target" not in build
for relative, section, next_section in (
(
@@ -261,13 +263,11 @@ def test_canonical_bulk_matrix_omits_runtime_and_transport_aliases():
encoding="utf-8"
)
assert "Adafruit_USBD_CDC dedicated_usb_logging_port" in usb_logging
assert "new (dedicated_usb_logging_port_storage)" in usb_logging
assert "USBCDC(1)" in usb_logging
assert "dedicated_usb_logging_port->enableReboot(false)" in usb_logging
assert "return result == ESP_OK && value != 0" in usb_logging
assert "supported only by nRF52 Full Companion" in usb_logging
assert "MESH_ESP32_DUAL_CDC_LOGGING" not in usb_logging
assert "USBCDC(1)" not in usb_logging
assert "TinyUSBDevice.detach()" in usb_logging
assert "return dedicated_usb_logging_port" in usb_logging
assert "return *dedicated_usb_logging_port" in usb_logging
assert "return null_usb_logging_stream" in usb_logging
companion = (root / "examples/companion_radio/MyMesh.cpp").read_text(
@@ -276,8 +276,15 @@ def test_canonical_bulk_matrix_omits_runtime_and_transport_aliases():
companion_features = (
root / "examples/companion_radio/CompanionFeatures.h"
).read_text(encoding="utf-8")
companion_main = (root / "examples/companion_radio/main.cpp").read_text(
encoding="utf-8"
)
assert "COMPANION_FEATURE_DEDICATED_USB_LOGGING" in companion_features
assert "defined(COMPANION_RADIO_FULL)" in companion
assert "if (!mesh::hasDedicatedUsbLoggingPort())" in companion_main
assert "if (mesh::isUsbLoggingEnabled())" in companion_main
assert "enterUsbLoggingTerminalMode();" in companion_main
assert "usb_logging_terminal_mode" in companion_main
assert "_prefs.usb_logging_enabled = 0" in companion
assert 'strcmp(value, "on reboot") == 0' in companion
assert 'strcmp(value, "off reboot") == 0' in companion
@@ -299,6 +306,49 @@ def test_canonical_bulk_matrix_omits_runtime_and_transport_aliases():
assert "is_esp32_dual_cdc_companion_radio_full_target" not in replacement
def test_single_tty_logging_off_requires_a_subsequent_mode_switch():
root = Path(__file__).resolve().parents[2]
companion_main = (
root / "examples/companion_radio/main.cpp"
).read_text(encoding="utf-8")
service = companion_main.split(
"static void serviceUsbTerminal() {", 1
)[1].split(
"static void expireUsbBinaryStartupProbeBeforeDispatch()", 1
)[0]
# A logging preference changed through BLE/WiFi must release logging's
# exclusive ownership without silently changing the protocol of an open
# USB ASCII session.
external_disable = service.split(
"else if (usb_logging_terminal_mode && the_mesh.isTerminalMode()) {",
1,
)[1].split("\n }\n }\n#endif", 1)[0]
assert "usb_logging_terminal_mode = false;" in external_disable
assert "clearUsbTerminalLine();" in external_disable
assert "usb_terminal_discard_line = false;" in external_disable
assert "leaveUsbTerminalMode" not in external_disable
# The same rule applies when `set usb.logging off` is entered on the USB
# logging terminal itself: keep the fresh-flash ASCII mode after replying.
command_disable = service.split(
"the_mesh.handleTerminalCommand(usb_terminal_line);", 1
)[1].split('Serial.print("> ");', 1)[0]
assert "!mesh::isUsbLoggingEnabled()" in command_disable
assert "usb_logging_terminal_mode = false;" in command_disable
assert "leaveUsbTerminalMode" not in command_disable
# Binary Companion remains reachable, but only through the ordinary mode
# switch that follows the logging-off command (explicit token or framed
# startup probe), never as a side effect of changing the preference.
terminal_stop = service.split(
"if (strcmp(usb_terminal_line, USB_TERMINAL_STOP_TOKEN) == 0) {", 1
)[1].split("}", 1)[0]
assert "leaveUsbTerminalMode(true);" in terminal_stop
assert "if (!usb_logging_terminal_mode" in service
assert "usb_binary_startup_probe.shouldStart(" in service
def test_measured_full_companion_promotions_are_exact_and_bounded():
root = Path(__file__).resolve().parents[2]
build = (root / "build.sh").read_text(encoding="utf-8")
@@ -395,25 +445,26 @@ def test_full_companion_wireless_startup_and_psram_contacts_are_resilient():
)
def test_esp32_s3_full_profiles_inherit_dio_boot_mode():
def test_esp32_s3_full_profiles_use_arduino2_and_dio_boot_mode():
root = Path(__file__).resolve().parents[2]
project = (root / "platformio.ini").read_text(encoding="utf-8")
esp32_base = project.split("[esp32_base]", 1)[1].split("\n[", 1)[0]
assert "platformio/espressif32@6.11.0" in esp32_base
shared = project.split("[esp32_s3_full]", 1)[1].split(
"\n[", 1
)[0]
assert "board_build.flash_mode = dio" in shared
dual_cdc = project.split("[esp32_s3_dual_cdc_full]", 1)[1].split(
"\n[", 1
)[0]
assert (
"board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}"
in dual_cdc
)
assert "esp32-core-3.3.11.tar.xz" in dual_cdc
assert "esp32-core-3.3.11-libs.tar.xz" in dual_cdc
assert "[esp32_s3_dual_cdc_full]" not in project
build = (root / "build.sh").read_text(encoding="utf-8")
framework_selector = build.split(
"requires_esp32_arduino3_framework()", 1
)[1].split("prepare_esp32_arduino3_framework()", 1)[0]
assert "heltec_rc32_" in framework_selector
assert (
"is_esp32_dual_cdc_companion_radio_full_target"
not in framework_selector
)
framework_preflight = build.split(
"prepare_esp32_arduino3_framework()", 1
)[1].split("requires_esp32_companion_full_ota_fallback()", 1)[0]
@@ -461,8 +512,12 @@ def test_esp32_s3_full_profiles_inherit_dio_boot_mode():
"board_build.flash_mode = "
"${esp32_s3_full.board_build.flash_mode}"
) in section, f"ESP32-S3 Full profile lacks shared DIO mode: {path}"
assert "esp32_s3_dual_cdc_full" not in section
if variant_dir != "heltec_rc32":
assert "55.03.311" not in section
assert "esp32-core-3.3.11" not in section
assert profile_count == 17
assert profile_count == 18
def test_tbeam_1w_release_hardware_fixes_are_preserved():
@@ -542,18 +597,14 @@ def test_release_catalog_resolves_canonical_runtime_aliases():
provider = importlib.util.module_from_spec(spec)
spec.loader.exec_module(provider)
assert provider.ESP32_DUAL_CDC_FULL_RE.match(
"heltec_v4_2_v4_3_companion_radio_full_femon"
)
assert not provider.ESP32_DUAL_CDC_FULL_RE.match(
"RAK_3112_companion_radio_full"
)
assert not provider.ESP32_DUAL_CDC_FULL_RE.match(
"heltec_rc32_companion_radio_full"
)
assert not provider.ESP32_DUAL_CDC_FULL_RE.match(
"heltec_rc32_without_display_companion_radio_full"
)
esp32_dual_cdc = getattr(provider, "ESP32_DUAL_CDC_FULL_RE", None)
for identity in (
"heltec_v4_2_v4_3_companion_radio_full_femon",
"RAK_3112_companion_radio_full",
"heltec_rc32_companion_radio_full",
"heltec_rc32_without_display_companion_radio_full",
):
assert esp32_dual_cdc is None or not esp32_dual_cdc.match(identity)
release_files = {
"RAK_4631_companion_radio_full": [Path("rak-full.zip")],
@@ -626,21 +677,27 @@ def test_release_catalog_resolves_canonical_runtime_aliases():
assert "interface 02" in dual_notes
assert "Input received on interface 02 is ignored" in dual_notes
v4_notes = provider.normalize_esp32_dual_cdc_full_companion_metadata(
v4_notes = provider.normalize_esp32_full_companion_metadata(
{"title": "Companion USB", "subTitle": "USB logging"},
"PROFILE - old profile\n\nLOGGING USE - old use\n\nSELECTION - USB.",
)
assert "interface 00" in v4_notes
assert "interface 02" in v4_notes
assert "cannot reboot the board" in v4_notes
assert "ordinary Wi-Fi" in v4_notes
assert "input-capable plaintext" in v4_notes
assert "set usb.logging off" in v4_notes
assert "remains in the normal ASCII terminal" in v4_notes
assert "normal terminal stop token" in v4_notes
assert "valid framed probe" in v4_notes
assert "do not share the single TTY" in v4_notes
assert "interface 02" not in v4_notes
single_tty_notes = provider.normalize_esp32_single_tty_full_companion_metadata(
single_tty_notes = provider.normalize_esp32_full_companion_metadata(
{"title": "Companion USB", "subTitle": "USB logging"},
"PROFILE - old profile\n\nLOGGING USE - old use\n\nSELECTION - USB.",
)
assert "input-capable plaintext" in single_tty_notes
assert "set usb.logging off" in single_tty_notes
assert "remains in the normal ASCII terminal" in single_tty_notes
assert "normal terminal stop token" in single_tty_notes
assert "valid framed probe" in single_tty_notes
assert "do not share the single TTY" in single_tty_notes
legacy = {
+2 -4
View File
@@ -207,8 +207,7 @@ build_flags =
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ENV_PIN_SDA=21
-D ENV_PIN_SCL=18
; Environmental sensors share the board Wire bus at GPIO21/18.
build_src_filter = ${Heltec_RC32.build_src_filter}
+<../examples/simple_sensor>
lib_deps =
@@ -350,8 +349,7 @@ build_flags =
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ENV_PIN_SDA=21
-D ENV_PIN_SCL=18
; Environmental sensors share the board Wire bus at GPIO21/18.
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
+<../examples/simple_sensor>
lib_deps =
+6
View File
@@ -198,6 +198,9 @@ build_flags =
-D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456
-D ENV_INCLUDE_GPS=1 ; enable the GPS page in UI
; External Grove/Qwiic sensors are on the board's second I2C controller.
-D ENV_PIN_SDA=PIN_WIRE1_SDA
-D ENV_PIN_SCL=PIN_WIRE1_SCL
; -D BLE_DEBUG_LOGGING=1
-D OFFLINE_QUEUE_SIZE=256
; -D MESH_PACKET_LOGGING=1
@@ -227,6 +230,9 @@ build_flags =
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D ENABLE_USB_INTERFACE
; The synthesized Full Companion inherits this verified external sensor bus.
-D ENV_PIN_SDA=PIN_WIRE1_SDA
-D ENV_PIN_SCL=PIN_WIRE1_SCL
; -D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
@@ -416,12 +416,9 @@ lib_deps =
[env:heltec_tracker_v2_companion_radio_full_femon]
extends = env:heltec_tracker_v2_companion_radio_wifi_femon
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:heltec_tracker_v2_companion_radio_wifi_femon.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:heltec_tracker_v2_companion_radio_wifi_femoff]
extends = env:heltec_tracker_v2_companion_radio_wifi_femon
+17
View File
@@ -274,6 +274,10 @@ build_flags =
-D MAX_GROUP_CHANNELS=40
-D DISPLAY_CLASS=SSD1306Display
-D ENABLE_USB_INTERFACE
; Preserve the established external-sensor header convention. The onboard
; OLED remains on the board Wire bus at GPIO17/18.
-D ENV_PIN_SDA=33
-D ENV_PIN_SCL=34
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
@@ -294,6 +298,8 @@ build_flags =
-D MAX_GROUP_CHANNELS=40
-D DISPLAY_CLASS=SSD1306Display
-D BLE_PIN_CODE=123456 ; dynamic, random PIN
-D ENV_PIN_SDA=33
-D ENV_PIN_SCL=34
-D AUTO_SHUTDOWN_MILLIVOLTS=3400
-D BLE_DEBUG_LOGGING=1
-D OFFLINE_QUEUE_SIZE=256
@@ -321,6 +327,10 @@ build_flags =
-D MAX_GROUP_CHANNELS=40
-D DISPLAY_CLASS=SSD1306Display
-D WIFI_DEBUG_LOGGING=1
; Full Companion is synthesized from this profile and must retain the
; external sensor bus rather than probing the OLED bus.
-D ENV_PIN_SDA=33
-D ENV_PIN_SCL=34
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D OFFLINE_QUEUE_SIZE=256
@@ -363,6 +373,13 @@ lib_deps =
JChristensen/Timezone
paulstoffregen/Time@1.6.1
; One canonical Companion image carries USB, BLE, ordinary WiFi, direct MQTT,
; WebConfig, logging, and source-only LoRa OTA. The legacy transport targets
; remain available for explicit compatibility builds.
[env:Heltec_v3_companion_radio_full]
extends = env:Heltec_v3_companion_radio_wifi_mqtt
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
[env:Heltec_v3_sensor]
extends = Heltec_lora32_v3
build_flags =
+32 -35
View File
@@ -57,6 +57,9 @@ build_flags =
-D HELTEC_LORA_V4_OLED
-D PIN_BOARD_SDA=17
-D PIN_BOARD_SCL=18
; The OLED uses Wire above; the expansion/general sensor connector is 4/3.
-D ENV_PIN_SDA=4
-D ENV_PIN_SCL=3
-D PIN_OLED_RESET=21
build_src_filter= ${Heltec_lora32_v4.build_src_filter}
lib_deps = ${Heltec_lora32_v4.lib_deps}
@@ -520,29 +523,6 @@ build_flags =
${env:heltec_v4_companion_radio_wifi_femon.build_flags}
-D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off
; Full Companion needs two independent native USB CDC interfaces. The pinned
; Arduino-ESP32 2.0.x platform supports only one; the 3.x platform already used
; by this tree's C6 targets supplies CDC 0 + CDC 1 on ESP32-S3.
[env:heltec_v4_2_v4_3_companion_radio_full_femon]
extends = env:heltec_v4_companion_radio_wifi_femon
platform = ${esp32_s3_dual_cdc_full.platform}
; Marking the merged image QIO makes this V4 reset in the ROM before the
; software bootloader can start. Keep the Full image on the shared DIO policy.
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:heltec_v4_companion_radio_wifi_femon.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:heltec_v4_3_companion_radio_full_femoff]
extends = env:heltec_v4_3_companion_radio_wifi_femoff
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:heltec_v4_3_companion_radio_wifi_femoff.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:heltec_v4_companion_radio_wifi_mqtt_femon]
extends = env:heltec_v4_companion_radio_wifi_femon
extra_scripts =
@@ -576,6 +556,24 @@ build_flags =
${env:heltec_v4_companion_radio_wifi_mqtt_femon.build_flags}
-D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off
; Full Companion subsumes the former direct-MQTT Companion so this is the one
; canonical V4 OLED image. Arduino-ESP32 2.x exposes one USB TTY; enabling USB
; logging gives that TTY exclusively to the plaintext CLI/logging stream until
; logging is disabled again.
[env:heltec_v4_2_v4_3_companion_radio_full_femon]
extends = env:heltec_v4_companion_radio_wifi_mqtt_femon
; Marking the merged image QIO makes this V4 reset in the ROM before the
; software bootloader can start. Keep the Full image on the shared DIO policy.
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_flags =
${env:heltec_v4_companion_radio_wifi_mqtt_femon.build_flags}
[env:heltec_v4_3_companion_radio_full_femoff]
extends = env:heltec_v4_3_companion_radio_wifi_mqtt_femoff
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_flags =
${env:heltec_v4_3_companion_radio_wifi_mqtt_femoff.build_flags}
[env:heltec_v4_sensor]
extends = heltec_v4_oled
build_flags =
@@ -584,8 +582,6 @@ build_flags =
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ENV_PIN_SDA=3
-D ENV_PIN_SCL=4
-D DISPLAY_CLASS=SSD1306Display
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
@@ -733,8 +729,7 @@ build_flags =
extends = env:heltec_v4_tft_companion_radio_ble_femon
build_flags =
${env:heltec_v4_tft_companion_radio_ble_femon.build_flags}
-D ENV_PIN_SDA=4
-D ENV_PIN_SCL=3
; The TFT profile already uses Wire at the expansion I2C pins, GPIO4/3.
[env:heltec_v4_3_expansionkit_tft_companion_radio_ble_femoff]
extends = env:heltec_v4_expansionkit_tft_companion_radio_ble_femon
@@ -745,6 +740,15 @@ build_flags =
[env:heltec_v4_expansionkit_tft_companion_radio_ble_ps]
extends = env:heltec_v4_expansionkit_tft_companion_radio_ble_femon
; The expansion-kit layout has different I2C ownership from the base TFT. Its
; Full image retains that exact hardware recipe while using the common
; Arduino-ESP32 2.x single-TTY policy.
[env:heltec_v4_expansionkit_tft_companion_radio_full_femon]
extends = env:heltec_v4_expansionkit_tft_companion_radio_ble_femon
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_flags =
${env:heltec_v4_expansionkit_tft_companion_radio_ble_femon.build_flags}
[env:heltec_v4_tft_companion_radio_wifi_femon]
extends = heltec_v4_tft
build_flags =
@@ -777,21 +781,15 @@ build_flags =
[env:heltec_v4_tft_companion_radio_full_femon]
extends = env:heltec_v4_tft_companion_radio_wifi_femon
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:heltec_v4_tft_companion_radio_wifi_femon.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:heltec_v4_3_tft_companion_radio_full_femoff]
extends = env:heltec_v4_3_tft_companion_radio_wifi_femoff
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:heltec_v4_3_tft_companion_radio_wifi_femoff.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:heltec_v4_tft_sensor]
extends = heltec_v4_tft
@@ -801,8 +799,7 @@ build_flags =
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ENV_PIN_SDA=3
-D ENV_PIN_SCL=4
; Environmental sensors use the TFT profile's Wire bus at GPIO4/3.
-D DISPLAY_CLASS=ST7789LCDDisplay
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
+4 -1
View File
@@ -1,5 +1,9 @@
#pragma once
// GPIO40 and GPIO41 are hard-wired to GNSS WAKE and PPS respectively.
// Keep them unavailable even when no GNSS data is detected at startup.
#define USER_GPIO_RESERVED_PINS 40, 41
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/radiolib/RadioLibWrappers.h>
@@ -34,4 +38,3 @@ extern EnvironmentSensorManager sensors;
bool radio_init();
mesh::LocalIdentity radio_new_identity();
-6
View File
@@ -196,12 +196,9 @@ lib_deps =
[env:heltec_v4_r8_companion_radio_full]
extends = env:heltec_v4_r8_companion_radio_wifi
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:heltec_v4_r8_companion_radio_wifi.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:heltec_v4_r8_sensor]
extends = heltec_v4_r8_oled
@@ -330,12 +327,9 @@ lib_deps =
[env:heltec_v4_r8_tft_companion_radio_full]
extends = env:heltec_v4_r8_tft_companion_radio_wifi
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:heltec_v4_r8_tft_companion_radio_wifi.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:heltec_v4_r8_tft_sensor]
extends = heltec_v4_r8_tft
-3
View File
@@ -187,12 +187,9 @@ lib_deps =
[env:LilyGo_TBeam_1W_companion_radio_full]
extends = env:LilyGo_TBeam_1W_companion_radio_wifi
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:LilyGo_TBeam_1W_companion_radio_wifi.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
; === LILYGO T-Beam 1W Repeater with ESPNow Bridge ===
[env:LilyGo_TBeam_1W_repeater_bridge_espnow]
+1 -5
View File
@@ -193,12 +193,9 @@ lib_deps =
[env:meshnology_w12_companion_radio_full]
extends = env:meshnology_w12_companion_radio_wifi
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:meshnology_w12_companion_radio_wifi.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:meshnology_w12_sensor]
extends = meshnology_w12
@@ -208,8 +205,7 @@ build_flags =
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ENV_PIN_SDA=3
-D ENV_PIN_SCL=4
; Sensors and the OLED share Wire at GPIO17/18. GPIO3/4 control the RF PAs.
-D DISPLAY_CLASS=SSD1306Display
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
@@ -163,12 +163,9 @@ lib_deps =
[env:nibble_screen_connect_companion_radio_full_]
extends = env:nibble_screen_connect_companion_radio_wifi_
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:nibble_screen_connect_companion_radio_wifi_.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:nibble_screen_connect_kiss_modem_]
@@ -159,9 +159,6 @@ lib_deps =
[env:nibble_zero_connect_companion_radio_full_]
extends = env:nibble_zero_connect_companion_radio_wifi_
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:nibble_zero_connect_companion_radio_wifi_.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
+13 -6
View File
@@ -137,8 +137,10 @@ build_flags =
# -D MQTT_PORT=1883
# -D MQTT_USERNAME='"your-username"'
# -D MQTT_PASSWORD='"your-password"'
-D ENV_PIN_SDA=33
-D ENV_PIN_SCL=34
; Secondary I2C exposed by the RAK3112 module; GPIO33/34 can belong to
; octal PSRAM and are not safe sensor pins.
-D ENV_PIN_SDA=17
-D ENV_PIN_SCL=18
build_src_filter = ${rak3112.build_src_filter}
+<helpers/bridges/MQTTBridge.cpp>
+<helpers/MQTTMessageBuilder.cpp>
@@ -199,8 +201,8 @@ build_flags =
-D ESP32_CPU_FREQ=160
-D MQTT_WIFI_TX_POWER=WIFI_POWER_11dBm
-D WITH_SNMP=1
-D ENV_PIN_SDA=33
-D ENV_PIN_SCL=34
-D ENV_PIN_SDA=17
-D ENV_PIN_SCL=18
build_src_filter = ${rak3112.build_src_filter}
+<helpers/bridges/MQTTBridge.cpp>
+<helpers/MQTTMessageBuilder.cpp>
@@ -293,6 +295,11 @@ lib_deps =
[env:RAK_3112_companion_radio_full]
extends = env:RAK_3112_companion_radio_wifi
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_flags =
${env:RAK_3112_companion_radio_wifi.build_flags}
; Keep the primary Wire bus at 9/40 and put external sensors on Wire1.
-D ENV_PIN_SDA=17
-D ENV_PIN_SCL=18
[env:RAK_3112_sensor]
extends = rak3112
@@ -302,8 +309,8 @@ build_flags =
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ENV_PIN_SDA=33
-D ENV_PIN_SCL=34
-D ENV_PIN_SDA=17
-D ENV_PIN_SCL=18
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rak3112.build_src_filter}
+15
View File
@@ -24,6 +24,21 @@ public:
return 0;
}
bool isUserGpioAvailable(uint8_t pin) const override {
// Only permit GPIOs exposed on the Station G2 IO extension socket.
switch (pin) {
case 8:
case 9:
case 16:
case 17:
case 18:
case 39:
return ESP32Board::isUserGpioAvailable(pin);
default:
return false;
}
}
const char* getManufacturerName() const override {
return "Station G2";
}
+3 -6
View File
@@ -10,9 +10,9 @@ build_unflags =
; Preserve the original 0x10000/0x150000 dual-OTA layout so app-only updates do
; not require replacing the partition table or erasing an installed G2.
board_build.partitions = default.csv
; The Arduino-ESP32 3.x board manifest defaults to QIO, but the Station G2
; bootloader watchdog-resets before starting the app in that mode. The deployed
; hardware and the working 2.x bootloader both require DIO.
; The board manifest defaults to QIO, but the Station G2 bootloader
; watchdog-resets before starting the app in that mode. Deployed hardware and
; the working bootloader require DIO.
board_build.flash_mode = dio
build_flags =
${esp32_base.build_flags}
@@ -331,14 +331,11 @@ lib_deps =
[env:Station_G2_companion_radio_full]
extends = env:Station_G2_companion_radio_wifi
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags =
${Station_G2.build_unflags}
${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:Station_G2_companion_radio_wifi.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:Station_G2_kiss_modem]
extends = Station_G2
-3
View File
@@ -158,12 +158,9 @@ lib_deps =
[env:Station_G3_ESP32_companion_radio_full]
extends = env:Station_G3_ESP32_companion_radio_wifi
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:Station_G3_ESP32_companion_radio_wifi.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
[env:Station_G3_ESP32_kiss_modem]
extends = Station_G3_ESP32
-3
View File
@@ -307,12 +307,9 @@ lib_deps =
[env:Xiao_S3_WIO_companion_radio_full]
extends = env:Xiao_S3_WIO_companion_radio_wifi
platform = ${esp32_s3_dual_cdc_full.platform}
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_unflags = ${esp32_s3_dual_cdc_full.build_unflags}
build_flags =
${env:Xiao_S3_WIO_companion_radio_wifi.build_flags}
${esp32_s3_dual_cdc_full.build_flags}
-D SERIAL_TX=D6
-D SERIAL_RX=D7