Skip to content

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_create creates the client; set_host/set_port/set_path configure the target;
  • rws_socket_set_scheme selects ws (or wss when built with WEBSOCKET_SSL_ENABLE); set_server_cert sets the TLS certificate;
  • rws_socket_connect initiates the connection; rws_socket_is_connected polls until success;
  • rws_on_socket_callback handles disconnects and prints the error code.
  • Related reference: HTTP requests in HTTP GET/POST / HTTPS; WebSocket replaces polling for real-time push.

Operation Steps

1
Enter the Example Directory

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/websocket
2
Build the Project

Both 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=bl616dk
3
Flash the Firmware

Hold BOOT, briefly press EN/RST to enter download mode, then flash:

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Prepare a WebSocket Server

Start 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 8765
5
Connect Wi-Fi and Join the WebSocket

Serial 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 /
6
Run and Verify

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 handle
  • host / 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 handle
  • scheme: "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 handle
  • callback: 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
c
/****************************************************************************
 *
 * 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
c
#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

Released under the MIT License. Build Time 2026-09-11 14:52:23