Overview
When the board acts as a TCP server, it listens on a fixed port waiting for clients (phone/computer/other devices) to connect, commonly used for LAN direct control (e.g. device control after provisioning, tool direct-connect debugging). This tutorial demonstrates: starting a TCP server listening on port 7878, echoing received data back to the connected client, and disconnecting that client on receiving close.
In plain words: the roles are swapped in this lesson — the board plays customer service: first it claims a seat (listens on port 7878; a port is the "door number" on a server), then sits waiting for others to call in (client connections). Once the call connects, it reads back what the other side says verbatim (echo), and when the other side says "hang up" (close) it drops that line. TCP's reliable traits — dial first then talk, auto re-send on lost words — apply on the server side just the same.
This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version
release_bl_iot_sdk_1.6.40) exampleapplications/protocols/socket/tcp_server; the code can be found directly in your local SDK.
Open a terminal and enter the official tcp_server example project directory:
cd ~/Ai-Thinker-WB2/applications/protocols/socket/tcp_server
Note:
cdis the “change directory” command, entering the official example project; all subsequentmakecommands must run in this directory.
Open tcp_server/main.c and change the SSID/password at the top (same as Connect Wi-Fi):
#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"
#define TCP_SERVER_PORT 7878
💡
TCP_SERVER_PORT 7878is the listening port (the “door number” on the server; clients must connect to the same port), changeable as you wish (avoid common ports). The server and the computer client must be on the same LAN, and the router must not have AP isolation enabled.
Open tcp_server/main.c. The full code for this step has been moved to the end of this page:
📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (
applications/protocols/socket/tcp_server/tcp_server/main.c).
Code highlights:
| Code | Purpose |
|---|---|
tcp_server_init(NULL, 7878) |
Creates the socket and binds and listens on port 7878; only after claiming the seat can anyone call in |
tcp_server_accept(fd, cb) |
Blocks waiting for client connections; each join automatically creates a callback task handling that connection; one client one line, no interference |
tcp_client_msg_t |
Connection info struct: socket_id (connection ID), ip_addr (client address); the callback uses it to know “who called” |
tcp_server_receive(socket_id, data) |
Receives that client’s data; listening to what the other side said |
tcp_server_send(socket_id, data) |
Echoes data back to that client; reading it back verbatim so the other side knows you received it |
inet_ntoa(socket_addr->sin_addr.s_addr) |
Prints the client IP; translating the binary address into human-readable numbers |
tcp_server_close(socket_id) |
Disconnects that client on receiving close; when the other side says hang up, drop the line |
💡 The official wrapper supports multiple clients:
tcp_server_acceptcreates an independent callback task for every accepted connection (implemented insrc/tcp_server.c), each connection isolated from the others.
Build in the project directory:
make -j8
Note:
makeis the “build” command, turning code into firmware (the program flashed into the board) the board can run;-j8builds with 8 parallel cores, faster.
On success a firmware build_out/tcp_server.bin is generated.
Keep the board connected via USB, confirm the serial device, and flash:
make flash p=/dev/ttyUSB0 b=921600
Note:
make flashis the “flash” command, writing the compiled firmware into the board. Afterp=comes the serial device (often/dev/ttyUSB0on Linux,COM3-like on Windows — use your computer’s actual one),b=921600is the flash baud rate (serial transfer speed), keep the default.
⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode; wait for the progress bar to complete — that means the flash succeeded.
After flashing, the board automatically restarts and runs. Open a serial assistant (baud rate 921600, the “speech speed” of serial data — both ends must match). First you see GOT IP; note the board’s IP address (check it in the router admin page, or it’s shown by the UART logs).
Open a network debugging assistant on your computer (TCP Client mode) and connect:
| Setting | Value |
|---|---|
| Server IP | The board’s IP (e.g. 192.168.1.100) |
| Port | 7878 |
After connecting, send hello wb2 to the board; the serial prints:
tcp_server task run
192.168.1.50:hello wb2
The computer assistant receives the echo hello wb2 at the same time (the server returns the data verbatim). After sending close the board disconnects the connection and the assistant reports the connection closed.
The serial printing tcp_server task run and “client IP:content” (e.g. 192.168.1.50:hello wb2), plus the computer assistant receiving the echo hello wb2 means success. If the computer reports a connection failure, first confirm the board is online (you saw GOT IP), the computer and board are on the same LAN, and the port filled in is 7878, see the FAQ at the end.
💡 A phone works too: connect the phone to the same Wi-Fi, use a TCP debug App to connect to the board’s IP:7878, same result.
API Summary for This Tutorial
tcp_server_init(s_ip, s_port)
Creates the socket and binds the listening port (project wrapper; pass NULL as s_ip to listen on all local addresses).
Parameters:
s_ip: listen address; passNULLto listen on all addressess_port: listening port (official example7878)
Return: socket descriptor on success; -1 on failure
tcp_server_accept(socketfd, cb)
Accepts client connections, calling the callback once per accepted connection (project wrapper).
Parameters:
socketfd: listening socket descriptorcb: connection callback function (arguments include the connection ID and client address)
Return: 0 on success; negative error code on failure
tcp_server_receive(socket_id, recv_data)
Receives data from the specified connection (project wrapper).
Parameters:
socket_id: connection IDrecv_data: receive buffer
Return: bytes received on success; negative on failure
tcp_server_send(socket_id, data)
Sends data to the specified connection (project wrapper).
Parameters:
socket_id: connection IDdata: data to send
Return: bytes sent on success; negative on failure
tcp_server_close(socket_id)
Closes the specified client connection (project wrapper).
Parameters:
socket_id: connection ID
Return: none
bind / listen(fd, addr, len / fd, backlog)
Binds the address / starts listening (the wrapper's underlying layer).
Parameters:
fd: socket descriptoraddr: local address (struct sockaddr*)len: address lengthbacklog: maximum pending connections
Return: 0 on success; negative error code on failure
accept(fd, addr, len)
Accepts a connection (the wrapper's underlying layer, blocking).
Parameters:
fd: listening socket descriptoraddr: output parameter, client addresslen: address length
Return: client socket descriptor on success; -1 on failure
📌
tcp_client_msg_tstruct:socket_id(connection ID),socket_fd(listening socket),ip_addr(clientstruct sockaddr_in*).
Full Code
Below is the complete tcp_server/main.c source, identical to the official example (applications/protocols/socket/tcp_server/tcp_server/main.c):
📜 Click to expand the full tcp_server/main.c code
/**
* @file main.c
* @author your name (you@domain.com)
* @brief
* @version 0.1
* @date 2022-10-13
*
* @copyright Copyright (c) 2022
*
*/
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <string.h>
#include <blog.h>
#include <aos/yloop.h>
#include <aos/kernel.h>
#include <lwip/sockets.h>
#include <lwip/tcpip.h>
#include <wifi_mgmr_ext.h>
#include <cli.h>
#include <hal_wifi.h>
#include <lwip/init.h>
#include "tcp_server.h"
#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"
#define TCP_SERVER_PORT 7878
static wifi_conf_t conf = {
.country_code = "CN",
};
/**
* @brief wifi_sta_connect
* wifi station mode connect start
* @param ssid
* @param password
*/
static void wifi_sta_connect(char* ssid, char* password)
{
wifi_interface_t wifi_interface;
wifi_interface = wifi_mgmr_sta_enable();
wifi_mgmr_sta_connect(wifi_interface, ssid, password, NULL, NULL, 0, 0);
}
/**
* @brief tcp_accpet_handle_cb
*
* @param arg
*/
static void tcp_accpet_handle_cb(void* arg)
{
tcp_client_msg_t* tcp_client_msg = (tcp_client_msg_t*)arg;
struct sockaddr_in* socket_addr = (struct sockaddr_in*)tcp_client_msg->ip_addr;
int ret = 0;
char data[1024] = { 0 };
while (1) {
ret = tcp_server_receive(tcp_client_msg->socket_id, data);
if (ret>0) {
printf("%s:%s\r\n", inet_ntoa(socket_addr->sin_addr.s_addr), data);
tcp_server_send(tcp_client_msg->socket_id, data);
if (strstr(data, "close")!=NULL) tcp_server_close(tcp_client_msg->socket_id);
}
vTaskDelay(500/portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
/**
* @brief tcp_server_task
*
* @param arg
*/
static void tcp_server_task(void* arg)
{
int socket_fd;
socket_fd = tcp_server_init(NULL, 7878);
tcp_server_accept(socket_fd, tcp_accpet_handle_cb);
vTaskDelete(NULL);
}
/**
* @brief event_cb_wifi_event
* wifi connet ap event Callback function
* @param event
* @param private_data
*/
static void event_cb_wifi_event(input_event_t* event, void* private_data)
{
static char* ssid;
static char* password;
switch (event->code)
{
case CODE_WIFI_ON_INIT_DONE:
{
printf("[APP] [EVT] INIT DONE %lld\r\n", aos_now_ms());
wifi_mgmr_start_background(&conf);
}
break;
case CODE_WIFI_ON_MGMR_DONE:
{
printf("[APP] [EVT] MGMR DONE %lld\r\n", aos_now_ms());
//_connect_wifi();
wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
}
break;
case CODE_WIFI_ON_SCAN_DONE:
{
printf("[APP] [EVT] SCAN Done %lld\r\n", aos_now_ms());
// wifi_mgmr_cli_scanlist();
}
break;
case CODE_WIFI_ON_DISCONNECT:
{
printf("[APP] [EVT] disconnect %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_CONNECTING:
{
printf("[APP] [EVT] Connecting %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_CMD_RECONNECT:
{
printf("[APP] [EVT] Reconnect %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_CONNECTED:
{
printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_PRE_GOT_IP:
{
printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_GOT_IP:
{
printf("[APP] [EVT] GOT IP %lld\r\n", aos_now_ms());
printf("[SYS] Memory left is %d Bytes\r\n", xPortGetFreeHeapSize());
// wifi connection succeeded, create tcp server task
xTaskCreate(tcp_server_task, "tcp_server_task", 2048, NULL, 16, NULL);
}
break;
case CODE_WIFI_ON_PROV_SSID:
{
printf("[APP] [EVT] [PROV] [SSID] %lld: %s\r\n",
aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (ssid)
{
vPortFree(ssid);
ssid = NULL;
}
ssid = (char*)event->value;
}
break;
case CODE_WIFI_ON_PROV_BSSID:
{
printf("[APP] [EVT] [PROV] [BSSID] %lld: %s\r\n",
aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (event->value)
{
vPortFree((void*)event->value);
}
}
break;
case CODE_WIFI_ON_PROV_PASSWD:
{
printf("[APP] [EVT] [PROV] [PASSWD] %lld: %s\r\n", aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (password)
{
vPortFree(password);
password = NULL;
}
password = (char*)event->value;
}
break;
case CODE_WIFI_ON_PROV_CONNECT:
{
printf("[APP] [EVT] [PROV] [CONNECT] %lld\r\n", aos_now_ms());
printf("connecting to %s:%s...\r\n", ssid, password);
wifi_sta_connect(ssid, password);
}
break;
case CODE_WIFI_ON_PROV_DISCONNECT:
{
printf("[APP] [EVT] [PROV] [DISCONNECT] %lld\r\n", aos_now_ms());
}
break;
default:
{
printf("[APP] [EVT] Unknown code %u, %lld\r\n", event->code, aos_now_ms());
/*nothing*/
}
}
}
static void proc_main_entry(void* pvParameters)
{
aos_register_event_filter(EV_WIFI, event_cb_wifi_event, NULL);
hal_wifi_start_firmware_task();
aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0);
vTaskDelete(NULL);
}
void main()
{
puts("[OS] Starting TCP/IP Stack...\r\n");
tcpip_init(NULL, NULL);
puts("[OS] proc_main_entry task...\r\n");
xTaskCreate(proc_main_entry, (char*)"main_entry", 1024, NULL, 15, NULL);
}FAQ & Troubleshooting
⚠️ Computer can't connect to the board's server
Cause: different subnets, router AP isolation enabled, or a wrong board IP entered
Fix: confirm the computer and board are on the same router and subnet; disable the router's AP isolation (guest network isolation); double-check the board IP
⚠️ Port occupied (bind fails)
Cause: another app occupies 7878, or the previous connection wasn't released
Fix: change the port; reboot the board to release residual connections
⚠️ Echoed content garbled/truncated
Cause: the client sent non-text data (containing \0), or more than 1024 bytes
Fix: with the data[1024] buffer keep single sends under 1KB; for text transfers confirm consistent encoding (UTF-8)
⚠️ Data corrupted when multiple clients connect at once
Cause: callback tasks share a global variable
Fix: pass each connection's data via the tcp_client_msg_t argument; don't share buffers outside the callback; use a semaphore when mutual exclusion is needed
⚠️ Keeps printing Connecting, never GOT IP (can't connect to the router)
Cause: wrong SSID/password, the router is on 5GHz, or the signal is too weak
Fix: double-check ROUTER_SSID/ROUTER_PWD in tcp_server/main.c match the router exactly; confirm the router is 2.4GHz (the board doesn't support 5GHz); try moving the board closer to the router
⚠️ Serial device not found / can't open
Cause: USB-to-serial driver not installed, insufficient permission, or the cable only charges and can't transfer data
Fix: on Linux check the device with lsusb/dmesg; if permission denied run sudo chmod 666 /dev/ttyUSB0; on Windows install the driver and check the COM port in Device Manager; try a data-capable cable
⚠️ Flashing keeps waiting / fails
Cause: download mode wasn't entered, wrong baud rate, or a wrong serial number
Fix: press and hold EN during flashing to enter download mode as prompted; change p=/dev/ttyUSB0 to your actual serial port; try another USB port or cable
Self-Check
After the computer client connects, sending any content makes the board echo it and the serial prints the client IP and content — the TCP server is verified.

