Skip to content

Concepts First

  • TCP client: the device that actively connects to a server. After connect() succeeds, it can send and receive data over the connection.
  • Socket: the handle created by socket(AF_INET, SOCK_STREAM, 0); all operations (connect/read/write/close) use it.
  • RX/TX tasks: the example forks two tasks — one continuously reads (read), the other continuously writes (write) — to test bidirectional transfer.
  • Ctrl+C: the shell signal stops the client and prints total received/sent byte counts.

Example Overview

This page covers the TCP client part of the wifi_tcp example in the official Bouffalo SDK (examples/wifi/sta/wifi_tcp/wifi_tcp_client.c):

  • socket() creates a TCP socket, inet_addr() + htons() fill the server address, connect() establishes the connection;
  • two tasks wifi_tcp_client_rx / wifi_tcp_client_tx loop read()/write() every 200ms, handling ERR_TIMEOUT retries;
  • the shell command wifi_tcp_test <ip> <port> starts the client;
  • optional SO_SNDTIMEO receive/send timeout can be enabled with TCP_CLIENT_BLOCK_TIMEOUT.
  • Sibling example: the same directory's wifi_tcp_server.c provides the echo server command wifi_tcp_echo_test <port> (see TCP Server).

Operation Steps

1
Enter the Example Directory

Open a terminal and enter the SDK TCP example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/wifi/sta/wifi_tcp
2
Build the Project

Both Ai-M61 and Ai-M62 use bl616:

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
Start a TCP Server on the PC

On the PC, listen on port 3365 with netcat:

nc -lp 3365
5
Connect Wi-Fi and Run the Client

Serial tool at 2000000 baud. Wait for bouffalolab />, connect to the router, then start the TCP client pointing at the PC’s IP and port:

wifi_sta_connect Your_SSID 12345678
wifi_tcp_test 192.168.1.2 3365
6
Run and Verify

The module logs TCP client connect server success! and creates RX/TX tasks; the PC’s netcat receives the wifi tcp client test, helloworld! payload repeatedly, and data typed on the PC is received by the module’s RX task.

Code Execution Flow

The complete flow from boot to bidirectional transfer:

APIs Used by the Example

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)

Creates a TCP socket.

Parameters:

  • AF_INET: IPv4 address family
  • SOCK_STREAM: stream (TCP) socket

Return: >= 0 handle; < 0 on failure

connect(sock, &remote_addr, sizeof(sockaddr))

Connects to the server address (IP + port).

Parameters:

  • sock: socket handle
  • remote_addr: sockaddr_in with sin_port = htons(port) and sin_addr = inet_addr(ip)

Return: 0 on success; nonzero on failure

read / write(sock, buf, len)

Read from / write to the connected socket. ERR_TIMEOUT means the non-blocking operation timed out.

Parameters:

  • sock: connected socket
  • buf / len: buffer and length

Return: bytes transferred; negative on error/timeout

shell_signal / closesocket(SHELL_SIGINT, cb) / (sock)

Registers a Ctrl+C handler that stops the tasks, then closes the socket and prints RX/TX totals.

Parameters:

  • SHELL_SIGINT: interrupt signal
  • cb: handler (sig_close)

Return: none

Complete Code

The full wifi_tcp example sources related to the TCP client, identical to the official example (examples/wifi/sta/wifi_tcp). Collapsed by default; click to expand:

📜 Click to expand wifi_tcp/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"
#ifndef BL602
#include "fhost_api.h"
#include "wifi_mgmr.h"
#endif

#include "bflb_irq.h"
#include "bflb_uart.h"

#include "rfparam_adapter.h"
#include "async_event.h"
#include "mm.h"
#include "board.h"
#include "shell.h"

#define DBG_TAG "MAIN"
#include "log.h"

struct bflb_device_s *gpio;

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Data
 ****************************************************************************/

static struct bflb_device_s *uart0;

extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);
#ifdef BL602
extern void wifi_task_create(void);
extern int fhost_init(void);
extern int wifi_mgmr_task_start(void);
#endif

/****************************************************************************
 * 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");
#ifndef BL602
    fhost_init();
#endif

    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_task_start();
        } 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;
        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 wifi_tcp_client.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"

#define TCP_CLIENT_BLOCK_TIMEOUT 0
#define TCP_CLIENT_NOT_DEL_SELF  0

struct arg_param {
    int argc;
    char **argv;
};

// clang-format off
static const uint8_t write_buf[128] = "wifi tcp client test, helloworld!\r\n";
static uint8_t read_buf[128];
// clang-format on

static volatile int wifi_tcp_client_exit;
static shell_sig_func_ptr abort_exec;
static uint64_t total_rx_cnt;
static uint64_t total_tx_cnt;

static void sig_close(int sig)
{
    wifi_tcp_client_exit = 1;

    if (abort_exec) {
        abort_exec(sig);
    }
}

#define PING_USAGE                    \
    "wifi_tcp_test [ip] [port]\r\n"   \
    "\t ip: dest ip or server ip\r\n" \
    "\t port: dest server listen port\r\n"

static void wifi_tcp_client_rx(void *sock_client)
{
    printf("tcp client rx task start ...\r\n");

    int sock = *(int*)sock_client;
    int ret;
    int timeout_cnt = 0;

    /* read */
    while (1) {
        ret = read(sock, read_buf, sizeof(read_buf));

        if (ret >= 0) {
            total_rx_cnt += ret;
            timeout_cnt = 0;
        } else if (ret == ERR_TIMEOUT) {
            if (++timeout_cnt > 3) {
                printf("read failed, timeout_cnt: %d\n\r", timeout_cnt);
                break;
            }
        } else {
            printf("read failed, ret: %d, errno: %d\n\r", ret, errno);
            break;
        }

        vTaskDelay(200);
    }

    /* wait to be deleted */
    #if TCP_CLIENT_NOT_DEL_SELF
    printf("tcp client rx task exiting...\r\n");
    while (1) {
        vTaskDelay(200);
    }
    #else
    vTaskDelete(NULL);
    #endif

    printf("tcp client rx task exit!\r\n");
}

