Skip to content

Commit 7a98220

Browse files
committed
feat(mosq): Update brokerless example to work with esp-peer
* Relax CI criteria to build on v5.2+ (for the brokerless due to esp-peer dependency)
1 parent 462561b commit 7a98220

15 files changed

+794
-259
lines changed

.github/workflows/mosq__build.yml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ jobs:
1414
strategy:
1515
matrix:
1616
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
17+
example: ["broker", "serverless_mqtt"]
18+
exclude:
19+
- idf_ver: "release-v5.1"
20+
example: "serverless_mqtt" # serverless_mqtt is not supported due to esp-peer
21+
1722
runs-on: ubuntu-22.04
1823
container: espressif/idf:${{ matrix.idf_ver }}
1924
env:
20-
TEST_DIR: components/mosquitto/examples
25+
TEST_DIR: components/mosquitto/examples/${{ matrix.example }}
2126
TARGET_TEST: broker
2227
TARGET_TEST_DIR: build_esp32_default
2328
steps:
@@ -31,14 +36,17 @@ jobs:
3136
. ${IDF_PATH}/export.sh
3237
pip install idf-component-manager idf-build-apps --upgrade
3338
python ci/build_apps.py -c ${TEST_DIR} -m components/mosquitto/.build-test-rules.yml
34-
# upload only the target test artifacts
35-
cd ${TEST_DIR}/${TARGET_TEST}
36-
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
37-
zip -qur artifacts.zip ${TARGET_TEST_DIR}
39+
if [ "${{ matrix.example }}" == "${TARGET_TEST}" ]; then
40+
# upload only the target test artifacts
41+
cd ${TEST_DIR}
42+
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
43+
zip -qur artifacts.zip ${TARGET_TEST_DIR}
44+
fi
3845
- uses: actions/upload-artifact@v4
46+
if: ${{ matrix.example == 'broker' }}
3947
with:
4048
name: mosq_target_esp32_${{ matrix.idf_ver }}
41-
path: ${{ env.TEST_DIR }}/${{ env.TARGET_TEST }}/artifacts.zip
49+
path: ${{ env.TEST_DIR }}/artifacts.zip
4250
if-no-files-found: error
4351

4452
test_mosq:

components/mosquitto/examples/serverless_mqtt/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,21 @@
33
cmake_minimum_required(VERSION 3.16)
44

55
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
7+
# Setup ESP-PEER from GitHub repo (but it's supported only on certain targets)
8+
set(ESP_PEER_COMPATIBLE_TARGETS "esp32s2" "esp32s3" "esp32p4" "esp32")
9+
if(IDF_TARGET IN_LIST ESP_PEER_COMPATIBLE_TARGETS)
10+
execute_process(COMMAND ${CMAKE_BINARY_DIR}/../esp_peer_setup/install.sh
11+
${CMAKE_BINARY_DIR}
12+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
13+
RESULT_VARIABLE script_result)
14+
15+
if(script_result)
16+
message(FATAL_ERROR "Script esp_peer_setup.sh failed with exit code ${script_result}")
17+
endif()
18+
list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_BINARY_DIR}/esp-peer/components/")
19+
else()
20+
message(STATUS "ESP-PEER is not compatible with this target")
21+
endif()
22+
623
project(serverless_mqtt)

components/mosquitto/examples/serverless_mqtt/README.md

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
MQTT served by (two) mosquitto's running on two ESP chips.
44

