Overview
UART is the most commonly used serial communication interface, used to talk to computers, sensors, GPS modules and more. The Ai-WB2 module has 2 UARTs (UART0 and UART1) built in: UART0 is the system log port (blog_info and other logs always output via UART0), while UART1 can be freely used as a communication serial port. This tutorial implements UART1 receiving data and echoing it back as-is (loopback).
In plain words: UART (serial) is like a "telephone line" between the board and the computer — both sides speak at the same speed, word by word (send one byte, listen one byte). The "loopback" in this tutorial makes the board a repeater: whatever the computer sends, it sends back as-is, like shouting into a valley and hearing the echo.
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/peripherals/uart; the code can be found directly in your local SDK.
Wire per the official example (see SDK applications/peripherals/uart/README.md). The communication port is UART1: TX GPIO16, RX GPIO7, cross-connected to the USB-TTL module:
| Ai-WB2 Pin | USB-TTL Module |
|---|---|
| IO16 (UART1 TX) | RXD |
| IO7 (UART1 RX) | TXD |
| GND | GND |
💡 Serial TX/RX must be cross-connected (TX to the other side’s RX, like speaking into the microphone of a phone); and be sure to share ground (GND to GND, otherwise there’s no voltage reference and communication garbles). The USB-TTL module’s VCC can take 3V3 power, or the board can be powered separately via Type-C.
📌 Note the difference: the log you see with the board’s USB port connected directly to the computer is UART0 (the log port, TX GPIO4 / RX GPIO3, always outputs system logs); this tutorial’s UART1 needs a USB-TTL module to bring out.
Open a terminal and enter the official uart example project directory:
cd ~/Ai-Thinker-WB2/applications/peripherals/uart
Note:
cdis the “change directory” command; this enters the uart example project. All subsequentmakebuild andmake flashcommands must run inside this directory first.
Open uart/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/peripherals/uart/uart/main.c).
Code highlights:
| Code | Purpose |
|---|---|
uart_id = 1, tx_pin = 16, rx_pin = 7 |
The communication port UART1 (TX16/RX7), baud rate (transfer speed; both ends must match) 115200; wrong pin or baud means no data |
uart_id = 0, tx_pin = 4, rx_pin = 3 |
The system log port UART0; blog_info always outputs here — don’t connect communication data to it |
mode = HOSAL_UART_MODE_POLL |
Poll mode: hosal_uart_receive waits for data; without it it blocks and doesn’t continue |
hosal_uart_receive(&uart_dev_log, data, sizeof(data)) |
Receives data from the computer, returns the actual byte count; no echo if nothing was received (returns 0) |
hosal_uart_send(&uart_dev_log, data, ret) |
Sends the received data back as-is (loopback); without it the computer sees no echo |
Build in the project directory:
make -j8
Note:
makeis the “build” command, turning code into firmware (a program file) the board can run;-j8builds with 8 parallel cores, faster.
On success a firmware build_out/uart.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’s chip. Afterp=comes the serial device (change it to your computer’s actual port — check withls /dev/ttyUSB*);b=is the flash baud rate (transfer speed).
⏳ 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:
- Open two serial tools: the direct USB port (UART0, baud rate 921600, for system logs) and the USB-TTL-connected UART1 (baud rate 115200, for the communication test).
- The UART0 log port prints
Uart Demo Start. - Send any characters (e.g.
hello) in the USB-TTL serial tool; UART1 echoes them as-is, while the UART0 log port printsGet data.
[UART0 Log Port]
Uart Demo Start
Get data
Get data
[UART1 Communication Port]
hello ← sent
hello ← echoed
💡 The example baud rates are 115200 (UART1) and 9600 (uart_dev_echo, not actually used in this example); both ends’ baud rates (the serial transfer speed; both ends must match) must agree, otherwise you get garbled text.
Seeing hello echoed back as hello after sending from the USB-TTL and Get data on the log port means success; if there’s no echo after sending, check the “FAQ & Troubleshooting” section at the end.
API Summary for This Tutorial
hosal_uart_init(uart)
Configures the UART controller per the uart_id, pins, baud rate, etc. in the dev struct (initializing the UART1 communication port and UART0 log port in this tutorial).
Parameters:
uart:hosal_uart_dev_tstruct pointer, required. Key fields:config.uart_id(UART number, values:0/1/2; communication port uses1here),config.tx_pin/config.rx_pin(TX/RX pins, TX16/RX7 here),config.baud_rate(baud rate, values:115200/921600etc.),config.data_width(values:HOSAL_DATA_WIDTH_8BIT),config.parity(values:HOSAL_NO_PARITY),config.stop_bits(values:HOSAL_STOP_BITS_1),config.mode(values:HOSAL_UART_MODE_POLLpoll /HOSAL_UART_MODE_INTinterrupt send/receive)
Return: 0 on success; negative error code on failure
hosal_uart_send(uart, txbuf, size)
Sends buffer data out via the serial port (echoing received data in this tutorial).
Parameters:
uart:hosal_uart_dev_tstruct pointertxbuf: pointer to the buffer to send, requiredsize: byte count to send, values:1~256
Return: the actual number of bytes sent (≤ size) on success; negative error code on failure
hosal_uart_receive(uart, data, expect_size)
Reads data from the serial port into the buffer (blocks in poll mode).
Parameters:
uart:hosal_uart_dev_tstruct pointerdata: receive buffer pointer, requiredexpect_size: expected byte count
Return: the actual number of bytes received (may be less than expect_size) on success; negative error code on failure
hosal_uart_finalize(uart)
Stops the serial port and frees the occupied pins (call before no longer using it).
Parameters:
uart:hosal_uart_dev_tstruct pointer
Return: 0 on success; negative error code on failure
blog_info(fmt, ...)
Outputs an INFO-level log (always via UART0, filtered by level).
Parameters:
fmt: format string, same usage asprintf, required...: variadic args matchingfmtplaceholders, optional
Return: none
xTaskCreate(task, name, stack, param, prio, handle)
Creates a task and adds it to the ready queue; the scheduler runs it according to priority.
Parameters:
task: pointer to the task entry function, shapedvoid task(void *arg), requiredname: task name string (for debugging), e.g."uart_task"stack: task stack size (in words), values: any size within memory limits, e.g.2048(too small a stack overflows easily and crashes)param: pointer to the argument passed to the entry function; passNULLif noneprio: task priority, values:0(lowest)~19(highest, SDK config), e.g.16handle: output pointer for the task handle; passNULLif not needed
Return: pdPASS on success; pdFAIL on failure (e.g. out of memory)
Full Code
Below is the complete uart/main.c source, identical to the official example (applications/peripherals/uart/uart/main.c):
📜 Click to expand the full uart/main.c code
/*
* @Author: xuhongv@yeah.net
* @Date: 2022-10-03 15:02:19
* @LastEditors: xuhongv@yeah.net xuhongv@yeah.net
* @LastEditTime: 2022-10-20 17:42:45
* @FilePath: \bl_iot_sdk_for_aithinker\applications\get-started\helloworld\helloworld\main.c
* @Description: Uart
*/
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include "bl_sys.h"
#include <stdio.h>
#include <cli.h>
#include <hosal_uart.h>
#include <blog.h>
#include <hosal_uart.h>
void TaskUart(void *param)
{
uint8_t data[32];
int ret;
hosal_uart_dev_t uart_dev_echo = {
.config = {
.uart_id = 0,
.tx_pin = 4, // TXD GPIO
.rx_pin = 3, // RXD GPIO
.cts_pin = 255,
.rts_pin = 255,
.baud_rate = 9600,
.data_width = HOSAL_DATA_WIDTH_8BIT,
.parity = HOSAL_NO_PARITY,
.stop_bits = HOSAL_STOP_BITS_1,
.mode = HOSAL_UART_MODE_POLL,
},
};
hosal_uart_dev_t uart_dev_log = {
.config = {
.uart_id = 1,
.tx_pin = 16, // TXD GPIO
.rx_pin = 7, // RXD GPIO
.cts_pin = 255,
.rts_pin = 255,
.baud_rate = 115200,
.data_width = HOSAL_DATA_WIDTH_8BIT,
.parity = HOSAL_NO_PARITY,
.stop_bits = HOSAL_STOP_BITS_1,
.mode = HOSAL_UART_MODE_POLL,
},
};
/* Uart init device */
hosal_uart_init(&uart_dev_log);
/* Uart init device */
hosal_uart_init(&uart_dev_echo);
blog_info("Uart Demo Start");
while (1)
{
/* Uart receive poll */
ret = hosal_uart_receive(&uart_dev_log, data, sizeof(data));
if (ret > 0)
{
/* Uart send poll */
hosal_uart_send(&uart_dev_log, data, ret);
blog_info("Get data ");
}
}
}
/**
* @brief main
*
*/
void main(void)
{
xTaskCreate(TaskUart, "TaskUart", 1024, NULL, 15, NULL);
}FAQ & Troubleshooting
⚠️ No echo after sending from the USB-TTL
Cause: TX/RX not cross-connected, no common ground, or baud rate mismatch
Fix: confirm IO16 → USB-TTL RXD, IO7 → USB-TTL TXD, and GND must be connected; communication port baud 115200 and identical on both ends
⚠️ Garbled text received
Cause: baud rate mismatch, or the USB-TTL module's levels are incompatible (3.3V vs 5V)
Fix: confirm both ends use the same baud rate (115200); use a 3.3V-level USB-TTL module, not an old 5V-powered module wired directly
⚠️ Mixing up the log port and the communication port
Cause: the log you see with the board's USB directly to the computer is UART0, not UART1 communication data
Fix: remember the two serial ports: UART0 = log (TX4/RX3), UART1 = communication (TX16/RX7); blog_info output always goes through UART0
⚠️ The log port stops printing after a system restart
Cause: per the official README, some configs disable log output after restart
Fix: to keep printing logs after restart, set CONFIG_SYS_REBOOT_LOG_DISENABLE:=1 in proj_config.mk
⚠️ Flashing keeps waiting, the progress bar doesn't move
Cause: download mode wasn't entered, or the cable only charges and can't transfer data
Fix: press and hold EN to enter download mode when prompted; try a Type-C cable that can transfer data and retry
⚠️ Serial device not found or permission denied
Cause: /dev/ttyUSB0 doesn't exist or permissions are insufficient on Linux; USB-to-serial driver not installed on Windows
Fix: on Linux confirm the device with ls /dev/ttyUSB*, for permissions run sudo usermod -aG dialout $USER then log in again; on Windows install the driver in Device Manager and confirm the COM number
Self-Check
Sending characters from the USB-TTL gets them echoed back as-is, and the log port prints Get data — UART communication is verified.

