Skip to content

Concepts First

  • TCP server: a device that listens on a port and waits for clients to connect; once connected, both sides can exchange data.
  • listen: after socket() and bind(), the server calls listen() to start listening and accept() to take incoming connections.
  • Echo: the example returns received data verbatim to verify bidirectional transfer.
  • Point-to-point connections: a TCP server can accept clients one after another, but each connection is independent and has its own socket.

Example Overview

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

  • standard lwIP server flow: socket → bind → listen → accept;
  • prints the received length and writes the data back (echo), processed every 500ms;
  • setsockopt(TCP_NODELAY) disables Nagle's algorithm to reduce small-packet latency;
  • the shell command wifi_tcp_echo_test <port> starts the server; Ctrl+C exits and prints transfer statistics.
  • Sibling example: wifi_tcp_client.c in the same directory provides the client command wifi_tcp_test <ip> <port> (see TCP Client).

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 (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash:

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Connect Wi-Fi and Start the Server

Serial tool at 2000000 baud. Wait for bouffalolab />, connect to the router, then start the TCP server listening on port 3365:

wifi_sta_connect Your_SSID 12345678
wifi_tcp_echo_test 3365
5
Run and Verify

On the PC, connect to the module’s IP (port 3365) with netcat; anything you type is echoed back verbatim:

nc -v 192.168.1.3 3365

Code Execution Flow

The complete server flow from boot to echo:

APIs Used by the Example

socket(AF_INET, SOCK_STREAM, 0)

Creates a TCP socket; the handle is used by bind/listen/accept.

Parameters:

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

Return: >= 0 handle; < 0 on failure

bind(sock, addr, len)

Binds the socket to a local address and port — required before a server can be reached.

Parameters:

  • sock: socket handle
  • addr: sockaddr_in with htons(port) and INADDR_ANY

Return: 0 on success; -1 on failure

listen / accept(sock, backlog) / (sock, addr, len)

listen() enters listening state (backlog = queue length, 5 in the example); accept() blocks until a client connects and returns a new connection socket.

Parameters:

  • sock: listening socket

Return: accept returns the new connection handle; -1 on failure

recv / write(sock, buf, len, 0)

recv() reads data (returns byte count; <= 0 means closed/error); write() sends data.

Parameters:

  • sock: accepted connection socket
  • buf / len: buffer and length

Return: bytes transferred; <= 0 on error/close

setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, ...)

Disables Nagle's algorithm so each write is sent immediately — good for interactive small packets.

Parameters:

  • sock: connection socket
  • TCP_NODELAY: option, flag = 1

Return: 0 on success; -1 on failure

Complete Code

The full wifi_tcp example sources related to the TCP server, 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_server.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"

// clang-format off
// clang-format on

#define RECV_DATA_LEN (1024)
#define PING_USAGE                  \
    "wifi_tcp_echo_test [port]\r\n" \
    "\t port: local server listen port\r\n"

shell_sig_func_ptr abort_exec;
uint64_t recv_data_cnt = 0;
int sock_server = -1, connected;
char *recv_data;

static void test_close(int sig)
{
    if (connected >= 0) {
        closesocket(connected);
        connected = -1;
    }
    if (sock_server >= 0) {
        closesocket(sock_server);
    }
    if (recv_data) {
        free(recv_data);
        recv_data = NULL;
    }
    abort_exec(sig);
    if (recv_data_cnt > 0) {
        printf("recv data=%lld\r\n", recv_data_cnt);
    }
}

static void wifi_test_tcp_server_init(int argc, char **argv)
{
    abort_exec = shell_signal(SHELL_SIGINT, test_close);
    printf("tcp server task start ...\r\n");

    char *port;
    struct sockaddr_in server_addr, client_addr;
    socklen_t sin_size;

    if (argc < 2) {
        printf("%s", PING_USAGE);
        return;
    }

    /* get listen port (argv[1] if present) */
    port = argv[1];

    recv_data = (char *)pvPortMalloc(RECV_DATA_LEN);
    if (recv_data == NULL) {
        printf("not memory\r\n");
        return;
    }
    /* create socket */
    if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("TCP server create socket error\r\n");
        return;
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(port));
    server_addr.sin_addr.s_addr = INADDR_ANY;
    memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

    /* bind socket */
    if (bind(sock_server, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
        printf("TCP server socket bind fail!\r\n");
        return;
    }

    /* listen port */
    if (listen(sock_server, 5) == -1) {
        printf("TCP server listen error!\r\n ");
        return;
    }

    printf("TCP Server listen start: localhost:%s\r\n", port);
    printf("please connet ...\r\n");

    while (1) {
        sin_size = sizeof(struct sockaddr_in);
        connected = accept(sock_server, (struct sockaddr *)&client_addr, &sin_size);
        printf("new client connected from (%s, %d)\r\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        int flag = 1;
        setsockopt(connected, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, sizeof(int));

        printf("Press CTRL-C to exit.\r\n");
        recv_data_cnt = 0;
        while (1) {
            recv_data_cnt = recv(connected, recv_data, RECV_DATA_LEN, 0);
            if (recv_data_cnt <= 0) {
                break;
            }
            printf("recv %lld len data\r\n", recv_data_cnt);
            if (write(connected, recv_data, recv_data_cnt) < 0) {
                printf("write falied!\r\n");
                break;
            }
            vTaskDelay(500);
        }
        if (connected >= 0) {
            closesocket(connected);
            connected = -1;
        }
        if (sock_server >= 0) {
            closesocket(sock_server);
        }
        if (recv_data) {
            free(recv_data);
            recv_data = NULL;
        }

        return;
    }
}

#ifdef CONFIG_SHELL
#include <shell.h>

int cmd_wifi_tcp_server(int argc, char **argv)
{
    wifi_test_tcp_server_init(argc, argv);

    return 0;
}

SHELL_CMD_EXPORT_ALIAS(cmd_wifi_tcp_server, wifi_tcp_echo_test, wifi tcp test);
#endif

FAQ

The PC cannot connect to the module's port

Confirm the module and PC are on the same router/subnet (find the module's IP via CODE_WIFI_ON_GOT_IP or the ifconfig command); use wifi_tcp_echo_test 3365 (not the client command); disable AP isolation if the router has it.

Connected but no echo

Check the nc address is the module's IP; the example handles one client at a time and exits after that connection ends — rerun the command to listen again.

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