Skip to content

Commit e2b9360

Browse files
feat(websocket): Added remote console example over websocket
1 parent e9b21ea commit e2b9360

File tree

7 files changed

+526
-0
lines changed

7 files changed

+526
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# For more information about build system see
2+
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
3+
# The following five lines of boilerplate have to be in your project's
4+
# CMakeLists in this exact order for cmake to work correctly
5+
cmake_minimum_required(VERSION 3.16)
6+
set(COMPONENTS main)
7+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
8+
project(websocket-client-console_exp)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env python
2+
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
# SPDX-License-Identifier: Unlicense OR CC0-1.0
4+
5+
import argparse
6+
import asyncio
7+
8+
import aioconsole
9+
import websockets
10+
11+
cur_websocket = None
12+
last_input = ''
13+
14+
15+
async def handler(websocket):
16+
global cur_websocket
17+
global last_input
18+
cur_websocket = websocket
19+
await aioconsole.aprint('Connected!')
20+
while True:
21+
try:
22+
message = await websocket.recv()
23+
if last_input and message.startswith(last_input):
24+
message = message[len(last_input):]
25+
last_input = ''
26+
await aioconsole.aprint(message.decode('utf-8'), end='')
27+
except websockets.exceptions.ConnectionClosedError:
28+
return
29+
30+
31+
async def websocket_loop(host: str, port: int):
32+
async with websockets.serve(handler, host, port):
33+
await asyncio.Future()
34+
35+
36+
async def console_loop():
37+
while True:
38+
cmd = await aioconsole.ainput('') + '\n'
39+
if cur_websocket is not None:
40+
await cur_websocket.send(cmd.encode('utf-8'))
41+
42+
43+
def main():
44+
parser = argparse.ArgumentParser()
45+
parser.add_argument(
46+
'--host', type=str, help='Host to listen on', default='localhost'
47+
)
48+
parser.add_argument(
49+
'-p', '--port', type=int, help='Port to listen on', default=8080
50+
)
51+
args = parser.parse_args()
52+
loop = asyncio.get_event_loop()
53+
loop.create_task(websocket_loop(args.host, args.port))
54+
loop.create_task(console_loop())
55+
try:
56+
loop.run_forever()
57+
except KeyboardInterrupt:
58+
pass
59+
60+
61+
if __name__ == '__main__':
62+
main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
idf_component_register(SRCS "websocket_client_vfs.c"
2+
"main.c"
3+
INCLUDE_DIRS "."
4+
PRIV_REQUIRES console vfs nvs_flash esp_event esp_netif esp_wifi esp_ringbuf esp_eth)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
dependencies:
2+
idf: ">=5.0"
3+
espressif/esp_websocket_client:
4+
version: "^1.0.1"
5+
override_path: ../../../
6+
protocol_examples_common:
7+
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
8+
cmd_nvs:
9+
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_nvs
10+
cmd_system:
11+
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system
12+
espressif/console_cmd_ifconfig:
13+
version: "^1.0.0"
14+
espressif/console_cmd_ping:
15+
version: "^1.1.0"
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
#include <stdio.h>
7+
#include "esp_log.h"
8+
#include "esp_vfs.h"
9+
#include "esp_console.h"
10+
#include "nvs_flash.h"
11+
#include "protocol_examples_common.h"
12+
#include "freertos/FreeRTOS.h"
13+
#include "freertos/task.h"
14+
#include "freertos/ringbuf.h"
15+
#include "linenoise/linenoise.h"
16+
#include "argtable3/argtable3.h"
17+
#include "esp_websocket_client.h"
18+
#include "websocket_client_vfs.h"
19+
#include "cmd_nvs.h"
20+
//#include "cmd_system.h"
21+
#include "console_simple_init.h"
22+
23+
static const char* TAG = "example";
24+
static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
25+
static esp_websocket_client_handle_t websocket_app_init(void);
26+
static void websocket_app_exit(esp_websocket_client_handle_t client);
27+
static void run_console_task(void);
28+
static void console_task(void* arg);
29+
static void vfs_exit(FILE* websocket_io);
30+
static FILE* vfs_init(void);
31+
//static void test_task(void* arg);
32+
33+
typedef struct {
34+
esp_websocket_client_handle_t client;
35+
RingbufHandle_t from_websocket;
36+
RingbufHandle_t to_websocket;
37+
} console_task_args_t;
38+
39+
40+
void app_main(void)
41+
{
42+
ESP_ERROR_CHECK(nvs_flash_init());
43+
ESP_ERROR_CHECK(esp_netif_init());
44+
ESP_ERROR_CHECK(esp_event_loop_create_default());
45+
ESP_ERROR_CHECK(example_connect());
46+
47+
esp_websocket_client_handle_t client = websocket_app_init();
48+
if (client == NULL) {
49+
ESP_LOGE(TAG, "Failed to initialize websocket client");
50+
return;
51+
}
52+
ESP_LOGI(TAG, "Websocket client initialized");
53+
54+
FILE* websocket_io = vfs_init();
55+
if (websocket_io == NULL) {
56+
ESP_LOGE(TAG, "Failed to open websocket I/O file");
57+
return;
58+
}
59+
60+
run_console_task();
61+
62+
while (true) {
63+
vTaskDelay(pdMS_TO_TICKS(1000));
64+
}
65+
66+
vfs_exit(websocket_io);
67+
websocket_app_exit(client);
68+
}
69+
70+
static esp_websocket_client_handle_t websocket_app_init(void)
71+
{
72+
websocket_client_vfs_config_t config = {
73+
.base_path = "/websocket",
74+
.send_timeout_ms = 10000,
75+
.recv_timeout_ms = 10000,
76+
.recv_buffer_size = 256,
77+
.fallback_stdout = stdout
78+
};
79+
ESP_ERROR_CHECK(websocket_client_vfs_register(&config));
80+
81+
esp_websocket_client_config_t websocket_cfg = {};
82+
//websocket_cfg.uri = "ws://10.0.2.83:8080";
83+
websocket_cfg.uri = "ws://192.168.50.231:8080";
84+
websocket_cfg.reconnect_timeout_ms = 1000;
85+
websocket_cfg.network_timeout_ms = 10000;
86+
87+
ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri);
88+
89+
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
90+
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client);
91+
esp_websocket_client_start(client);
92+
websocket_client_vfs_add_client(client, 0);
93+
94+
return client;
95+
}
96+
97+
static void websocket_app_exit(esp_websocket_client_handle_t client)
98+
{
99+
esp_websocket_client_close(client, portMAX_DELAY);
100+
ESP_LOGI(TAG, "Websocket Stopped");
101+
esp_websocket_client_destroy(client);
102+
}
103+
104+
static FILE* vfs_init(void)
105+
{
106+
FILE* websocket_io = NULL;
107+
108+
websocket_io = fopen("/websocket/0", "r+");
109+
if (websocket_io == NULL) {
110+
ESP_LOGE(TAG, "Failed to open websocket I/O file");
111+
vTaskDelete(NULL);
112+
return NULL;
113+
}
114+
115+
stdin = websocket_io;
116+
stdout = websocket_io;
117+
setvbuf(stdin, NULL, _IONBF, 0);
118+
setvbuf(stdout, NULL, _IONBF, 0);
119+
120+
_GLOBAL_REENT->_stdin = websocket_io;
121+
_GLOBAL_REENT->_stdout = websocket_io;
122+
_GLOBAL_REENT->_stderr = websocket_io;
123+
124+
return websocket_io;
125+
}
126+
127+
static void vfs_exit(FILE* websocket_io)
128+
{
129+
if (websocket_io) {
130+
fclose(websocket_io);
131+
websocket_io = NULL;
132+
}
133+
}
134+
135+
static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
136+
{
137+
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
138+
139+
switch (event_id) {
140+
case WEBSOCKET_EVENT_CONNECTED:
141+
ESP_LOGI(TAG, "Websocket connected");
142+
break;
143+
case WEBSOCKET_EVENT_DISCONNECTED:
144+
ESP_LOGI(TAG, "Websocket disconnected");
145+
break;
146+
case WEBSOCKET_EVENT_DATA:
147+
if (data->op_code == 0x08 && data->data_len == 2) {
148+
ESP_LOGI(TAG, "Received closed message with code=%d", 256 * data->data_ptr[0] + data->data_ptr[1]);
149+
}
150+
break;
151+
case WEBSOCKET_EVENT_ERROR:
152+
ESP_LOGI(TAG, "Websocket error");
153+
break;
154+
}
155+
156+
websocket_client_vfs_event_handler(data->client, event_id, event_data);
157+
}
158+
159+
160+
static void run_console_task(void)
161+
{
162+
vTaskDelay(pdMS_TO_TICKS(1000));
163+
164+
xTaskCreate(console_task, "console_task", 16 * 1024, NULL, 5, NULL);
165+
//vTaskDelay(pdMS_TO_TICKS(1000));
166+
//xTaskCreate(test_task, "test_task", 4*1024, NULL, 5, NULL);
167+
}
168+
169+
#if 0
170+
static void test_task(void* arg)
171+
{
172+
printf("From: %s(%d)\n", __func__, __LINE__);
173+
while (true) {
174+
//fread(websocket_io, 1, 1, stdin);
175+
//printf("From: %s(%d)\n", __func__, __LINE__);
176+
//printf("From: %s(%d), %c, %c\n", __func__, __LINE__, buffer[0], buffer[1]);
177+
vTaskDelay(pdMS_TO_TICKS(5000));
178+
}
179+
printf("From: %s(%d)\n", __func__, __LINE__);
180+
vTaskDelete(NULL);
181+
printf("From: %s(%d)\n", __func__, __LINE__);
182+
}
183+
#endif
184+
185+
static void console_task(void* arg)
186+
{
187+
// Initialize console REPL
188+
ESP_ERROR_CHECK(console_cmd_init());
189+
ESP_ERROR_CHECK(console_cmd_all_register());
190+
191+
// start console REPL
192+
ESP_ERROR_CHECK(console_cmd_start());
193+
194+
while (true) {
195+
//fprintf(websocket_io, "From: %s(%d)\n", __func__, __LINE__);
196+
vTaskDelay(pdMS_TO_TICKS(5000));
197+
}
198+
199+
vTaskDelete(NULL);
200+
}

0 commit comments

Comments
 (0)