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 schemes

  • WebSocket samples:

    • ws://echo.websocket.org: WebSocket over TCP, default port 80

    • wss://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:

    1. 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”;

    2. 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

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

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.

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
enum esp_websocket_error_type_t

Websocket connection error codes propagated via ERROR event.

Values:

enumerator WEBSOCKET_ERROR_TYPE_NONE
enumerator WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT
enumerator WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT
enumerator WEBSOCKET_ERROR_TYPE_HANDSHAKE
enum esp_websocket_transport_t

Websocket Client transport.

Values:

enumerator WEBSOCKET_TRANSPORT_UNKNOWN

Transport unknown

enumerator WEBSOCKET_TRANSPORT_OVER_TCP

Transport over tcp

enumerator WEBSOCKET_TRANSPORT_OVER_SSL

Transport over ssl