WebSocket Arduino esp8266 esp32 client
Definition
WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection.
Unlike HTTP, WebSocket provides full-duplex communication. Additionally, WebSocket enables streams of messages on top of TCP. TCP alone deals with streams of bytes with no inherent concept of a message.(ct. wiki)
So with WebSocket you can do a full-duplex communucation, and by subscribing to a channel, you can avoid polling. I used this protocol in a lot of application, and I think become the future of modern application.
Architecture
Basically you have a server that share the service, and a set of client that can connect to the server.
The connection open a channel from the devices, but to get information you must subscribe to a topic (sometime subscription is implicit with connect).
The big difference from REST server is that in an http request you send request and you must wait response to have the data and start new request on the same connection, with the WS you can stream requests and stream responses than operate when you want.
REST WebSocket behaivor
But how can we use the WebSocket in our projects? A classic example is a table with a lot of row, first we do a REST call and get all the data of the table, but to update the single cell we can use WebSocket subscription, and with JavaScript go to update single cell maybe with a nice graphic effect.
WebSocket client
Now we are going to see a simple WebSocket client, that are going to connect to a WebSocket echo server. Its behavior is pretty simple, you send a message and the server replies with the same message, so you can test the send and the receive of your Client.
I add this simple WebSocket test client in js here, you can test the behavior.
My site is in https so the embedded clien works only on wss not ws (we use ws without secure layer), so I create a link to a simple http version of the client here.
WebSocket http client
JavaScript WebSocket client
Here a simplified version of WebSocket test client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<!--
js simple WebSocket client
https:
I use the ws:
When you send a message to
this
server you receive
a response
with
the same message
-->
<!DOCTYPE html>
<html>
<head>
<meta charset=
"utf-8"
/>
<title>WebSocket Test</title>
<style>
#output {
border: solid 1px
#999999;
border-top-color:
#CCCCCC;
border-left-color:
#CCCCCC;
padding: 5px;
width: 300px;
height: 172px;
overflow-y: scroll;
font-family:
"Open Sans"
;
font-size: 13px;
}
</style>
<script language=
"javascript"
type=
"text/javascript"
>
var
wsUri =
"wss://echo.websocket.org/"
;
var
output;
function
init() {
output = document.getElementById(
"output"
);
testWebSocket();
}
function
testWebSocket() {
websocket =
new
WebSocket(wsUri);
websocket.onopen =
function
(evt) {
onOpen(evt)
};
websocket.onclose =
function
(evt) {
onClose(evt)
};
websocket.onmessage =
function
(evt) {
onMessage(evt)
};
websocket.onerror =
function
(evt) {
onError(evt)
};
}
function
onOpen(evt) {
writeToScreen(
"CONNECTED"
);
}
function
onClose(evt) {
writeToScreen(
"DISCONNECTED"
);
}
function
onMessage(evt) {
writeToScreen('<span style=
"color: blue;"
>RESPONSE:
' + evt.data + '
</span>
');
// websocket.close();
}
function onError(evt) {
writeToScreen('
<span style=
"color: red;"
>ERROR:</span>
' + evt.data);
}
function doSend(message) {
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
output.scrollTop = output.scrollHeight;
}
window.addEventListener("load", init, false);
</script>
</head>
<body>
<h2>WebSocket Test</h2>
<div id="output"></div>
<br/>
<input type="button" value="Send message!" onclick="doSend('
Simple js client message!!')"/></div>
<input type=
"button"
value=
"Close connection!"
onclick=
"websocket.close()"
/></div>
</body>
</html>
The core part of the code is
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function
testWebSocket() {
websocket =
new
WebSocket(wsUri);
websocket.onopen =
function
(evt) {
onOpen(evt)
};
websocket.onclose =
function
(evt) {
onClose(evt)
};
websocket.onmessage =
function
(evt) {
onMessage(evt)
};
websocket.onerror =
function
(evt) {
onError(evt)
};
}
This simple code do a connection, and attach a series of callback functions to a specified events:
onopen : this is called when connection is establish;onclose : when client is disconnected;onmessage : here when a message is arrived;onerror : when an error is generated.
As you can see to send message you must simply call websocket.send
after the connection.
Library
You can find the library directly in the Arduino libraries repository (Tools --> Manage libraries..
).
WebSocket Arduino esp8266 esp32 library
Pay attention for esp8266 and esp32 you must use 2.x.x version for AVR you must use 1.3 version.
esp8266 WebSocket client
To connect with your esp8266 the code is quite simple
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include
<Arduino.h>
#include
<ESP8266WiFi.h>
#include
<WebSocketsClient.h>
WebSocketsClient webSocket;
const
char
*
ssid
=
"<YOUR-SSID>"
;
const
char
*
password
=
"<YOUR-PASSWD>"
;
unsigned
long
messageInterval
=
5000
;
bool connected
=
false
;
#define
DEBUG_SERIAL Serial
void
webSocketEvent(WStype_t type, uint8_t
*
payload, size_t length) {
switch
(type) {
case
WStype_DISCONNECTED:
DEBUG_SERIAL.printf(
"[WSc] Disconnected!\n"
);
connected
=
false
;
break
;
case
WStype_CONNECTED: {
DEBUG_SERIAL.printf(
"[WSc] Connected to url: %s\n"
, payload);
connected
=
true
;
DEBUG_SERIAL.println(
"[WSc] SENT: Connected"
);
webSocket.sendTXT(
"Connected"
);
}
break
;
case
WStype_TEXT:
DEBUG_SERIAL.printf(
"[WSc] RESPONSE: %s\n"
, payload);
break
;
case
WStype_BIN:
DEBUG_SERIAL.printf(
"[WSc] get binary length: %u\n"
, length);
hexdump(payload, length);
break
;
case
WStype_PING:
DEBUG_SERIAL.printf(
"[WSc] get ping\n"
);
break
;
case
WStype_PONG:
DEBUG_SERIAL.printf(
"[WSc] get pong\n"
);
break
;
}
}
void
setup
() {
DEBUG_SERIAL.begin(
115200
);
DEBUG_SERIAL.println();
DEBUG_SERIAL.println();
DEBUG_SERIAL.println();
for
(uint8_t t
=
4
; t >
0
; t
-
-
) {
DEBUG_SERIAL.printf(
"[SETUP] BOOT WAIT %d...\n"
, t);
DEBUG_SERIAL.flush();
delay
(
1000
);
}
WiFi.begin(ssid, password);
while
( WiFi.status()
!
=
WL_CONNECTED ) {
delay
(
500
);
Serial.print
(
"."
);
}
DEBUG_SERIAL.print(
"Local IP: "
); DEBUG_SERIAL.println(WiFi.localIP());
webSocket.begin(
"echo.websocket.org"
,
80
,
"/"
);
webSocket.onEvent(webSocketEvent);
}
unsigned
long
lastUpdate
=
millis
();
void
loop
() {
webSocket.
loop
();
if
(connected
&
&
lastUpdate
+
messageInterval<
millis
()){
DEBUG_SERIAL.println(
"[WSc] SENT: Simple js client message!!"
);
webSocket.sendTXT(
"Simple js client message!!"
);
lastUpdate
=
millis
();
}
}
The core part is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
switch
(type) {
case
WStype_DISCONNECTED:
DEBUG_SERIAL.printf(
"[WSc] Disconnected!\n"
);
connected
=
false
;
break
;
case
WStype_CONNECTED: {
DEBUG_SERIAL.printf(
"[WSc] Connected to url: %s\n"
, payload);
connected
=
true
;
DEBUG_SERIAL.println(
"[WSc] SENT: Connected"
);
webSocket.sendTXT(
"Connected"
);
}
break
;
case
WStype_TEXT:
DEBUG_SERIAL.printf(
"[WSc] RESPONSE: %s\n"
, payload);
break
;
case
WStype_BIN:
DEBUG_SERIAL.printf(
"[WSc] get binary length: %u\n"
, length);
hexdump(payload, length);
break
;
case
WStype_PING:
DEBUG_SERIAL.printf(
"[WSc] get ping\n"
);
break
;
case
WStype_PONG:
DEBUG_SERIAL.printf(
"[WSc] get pong\n"
);
break
;
}
e puoi controllare vari eventi:
WStype_CONNECTED : when connection is established;WStype_DISCONNECTED : when client is disconnected;WStype_TEXT : when a text message arrive;WStype_BIN : when binary message arrive;WStype_PING and WStype_PONG: here the keep alive message.
To connect you must use this command
1
2
webSocket.begin(
"echo.websocket.org"
,
80
,
"/"
);
and with webSocket.onEvent
you attach the callback to the events.
With the client to send a simple message you can use webSocket.sendTXT
.
As you can see the management of WS is quite simple and similar in all language.
esp32 WebSocket client
The esp32 version is quite similar to esp8266
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include
<Arduino.h>
#include
<WiFi.h>
#include
<WebSocketsClient.h>
WebSocketsClient webSocket;
const
char
*
ssid
=
"<YOUR-SSID>"
;
const
char
*
password
=
"<YOUR-PASSWD>"
;
unsigned
long
messageInterval
=
5000
;
bool connected
=
false
;
#define
DEBUG_SERIAL Serial
void
hexdump(
const
void
*
mem, uint32_t len, uint8_t cols
=
16
) {
const
uint8_t
*
src
=
(
const
uint8_t
*
) mem;
DEBUG_SERIAL.printf(
"\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)"
, (ptrdiff_t)src, len, len);
for
(uint32_t i
=
0
; i < len; i
+
+
) {
if
(i
%
cols
=
=
0
) {
DEBUG_SERIAL.printf(
"\n[0x%08X] 0x%08X: "
, (ptrdiff_t)src, i);
}
DEBUG_SERIAL.printf(
"%02X "
,
*
src);
src
+
+
;
}
DEBUG_SERIAL.printf(
"\n"
);
}
void
webSocketEvent(WStype_t type, uint8_t
*
payload, size_t length) {
switch
(type) {
case
WStype_DISCONNECTED:
DEBUG_SERIAL.printf(
"[WSc] Disconnected!\n"
);
connected
=
false
;
break
;
case
WStype_CONNECTED: {
DEBUG_SERIAL.printf(
"[WSc] Connected to url: %s\n"
, payload);
connected
=
true
;
DEBUG_SERIAL.println(
"[WSc] SENT: Connected"
);
webSocket.sendTXT(
"Connected"
);
}
break
;
case
WStype_TEXT:
DEBUG_SERIAL.printf(
"[WSc] RESPONSE: %s\n"
, payload);
break
;
case
WStype_BIN:
DEBUG_SERIAL.printf(
"[WSc] get binary length: %u\n"
, length);
hexdump(payload, length);
break
;
case
WStype_PING:
DEBUG_SERIAL.printf(
"[WSc] get ping\n"
);
break
;
case
WStype_PONG:
DEBUG_SERIAL.printf(
"[WSc] get pong\n"
);
break
;
case
WStype_ERROR:
case
WStype_FRAGMENT_TEXT_START:
case
WStype_FRAGMENT_BIN_START:
case
WStype_FRAGMENT:
case
WStype_FRAGMENT_FIN:
break
;
}
}
void
setup
() {
DEBUG_SERIAL.begin(
115200
);
DEBUG_SERIAL.println();
DEBUG_SERIAL.println();
DEBUG_SERIAL.println();
for
(uint8_t t
=
4
; t >
0
; t
-
-
) {
DEBUG_SERIAL.printf(
"[SETUP] BOOT WAIT %d...\n"
, t);
DEBUG_SERIAL.flush();
delay
(
1000
);
}
WiFi.begin(ssid, password);
while
( WiFi.status()
!
=
WL_CONNECTED ) {
delay
(
500
);
DEBUG_SERIAL.print (
"."
);
}
DEBUG_SERIAL.print(
"Local IP: "
); DEBUG_SERIAL.println(WiFi.localIP());
webSocket.begin(
"echo.websocket.org"
,
80
,
"/"
);
webSocket.onEvent(webSocketEvent);
}
unsigned
long
lastUpdate
=
millis
();
void
loop
() {
webSocket.
loop
();
if
(connected
&
&
lastUpdate
+
messageInterval<
millis
()){
DEBUG_SERIAL.println(
"[WSc] SENT: Simple js client message!!"
);
webSocket.sendTXT(
"Simple js client message!!"
);
lastUpdate
=
millis
();
}
}
But you can immediately find some difference, in the webSocketEvent you have to pay attention to the hexdump method to decode the binary message and there are a set of case switches that must be implemented (I added them at the end).
Arduino Mega WebSocket client with enc28j60
Library and connection schema
With Arduino you must download another version of the library, you must select 1.3 version from library manager or if you want exist a specific branch for the AVR devices.
You can find library here . To download. Click the DOWNLOADS button in the top right corner, rename the uncompressed folder WebSocket-ATMega. Check that the WebSocket-ATMega folder contains WebSocket.cpp and WebSocket.h. Place the WebSocket-ATMega library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.
I use an enc28j60 adapter with UIPEthernet (Tools --> Manage libraries..
), so you must do a change in the library WebSocket, you must change
52
53
54
55
56
57
#ifdef
ESP8266
#define
WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
#else
#define
WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
#endif
in
52
53
54
55
56
57
#ifdef
ESP8266
#define
WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
#else
#define
WEBSOCKETS_NETWORK_TYPE NETWORK_ENC28J60
#endif
Now the connection schema
Arduino Mega And Enc28j60 Ethernet connection schema
I select an Arduino Mega because UIPEthernet need more memory to work.
WebSocket client
Now the sketch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include
<Arduino.h>
#include
<SPI.h>
#include
<UIPEthernet.h>
#include
<WebSocketsClient.h>
#define
DEBUG_SERIAL Serial
byte
mac[]
=
{
0xDE
,
0xAD
,
0xBE
,
0xEF
,
0xFE
,
0xED
};
IPAddress ip(
192
,
168
,
0
,
177
);
WebSocketsClient webSocket;
unsigned
long
messageInterval
=
5000
;
bool connected
=
false
;
void
webSocketEvent(WStype_t type, uint8_t
*
payload, size_t length) {
switch
(type) {
case
WStype_DISCONNECTED:
DEBUG_SERIAL.println(
"[WSc] Disconnected!\n"
);
connected
=
false
;
break
;
case
WStype_CONNECTED:
{
DEBUG_SERIAL.println(
"[WSc] SENT: Connected"
);
DEBUG_SERIAL.println((
char
*
)payload);
webSocket.sendTXT(
"Connected"
);
connected
=
true
;
}
break
;
case
WStype_TEXT:
DEBUG_SERIAL.print(
"[WSc] RESPONSE: "
);
DEBUG_SERIAL.println((
char
*
)payload);
break
;
case
WStype_BIN:
DEBUG_SERIAL.print(
"[WSc] get binary length: "
);
DEBUG_SERIAL.println(length);
break
;
case
WStype_ERROR:
break
;
}
}
void
setup
()
{
DEBUG_SERIAL.begin(
115200
);
while
(
!
Serial) {}
if
(Ethernet.begin(mac)
=
=
0
) {
DEBUG_SERIAL.println(
"Failed to configure Ethernet using DHCP"
);
Ethernet.begin(mac, ip);
}
DEBUG_SERIAL.print(
"IP address "
);
DEBUG_SERIAL.println(Ethernet.localIP());
webSocket.begin(
"echo.websocket.org"
,
80
,
"/"
);
webSocket.onEvent(webSocketEvent);
}
unsigned
long
lastUpdate
=
millis
();
void
loop
() {
webSocket.
loop
();
if
(connected
&
&
lastUpdate
+
messageInterval<
millis
()){
DEBUG_SERIAL.println(
"[WSc] SENT: Simple js client message!!"
);
webSocket.sendTXT(
"Simple js client message!!"
);
lastUpdate
=
millis
();
}
}
The sketch is similar, but you can note that there are less event managed (no ping pong), but all core event is correctly manged.
Additional options
SSL
In the previows example we are use a simple connection to ws protocol for exactly we connect to this uri:
ws://echo.websocket.org/
but the echo server support SSL also, and you can connect with wss protocol
wss://echo.websocket.org/
to do this you must use
1
webSocket.beginSSL(
"echo.websocket.org"
,
80
);
Automatic reconnection
Sometime connection can fail, so you can set an automatic reconnection time
1
2
webSocket.setReconnectInterval(
5000
);
Authorization credential
You can also set authorization credentials via this command
1
2
webSocket.setAuthorization(
"user"
,
"Password"
);
Thanks
WebSocket on Arduino, esp8266 and esp32: client WebSocket on Arduino, esp8266 and esp32: server and authentication WebSocket on Arduino, esp8266 and esp32: temperature and humidity realtime update
Complete code on GitHub