ESP WebSocket Client
Overview
The ESP WebSocket client is an implementation of WebSocket protocol client for ESP32
Features
Supports WebSocket over TCP, TLS with mbedtls
Easy to setup with URI
Multiple instances (Multiple clients in one application)
Configuration
URI
Supports
ws
,wss
schemesWebSocket samples:
ws://echo.websocket.org
: WebSocket over TCP, default port 80wss://echo.websocket.org
: WebSocket over SSL, default port 443
Minimal configurations:
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://echo.websocket.org",
};
The WebSocket client supports the use of both path and query in the URI. Sample:
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://echo.websocket.org/connectionhandler?id=104",
};
If there are any options related to the URI in
esp_websocket_client_config_t
, the option defined by the URI will be
overridden. Sample:
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://echo.websocket.org:123",
.port = 4567,
};
//WebSocket client will connect to websocket.org using port 4567
TLS
Configuration:
const esp_websocket_client_config_t ws_cfg = {
.uri = "wss://echo.websocket.org",
.cert_pem = (const char *)websocket_org_pem_start,
};
Note
If you want to verify the server, then you need to provide a certificate in PEM format, and provide to cert_pem
in websocket_client_config_t
. If no certficate is provided then the TLS connection will default to not requiring verification.
PEM certificate for this example could be extracted from an openssl s_client command connecting to websocket.org.
In case a host operating system has openssl and sed packages installed, one could execute the following command to download and save the root or intermediate root certificate to a file (Note for Windows users: Both Linux like environment or Windows native packages may be used).
`
echo "" | openssl s_client -showcerts -connect websocket.org:443 | sed -n "1,/Root/d; /BEGIN/,/END/p" | openssl x509 -outform PEM >websocket_org.pem
`
This command will extract the second certificate in the chain and save it as a pem-file.
Subprotocol
The subprotocol field in the config struct can be used to request a subprotocol
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://websocket.org",
.subprotocol = "soap",
};
Note
The client is indifferent to the subprotocol field in the server response and will accept the connection no matter what the server replies.
For more options on esp_websocket_client_config_t
, please refer to API reference below
Events
`WEBSOCKET_EVENT_BEGIN’: The client thread is running.
WEBSOCKET_EVENT_BEFORE_CONNECT: The client is about to connect.
WEBSOCKET_EVENT_CONNECTED: The client has successfully established a connection to the server. The client is now ready to send and receive data. Contains no event data.
WEBSOCKET_EVENT_DATA: The client has successfully received and parsed a WebSocket frame. The event data contains a pointer to the payload data, the length of the payload data as well as the opcode of the received frame. A message may be fragmented into multiple events if the length exceeds the buffer size. This event will also be posted for non-payload frames, e.g. pong or connection close frames.
WEBSOCKET_EVENT_ERROR: The client has experienced an error. Examples include transport write or read failures.
WEBSOCKET_EVENT_DISCONNECTED: The client has aborted the connection due to the transport layer failing to read data, e.g. because the server is unavailable. Contains no event data.
WEBSOCKET_EVENT_CLOSED: The connection has been closed cleanly.
`WEBSOCKET_EVENT_FINISH’: The client thread is about to exit.
If the client handle is needed in the event handler it can be accessed through the pointer passed to the event handler:
esp_websocket_client_handle_t client = (esp_websocket_client_handle_t)handler_args;
Limitations and Known Issues
The client is able to request the use of a subprotocol from the server during the handshake, but does not do any subprotocol related checks on the response from the server.
Application Example
A simple WebSocket example that uses esp_websocket_client to establish a websocket connection and send/receive data with the websocket.org server can be found here: example
Sending Text Data
The WebSocket client supports sending data as a text data frame, which informs the application layer that the payload data is text data encoded as UTF-8. Example:
esp_websocket_client_send_text(client, data, len, portMAX_DELAY);
API Reference
Header File
Functions
-
esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config)
Start a Websocket session This function must be the first function to call, and it returns a esp_websocket_client_handle_t that you must use as input to other functions in the interface. This call MUST have a corresponding call to esp_websocket_client_destroy when the operation is complete.
- Parameters
config – [in] The configuration
- Returns
esp_websocket_client_handle_t
NULL if any errors
-
esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri)
Set URL for client, when performing this behavior, the options in the URL will replace the old ones Must stop the WebSocket client before set URI if the client has been connected.
- Parameters
client – [in] The client
uri – [in] The uri
- Returns
esp_err_t
-
esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, const char *headers)
Set additional websocket headers for the client, when performing this behavior, the headers will replace the old ones.
This API should be used after the WebSocket client connection has succeeded (i.e., once the transport layer is initialized).
If you wish to set or append headers before the WebSocket client connection is established(before handshake), consider the following options:
Input headers directly into the config options, terminating each item with [CR][LF]. This approach will replace any previous headers. Example: websocket_cfg.headers = “Sec-WebSocket-Key: my_key\r\nPassword: my_pass\r\n”;
Use the
esp_websocket_client_append_header
API to append a single header to the current set.
- Pre
Must stop the WebSocket client before set headers if the client has been connected
- Parameters
client – [in] The WebSocket client handle
headers – [in] Additional header strings each terminated with [CR][LF]
- Returns
esp_err_t
-
esp_err_t esp_websocket_client_append_header(esp_websocket_client_handle_t client, const char *key, const char *value)
Appends a new key-value pair to the headers of a WebSocket client.
- Parameters
client – [in] The WebSocket client handle
key – [in] The header key to append
value – [in] The associated value for the given key
- Pre
Ensure that this function is called before starting the WebSocket client.
- Returns
esp_err_t
-
esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
Open the WebSocket connection.
- Parameters
client – [in] The client
- Returns
esp_err_t
-
esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
Stops the WebSocket connection without websocket closing handshake.
This API stops ws client and closes TCP connection directly without sending close frames. It is a good practice to close the connection in a clean way using esp_websocket_client_close().
Notes:
Cannot be called from the websocket event handler
- Parameters
client – [in] The client
- Returns
esp_err_t
-
esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client)
Destroy the WebSocket connection and free all resources. This function must be the last function to call for an session. It is the opposite of the esp_websocket_client_init function and must be called with the same handle as input that a esp_websocket_client_init call returned. This might close all connections this handle has used.
Notes:
Cannot be called from the websocket event handler
- Parameters
client – [in] The client
- Returns
esp_err_t
-
esp_err_t esp_websocket_client_destroy_on_exit(esp_websocket_client_handle_t client)
If this API called, WebSocket client will destroy and free all resources at the end of event loop.
Notes:
After event loop finished, client handle would be dangling and should never be used
- Parameters
client – [in] The client
- Returns
esp_err_t
-
int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
Write binary data to the WebSocket connection (data send with WS OPCODE=02, i.e. binary)
- Parameters
client – [in] The client
data – [in] The data
len – [in] The length
timeout – [in] Write data timeout in RTOS ticks
- Returns
Number of data was sent
(-1) if any errors
-
int esp_websocket_client_send_bin_partial(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
Write binary data to the WebSocket connection and sends it without setting the FIN flag(data send with WS OPCODE=02, i.e. binary)
Notes:
To send continuation frame, you should use ‘esp_websocket_client_send_cont_msg(…)’ API.
To mark the end of fragmented data, you should use the ‘esp_websocket_client_send_fin(…)’ API. This sends a FIN frame.
- Parameters
client – [in] The client
data – [in] The data
len – [in] The length
timeout – [in] Write data timeout in RTOS ticks
- Returns
Number of data was sent
(-1) if any errors
-
int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
Write textual data to the WebSocket connection (data send with WS OPCODE=01, i.e. text)
- Parameters
client – [in] The client
data – [in] The data
len – [in] The length
timeout – [in] Write data timeout in RTOS ticks
- Returns
Number of data was sent
(-1) if any errors
-
int esp_websocket_client_send_text_partial(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
Write textual data to the WebSocket connection and sends it without setting the FIN flag(data send with WS OPCODE=01, i.e. text)
Notes:
To send continuation frame, you should use ‘esp_websocket_client_send_cont_mgs(…)’ API.
To mark the end of fragmented data, you should use the ‘esp_websocket_client_send_fin(…)’ API. This sends a FIN frame.
- Parameters
client – [in] The client
data – [in] The data
len – [in] The length
timeout – [in] Write data timeout in RTOS ticks
- Returns
Number of data was sent
(-1) if any errors
-
int esp_websocket_client_send_cont_msg(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
Write textual data to the WebSocket connection and sends it as continuation frame (OPCODE=0x0)
Notes:
Continuation frames have an opcode of 0x0 and do not explicitly signify whether they are continuing a text or a binary message.
You determine the type of message (text or binary) being continued by looking at the opcode of the initial frame in the sequence of fragmented frames.
To mark the end of fragmented data, you should use the ‘esp_websocket_client_send_fin(…)’ API. This sends a FIN frame.
- Parameters
client – [in] The client
data – [in] The data
len – [in] The length
timeout – [in] Write data timeout in RTOS ticks
- Returns
Number of data was sent
(-1) if any errors
-
int esp_websocket_client_send_fin(esp_websocket_client_handle_t client, TickType_t timeout)
Sends FIN frame.
- Parameters
client – [in] The client
timeout – [in] Write data timeout in RTOS ticks
- Returns
Number of data was sent
(-1) if any errors
-
int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout)
Write opcode data to the WebSocket connection.
Notes:
In order to send a zero payload, data and len should be set to NULL/0
This API sets the FIN bit on the last fragment of message
- Parameters
client – [in] The client
opcode – [in] The opcode
data – [in] The data
len – [in] The length
timeout – [in] Write data timeout in RTOS ticks
- Returns
Number of data was sent
(-1) if any errors
-
esp_err_t esp_websocket_client_close(esp_websocket_client_handle_t client, TickType_t timeout)
Close the WebSocket connection in a clean way.
Sequence of clean close initiated by client:
Client sends CLOSE frame
Client waits until server echos the CLOSE frame
Client waits until server closes the connection
Client is stopped the same way as by the
esp_websocket_client_stop()
Notes:
Cannot be called from the websocket event handler
- Parameters
client – [in] The client
timeout – [in] Timeout in RTOS ticks for waiting
- Returns
esp_err_t
-
esp_err_t esp_websocket_client_close_with_code(esp_websocket_client_handle_t client, int code, const char *data, int len, TickType_t timeout)
Close the WebSocket connection in a clean way with custom code/data Closing sequence is the same as for esp_websocket_client_close()
Notes:
Cannot be called from the websocket event handler
- Parameters
client – [in] The client
code – [in] Close status code as defined in RFC6455 section-7.4
data – [in] Additional data to closing message
len – [in] The length of the additional data
timeout – [in] Timeout in RTOS ticks for waiting
- Returns
esp_err_t
-
bool esp_websocket_client_is_connected(esp_websocket_client_handle_t client)
Check the WebSocket client connection state.
- Parameters
client – [in] The client handle
- Returns
true
false
-
size_t esp_websocket_client_get_ping_interval_sec(esp_websocket_client_handle_t client)
Get the ping interval sec for client.
- Parameters
client – [in] The client
- Returns
The ping interval in sec
-
esp_err_t esp_websocket_client_set_ping_interval_sec(esp_websocket_client_handle_t client, size_t ping_interval_sec)
Set new ping interval sec for client.
- Parameters
client – [in] The client
ping_interval_sec – [in] The new interval
- Returns
esp_err_t
-
int esp_websocket_client_get_reconnect_timeout(esp_websocket_client_handle_t client)
Get the next reconnect timeout for client. Returns -1 when client is not initialized or automatic reconnect is disabled.
- Parameters
client – [in] The client
- Returns
Reconnect timeout in msec
-
esp_err_t esp_websocket_client_set_reconnect_timeout(esp_websocket_client_handle_t client, int reconnect_timeout_ms)
Set next reconnect timeout for client.
Notes:
Changing this value when reconnection delay is already active does not immediately affect the active delay and may have unexpected result.
Good place to change this value is when handling WEBSOCKET_EVENT_DISCONNECTED or WEBSOCKET_EVENT_ERROR events.
- Parameters
client – [in] The client
reconnect_timeout_ms – [in] The new timeout
- Returns
esp_err_t
-
esp_err_t esp_websocket_register_events(esp_websocket_client_handle_t client, esp_websocket_event_id_t event, esp_event_handler_t event_handler, void *event_handler_arg)
Register the Websocket Events.
- Parameters
client – The client handle
event – The event id
event_handler – The callback function
event_handler_arg – User context
- Returns
esp_err_t
Structures
-
struct esp_websocket_error_codes_t
Websocket error code structure to be passed as a contextual information into ERROR event.
Public Members
-
esp_err_t esp_tls_last_esp_err
last esp_err code reported from esp-tls component
-
int esp_tls_stack_err
tls specific error code reported from underlying tls stack
-
int esp_tls_cert_verify_flags
tls flags reported from underlying tls stack during certificate verification
-
int esp_ws_handshake_status_code
http status code of the websocket upgrade handshake
-
int esp_transport_sock_errno
errno from the underlying socket
-
esp_err_t esp_tls_last_esp_err
-
struct esp_websocket_event_data_t
Websocket event data.
Public Members
-
const char *data_ptr
Data pointer
-
int data_len
Data length
-
bool fin
Fin flag
-
uint8_t op_code
Received opcode
-
esp_websocket_client_handle_t client
esp_websocket_client_handle_t context
-
void *user_context
user_data context, from esp_websocket_client_config_t user_data
-
int payload_len
Total payload length, payloads exceeding buffer will be posted through multiple events
-
int payload_offset
Actual offset for the data associated with this event
-
esp_websocket_error_codes_t error_handle
esp-websocket error handle including esp-tls errors as well as internal websocket errors
-
const char *data_ptr
-
struct esp_websocket_client_config_t
Websocket client setup configuration.
Public Members
-
const char *uri
Websocket URI, the information on the URI can be overrides the other fields below, if any
-
const char *host
Domain or IP as string
-
int port
Port to connect, default depend on esp_websocket_transport_t (80 or 443)
-
const char *username
Using for Http authentication, only support basic auth now
-
const char *password
Using for Http authentication
-
const char *path
HTTP Path, if not set, default is
/
-
bool disable_auto_reconnect
Disable the automatic reconnect function when disconnected
-
void *user_context
HTTP user data context
-
int task_prio
Websocket task priority
-
const char *task_name
Websocket task name
-
int task_stack
Websocket task stack
-
int buffer_size
Websocket buffer size
-
const char *cert_pem
Pointer to certificate data in PEM or DER format for server verify (with SSL), default is NULL, not required to verify the server. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in cert_len.
-
size_t cert_len
Length of the buffer pointed to by cert_pem. May be 0 for null-terminated pem
-
const char *client_cert
Pointer to certificate data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also
client_key
has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_cert_len.
-
size_t client_cert_len
Length of the buffer pointed to by client_cert. May be 0 for null-terminated pem
-
const char *client_key
Pointer to private key data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also
client_cert
has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_key_len
-
size_t client_key_len
Length of the buffer pointed to by client_key_pem. May be 0 for null-terminated pem
-
esp_websocket_transport_t transport
Websocket transport type, see `esp_websocket_transport_t
-
const char *subprotocol
Websocket subprotocol
-
const char *user_agent
Websocket user-agent
-
const char *headers
Websocket additional headers
-
int pingpong_timeout_sec
Period before connection is aborted due to no PONGs received
-
bool disable_pingpong_discon
Disable auto-disconnect due to no PONG received within pingpong_timeout_sec
-
bool use_global_ca_store
Use a global ca_store for all the connections in which this bool is set.
-
esp_err_t (*crt_bundle_attach)(void *conf)
Function pointer to esp_crt_bundle_attach. Enables the use of certification bundle for server verification, MBEDTLS_CERTIFICATE_BUNDLE must be enabled in menuconfig. Include esp_crt_bundle.h, and use
esp_crt_bundle_attach
here to include bundled CA certificates.
-
const char *cert_common_name
Expected common name of the server certificate
-
bool skip_cert_common_name_check
Skip any validation of server certificate CN field
-
bool keep_alive_enable
Enable keep-alive timeout
-
int keep_alive_idle
Keep-alive idle time. Default is 5 (second)
-
int keep_alive_interval
Keep-alive interval time. Default is 5 (second)
-
int keep_alive_count
Keep-alive packet retry send count. Default is 3 counts
-
int reconnect_timeout_ms
Reconnect after this value in miliseconds if disable_auto_reconnect is not enabled (defaults to 10s)
-
int network_timeout_ms
Abort network operation if it is not completed after this value, in milliseconds (defaults to 10s)
-
size_t ping_interval_sec
Websocket ping interval, defaults to 10 seconds if not set
-
struct ifreq *if_name
The name of interface for data to go through. Use the default interface without setting
-
esp_transport_handle_t ext_transport
External WebSocket tcp_transport handle to the client; or if null, the client will create its own transport handle.
-
const char *uri
Type Definitions
-
typedef struct esp_websocket_client *esp_websocket_client_handle_t
Enumerations
-
enum esp_websocket_event_id_t
Websocket Client events id.
Values:
-
enumerator WEBSOCKET_EVENT_ANY
-
enumerator WEBSOCKET_EVENT_ERROR
This event occurs when there are any errors during execution
-
enumerator WEBSOCKET_EVENT_CONNECTED
Once the Websocket has been connected to the server, no data exchange has been performed
-
enumerator WEBSOCKET_EVENT_DISCONNECTED
The connection has been disconnected
-
enumerator WEBSOCKET_EVENT_DATA
When receiving data from the server, possibly multiple portions of the packet
-
enumerator WEBSOCKET_EVENT_CLOSED
The connection has been closed cleanly
-
enumerator WEBSOCKET_EVENT_BEFORE_CONNECT
The event occurs before connecting
-
enumerator WEBSOCKET_EVENT_BEGIN
The event occurs once after thread creation, before event loop
-
enumerator WEBSOCKET_EVENT_FINISH
The event occurs once after event loop, before thread destruction
-
enumerator WEBSOCKET_EVENT_MAX
-
enumerator WEBSOCKET_EVENT_ANY