diff --git a/.github/workflows/eastmesh-build-repeater-mqtt-bridge-firmwares.yml b/.github/workflows/eastmesh-build-repeater-mqtt-bridge-firmwares.yml new file mode 100644 index 00000000..9044f63b --- /dev/null +++ b/.github/workflows/eastmesh-build-repeater-mqtt-bridge-firmwares.yml @@ -0,0 +1,31 @@ +name: EastMesh Build Repeater MQTT Bridge Firmwares + +permissions: + contents: write + +on: + workflow_dispatch: + +jobs: + + build: + runs-on: ubuntu-latest + env: + OFFICIAL_MESHCORE_VERSION: ${{ vars.OFFICIAL_MESHCORE_VERSION }} + PLATFORMIO_BUILD_FLAGS: -UMQTT_DEBUG + steps: + + - name: Clone Repo + uses: actions/checkout@v6 + + - name: Setup Build Environment + uses: ./.github/actions/setup-build-environment + + - name: Build Firmwares + run: /usr/bin/env bash eastmesh-build.sh build-repeater-mqtt-bridge-firmwares + + - name: Upload Workflow Artifacts + uses: actions/upload-artifact@v7 + with: + name: repeater-mqtt-bridge-firmwares + path: out diff --git a/eastmesh-build.sh b/eastmesh-build.sh index 1a3d9c49..764487d4 100755 --- a/eastmesh-build.sh +++ b/eastmesh-build.sh @@ -35,6 +35,7 @@ Commands: build-companion-wifi-firmwares: Build all companion WiFi firmwares for all build targets. build-repeater-firmwares: Build all repeater firmwares for all build targets. build-repeater-mqtt-firmwares: Build all repeater MQTT firmwares for all build targets. + build-repeater-mqtt-bridge-firmwares: Build all repeater MQTT bridge firmwares for all build targets. build-room-server-firmwares: Build all chat room server firmwares for all build targets. Examples: @@ -56,6 +57,9 @@ $ sh eastmesh-build.sh build-repeater-firmwares Build all repeater MQTT firmwares $ sh eastmesh-build.sh build-repeater-mqtt-firmwares +Build all repeater MQTT bridge firmwares +$ sh eastmesh-build.sh build-repeater-mqtt-bridge-firmwares + Build all chat room server firmwares $ sh eastmesh-build.sh build-room-server-firmwares @@ -77,7 +81,14 @@ EOF # get a list of pio env names that start with "env:" get_pio_envs() { - run_pio project config | grep 'env:' | sed 's/env://' + run_pio project config --json-output | run_python -c " +import json +import sys + +for section, _options in json.load(sys.stdin): + if section.startswith('env:'): + print(section[4:]) +" } # Catch cries for help before doing anything else. @@ -87,6 +98,8 @@ case $1 in exit 1 ;; list|-l) + require_uv + sync_python_tooling get_pio_envs exit 0 ;; @@ -298,6 +311,12 @@ build_repeater_mqtt_firmwares() { } +build_repeater_mqtt_bridge_firmwares() { + + build_all_firmwares_by_suffix "_repeater_mqtt_bridge" + +} + build_room_server_firmwares() { # # build specific room server firmwares @@ -347,6 +366,8 @@ elif [[ $1 == "build-repeater-firmwares" ]]; then build_repeater_firmwares elif [[ $1 == "build-repeater-mqtt-firmwares" ]]; then build_repeater_mqtt_firmwares +elif [[ $1 == "build-repeater-mqtt-bridge-firmwares" ]]; then + build_repeater_mqtt_bridge_firmwares elif [[ $1 == "build-room-server-firmwares" ]]; then build_room_server_firmwares fi diff --git a/eastmesh-docs/custom-cli.md b/eastmesh-docs/custom-cli.md index 84421883..c07e74aa 100644 --- a/eastmesh-docs/custom-cli.md +++ b/eastmesh-docs/custom-cli.md @@ -63,13 +63,43 @@ Legacy dotted aliases are also accepted: ### WiFi Settings For MQTT Repeaters -- `get wifi.status`: shows SSID, connection state, raw WiFi status code, and IP when connected. +- `get wifi.status`: shows SSID, connection state, raw WiFi status code, IP, channel, and signal when connected. - `get wifi.ssid`: shows the configured WiFi SSID. - `set wifi.ssid `: sets the WiFi SSID. - `set wifi.pwd `: sets the WiFi password. - `get wifi.powersaving`: shows the current WiFi power save mode. - `set wifi.powersaving none|min|max`: sets WiFi power saving mode. +### ESP-NOW Bridge Settings For MQTT Bridge Repeaters + +These commands are available on `*_repeater_mqtt_bridge` firmware targets. + +- `get bridge.channel`: shows the configured ESP-NOW bridge channel. +- `set bridge.channel `: sets the ESP-NOW bridge channel and restarts the bridge. Use a value from `1` to `14`. +- `get bridge.secret`: shows the configured ESP-NOW bridge secret. +- `set bridge.secret `: sets the shared ESP-NOW bridge secret and restarts the bridge. + +After running `set bridge.channel`, expect the bridge and web panel connection to drop briefly while the radio restarts. On current MQTT bridge test builds, this can look like the board rebooted. + +For `*_repeater_mqtt_bridge` builds that are connected to WiFi, the ESP-NOW bridge channel must match the active 2.4 GHz WiFi channel: + +1. Run `get wifi.status`. +2. Read the `channel:` value from the connected WiFi status. +3. Run `get bridge.channel`. +4. If the values differ, run `set bridge.channel ` using the WiFi channel value. +5. Use the same `bridge.channel` and `bridge.secret` on every ESP-NOW bridge node that should talk together. + +Example: + +```text +> get wifi.status +> ssid:EastMesh-IoT status:connected code:3 state:connected ip:192.168.1.50 channel:6 rssi:-61 quality:78% signal:good +> get bridge.channel +> 1 +> set bridge.channel 6 +OK +``` + ### Web Panel Controls - `get web` @@ -141,7 +171,7 @@ To enter `CLI Rescue`: - long-press the user button within the first 8 seconds after boot - wait for `========= CLI Rescue =========` -- `get wifi.status`: shows configured SSID, connection status, raw WiFi status code, and IP when connected. +- `get wifi.status`: shows configured SSID, connection status, raw WiFi status code, IP, channel, and signal when connected. - `get wifi.ssid`: shows the configured WiFi SSID. - `get wifi.powersaving`: shows the current WiFi power saving mode. - `set wifi.ssid `: saves a WiFi SSID and immediately retries connection. diff --git a/eastmesh-docs/web-panel.md b/eastmesh-docs/web-panel.md index 33185a5b..d97dd712 100644 --- a/eastmesh-docs/web-panel.md +++ b/eastmesh-docs/web-panel.md @@ -59,7 +59,7 @@ Some constrained targets disable the web panel to stay within flash limits. If y Useful CLI commands: -- `get wifi.status`: shows WiFi state and IP address when connected. +- `get wifi.status`: shows WiFi state, IP address, channel, and signal when connected. - `get web.status`: shows whether the web panel is up and which URL to use. Example: diff --git a/src/helpers/NetworkService.cpp b/src/helpers/NetworkService.cpp index a3483c0b..d383881b 100644 --- a/src/helpers/NetworkService.cpp +++ b/src/helpers/NetworkService.cpp @@ -180,9 +180,9 @@ void NetworkService::formatWifiStatusReply(char* reply, size_t reply_size) const if (wifi_status == WL_CONNECTED) { const int rssi_dbm = WiFi.RSSI(); snprintf(reply, reply_size, - "> ssid:%s status:%s code:%d state:%s ip:%s rssi:%d quality:%d%% signal:%s", + "> ssid:%s status:%s code:%d state:%s ip:%s channel:%d rssi:%d quality:%d%% signal:%s", _prefs.wifi_ssid, status, static_cast(wifi_status), state, WiFi.localIP().toString().c_str(), - rssi_dbm, getWifiQualityPercent(rssi_dbm), getWifiQualityLabel(rssi_dbm)); + WiFi.channel(), rssi_dbm, getWifiQualityPercent(rssi_dbm), getWifiQualityLabel(rssi_dbm)); } else { snprintf(reply, reply_size, "> ssid:%s status:%s code:%d state:%s", _prefs.wifi_ssid[0] ? _prefs.wifi_ssid : "-", status, static_cast(wifi_status), state); diff --git a/src/helpers/web/WebService.cpp b/src/helpers/web/WebService.cpp index 2f588d11..f0323dd4 100644 --- a/src/helpers/web/WebService.cpp +++ b/src/helpers/web/WebService.cpp @@ -98,16 +98,16 @@ void WebService::formatWebStatusReply(char* reply, size_t reply_size) const { #if defined(ESP_PLATFORM) && WITH_WEB_PANEL void WebService::ensureWebServer() { - if (_runner == nullptr || _prefs.web_enabled == 0 || _network == nullptr || !_network->isWifiConnected()) { - _panel.stop(); - return; - } if (_suspended_for_ota) { if (_panel.isRunning()) { _panel.stopRedirectServer(); } return; } + if (_runner == nullptr || _prefs.web_enabled == 0 || _network == nullptr || !_network->isWifiConnected()) { + _panel.stop(); + return; + } if (_panel.isRunning()) { return; } diff --git a/variants/eastmesh_mqtt/platformio.ini b/variants/eastmesh_mqtt/platformio.ini index 82096f15..23044d7d 100644 --- a/variants/eastmesh_mqtt/platformio.ini +++ b/variants/eastmesh_mqtt/platformio.ini @@ -863,3 +863,267 @@ build_flags = lib_deps = ${Xiao_S3_WIO.lib_deps} ${esp32_ota.lib_deps} + +[env:Ebyte_EoRa-S3_Repeater_mqtt_bridge] +extends = env:Ebyte_EoRa-S3_Repeater_mqtt +build_flags = + ${env:Ebyte_EoRa-S3_Repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Ebyte_EoRa-S3_Repeater_mqtt.build_src_filter} + + + +[env:Generic_E22_sx1262_repeater_mqtt_bridge] +extends = env:Generic_E22_sx1262_repeater_mqtt +build_flags = + ${env:Generic_E22_sx1262_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Generic_E22_sx1262_repeater_mqtt.build_src_filter} + + + +[env:Generic_E22_sx1268_repeater_mqtt_bridge] +extends = env:Generic_E22_sx1268_repeater_mqtt +build_flags = + ${env:Generic_E22_sx1268_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Generic_E22_sx1268_repeater_mqtt.build_src_filter} + + + +[env:Heltec_E213_repeater_mqtt_bridge] +extends = env:Heltec_E213_repeater_mqtt +build_flags = + ${env:Heltec_E213_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_E213_repeater_mqtt.build_src_filter} + + + +[env:Heltec_E290_repeater_mqtt_bridge] +extends = env:Heltec_E290_repeater_mqtt +build_flags = + ${env:Heltec_E290_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_E290_repeater_mqtt.build_src_filter} + + + +[env:Heltec_T190_repeater_mqtt_bridge] +extends = env:Heltec_T190_repeater_mqtt +build_flags = + ${env:Heltec_T190_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_T190_repeater_mqtt.build_src_filter} + + + +[env:heltec_tracker_v2_repeater_mqtt_bridge] +extends = env:heltec_tracker_v2_repeater_mqtt +build_flags = + ${env:heltec_tracker_v2_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:heltec_tracker_v2_repeater_mqtt.build_src_filter} + + + +[env:Heltec_v2_repeater_mqtt_bridge] +extends = env:Heltec_v2_repeater_mqtt +build_flags = + ${env:Heltec_v2_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_v2_repeater_mqtt.build_src_filter} + + + +[env:Heltec_v3_repeater_mqtt_bridge] +extends = env:Heltec_v3_repeater_mqtt +build_flags = + ${env:Heltec_v3_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_v3_repeater_mqtt.build_src_filter} + + + +[env:heltec_v4_repeater_mqtt_bridge] +extends = env:heltec_v4_repeater_mqtt +build_flags = + ${env:heltec_v4_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:heltec_v4_repeater_mqtt.build_src_filter} + + + +[env:heltec_v4_tft_repeater_mqtt_bridge] +extends = env:heltec_v4_tft_repeater_mqtt +build_flags = + ${env:heltec_v4_tft_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:heltec_v4_tft_repeater_mqtt.build_src_filter} + + + +[env:Heltec_Wireless_Paper_repeater_mqtt_bridge] +extends = env:Heltec_Wireless_Paper_repeater_mqtt +build_flags = + ${env:Heltec_Wireless_Paper_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_Wireless_Paper_repeater_mqtt.build_src_filter} + + + +[env:Heltec_Wireless_Tracker_repeater_mqtt_bridge] +extends = env:Heltec_Wireless_Tracker_repeater_mqtt +build_flags = + ${env:Heltec_Wireless_Tracker_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_Wireless_Tracker_repeater_mqtt.build_src_filter} + + + +[env:Heltec_WSL3_repeater_mqtt_bridge] +extends = env:Heltec_WSL3_repeater_mqtt +build_flags = + ${env:Heltec_WSL3_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Heltec_WSL3_repeater_mqtt.build_src_filter} + + + +[env:LilyGo_T3S3_sx1262_repeater_mqtt_bridge] +extends = env:LilyGo_T3S3_sx1262_repeater_mqtt +build_flags = + ${env:LilyGo_T3S3_sx1262_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:LilyGo_T3S3_sx1262_repeater_mqtt.build_src_filter} + + + +[env:LilyGo_T3S3_sx1276_repeater_mqtt_bridge] +extends = env:LilyGo_T3S3_sx1276_repeater_mqtt +build_flags = + ${env:LilyGo_T3S3_sx1276_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:LilyGo_T3S3_sx1276_repeater_mqtt.build_src_filter} + + + +[env:LilyGo_TBeam_1W_repeater_mqtt_bridge] +extends = env:LilyGo_TBeam_1W_repeater_mqtt +build_flags = + ${env:LilyGo_TBeam_1W_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:LilyGo_TBeam_1W_repeater_mqtt.build_src_filter} + + + +[env:LilyGo_TDeck_repeater_mqtt_bridge] +extends = env:LilyGo_TDeck_repeater_mqtt +build_flags = + ${env:LilyGo_TDeck_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:LilyGo_TDeck_repeater_mqtt.build_src_filter} + + + +[env:LilyGo_Tlora_C6_repeater_mqtt_bridge] +extends = env:LilyGo_Tlora_C6_repeater_mqtt +build_flags = + ${env:LilyGo_Tlora_C6_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:LilyGo_Tlora_C6_repeater_mqtt.build_src_filter} + + + +[env:M5Stack_Unit_C6L_repeater_mqtt_bridge] +extends = env:M5Stack_Unit_C6L_repeater_mqtt +build_flags = + ${env:M5Stack_Unit_C6L_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:M5Stack_Unit_C6L_repeater_mqtt.build_src_filter} + + + +[env:Meshadventurer_sx1262_repeater_mqtt_bridge] +extends = env:Meshadventurer_sx1262_repeater_mqtt +build_flags = + ${env:Meshadventurer_sx1262_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Meshadventurer_sx1262_repeater_mqtt.build_src_filter} + + + +[env:Meshadventurer_sx1268_repeater_mqtt_bridge] +extends = env:Meshadventurer_sx1268_repeater_mqtt +build_flags = + ${env:Meshadventurer_sx1268_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Meshadventurer_sx1268_repeater_mqtt.build_src_filter} + + + +[env:nibble_screen_connect_repeater_mqtt_bridge] +extends = env:nibble_screen_connect_repeater_mqtt +build_flags = + ${env:nibble_screen_connect_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:nibble_screen_connect_repeater_mqtt.build_src_filter} + + + +[env:RAK_3112_repeater_mqtt_bridge] +extends = env:RAK_3112_repeater_mqtt +build_flags = + ${env:RAK_3112_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:RAK_3112_repeater_mqtt.build_src_filter} + + + +[env:Station_G2_repeater_mqtt_bridge] +extends = env:Station_G2_repeater_mqtt +build_flags = + ${env:Station_G2_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Station_G2_repeater_mqtt.build_src_filter} + + + +[env:Station_G2_logging_repeater_mqtt_bridge] +extends = env:Station_G2_logging_repeater_mqtt +build_flags = + ${env:Station_G2_logging_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Station_G2_logging_repeater_mqtt.build_src_filter} + + + +[env:T_Beam_S3_Supreme_SX1262_repeater_mqtt_bridge] +extends = env:T_Beam_S3_Supreme_SX1262_repeater_mqtt +build_flags = + ${env:T_Beam_S3_Supreme_SX1262_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:T_Beam_S3_Supreme_SX1262_repeater_mqtt.build_src_filter} + + + +[env:Tbeam_SX1262_repeater_mqtt_bridge] +extends = env:Tbeam_SX1262_repeater_mqtt +build_flags = + ${env:Tbeam_SX1262_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Tbeam_SX1262_repeater_mqtt.build_src_filter} + + + +[env:Tbeam_SX1276_repeater_mqtt_bridge] +extends = env:Tbeam_SX1276_repeater_mqtt +build_flags = + ${env:Tbeam_SX1276_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Tbeam_SX1276_repeater_mqtt.build_src_filter} + + + +[env:ThinkNode_M2_Repeater_mqtt_bridge] +extends = env:ThinkNode_M2_Repeater_mqtt +build_flags = + ${env:ThinkNode_M2_Repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:ThinkNode_M2_Repeater_mqtt.build_src_filter} + + + +[env:ThinkNode_M5_Repeater_mqtt_bridge] +extends = env:ThinkNode_M5_Repeater_mqtt +build_flags = + ${env:ThinkNode_M5_Repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:ThinkNode_M5_Repeater_mqtt.build_src_filter} + + + +[env:WHY2025_badge_repeater_mqtt_bridge] +extends = env:WHY2025_badge_repeater_mqtt +build_flags = + ${env:WHY2025_badge_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:WHY2025_badge_repeater_mqtt.build_src_filter} + + + +[env:Xiao_S3_WIO_repeater_mqtt_bridge] +extends = env:Xiao_S3_WIO_repeater_mqtt +build_flags = + ${env:Xiao_S3_WIO_repeater_mqtt.build_flags} + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${env:Xiao_S3_WIO_repeater_mqtt.build_src_filter} + +