55
* Leverages MQTT connectivity between two private networks without cloud premisses.
6-
* Creates two local MQTT servers (on ESP32x's) which are being synchronized over peer to peer connection (established via ICE protocol, by [libjuice](https://github.com/paullouisageneau/libjuice)).
6+
* Creates two local MQTT servers (on ESP32x's) which are being synchronized over peer to peer connection (established via ICE/WebRTC protocol)
7+
8+
## Peer to peer connection
9+
10+
Could be established either by [libjuice](https://github.com/paullouisageneau/libjuice) or [esp-webRTC](https://github.com/espressif/esp-webrtc-solution). While `juice` is just a low level implementation of ICE-UDP, we need to provide some signalling and synchronization, the `WebRTC` is full-fledged solution to establish a peer connection using standardized signalling, security and transfer protocols.
11+
712

813
## How it works
914

1015
This example needs two ESP32 chipsets, that will create two separate Wi-Fi networks (IoT networks) used for IoT devices.
1116
Each IoT network is served by an MQTT server (using mosquitto component).
1217
This example will also synchronize these two MQTT brokers, as if there was only one IoT network with one broker.
13-
This example creates a peer to peer connection between two chipsets to keep them synchronize. This connection utilizes libjuice (which implements a simplified ICE-UDP) to traverse NATs, which enabling direct connection between two private networks behind NATs.
18+
This example creates a peer to peer connection between two chipsets to keep them synchronize. This connection utilizes libjuice (which implements a simplified ICE-UDP) or esp-webRTC (which implements WebRTC) to traverse NATs, which enabling direct connection between two private networks behind NATs.
1419

1520
* Diagram
1621

@@ -19,12 +24,16 @@ This example creates a peer to peer connection between two chipsets to keep them
1924
Here's a step-by-step procedure of establishing this remote connection:
2025
1) Initialize and start Wi-Fi AP (for IoT networks) and Wi-Fi station (for internet connection)
2126
2) Start mosquitto broker on IoT network
22-
3) Start libjuice to gather connection candidates
23-
4) Synchronize using a public MQTT broker and exchange ICE descriptors
24-
5) Establish ICE UDP connection between the two ESP32 chipsets
27+
3) Start peer to peer connection
28+
- In case of `libjuice`
29+
- gather connection candidates
30+
- synchronize using a public MQTT broker and exchange ICE descriptors
31+
- establish ICE UDP connection between the two ESP32 chipsets
32+
- In case of `webRTC` simply start the connection.
2533
6) Start forwarding mqtt messages
2634
- Each remote datagram (received from ICE-UDP channel) is re-published to the local MQTT server
27-
- Each local MQTT message (received from mosquitto on_message callback) is sent in ICE-UDP datagram
35+
- Each local MQTT message (received from mosquitto on_message callback) is sent in a peer message
36+
2837

2938
## How to use this example
3039

