Skip to content

Commit c409d10

Browse files
jukkarnashif
authored andcommitted
samples: net: echo-server: Add websocket console support
Add enablers that can be used to setup websocket console for this sample. Signed-off-by: Jukka Rissanen <[email protected]>
1 parent 7510e2d commit c409d10

File tree

10 files changed

+364
-1
lines changed

10 files changed

+364
-1
lines changed

samples/net/sockets/echo_server/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,21 @@ foreach(inc_file
4242
${gen_dir}/${inc_file}.inc
4343
)
4444
endforeach()
45+
46+
if(CONFIG_NET_SAMPLE_WEBSOCKET_CONSOLE)
47+
zephyr_linker_sources(SECTIONS src/ws_console/sections-rom.ld)
48+
target_sources(app PRIVATE src/ws_console/ws.c)
49+
generate_inc_file_for_target(app src/ws_console/index.html ${gen_dir}/index.html.gz.inc --gzip)
50+
generate_inc_file_for_target(app src/ws_console/style.css ${gen_dir}/style.css.gz.inc --gzip)
51+
generate_inc_file_for_target(app src/ws_console/favicon-16x16.png ${gen_dir}/favicon-16x16.png.gz.inc --gzip)
52+
endif()
53+
if(CONFIG_NET_SAMPLE_HTTPS_SERVICE)
54+
zephyr_linker_section_ifdef(CONFIG_NET_SAMPLE_WEBSOCKET_CONSOLE NAME
55+
http_resource_desc_wss_console_service
56+
KVMA RAM_REGION GROUP RODATA_REGION
57+
SUBALIGN Z_LINK_ITERABLE_SUBALIGN)
58+
endif()
59+
zephyr_linker_section_ifdef(CONFIG_NET_SAMPLE_WEBSOCKET_CONSOLE NAME
60+
http_resource_desc_ws_console_service
61+
KVMA RAM_REGION GROUP RODATA_REGION
62+
SUBALIGN Z_LINK_ITERABLE_SUBALIGN)

samples/net/sockets/echo_server/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,21 @@ config NET_SAMPLE_CERTS_WITH_SC
8787
Enable this flag, if you are interested to run this
8888
application with signed certificates and keys.
8989

90+
config NET_SAMPLE_WEBSOCKET_CONSOLE
91+
bool "Websocket console support"
92+
default y if WEBSOCKET_CONSOLE
93+
help
94+
Enable this flag, if you are interested to enable websocket console.
95+
You can use the overlay-ws-console.conf to set websocket options.
96+
97+
config NET_SAMPLE_HTTPS_SERVICE
98+
bool "Enable HTTPS service for the Webconsole"
99+
default y if NET_SAMPLE_WEBSOCKET_CONSOLE
100+
depends on NET_SOCKETS_SOCKOPT_TLS || TLS_CREDENTIALS
101+
102+
config NET_SAMPLE_HTTPS_SERVER_SERVICE_PORT
103+
int "Port number for HTTPS service"
104+
default 443
105+
depends on NET_SAMPLE_HTTPS_SERVICE
106+
90107
source "Kconfig.zephyr"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CONFIG_SHELL_BACKEND_WEBSOCKET=y
2+
CONFIG_LOG_BACKEND_WS=y
3+
CONFIG_HTTP_SERVER_WEBSOCKET=y
4+
CONFIG_HTTP_SERVER=y
5+
CONFIG_EVENTFD=y
6+
CONFIG_POSIX_API=y
7+
CONFIG_FDTABLE=y
8+
CONFIG_NET_SOCKETS_POLL_MAX=32
9+
CONFIG_NET_MAX_CONN=32
10+
CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=32
11+
CONFIG_ZVFS_EVENTFD_MAX=10
12+
CONFIG_ZVFS_OPEN_MAX=32

samples/net/sockets/echo_server/src/common.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ static inline int init_vlan(void)
8282
}
8383
#endif /* CONFIG_NET_VLAN */
8484

85+
#if defined(CONFIG_NET_SAMPLE_WEBSOCKET_CONSOLE)
86+
int init_ws(void);
87+
#else
88+
static inline int init_ws(void)
89+
{
90+
return 0;
91+
}
92+
#endif /* CONFIG_NET_SAMPLE_WEBSOCKET_CONSOLE */
93+
8594
#if defined(CONFIG_NET_L2_IPIP)
8695
int init_tunnel(void);
8796
bool is_tunnel(struct net_if *iface);

samples/net/sockets/echo_server/src/echo-server.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ static void init_app(void)
196196

197197
init_vlan();
198198
init_tunnel();
199-
199+
init_ws();
200200
init_usb();
201201
}
202202

