Concepts First
- WebSocket: a bidirectional long-lived connection over TCP. HTTP is request-response; WebSocket keeps one connection open so both sides can send messages anytime — great for chat, real-time push and remote control.
- ws / wss:
ws://is plaintext;wss://is the TLS-encrypted variant (requires mbedTLS). - Handshake: a WebSocket connection starts with an HTTP Upgrade request, then data flows as frames.
- librws: the WebSocket client library used by the example (
rws_socket_create/connect/set_*).
Example Overview
This page covers the websocket example in the official Bouffalo SDK (examples/wifi/sta/websocket):
websocket_test <host> <port> <path>connects to a WebSocket server;rws_socket_createcreates the client;set_host/set_port/set_pathconfigure the target;rws_socket_set_schemeselectsws(orwsswhen built withWEBSOCKET_SSL_ENABLE);set_server_certsets the TLS certificate;rws_socket_connectinitiates the connection;rws_socket_is_connectedpolls until success;rws_on_socket_callbackhandles disconnects and prints the error code.- Related reference: HTTP requests in HTTP GET/POST / HTTPS; WebSocket replaces polling for real-time push.
Operation Steps
Open a terminal and enter the SDK WebSocket example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/wifi/sta/websocketBoth Ai-M61 and Ai-M62 use bl616 (the example builds ws:// by default; enable WEBSOCKET_SSL_ENABLE in the Makefile for the wss:// TLS variant):
make CHIP=bl616 BOARD=bl616dkHold BOOT, briefly press EN/RST to enter download mode, then flash:
make flash CHIP=bl616 COMX=/dev/ttyUSB0Start a WebSocket server on the PC (e.g. Node’s ws library or websocat -s 8765) and note the server IP and port:
websocat -s 8765Serial tool at 2000000 baud. Connect to the router, then run (the path is optional, default /):
wifi_sta_connect Your_SSID 12345678
websocket_test 192.168.1.2 8765 /The log prints websocket connecting to ws://192.168.1.2:8765/ ..., then websocket connect successfully; on disconnect it prints websocket disconnected with an error code.
Code Execution Flow
The complete WebSocket flow from boot to connection:
APIs Used by the Example
rws_socket_create()
Creates a WebSocket client object.
Parameters: none
Return: rws_socket handle; NULL on failure
rws_socket_set_host / set_port / set_path(ws, ...)
Configures the target: host/IP, port, request path (default /).
Parameters:
ws: client handlehost/port/path: target address info
Return: none
rws_socket_set_scheme / rws_socket_set_server_cert(ws, "ws"/"wss") / (ws, cert, len)
Sets the protocol (plain ws or encrypted wss); in encrypted mode sets the server CA certificate (the example embeds test_ca_crt).
Parameters:
ws: client handlescheme:"ws"or"wss"cert/len: PEM certificate and length
Return: none
rws_socket_connect / rws_socket_is_connected(ws)
Initiates the connection (spawns a connection thread internally); is_connected polls the connection state.
Parameters:
ws: client handle
Return: connect returns true/false; is_connected returns the boolean state
rws_socket_set_on_disconnected(ws, callback)
Registers a disconnect callback that prints the error code and description (rws_socket_get_error).
Parameters:
ws: client handlecallback: disconnect callback
Return: none
Complete Code
The full websocket.c source, identical to the official example (examples/wifi/sta/websocket). Collapsed by default; click to expand:
📜 Click to expand websocket/main.c full code
/****************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include "wifi_mgmr_ext.h"
#include "bflb_irq.h"
#include "bflb_uart.h"
#include "rfparam_adapter.h"
#include "board.h"
#include "shell.h"
#define DBG_TAG "MAIN"
#include "log.h"
#include "async_event.h"
struct bflb_device_s *gpio;
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
static struct bflb_device_s *uart0;
static wifi_conf_t conf = {
.country_code = "CN",
};
extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Functions
****************************************************************************/
void wifi_start_firmware_task(void *param)
{
LOG_I("Starting wifi ...\r\n");
async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);
wifi_task_create();
LOG_I("Starting fhost ...\r\n");
fhost_init();
vTaskDelete(NULL);
}
void wifi_event_handler(async_input_event_t ev, void *priv)
{
uint32_t code = ev->code;
switch (code) {
case CODE_WIFI_ON_INIT_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE\r\n", __func__);
wifi_mgmr_init(&conf);
} break;
case CODE_WIFI_ON_MGMR_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE\r\n", __func__);
} break;
case CODE_WIFI_ON_SCAN_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE\r\n", __func__);
wifi_mgmr_sta_scanlist();
} break;
case CODE_WIFI_ON_CONNECTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED\r\n", __func__);
void mm_sec_keydump();
mm_sec_keydump();
} break;
#ifdef CODE_WIFI_ON_GOT_IP_ABORT
case CODE_WIFI_ON_GOT_IP_ABORT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_ABORT\r\n", __func__);
} break;
#endif
#ifdef CODE_WIFI_ON_GOT_IP_TIMEOUT
case CODE_WIFI_ON_GOT_IP_TIMEOUT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_TIMEOUT\r\n", __func__);
} break;
#endif
case CODE_WIFI_ON_GOT_IP: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP\r\n", __func__);
LOG_I("[SYS] Memory left is %d Bytes\r\n", kfree_size(0));
} break;
case CODE_WIFI_ON_DISCONNECT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STARTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STOPPED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STA_ADD: {
LOG_I("[APP] [EVT] [AP] [ADD] %lld\r\n", xTaskGetTickCount());
} break;
case CODE_WIFI_ON_AP_STA_DEL: {
LOG_I("[APP] [EVT] [AP] [DEL] %lld\r\n", xTaskGetTickCount());
} break;
default: {
LOG_I("[APP] [EVT] Unknown code %u \r\n", code);
}
}
}
int main(void)
{
board_init();
uart0 = bflb_device_get_by_name("uart0");
shell_init_with_task(uart0);
if (0 != rfparam_init(0, NULL, 0)) {
LOG_I("PHY RF init failed!\r\n");
return 0;
}
LOG_I("PHY RF init success!\r\n");
tcpip_init(NULL, NULL);
xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
vTaskStartScheduler();
while (1) {
}
}📜 Click to expand websocket.c full code
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <lwip/api.h>
#include <lwip/arch.h>
#include <lwip/opt.h>
#include <lwip/inet.h>
#include <lwip/errno.h>
#include <netdb.h>
#include "shell.h"
#include "utils_getopt.h"
#include "bflb_mtimer.h"
#include "librws.h"
#include "rws_error.h"
#include <mbedtls/error.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl.h>
#include <mbedtls/debug.h>
#include <mbedtls/x509.h>
static const char *test_ca_crt = {
"-----BEGIN CERTIFICATE-----\r\n"
"MIIDgjCCAmqgAwIBAgIUDXuOiFDUpfyctDliikuaRtsOA/UwDQYJKoZIhvcNAQEL\r\n"
"BQAwWTELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl\r\n"
"aWppbmcxEzARBgNVBAoMCk15IENvbXBhbnkxETAPBgNVBAMMCE15IFdTIENBMB4X\r\n"
"DTI1MTEwNzA2NTkzNloXDTM1MTEwNTA2NTkzNlowWTELMAkGA1UEBhMCQ04xEDAO\r\n"
"BgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWppbmcxEzARBgNVBAoMCk15IENv\r\n"
"bXBhbnkxETAPBgNVBAMMCE15IFdTIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\r\n"
"MIIBCgKCAQEArKRxwazPB1SZAmbibmqqKpj7uFVq/eV7swY2V1Fvy0pEgvk/EYsC\r\n"
"2Vw0TM29oKTvX3TinRg6Kodf9R+kTw9EB8fYeb782ImU6ZSiaVdPg7Dz2QCpiGY7\r\n"
"b4g92H6MUongLR7DWvFg3YIS9AXMwnDsKCD9WqoI6QmEPWGCnpknKGOCkSPmgInR\r\n"
"ruD7c+bxIN8/lefWkjwQOzb4XeqU+DOs74D+75iztaPvtHAQSI6amthfFQtODFDB\r\n"
"qP26pIQ2r6PSZqv7NwUIjXT2e4jqWmjY/OXJLj/TnrBlyjx2M6Vuh1T5hC3FiFUX\r\n"
"dpJMMdRt7mnDP09Dw1th+Kzm4tjVqrch1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\r\n"
"AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU37YNYVlsbUYi/mQu+OZQ5FJ/\r\n"
"0BowDQYJKoZIhvcNAQELBQADggEBAKYBU7IxJCauvlJLMoXW7lQIbItQhDgOp+3a\r\n"
"I+xdoTm2kwDtgwgQc2h1Fl7yVqy/TAF4RrZ/m5yuiz/8oAZqieEydWlaX9Akyim8\r\n"
"701G+GP88uAWa4wHMs7laaTWlGC2OHIdL5bg1hx69juA2NigxD3rMd1cMUnl2orc\r\n"
"2qUk/rWKtN0qO5yw9hRd0XYli1PLdQ53w5YYSlme/ABsJKQ/v2FuwrYUfM0haM1i\r\n"
"4eZxNxR69ohgloz4T6C/srCt1QXzdRrCNxk/wSBJM+EPrXCdiYbVgbuMP4oHKwLZ\r\n"
"OaWRtEoSKcObt4JRRPAo/LyS+a+/Q/Ixh13R7SnpoMwpLJaYNt0=\r\n"
"-----END CERTIFICATE-----\r\n"
};
struct arg_param {
int argc;
char **argv;
};
void rws_on_socket_callback(struct rws_socket_struct * wsocket){
rws_error err = rws_socket_get_error(wsocket);
printf("websocket disconnected\r\n");
if(err){
printf("error code: %d, description: %s\r\n", err->code, err->description);
}
}
void websocket_init(char * host, int port, char *path){
rws_socket ws_client = rws_socket_create();
if(!ws_client){
printf("create websocket socket failed\r\n");
return;
}
rws_socket_set_host(ws_client, host);
rws_socket_set_port(ws_client, port);
rws_socket_set_path(ws_client, path);
#ifdef WEBSOCKET_SSL_ENABLE
const char * scheme = "wss";
rws_socket_set_server_cert(ws_client, test_ca_crt, strlen(test_ca_crt)+1);
#else
const char * scheme = "ws";
#endif
rws_socket_set_on_disconnected(ws_client, rws_on_socket_callback);
rws_socket_set_scheme(ws_client, scheme);
if(!rws_socket_connect(ws_client)){
printf("websocet connect thread create failed\r\n");
rws_error err = rws_socket_get_error(ws_client);
if(err)
printf("error code: %d, description: %s\r\n", err->code, err->description);
rws_socket_disconnect_and_release(ws_client);
return;
}
printf("websocket connecting to ws://%s:%d%s ...\r\n", host, port, path);
while (!rws_socket_is_connected(ws_client));
printf("websocket connect successfully\r\n");
}
void cmd_websocket(int argc, char **argv){
if (argc < 3) {
printf("Usage: websocket_test <host> <port> <path>\r\n");
return;
}
char *host = argv[1];
char *port = argv[2];
char *path = "/";
if(argc >=4){
path = argv[3];
}
// struct arg_param arg = {argc, argv};
if (strlen(host) > 255) {
printf("Error: hostname too long (max 255 chars)\r\n");
return;
}
if (atoi(port) <= 0 || atoi(port) > 65535) {
printf("Error: invalid port number (1-65535)\r\n");
return;
}
printf("Set host: %s port: %d path: %s\r\n", host, atoi(port), path);
websocket_init(host, atoi(port), path);
}
SHELL_CMD_EXPORT_ALIAS(cmd_websocket, websocket_test, websocket test);FAQ
Connected but no data flows
The example only establishes the connection; it does not send/receive messages automatically. To exchange data, use rws_socket_send_text in websocket_init and register rws_socket_set_on_message, or have the server push messages to observe.
wss:// connection fails
Build with WEBSOCKET_SSL_ENABLE (off by default) and confirm the server certificate is signed by the embedded test_ca_crt CA; for public wss services, replace the CA with the public root certificate.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