@@ -33,7 +42,9 @@ You need two ESP32 devices that support Wi-Fi station and Wi-Fi software access
3342
* Configure Wi-Fi credentials for both devices on both interfaces
3443
* These devices would be deployed in distinct Wi-Fi environments, so the Wi-Fi station credentials would likely be different.
3544
* They also create their own IoT network (on the soft-AP interface) Wi-Fi, so the AP credentials would likely be the same, suggesting the IoT networks will be keep synchronized (even though these are two distict Wi-Fi networks).
36-
* Choose `CONFIG_EXAMPLE_SERVERLESS_ROLE_PEER1` for one device and `CONFIG_EXAMPLE_SERVERLESS_ROLE_PEER2` for another. It's not important which device is PEER1, since the code is symmetric, but these two devices need to have different role.
45+
* Choose the peer library
46+
* Only for `libjuice`:
47+
- Choose `CONFIG_EXAMPLE_SERVERLESS_ROLE_PEER1` for one device and `CONFIG_EXAMPLE_SERVERLESS_ROLE_PEER2` for another. It's not important which device is PEER1, since the code is symmetric, but these two devices need to have different role.
3748
* Optionally: You can use `idf.py` `-D` and `-B` flag to keep separate build directories and sdkconfigs for these two roles
3849
```
3950
idf.py -B build1 -DSDKCONFIG=build1/sdkconfig menuconfig build flash monitor
@@ -44,9 +55,64 @@ idf.py -B build1 -DSDKCONFIG=build1/sdkconfig menuconfig build flash monitor
4455
* Join PEER2 device's AP and connect to the MQTT broker with one or more clients, subscribing to one or more topics.
4556
* Whenever you publish to a topic, all subscribed clients should receive the message, no matter which Wi-Fi network they're connected to.
4657

58+
## Example output
59+
60+
### With esp-peer
61+
```
62+
5: mosquitto version v2.0.20~3 starting
63+
5: Using default config.
64+
5: Opening ipv4 listen socket on port 1883.
65+
5: mosquitto version v2.0.20~3 running
66+
I (6523) esp-x509-crt-bundle: Certificate validated
67+
I (7723) APPRTC_SIG: result SUCCESS
68+
Initials set to 1
69+
I (7733) HTTPS_CLIENT: HTTP POST Status = 200, content_length = 909
70+
I (8383) esp-x509-crt-bundle: Certificate validated
71+
Got url:stun:webrtc.espressif.com:3478 user_name: 1752826470:ninefingers psw:VF/56yvzv6gPnk+Vu2G6fG49rCQ=
72+
I (9563) HTTPS_CLIENT: HTTP POST Status = 200, content_length = 173
73+
I (9573) APPRTC_SIG: Registering signaling channel.
74+
I (9573) APPRTC_SIG: Connecting to wss://webrtc.espressif.com:8089/ws...
75+
I (9583) websocket_client: Started
76+
I (10223) esp-x509-crt-bundle: Certificate validated
77+
I (11403) APPRTC_SIG: WEBSOCKET_EVENT_CONNECTED
78+
I (11403) APPRTC_SIG: send to remote : {"cmd":"register","roomid":"12345","clientid":"22071010"}
79+
I (99583) serverless_mqtt: local client event id:7
80+
99: New connection from 192.168.4.1:56451 on port 1883.
81+
99: New client connected from 192.168.4.1:56451 as local_mqtt (p2, c1, k120).
82+
```
83+
84+
## With libjuice
85+
86+
```
87+
I (4746) esp_netif_handlers: sta ip: 192.168.0.40, mask: 255.255.255.0, gw: 192.168.0.1
88+
4: mosquitto version v2.0.20~3 starting
89+
4: Using default config.
90+
4: Opening ipv4 listen socket on port 1883.
91+
4: mosquitto version v2.0.20~3 running
92+
I (4756) serverless_mqtt1: desc: a=ice-ufrag:sGdl
93+
a=ice-pwd:R4IPGsFctITbT1dCZbfQTL
94+
a=ice-options:ice2,trickle
95+
96+
00:00:04 INFO agent.c:1100: Changing state to gathering
97+
I (4776) serverless_mqtt1: JUICE state change: gathering
98+
00:00:04 INFO agent.c:1100: Changing state to connecting
99+
I (4786) serverless_mqtt1: JUICE state change: connecting
100+
00:00:04 INFO agent.c:422: Using STUN server stun.l.google.com:19302
101+
00:00:04 INFO agent.c:1378: STUN server binding successful
102+
00:00:04 INFO agent.c:1397: Got STUN mapped address 185.194.44.31:62703 from server
103+
00:00:04 INFO agent.c:2428: Candidate gathering done
104+
I (5066) serverless_mqtt1: Gathering done
105+
I (5066) serverless_mqtt1: desc: {
106+
"desc": "a=ice-ufrag:sGdl\r\na=ice-pwd:R4IPGsFctITbT1dCZbfQTL\r\na=ice-options:ice2,trickle\r\n",
107+
"cand0": "a=candidate:1 1 UDP 2122317823 192.168.0.40 62703 typ host",
108+
"cand1": "a=candidate:2 1 UDP 1686109951 185.194.44.31 62703 typ srflx raddr 0.0.0.0 rport 0"
109+
}
110+
I (5096) serverless_mqtt1: Other event id:7
111+
```
112+
47113
## Warning
48114

49-
This example uses libjuice as a dependency:
115+
This example uses `libjuice` as a dependency:
50116

51117
* libjuice (UDP Interactive Connectivity Establishment): https://github.com/paullouisageneau/libjuice
52118

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
From cdc43a56f5ea1ab1935f55f47f8644f5dd30825e Mon Sep 17 00:00:00 2001
2+
From: David Cermak <[email protected]>
3+
Date: Thu, 10 Jul 2025 11:09:57 +0200
4+
Subject: [PATCH] fix(media_lib): Remove deprecated freeRTOS header
5+
6+
---
7+
components/media_lib_sal/port/media_lib_os_freertos.c | 4 ++++
8+
1 file changed, 4 insertions(+)
9+
10+
diff --git a/components/media_lib_sal/port/media_lib_os_freertos.c b/components/media_lib_sal/port/media_lib_os_freertos.c
11+
index d248d59..aea0527 100644
12+
--- a/components/media_lib_sal/port/media_lib_os_freertos.c
13+
+++ b/components/media_lib_sal/port/media_lib_os_freertos.c
14+
@@ -40,8 +40,12 @@
15+
#include "esp_idf_version.h"
16+
17+
#if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT
18+
+#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0))
19+
+#include "esp_private/freertos_debug.h"
20+
+#else
21+
#include "freertos/task_snapshot.h"
22+
#endif
23+
+#endif
24+
25+
#ifdef __XTENSA__
26+
#include "esp_debug_helpers.h"
27+
--
28+
2.43.0
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
echo "bin_dir: $1"
4+
5+
bin_dir="$1"
6+
THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
7+
ESP_PEER_VERSION="ccff3bd65cea750bf6c0abcf9d95b931ba9329f0"
8+
9+
ESP_PEER_URL="https://github.com/espressif/esp-webrtc-solution/archive/${ESP_PEER_VERSION}.zip"
10+
ESP_PEER_DIR="${bin_dir}/esp-peer"
11+
ZIP_PATH="${bin_dir}/esp-peer.zip"
12+
EXTRACTED_DIR="${ESP_PEER_DIR}/esp-webrtc-solution-${ESP_PEER_VERSION}"
13+
COMPONENTS_SRC="${EXTRACTED_DIR}/components"
14+
COMPONENTS_DST="${ESP_PEER_DIR}/components"
15+
PATCH_FILE_1="${THIS_DIR}/Remove-deprecated-freeRTOS-header.patch"
16+
PATCH_FILE_2="${THIS_DIR}/libpeer-Add-direct-dependency-to-libsrtp.patch"
17+
18+
# Download if not exists
19+
if [ ! -d "$EXTRACTED_DIR" ]; then
20+
echo "Downloading esp-peer ${ESP_PEER_VERSION}..."
21+
wget -O "$ZIP_PATH" "$ESP_PEER_URL"
22+
unzip -o "$ZIP_PATH" -d "$ESP_PEER_DIR"
23+
patch -p1 -d "$EXTRACTED_DIR" < "$PATCH_FILE_1"
24+
patch -p1 -d "$EXTRACTED_DIR" < "$PATCH_FILE_2"
25+
mv ${EXTRACTED_DIR}/components ${ESP_PEER_DIR}
26+
mv ${ESP_PEER_DIR}/components/esp_webrtc/impl/peer_default ${ESP_PEER_DIR}/components
27+
fi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
From 695e057000698f4897b6c5802851499842e2fe31 Mon Sep 17 00:00:00 2001
2+
From: David Cermak <[email protected]>
3+
Date: Fri, 11 Jul 2025 16:59:21 +0200
4+
Subject: [PATCH] fix(libpeer): Add direct dependency to libsrtp
5+
6+
---
7+
components/esp_webrtc/impl/peer_default/CMakeLists.txt | 2 +-
8+
1 file changed, 1 insertion(+), 1 deletion(-)
9+
10+
diff --git a/components/esp_webrtc/impl/peer_default/CMakeLists.txt b/components/esp_webrtc/impl/peer_default/CMakeLists.txt
11+
index 2af35cf..3fb4615 100644
12+
--- a/components/esp_webrtc/impl/peer_default/CMakeLists.txt
13+
+++ b/components/esp_webrtc/impl/peer_default/CMakeLists.txt
14+
@@ -2,6 +2,6 @@ idf_component_register(INCLUDE_DIRS ./include)
15+
16+
get_filename_component(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR} NAME)
17+
add_prebuilt_library(${BASE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/libs/${IDF_TARGET}/libpeer_default.a"
18+
- PRIV_REQUIRES ${BASE_DIR} esp_timer)
19+
+ PRIV_REQUIRES ${BASE_DIR} esp_timer espressif__esp_libsrtp)
20+
target_link_libraries(${COMPONENT_LIB} INTERFACE "-L ${CMAKE_CURRENT_SOURCE_DIR}/libs/${IDF_TARGET}")
21+
target_link_libraries(${COMPONENT_LIB} INTERFACE peer_default)
22+
--
23+
2.43.0
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1+
if(CONFIG_EXAMPLE_PEER_LIB_ESP_PEER)
2+
set(PEER_BACKEND_SRC "peer_impl_webrtc.c")
3+
else()
4+
set(PEER_BACKEND_SRC "peer_impl_juice.c")
5+
endif()
6+
17
idf_component_register(SRCS "serverless_mqtt.c"
28
"wifi_connect.c"
9+
"${PEER_BACKEND_SRC}"
310
INCLUDE_DIRS "."
411
REQUIRES libjuice nvs_flash mqtt json esp_wifi)
12+
if(CONFIG_EXAMPLE_PEER_LIB_ESP_PEER)
13+
idf_component_optional_requires(PUBLIC media_lib_sal esp_webrtc peer_default)
14+
endif()

components/mosquitto/examples/serverless_mqtt/main/Kconfig.projbuild

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,55 @@ menu "Example Configuration"
3333
WiFi station password for the example to use.
3434
endmenu
3535

36+
choice EXAMPLE_PEER_LIB
37+
prompt "Choose peer library"
38+
default EXAMPLE_PEER_LIB_LIBJUICE
39+
help
40+
Choose the peer library to use for WebRTC communication.
41+
libjuice: Use libjuice library for ICE/STUN/TURN (Performs manual signalling)
42+
esp_peer: Use ESP-IDF specific peer library
43+
44+
config EXAMPLE_PEER_LIB_ESP_PEER
45+
bool "esp_peer"
46+
47+
config EXAMPLE_PEER_LIB_LIBJUICE
48+
bool "libjuice"
49+
endchoice
50+
51+
config EXAMPLE_WEBRTC_URL
52+
string "WebRTC server URL"
53+
depends on EXAMPLE_PEER_LIB_ESP_PEER
54+
default "https://webrtc.espressif.com/join/"
55+
help
56+
URL of WebRTC remote endpoint.
57+
58+
config EXAMPLE_WEBRTC_ROOM_ID
59+
string "WebRTC room ID"
60+
depends on EXAMPLE_PEER_LIB_ESP_PEER
61+
default "12345"
62+
help
63+
Room ID for WebRTC synchronisation.
64+
Could be a random number, but the same for both peers.
65+
66+
3667
config EXAMPLE_MQTT_BROKER_URI
3768
string "MQTT Broker URL"
69+
depends on EXAMPLE_PEER_LIB_LIBJUICE
3870
default "mqtt://mqtt.eclipseprojects.io"
3971
help
4072
URL of the mqtt broker use for synchronisation and exchanging
4173
ICE connect info (description and candidates).
4274

4375
config EXAMPLE_MQTT_SYNC_TOPIC
4476
string "MQTT topic for synchronisation"
77+
depends on EXAMPLE_PEER_LIB_LIBJUICE
4578
default "/topic/serverless_mqtt"
4679
help
4780
MQTT topic used fo synchronisation.
4881

4982
config EXAMPLE_STUN_SERVER
5083
string "Hostname of STUN server"
84+
depends on EXAMPLE_PEER_LIB_LIBJUICE
5185
default "stun.l.google.com"
5286
help
5387
STUN server hostname.
@@ -67,6 +101,7 @@ menu "Example Configuration"
67101

68102
choice EXAMPLE_SERVERLESS_ROLE
69103
prompt "Choose your role"
104+
depends on EXAMPLE_PEER_LIB_LIBJUICE
70105
default EXAMPLE_SERVERLESS_ROLE_PEER1
71106
help
72107
Choose either peer1 or peer2.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
7+
#include <stdio.h>
8+
#include "esp_random.h"
9+
#include "esp_sleep.h"
10+
#include "mosq_broker.h"
11+
12+
typedef void (*on_peer_recv_t)(const char *data, size_t size);
13+
14+
esp_err_t peer_init(on_peer_recv_t cb);
15+
16+
void peer_get_buffer(char ** buffer, size_t *buffer_len);
17+
18+
void peer_send(char* data, size_t size);

0 commit comments

Comments
 (0)