static void wifi_tcp_client_tx(void *sock_client)
{
    printf("tcp client tx task start ...\r\n");

    int sock = *(int*)sock_client;
    int ret;
    int timeout_cnt = 0;

    /* write */
    while (1) {
        ret = write(sock, write_buf, sizeof(write_buf));

        if (ret >= 0) {
            total_tx_cnt += sizeof(write_buf);
            timeout_cnt = 0;
        } else if (ret == ERR_TIMEOUT) {
            if (++timeout_cnt > 3) {
                printf("write failed, timeout_cnt: %d\n\r", timeout_cnt);
                break;
            }
        } else {
            printf("write failed, ret: %d, errno: %d\n\r", ret, errno);
            break;
        }

        vTaskDelay(200);
    }

    /* wait to be deleted */
    #if TCP_CLIENT_NOT_DEL_SELF
    printf("tcp client tx task exiting...\r\n");
    while (1) {
        vTaskDelay(200);
    }
    #else
    vTaskDelete(NULL);
    #endif

    printf("tcp client tx task exit!\r\n");
}

static void wifi_tcp_client_init(void *input_arg)
{
    printf("tcp client task start ...\r\n");

    char *addr;
    char *port;
    int sock_client = -1;
    struct sockaddr_in remote_addr;
    TaskHandle_t px_tcpclient_rx_task = NULL;
    TaskHandle_t px_tcpclient_tx_task = NULL;
    struct arg_param* arg = (struct arg_param*)input_arg;

    /* check arg */
    if (arg->argc < 3) {
        printf("%s", PING_USAGE);
        goto __exit;
    }

    /* get address (argv[1] if present) */
    addr = arg->argv[1];
    /* get port number (argv[2] if present) */
    port = arg->argv[2];

    /* create socket */
    if ((sock_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        printf("TCP Client create socket error\r\n");
        goto __exit;
    }

    remote_addr.sin_family = AF_INET;
    remote_addr.sin_port = htons(atoi(port));
    remote_addr.sin_addr.s_addr = inet_addr(addr);
    memset(&(remote_addr.sin_zero), 0, sizeof(remote_addr.sin_zero));
    printf("Server ip Address : %s:%s\r\n", addr, port);

    /* connect socket */
    if (connect(sock_client, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) != 0) {
        printf("TCP client connect server falied!\r\n");
        goto __exit;
    }
    printf("TCP client connect server success!\r\n");

    #if TCP_CLIENT_BLOCK_TIMEOUT
    /* blocking timeout */
    int timeout_ms = 4000;
    #if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
    int opt_on = timeout_ms;
    #else
    struct timeval opt_on = {
        .tv_sec = timeout_ms / 1000,
        .tv_usec = (timeout_ms - (opt_on.tv_sec * 1000)) * 1000,
    };
    #endif
    setsockopt(sock_client, SOL_SOCKET, SO_SNDTIMEO, (void *)&opt_on, sizeof(opt_on));
    #endif

    total_rx_cnt = 0;
    total_tx_cnt = 0;
    wifi_tcp_client_exit = 0;
    abort_exec = shell_signal(SHELL_SIGINT, sig_close);
    printf("Press CTRL-C to exit before next Shell CMD.\r\n");

    /* fork recv and send task, dont care fail */
    xTaskCreate(wifi_tcp_client_rx, "tcp_client_rx", 512, (void *)&sock_client, 20, &px_tcpclient_rx_task);
    xTaskCreate(wifi_tcp_client_tx, "tcp_client_tx", 512, (void *)&sock_client, 20, &px_tcpclient_tx_task);

    while (!wifi_tcp_client_exit) {
        vTaskDelay(500);
    }

__exit:
    /* exit procedure */
    if (sock_client >= 0) {
        printf("closesocket!\r\n");
        closesocket(sock_client);
    }

    if (px_tcpclient_rx_task || px_tcpclient_tx_task) {
        vTaskDelay(500);
    }

    #if TCP_CLIENT_NOT_DEL_SELF
    if (px_tcpclient_rx_task) {
        vTaskDelete(px_tcpclient_rx_task);
    }
    if (px_tcpclient_tx_task) {
        vTaskDelete(px_tcpclient_tx_task);
    }
    #endif

    if (total_rx_cnt || total_tx_cnt) {
        printf("Total recv data=%lld\r\n", total_rx_cnt);
        printf("Total send data=%lld\r\n", total_tx_cnt);
    }

    printf("tcp_client exit!\r\n");
    vTaskDelete(NULL);
}

#ifdef CONFIG_SHELL
#include <shell.h>

int cmd_wifi_tcp_client(int argc, char **argv)
{
    struct arg_param arg = {argc, argv};

    if (pdPASS != xTaskCreate(wifi_tcp_client_init, "tcp_client", 512, (void *)&arg, 15, NULL)) {
        return -1;
    }

    return 0;
}

SHELL_CMD_EXPORT_ALIAS(cmd_wifi_tcp_client, wifi_tcp_test, wifi tcp test);
#endif

FAQ

TCP client connect server falied

Confirm the module is connected to Wi-Fi, the PC is on the same subnet, the server is actually listening (nc -lp 3365), and the firewall allows inbound TCP on that port.

PC receives data but the module's read task shows nothing

The RX task prints only after it reads data; type something in the netcat window and press Enter — the data is sent to the module and printed via the read buffer. Baud rate and terminal line endings can also affect display.

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