305 Bytes
Loading
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<html>
2+
<head>
3+
<meta charset="UTF-8">
4+
<link rel="stylesheet" type="text/css" href="style.css">
5+
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
6+
7+
<title>Zephyr WS Console</title>
8+
9+
<script language="javascript" type="text/javascript">
10+
var connected;
11+
var ws;
12+
13+
function init() {
14+
ws = new WebSocket(location.origin.replace("http", "ws") + "/console");
15+
16+
ws.onopen = function() {
17+
output("Connection opened");
18+
connected = "true";
19+
// No need to print Escape codes for colors
20+
ws.send("shell colors off\n");
21+
};
22+
23+
ws.onmessage = function(e) {
24+
zconsole(new Date().timeNow() + " : " + e.data.trim());
25+
};
26+
27+
ws.onclose = function() {
28+
output("Connection closed");
29+
connected = "false";
30+
changeCloseText();
31+
};
32+
33+
ws.onerror = function(e) {
34+
output("Error: " + e.data);
35+
};
36+
}
37+
38+
Date.prototype.timeNow = function () {
39+
return ((this.getHours() < 10)?"0":"") + this.getHours() +":"+
40+
((this.getMinutes() < 10)?"0":"") + this.getMinutes() +":"+
41+
((this.getSeconds() < 10)?"0":"") + this.getSeconds();
42+
}
43+
44+
function onSendClick() {
45+
var input = document.getElementById("prompt");
46+
if (connected == "false") {
47+
output("Not connected");
48+
input.value = "";
49+
input.focus();
50+
return;
51+
}
52+
53+
output("Sending: " + input.value);
54+
ws.send(input.value + "\n");
55+
input.value = "";
56+
input.focus();
57+
}
58+
59+
function changeCloseText() {
60+
if (connected == "false") {
61+
document.getElementById("closebutton").innerText= "Connect";
62+
} else {
63+
document.getElementById("closebutton").innerText= "Close";
64+
}
65+
}
66+
67+
function onCloseClick() {
68+
if (connected == "true") {
69+
ws.close();
70+
connected = "false";
71+
changeCloseText();
72+
} else {
73+
changeCloseText();
74+
wsConnect();
75+
}
76+
}
77+
78+
function wsConnect() {
79+
if (connected == "false") {
80+
location.reload();
81+
}
82+
}
83+
84+
function scrollToBottom(id){
85+
var div = document.getElementById(id);
86+
div.scrollTop = div.scrollHeight - div.clientHeight;
87+
}
88+
89+
function zconsole(str) {
90+
var log = document.getElementById("zconsoleline");
91+
var escaped = str.replace(/&/, "&amp;").replace(/</, "&lt;").
92+
replace(/>/, "&gt;").replace(/\u000d/, "").
93+
replace(/\u000a/, "").replace(/\[0\;31m/, "<em>").
94+
replace(/\[0m/, "</em>").replace(/"/, "&quot;"); // "
95+
log.innerHTML = log.innerHTML + escaped + "<br>";
96+
scrollToBottom("zconsole");
97+
}
98+
99+
function output(str) {
100+
var log = document.getElementById("output");
101+
var escaped = str.replace(/&/, "&amp;").replace(/</, "&lt;").
102+
replace(/>/, "&gt;").replace(/"/, "&quot;"); // "
103+
log.innerHTML = log.innerHTML + escaped + "<br>";
104+
scrollToBottom("output");
105+
}
106+
107+
</script>
108+
</head>
109+
110+
<body onload="init();">
111+
<div id="container" class="container">
112+
<div class="textcontainer">
113+
<div id="zconsole" class="zconsole">
114+
<p id="zconsoleline"></p>
115+
</div>
116+
<div id="output" class="output">
117+
</div>
118+
</div>
119+
<div id="inputbar" class="inputbar">
120+
<table class="inputtable">
121+
<tr class="inputrow">
122+
<td class="input_cell">
123+
<label for="prompt" class="command_prompt">Command:</label>
124+
<input id="prompt" type="text" size="32"
125+
onkeydown="if (event.keyCode == 13)
126+
document.getElementById('sendbutton').click()" />
127+
<button type="button" id="sendbutton"
128+
onclick="onSendClick()">Send</button>
129+
</td>
130+
<td class="close_cell">
131+
<button type="button" id="closebutton"
132+
onclick="onCloseClick()">Close</button>
133+
</td>
134+
</tr>
135+
</table>
136+
</div>
137+
</div>
138+
</body>
139+
</html>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#include <zephyr/linker/iterable_sections.h>
2+
3+
ITERABLE_SECTION_ROM(http_resource_desc_wss_console_service, Z_LINK_ITERABLE_SUBALIGN)
4+
ITERABLE_SECTION_ROM(http_resource_desc_ws_console_service, Z_LINK_ITERABLE_SUBALIGN)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
body {
8+
background-color: black;
9+
color: white;
10+
}
11+
12+
table, th, td {
13+
border: 1px solid black;
14+
border-collapse: collapse;
15+
}
16+
17+
th, td {
18+
padding: 5px;
19+
}
20+
21+
th {
22+
text-align: left;
23+
}
24+
25+
div.container {
26+
}
27+
28+
div.textcontainer {
29+
font-family: monospace !important;
30+
font-size: 150%;
31+
}
32+
33+
div.zconsole {
34+
white-space: pre-wrap !important;
35+
scroll-behavior: auto;
36+
border: 1px solid gray;
37+
padding: 5px;
38+
width: 95%;
39+
height: 70%;
40+
overflow: auto;
41+
}
42+
43+
div.output {
44+
scroll-behavior: auto;
45+
border: 1px solid gray;
46+
padding: 5px;
47+
margin-top: 20px;
48+
width: 95%;
49+
height: 10%;
50+
overflow: auto;
51+
}
52+
53+
div.inputbar {
54+
margin-top: 5px;
55+
width: 100%;
56+
}
57+
58+
.zconsole em {
59+
color: red;
60+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/logging/log.h>
8+
LOG_MODULE_DECLARE(net_echo_server_sample, LOG_LEVEL_DBG);
9+
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/net/tls_credentials.h>
12+
#include <zephyr/shell/shell_websocket.h>
13+
14+
static const char index_html_gz[] = {
15+
#include "index.html.gz.inc"
16+
};
17+
18+
struct http_resource_detail_static index_html_gz_resource_detail = {
19+
.common = {
20+
.type = HTTP_RESOURCE_TYPE_STATIC,
21+
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
22+
.content_encoding = "gzip",
23+
},
24+
.static_data = index_html_gz,
25+
.static_data_len = sizeof(index_html_gz),
26+
};
27+
28+
static const char style_css_gz[] = {
29+
#include "style.css.gz.inc"
30+
};
31+
32+
struct http_resource_detail_static style_css_gz_resource_detail = {
33+
.common = {
34+
.type = HTTP_RESOURCE_TYPE_STATIC,
35+
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
36+
.content_encoding = "gzip",
37+
},
38+
.static_data = style_css_gz,
39+
.static_data_len = sizeof(style_css_gz),
40+
};
41+
42+
static const char favicon_16x16_png_gz[] = {
43+
#include "favicon-16x16.png.gz.inc"
44+
};
45+
46+
struct http_resource_detail_static favicon_16x16_png_gz_resource_detail = {
47+
.common = {
48+
.type = HTTP_RESOURCE_TYPE_STATIC,
49+
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
50+
.content_encoding = "gzip",
51+
},
52+
.static_data = favicon_16x16_png_gz,
53+
.static_data_len = sizeof(favicon_16x16_png_gz),
54+
};
55+
56+
#if defined(CONFIG_NET_SAMPLE_HTTPS_SERVICE)
57+
#include <zephyr/net/tls_credentials.h>
58+
#include "../certificate.h"
59+
60+
static const sec_tag_t sec_tag_list_verify_none[] = {
61+
SERVER_CERTIFICATE_TAG,
62+
#if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
63+
PSK_TAG,
64+
#endif
65+
};
66+
67+
#define SEC_TAG_LIST sec_tag_list_verify_none
68+
#define SEC_TAG_LIST_LEN sizeof(sec_tag_list_verify_none)
69+
#else
70+
#define SEC_TAG_LIST NULL
71+
#define SEC_TAG_LIST_LEN 0
72+
#endif /* CONFIG_NET_SAMPLE_HTTPS_SERVICE */
73+
74+
WEBSOCKET_CONSOLE_DEFINE(ws_console_service, SEC_TAG_LIST,
75+
SEC_TAG_LIST_LEN);
76+
77+
HTTP_RESOURCE_DEFINE(root_resource, ws_console_service, "/",
78+
&index_html_gz_resource_detail);
79+
80+
HTTP_RESOURCE_DEFINE(index_html_gz_resource, ws_console_service, "/index.html",
81+
&index_html_gz_resource_detail);
82+
83+
HTTP_RESOURCE_DEFINE(style_css_gz_resource, ws_console_service, "/style.css",
84+
&style_css_gz_resource_detail);
85+
86+
HTTP_RESOURCE_DEFINE(favicon_16x16_png_gz_resource, ws_console_service,
87+
"/favicon-16x16.png",
88+
&favicon_16x16_png_gz_resource_detail);
89+
90+
int init_ws(void)
91+
{
92+
int ret;
93+
94+
WEBSOCKET_CONSOLE_ENABLE(ws_console_service);
95+
96+
ret = http_server_start();
97+
if (ret < 0) {
98+
LOG_DBG("Cannot start websocket console (%d)", ret);
99+
} else {
100+
LOG_DBG("Starting websocket console");
101+
}
102+
103+
return 0;
104+
}

0 commit comments

Comments
 (0)