Skip to content

Commit 84b61dc

Browse files
authored
Merge pull request #840 from david-cermak/feat/mosq_esp_peer
[mosq]: Add support for esp-peer in brokerless example
2 parents 462561b + 76e45f7 commit 84b61dc

15 files changed

+882
-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: 139 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,129 @@ 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 libjuice
61+
62+
```
63+
I (4746) esp_netif_handlers: sta ip: 192.168.0.40, mask: 255.255.255.0, gw: 192.168.0.1
64+
4: mosquitto version v2.0.20~3 starting
65+
4: Using default config.
66+
4: Opening ipv4 listen socket on port 1883.
67+
4: mosquitto version v2.0.20~3 running
68+
I (4756) serverless_mqtt1: desc: a=ice-ufrag:sGdl
69+
a=ice-pwd:R4IPGsFctITbT1dCZbfQTL
70+
a=ice-options:ice2,trickle
71+
72+
00:00:04 INFO agent.c:1100: Changing state to gathering
73+
I (4776) serverless_mqtt1: JUICE state change: gathering
74+
00:00:04 INFO agent.c:1100: Changing state to connecting
75+
I (4786) serverless_mqtt1: JUICE state change: connecting
76+
00:00:04 INFO agent.c:422: Using STUN server stun.l.google.com:19302
77+
00:00:04 INFO agent.c:1378: STUN server binding successful
78+
00:00:04 INFO agent.c:1397: Got STUN mapped address 185.194.44.31:62703 from server
79+
00:00:04 INFO agent.c:2428: Candidate gathering done
80+
I (5066) serverless_mqtt1: Gathering done
81+
I (5066) serverless_mqtt1: desc: {
82+
"desc": "a=ice-ufrag:sGdl\r\na=ice-pwd:R4IPGsFctITbT1dCZbfQTL\r\na=ice-options:ice2,trickle\r\n",
83+
"cand0": "a=candidate:1 1 UDP 2122317823 192.168.0.40 62703 typ host",
84+
"cand1": "a=candidate:2 1 UDP 1686109951 185.194.44.31 62703 typ srflx raddr 0.0.0.0 rport 0"
85+
}
86+
I (5096) serverless_mqtt1: Other event id:7
87+
```
88+
89+
### With esp-peer
90+
91+
```
92+
I (5992) esp_netif_handlers: sta ip: 192.168.0.42, mask: 255.255.255.0, gw: 192.168.0.1
93+
4: mosquitto version v2.0.20~3 starting
94+
4: Using default config.
95+
4: Opening ipv4 listen socket on port 1883.
96+
4: mosquitto version v2.0.20~3 running
97+
I (6702) esp-x509-crt-bundle: Certificate validated
98+
I (7982) APPRTC_SIG: result SUCCESS
99+
Initials set to 1
100+
I (7982) HTTPS_CLIENT: HTTP POST Status = 200, content_length = 911
101+
I (8652) esp-x509-crt-bundle: Certificate validated
102+
Got url:stun:webrtc.espressif.com:3478 user_name: 1752835118:ninefingers psw:/a8EMa7VBKpFa1I4Rdpv561YDPw=
103+
I (10022) HTTPS_CLIENT: HTTP POST Status = 200, content_length = 173
104+
I (10032) serverless_mqtt_webrtc: Signaling ice info handler 0x0
105+
I (10042) DTLS: Init SRTP OK
106+
I (11512) DTLS_SRTP: dtls_srtp init done
107+
I (11522) APPRTC_SIG: Registering signaling channel.
108+
I (11522) APPRTC_SIG: Connecting to wss://webrtc.espressif.com:8089/ws...
109+
I (11532) websocket_client: Started
110+
I (11532) serverless_mqtt_webrtc: Waiting for peer to connect
111+
I (12122) esp-x509-crt-bundle: Certificate validated
112+
I (13502) APPRTC_SIG: WEBSOCKET_EVENT_CONNECTED
113+
I (13502) APPRTC_SIG: send to remote : {"cmd":"register","roomid":"111116","clientid":"93827452"}
114+
I (13502) serverless_mqtt_webrtc: Peer state: 2
115+
I (13522) AGENT: Start agent as Controlling
116+
I (13522) PEER_DEF: Start DTLS role as 1
117+
I (13522) AGENT: Send STUN binding request
118+
I (13522) AGENT: Send allocate now
119+
Got error code 401
120+
I (13802) AGENT: Send allocate now
121+
I (14112) AGENT: 0 Get candidate success user:1752835118:ninefingers psw:/a8EMa7VBKpFa1I4Rdpv561YDPw=
122+
I (14112) APPRTC_SIG: Begin to send offer to https://webrtc.espressif.com/message/111116/93827452
123+
I (14782) esp-x509-crt-bundle: Certificate validated
124+
I (16472) HTTPS_CLIENT: HTTP POST Status = 200, content_length = 21
125+
I (21602) PEER_DEF: A SRC: 4
126+
I (21602) PEER_DEF: Get peer role 0
127+
I (21602) PEER_DEF: Get peer role 0
128+
I (21602) AGENT: 0 Add remote type:2 185.194.44.31:60872
129+
I (21612) AGENT: 0 Add remote type:4 172.31.6.33:59012
130+
I (21612) AGENT: 0 Add remote type:1 192.168.0.41:60872
131+
0 Sorted pair 0 type: 1 local:192.168.0.42:63459 Remote:192.168.0.41:60872
132+
0 Sorted pair 1 type: 2 local:185.194.44.31:63459 Remote:185.194.44.31:60872
133+
0 Sorted pair 2 type: 4 local:172.31.6.33:59013 Remote:172.31.6.33:59012
134+
I (21642) serverless_mqtt_webrtc: Peer state: 3
135+
I (22002) AGENT: 0 Send binding request (cand:0) local:192.168.0.42:63459 remote:192.168.0.41:60872 id:76e5004e797d87a62756c714
136+
I (22002) AGENT: 0 Send binding request (cand:0) local:185.194.44.31:63459 remote:185.194.44.31:60872 id:4c598d93643bd7252b449456
137+
I (22012) AGENT: 0 Send binding request (cand:0) local:172.31.6.33:59013 remote:172.31.6.33:59012 id:6db505597d157d8d71dfed43
138+
I (22022) AGENT: 0 send indication bind request
139+
140+
I (22202) AGENT: 0 PeerBinding recv local:192.168.0.42:63459 remote:192.168.0.41:60872
141+
I (22202) AGENT: 0 Send binding response local:192.168.0.42:63459 remote:192.168.0.41:60872
142+
I (22212) AGENT: 0 Select pair192.168.0.41:60872
143+
I (22212) AGENT: 0 Send binding request (cand:1) local:192.168.0.42:63459 remote:192.168.0.41:60872 id:45cb977b6ea3bbbe3a984b90
144+
I (22222) AGENT: 0 PeerIndication recv local:172.31.6.33:59013 remote:172.31.6.33:59012
145+
I (22402) AGENT: 0 PeerBinding recv local:192.168.0.42:63459 remote:192.168.0.41:60872
146+
I (22402) AGENT: 0 Send binding response local:192.168.0.42:63459 remote:192.168.0.41:60872
147+
I (22412) AGENT: 0 Candidate responsed
148+
I (22412) AGENT: 0 PeerBinding recv local:192.168.0.42:63459 remote:192.168.0.41:60872
149+
I (22422) AGENT: 0 Connection OK 192.168.0.41:60872
150+
I (22422) serverless_mqtt_webrtc: Peer state: 5
151+
I (22452) DTLS: Start to do server handshake
152+
153+
Works as 1
154+
I (23842) DTLS: SRTP connected OK
155+
I (23852) DTLS: Server handshake success
156+
I (23852) PEER_DEF: DTLS handshake success
157+
I (23852) serverless_mqtt_webrtc: Peer state: 6
158+
I (23852) serverless_mqtt_webrtc: Peer is connected!
159+
I (23862) serverless_mqtt: local client event id:7
160+
22: New connection from 192.168.4.1:63904 on port 1883.
161+
22: New client connected from 192.168.4.1:63904 as local_mqtt (p2, c1, k120).
162+
I (23892) serverless_mqtt: local client connected
163+
I (23872) serverless_mqtt: Everything is ready, exiting main task
164+
I (23892) main_task: Returned from app_main()
165+
I (24552) SCTP: 0 Receive chunk 1 SCTP_INIT
166+
I (24552) SCTP: 0 state 2
167+
I (24552) SCTP: Send INIT_ACK chunk
168+
I (24762) SCTP: 0 Receive chunk 10 SCTP_COOKIE_ECHO
169+
I (24762) SCTP: Send ECHO_ACK chunk
170+
I (24762) SCTP: 0 state 5
171+
I (24762) serverless_mqtt_webrtc: Peer state: 8
172+
I (24762) SCTP: 0 Receive chunk 10 SCTP_COOKIE_ECHO
173+
I (24772) SCTP: Send ECHO_ACK chunk
174+
I (24962) SCTP: Get DCEP esp_channel event:3 type:0 si:2
175+
I (24972) serverless_mqtt_webrtc: Peer state: 9
176+
```
177+
47178
## Warning
48179

49-
This example uses libjuice as a dependency:
180+
This example uses `libjuice` as a dependency:
50181

51182
* libjuice (UDP Interactive Connectivity Establishment): https://github.com/paullouisageneau/libjuice
52183

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()

0 commit comments

Comments
 (0)