Skip to content

Commit 9cd9b3f

Browse files
committed
feat(mosq): Update brokerless example to work with esp-peer
1 parent e9b21ea commit 9cd9b3f

File tree

6 files changed

+386
-1
lines changed

6 files changed

+386
-1
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
From a2d51a026449cf1ba2cb53996ce1cdf1f6f078d2 Mon Sep 17 00:00:00 2001
2+
From: David Cermak <cermak@espressif.com>
3+
Date: Wed, 9 Jul 2025 12:40:36 +0200
4+
Subject: [PATCH] fix(htt): Add default event to http-client handler
5+
6+
---
7+
components/esp_webrtc/impl/apprtc_signal/https_client.c | 2 ++
8+
1 file changed, 2 insertions(+)
9+
10+
diff --git a/components/esp_webrtc/impl/apprtc_signal/https_client.c b/components/esp_webrtc/impl/apprtc_signal/https_client.c
11+
index b7ee6fc..aca4a4e 100644
12+
--- a/components/esp_webrtc/impl/apprtc_signal/https_client.c
13+
+++ b/components/esp_webrtc/impl/apprtc_signal/https_client.c
14+
@@ -48,6 +48,8 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
15+
{
16+
http_info_t *info = evt->user_data;
17+
switch (evt->event_id) {
18+
+ default:
19+
+ break;
20+
case HTTP_EVENT_ERROR:
21+
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
22+
break;
23+
--
24+
2.25.1

components/mosquitto/examples/serverless_mqtt/CMakeLists.txt

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

55
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
7+
execute_process(COMMAND ${CMAKE_BINARY_DIR}/../esp_peer_setup.sh
8+
${CMAKE_BINARY_DIR}
9+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
10+
RESULT_VARIABLE script_result
11+
OUTPUT_VARIABLE script_output
12+
ERROR_VARIABLE script_error)
13+
14+
message(STATUS "script_result: ${script_result}")
15+
message(STATUS "script_output: ${script_output}")
16+
message(STATUS "script_error: ${script_error}")
17+
18+
19+
# file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/esp-peer/components/")
20+
# file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/esp-peer/components/esp_webrtc/impl/peer_default/")
21+
list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_BINARY_DIR}/esp-peer/components/")
22+
# list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_BINARY_DIR}/esp-peer/components/esp_webrtc/impl/peer_default/")
23+
624
project(serverless_mqtt)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
echo "bin_dir: $1"
4+
5+
6+
bin_dir="$1"
7+
THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
8+
ESP_PEER_VERSION="9512eef258a45aafcaaa309b1a67505c8b500363"
9+
10+
ESP_PEER_URL="https://github.com/espressif/esp-webrtc-solution/archive/${ESP_PEER_VERSION}.zip"
11+
ESP_PEER_DIR="${bin_dir}/esp-peer"
12+
ZIP_PATH="${bin_dir}/esp-peer.zip"
13+
EXTRACTED_DIR="${ESP_PEER_DIR}/esp-webrtc-solution-${ESP_PEER_VERSION}"
14+
COMPONENTS_SRC="${EXTRACTED_DIR}/components"
15+
COMPONENTS_DST="${ESP_PEER_DIR}/components"
16+
PATCH_FILE="${THIS_DIR}/Add-default-event-to-http-client-handler.patch"
17+
18+
echo $ESP_PEER_DIR
19+
20+
# Download if not exists
21+
if [ ! -d "$EXTRACTED_DIR" ]; then
22+
echo "Downloading esp-peer ${ESP_PEER_VERSION}..."
23+
wget -O "$ZIP_PATH" "$ESP_PEER_URL"
24+
unzip -o "$ZIP_PATH" -d "$ESP_PEER_DIR"
25+
patch -p1 -d "$EXTRACTED_DIR" < "$PATCH_FILE"
26+
mv ${EXTRACTED_DIR}/components ${ESP_PEER_DIR}
27+
mv ${ESP_PEER_DIR}/components/esp_webrtc/impl/peer_default ${ESP_PEER_DIR}/components
28+
fi
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
idf_component_register(SRCS "serverless_mqtt.c"
22
"wifi_connect.c"
3+
"webrtc.c"
34
INCLUDE_DIRS "."
4-
REQUIRES libjuice nvs_flash mqtt json esp_wifi)
5+
REQUIRES libjuice nvs_flash mqtt json esp_wifi media_lib_sal esp_webrtc peer_default)
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
/* OpenAI realtime communication Demo code
7+
8+
This example code is in the Public Domain (or CC0 licensed, at your option.)
9+
10+
Unless required by applicable law or agreed to in writing, this
11+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12+
CONDITIONS OF ANY KIND, either express or implied.
13+
*/
14+
15+
#include "media_lib_os.h"
16+
#include "esp_log.h"
17+
#include "esp_webrtc_defaults.h"
18+
#include "esp_peer_default.h"
19+
#include "common.h"
20+
#include "esp_timer.h"
21+
#include "esp_random.h"
22+
23+
24+
bool network_is_connected(void);
25+
/**
26+
* @brief Start WebRTC
27+
*
28+
* @param[in] url Signaling url
29+
*
30+
* @return
31+
* - 0 On success
32+
* - Others Fail to start
33+
*/
34+
int start_webrtc(char *url);
35+
36+
/**
37+
* @brief Query WebRTC Status
38+
*/
39+
void query_webrtc(void);
40+
41+
/**
42+
* @brief Stop WebRTC
43+
*
44+
* @return
45+
* - 0 On success
46+
* - Others Fail to stop
47+
*/
48+
int stop_webrtc(void);
49+
50+
#define TAG "PEER_DEMO"
51+
52+
#define TEST_PERIOD 1000
53+
54+
static esp_peer_signaling_handle_t signaling = NULL;
55+
static esp_peer_handle_t peer = NULL;
56+
static esp_timer_handle_t timer;
57+
static int send_sequence = 0;
58+
static bool peer_running = false;
59+
60+
typedef struct {
61+
const char* question;
62+
const char* answer;
63+
} data_channel_chat_content_t;
64+
65+
static data_channel_chat_content_t chat_content[] = {
66+
{"Hi!", "Hello!"},
67+
{"How are you?", "I am fine."},
68+
{"Wish to be your friend.", "Great!"},
69+
{"What's your name?", "I am a chatbot, nice to meet you!"},
70+
{"What do you do?", "I am here to chat and assist you with various tasks."},
71+
{"How old are you?", "I don't have an age. I was created to chat with you!"},
72+
{"Do you have hobbies?", "I enjoy chatting with you and learning new things."},
73+
{"Tell me a story.", "Once upon a time, a curious cat discovered a magical world..."},
74+
{"Tell me a joke.", "Why don't skeletons fight each other? They don't have the guts!"},
75+
{"What is the weather like?", "I am not sure, but you can check your local forecast."},
76+
{"What is your favorite color?", "I don't have a favorite color, but I like all of them!"},
77+
{"What is the time?", "Sorry, I can't tell the time. You can check your device for that."},
78+
{"What can you do?", "I can answer questions, tell jokes, help with tasks, and much more!"},
79+
{"Where are you from?", "I was created by developers, so I don't have a specific location."},
80+
{"What is love?", "Love is a complex emotion that connects people. What do you think love is?"},
81+
{"Do you like music?", "I don't listen to music, but I know about it! What's your favorite genre?"},
82+
{"Goodbye!", "Bye!"},
83+
};
84+
85+
static void send_cb(void *ctx)
86+
{
87+
if (peer) {
88+
uint8_t data[64];
89+
memset(data, (uint8_t)send_sequence, sizeof(data));
90+
esp_peer_audio_frame_t audio_frame = {
91+
.data = data,
92+
.size = sizeof(data),
93+
.pts = send_sequence,
94+
};
95+
// Send audio data
96+
esp_peer_send_audio(peer, &audio_frame);
97+
send_sequence++;
98+
int question = esp_random() % (sizeof(chat_content) / sizeof(chat_content[0]));
99+
100+
// Send question through data channel
101+
esp_peer_data_frame_t data_frame = {
102+
.type = ESP_PEER_DATA_CHANNEL_DATA,
103+
.data = (uint8_t*)chat_content[question].question,
104+
.size = strlen(chat_content[question].question) + 1,
105+
};
106+
ESP_LOGI(TAG, "Send question:%s", (char*)data_frame.data);
107+
esp_peer_send_data(peer, &data_frame);
108+
}
109+
}
110+
111+
static int peer_state_handler(esp_peer_state_t state, void* ctx)
112+
{
113+
if (state == ESP_PEER_STATE_CONNECTED) {
114+
if (timer == NULL) {
115+
esp_timer_create_args_t cfg = {
116+
.callback = send_cb,
117+
.name = "send",
118+
};
119+
esp_timer_create(&cfg, &timer);
120+
if (timer) {
121+
esp_timer_start_periodic(timer, TEST_PERIOD * 1000);
122+
}
123+
}
124+
} else if (state == ESP_PEER_STATE_DISCONNECTED) {
125+
if (timer) {
126+
esp_timer_stop(timer);
127+
esp_timer_delete(timer);
128+
timer = NULL;
129+
}
130+
}
131+
return 0;
132+
}
133+
134+
static int peer_msg_handler(esp_peer_msg_t* msg, void* ctx)
135+
{
136+
if (msg->type == ESP_PEER_MSG_TYPE_SDP) {
137+
// Send local SDP to signaling server
138+
esp_peer_signaling_send_msg(signaling, (esp_peer_signaling_msg_t *)msg);
139+
}
140+
return 0;
141+
}
142+
143+
static int peer_video_info_handler(esp_peer_video_stream_info_t* info, void* ctx)
144+
{
145+
return 0;
146+
}
147+
148+
static int peer_audio_info_handler(esp_peer_audio_stream_info_t* info, void* ctx)
149+
{
150+
return 0;
151+
}
152+
153+
static int peer_audio_data_handler(esp_peer_audio_frame_t* frame, void* ctx)
154+
{
155+
ESP_LOGI(TAG, "Audio Sequence %d(%d)", (int)frame->pts, (int)frame->data[0]);
156+
return 0;
157+
}
158+
159+
static int peer_video_data_handler(esp_peer_video_frame_t* frame, void* ctx)
160+
{
161+
return 0;
162+
}
163+
164+
static int peer_data_handler(esp_peer_data_frame_t* frame, void* ctx)
165+
{
166+
int ans = -1;
167+
for (int i = 0; i < sizeof(chat_content) / sizeof(chat_content[0]); i++) {
168+
if (strcmp((char*)frame->data, chat_content[i].question) == 0) {
169+
ans = i;
170+
break;
171+
}
172+
}
173+
if (ans >= 0) {
174+
ESP_LOGI(TAG, "Get question:%s", (char*)frame->data);
175+
esp_peer_data_frame_t data_frame = {
176+
.type = ESP_PEER_DATA_CHANNEL_DATA,
177+
.data = (uint8_t*)chat_content[ans].answer,
178+
.size = strlen(chat_content[ans].answer) + 1,
179+
};
180+
ESP_LOGI(TAG, "Send answer:%s", (char*)data_frame.data);
181+
esp_peer_send_data(peer, &data_frame);
182+
} else {
183+
ESP_LOGI(TAG, "Get answer:%s", (char*)frame->data);
184+
}
185+
return 0;
186+
}
187+
188+
static void pc_task(void *arg)
189+
{
190+
while (peer_running) {
191+
esp_peer_main_loop(peer);
192+
media_lib_thread_sleep(20);
193+
}
194+
media_lib_thread_destroy(NULL);
195+
}
196+
197+
static int signaling_ice_info_handler(esp_peer_signaling_ice_info_t* info, void* ctx)
198+
{
199+
if (peer == NULL) {
200+
esp_peer_default_cfg_t peer_cfg = {
201+
.agent_recv_timeout = 500,
202+
};
203+
esp_peer_cfg_t cfg = {
204+
.server_lists = &info->server_info,
205+
.server_num = 1,
206+
.audio_dir = ESP_PEER_MEDIA_DIR_SEND_RECV,
207+
.audio_info = {
208+
.codec = ESP_PEER_AUDIO_CODEC_G711A,
209+
},
210+
.enable_data_channel = true,
211+
.role = info->is_initiator ? ESP_PEER_ROLE_CONTROLLING : ESP_PEER_ROLE_CONTROLLED,
212+
.on_state = peer_state_handler,
213+
.on_msg = peer_msg_handler,
214+
.on_video_info = peer_video_info_handler,
215+
.on_audio_info = peer_audio_info_handler,
216+
.on_video_data = peer_video_data_handler,
217+
.on_audio_data = peer_audio_data_handler,
218+
.on_data = peer_data_handler,
219+
.ctx = ctx,
220+
.extra_cfg = &peer_cfg,
221+
.extra_size = sizeof(esp_peer_default_cfg_t),
222+
};
223+
int ret = esp_peer_open(&cfg, esp_peer_get_default_impl(), &peer);
224+
if (ret != ESP_PEER_ERR_NONE) {
225+
return ret;
226+
}
227+
media_lib_thread_handle_t thread = NULL;
228+
peer_running = true;
229+
media_lib_thread_create_from_scheduler(&thread, "pc_task", pc_task, NULL);
230+
if (thread == NULL) {
231+
peer_running = false;
232+
}
233+
}
234+
return 0;
235+
}
236+
237+
static int signaling_connected_handler(void* ctx)
238+
{
239+
if (peer) {
240+
return esp_peer_new_connection(peer);
241+
}
242+
return 0;
243+
}
244+
245+
static int signaling_msg_handler(esp_peer_signaling_msg_t* msg, void* ctx)
246+
{
247+
if (msg->type == ESP_PEER_SIGNALING_MSG_BYE) {
248+
esp_peer_close(peer);
249+
peer = NULL;
250+
} else if (msg->type == ESP_PEER_SIGNALING_MSG_SDP) {
251+
// Receive remote SDP
252+
if (peer) {
253+
esp_peer_send_msg(peer, (esp_peer_msg_t*)msg);
254+
}
255+
}
256+
return 0;
257+
}
258+
259+
static int signaling_close_handler(void *ctx)
260+
{
261+
return 0;
262+
}
263+
264+
static int start_signaling(char* url)
265+
{
266+
esp_peer_signaling_cfg_t cfg = {
267+
.signal_url = url,
268+
.on_ice_info = signaling_ice_info_handler,
269+
.on_connected = signaling_connected_handler,
270+
.on_msg = signaling_msg_handler,
271+
.on_close = signaling_close_handler,
272+
};
273+
// Use APPRTC signaling
274+
return esp_peer_signaling_start(&cfg, esp_signaling_get_apprtc_impl(), &signaling);
275+
}
276+
277+
int start_webrtc(char *url)
278+
{
279+
if (network_is_connected() == false) {
280+
ESP_LOGE(TAG, "Wifi not connected yet");
281+
return -1;
282+
}
283+
stop_webrtc();
284+
return start_signaling(url);
285+
}
286+
287+
void query_webrtc(void)
288+
{
289+
if (peer) {
290+
esp_peer_query(peer);
291+
}
292+
}
293+
294+
int stop_webrtc(void)
295+
{
296+
peer_running = false;
297+
if (timer) {
298+
esp_timer_stop(timer);
299+
esp_timer_delete(timer);
300+
timer = NULL;
301+
}
302+
if (peer) {
303+
esp_peer_close(peer);
304+
peer = NULL;
305+
}
306+
if (signaling) {
307+
esp_peer_signaling_stop(signaling);
308+
signaling = NULL;
309+
}
310+
return 0;
311+
}

0 commit comments

Comments
 (0)