Overview
Logging (in plain words: the program's "diary" while running — recording each step so you can look back later) is the most important debugging tool in embedded development. The Ai-WB2 SDK has a built-in blog log system with level-filtered output (in plain words: layered by importance, like "info/warning/error", for easy filtering), component-level filtering (in plain words: only show logs from one module) and ANSI color display. This tutorial demonstrates printing and filtering of 6 log levels: set different levels and observe which logs print.
In plain words: logs are like your "diary" — every time the program does something, it writes a line, so you can look back and see what it did and where it went wrong. But too many diary entries are hard to read, so importance levels exist (ordinary records, warnings, errors…) and you can set "show only warnings and above", like only following headlines in the news. This tutorial demonstrates these log levels and the filtering rules.
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/system/blog_demo; the code can be found directly in your local SDK.
Open a terminal and enter the official blog_demo example project directory:
cd ~/Ai-Thinker-WB2/applications/system/blog_demo
Note:
cdis the “change directory” command and~means your user home directory. This enters the blog_demo example project; all subsequentmakecommands must run in this directory. If it saysNo such file or directory, the path is wrong — see the FAQ at the end.
Open blog_demo/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/system/blog_demo/blog_demo/main.c).
Code highlights:
| Code | Purpose |
|---|---|
blog_set_level_log_component(level, "blog_demo") |
Set the component’s filter level; without it you never see the filtering effect |
blog_print(...) |
Print unconditionally; acts as the “control group”, proving printing itself works |
blog_debug / blog_info / blog_warn / blog_error |
Print by level; below the filter level they’re “screened out” and don’t output |
blog_assert(...) |
Assert-level log; notifies when a condition fails, for catching “should never happen” cases |
Log level definitions (filtered from high to low):
typedef enum _blog_leve {
BLOG_LEVEL_ALL = 0, /* 全部输出 */
BLOG_LEVEL_DEBUG, /* 调试 */
BLOG_LEVEL_INFO, /* 信息 */
BLOG_LEVEL_WARN, /* 警告 */
BLOG_LEVEL_ERROR, /* 错误 */
BLOG_LEVEL_ASSERT, /* 断言 */
BLOG_LEVEL_NEVER, /* 全部屏蔽 */
} blog_level_t;
📌 With
BLOG_LEVEL_INFO,blog_debugdoesn’t output whileblog_infoand above do; withBLOG_LEVEL_NEVEReverything is masked.
Build in the project directory:
make -j8
Note:
makeis the “build” command, translating the source code into machine code the board can run;-j8builds with 8 parallel cores, faster.
On success a firmware build_out/blog_demo.bin is generated (firmware: the program burned into the board after compilation, like the board’s “operating system + your program”).
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 chip (flashing: the process of writing a program into the chip);p=/dev/ttyUSB0is the serial device — change it to your computer’s actual port (likeCOM3on Windows),b=921600is 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. If it keeps waiting or reports the serial port can’t open, see the FAQ at the end.
After flashing, the board automatically restarts. Open a serial assistant (baud rate 921600) and observe the level-filtered pattern:
The log level is LOG_LEVEL_ALL
DEBUG (5)[main.c: 22] The log level is LOG_LEVEL_DEBUG
INFO (11)[main.c: 23] The log level is LOG_LEVEL_INFO
WARN (16)[main.c: 24] The log level is LOG_LEVEL_WARN
ERROR (22)[main.c: 25] The log level is LOG_LEVEL_ERROR
ASSERT (28)[main.c: 26] The log level is LOG_LEVEL_ASSERT
The log level is LOG_LEVEL_NEVER
DEBUG (37)[main.c: 31] The log level is LOG_LEVEL_DEBUG
INFO (43)[main.c: 32] The log level is LOG_LEVEL_INFO
...
Pattern verification: after setting BLOG_LEVEL_INFO, the DEBUG lines no longer appear; after setting BLOG_LEVEL_NEVER, all logs from this component disappear.
⚠️ Officially the DEBUG level doesn’t print by default (no output even if set to DEBUG); to output DEBUG logs, change
BLOG_POWERON_SOFTLEVEL_FILEin the component config headerblog_cfg.htoBLOG_LEVEL_DEBUG.
✅ Expected result: multiple groups of logs appear, with higher levels (e.g.
BLOG_LEVEL_ERROR) printing fewer logs, and settingBLOG_LEVEL_NEVERmaking this component’s logs all disappear — verified. If the logs never change or filtering doesn’t work, see the FAQ at the end.
API Summary for This Tutorial
blog_set_level_log_component(level, name)
Sets the output level of a log component; logs below this level are filtered out.
Parameters:
level: level value, values:BLOG_LEVEL_ALL(output everything) /BLOG_LEVEL_DEBUG/BLOG_LEVEL_INFO/BLOG_LEVEL_WARN/BLOG_LEVEL_ERROR/BLOG_LEVEL_ASSERT/BLOG_LEVEL_NEVER(mask everything)name: component name string (the name registered for the component, e.g."tcp","axk_mqtt")
Return: 0 on success; negative error code on failure
blog_print(fmt, ...)
Outputs a log not subject to level filtering; prints under any level setting.
Parameters:
fmt: format string, same usage asprintf, required...: variadic args matchingfmtplaceholders, optional
Return: none
blog_debug(fmt, ...)
Outputs a debug-level log (filtered by default, not shown; set the level ≤ DEBUG first).
Parameters:
fmt: format string, required...: variadic args, optional
Return: none
blog_info(fmt, ...)
Outputs an info-level log (everyday running status, the most common).
Parameters:
fmt: format string, required...: variadic args, optional
Return: none
blog_warn(fmt, ...)
Outputs a warning-level log (abnormal but can keep running).
Parameters:
fmt: format string, required...: variadic args, optional
Return: none
blog_error(fmt, ...)
Outputs an error-level log (serious abnormality).
Parameters:
fmt: format string, required...: variadic args, optional
Return: none
blog_assert(fmt, ...)
Outputs an assert-level log (the highest level; notifies when a condition isn't met).
Parameters:
fmt: format string, required...: variadic args, optional
Return: none
printf(fmt, ...)
Standard C library output to UART0, not subject to blog level filtering.
Parameters:
fmt: format string, required...: variadic args, optional
Return: the number of characters printed on success; negative on failure
Full Code
Below is the complete blog_demo/main.c source, identical to the official example (applications/system/blog_demo/blog_demo/main.c):
📜 Click to expand the full blog_demo/main.c code
/**
* @file main.c
* @author your name (you@domain.com)
* @brief
* @version 0.1
* @date 2022-10-22
*
* @copyright Copyright (c) 2022
*
*/
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include "bl_sys.h"
void main(void)
{
blog_set_level_log_component(BLOG_LEVEL_ALL, "blog_demo");
blog_print("The log level is LOG_LEVEL_ALL\n");
blog_debug("The log level is LOG_LEVEL_DEBUG");
blog_info("The log level is LOG_LEVEL_INFO");
blog_warn("The log level is LOG_LEVEL_WARN");
blog_error("The log level is LOG_LEVEL_ERROR");
blog_assert("The log level is LOG_LEVEL_ASSERT");
blog_print("The log level is LOG_LEVEL_NEVER\r\n");
printf("\r\n");
blog_set_level_log_component(BLOG_LEVEL_DEBUG, "blog_demo");
blog_debug("The log level is LOG_LEVEL_DEBUG");
blog_info("The log level is LOG_LEVEL_INFO");
blog_warn("The log level is LOG_LEVEL_WARN");
blog_error("The log level is LOG_LEVEL_ERROR");
blog_assert("The log level is LOG_LEVEL_ASSERT");
blog_print("The log level is LOG_LEVEL_NEVE\r\n");
printf("\r\n");
blog_set_level_log_component(BLOG_LEVEL_INFO, "blog_demo");
blog_debug("The log level is LOG_LEVEL_DEBUG");
blog_info("The log level is LOG_LEVEL_INFO");
blog_warn("The log level is LOG_LEVEL_WARN");
blog_error("The log level is LOG_LEVEL_ERROR");
blog_assert("The log level is LOG_LEVEL_ASSERT");
blog_print("The log level is LOG_LEVEL_NEVER\r\n");
printf("\r\n");
blog_set_level_log_component(BLOG_LEVEL_WARN, "blog_demo");
blog_debug("The log level is LOG_LEVEL_DEBUG");
blog_info("The log level is LOG_LEVEL_INFO");
blog_warn("The log level is LOG_LEVEL_WARN");
blog_error("The log level is LOG_LEVEL_ERROR");
blog_assert("The log level is LOG_LEVEL_ASSERT");
blog_print("The log level is LOG_LEVEL_NEVER\r\n");
printf("\r\n");
blog_set_level_log_component(BLOG_LEVEL_ERROR, "blog_demo");
blog_debug("The log level is LOG_LEVEL_DEBUG");
blog_info("The log level is LOG_LEVEL_INFO");
blog_warn("The log level is LOG_LEVEL_WARN");
blog_error("The log level is LOG_LEVEL_ERROR");
blog_assert("The log level is LOG_LEVEL_ASSERT");
blog_print("The log level is LOG_LEVEL_NEVER\r\n");
printf("\r\n");
blog_set_level_log_component(BLOG_LEVEL_ASSERT, "blog_demo");
blog_debug("The log level is LOG_LEVEL_DEBUG");
blog_info("The log level is LOG_LEVEL_INFO");
blog_warn("The log level is LOG_LEVEL_WARN");
blog_error("The log level is LOG_LEVEL_ERROR");
blog_assert("The log level is LOG_LEVEL_ASSERT");
blog_print("The log level is LOG_LEVEL_NEVER\r\n");
printf("\r\n");
blog_set_level_log_component(BLOG_LEVEL_NEVER, "blog_demo");
blog_debug("The log level is LOG_LEVEL_DEBUG");
blog_info("The log level is LOG_LEVEL_INFO");
blog_warn("The log level is LOG_LEVEL_WARN");
blog_error("The log level is LOG_LEVEL_ERROR");
blog_assert("The log level is LOG_LEVEL_ASSERT");
blog_print("The log level is LOG_LEVEL_NEVER\r\n");
}FAQ & Troubleshooting
⚠️ blog_debug logs don't print
Cause: the SDK disables DEBUG-level output by default (BLOG_POWERON_SOFTLEVEL_FILE defaults to BLOG_LEVEL_INFO in blog_cfg.h)
Fix: change #define BLOG_POWERON_SOFTLEVEL_FILE (BLOG_LEVEL_INFO) to BLOG_LEVEL_DEBUG in blog_cfg.h and rebuild
⚠️ Level set but logs aren't filtered
Cause: the second parameter of blog_set_level_log_component (component name) doesn't match the name of the component where the printing code lives
Fix: confirm the component name matches the project name (blog_demo in this example); or set the global level directly
⚠️ printf and blog logs mixed, inconsistent format
Cause: printf doesn't go through the blog system, so no level prefix or colors
Fix: use the blog API family uniformly for debugging; keep printf only for raw output that needs no filtering
⚠️ Serial port won't open / no logs at all
Cause: USB-to-serial driver not installed, port occupied, or (on Linux) no access permission
Fix: on Linux confirm the device is recognized with lsusb, run sudo chmod 666 /dev/ttyUSB0 or add your user to the dialout group and retry; on Windows check the COM port in Device Manager and install the CH340/CP210x driver; set the serial assistant's baud rate to 921600
⚠️ Flashing stuck waiting / chip not found
Cause: download mode wasn't entered, the cable only charges and can't transfer data, or the baud rate is wrong
Fix: press and hold EN during flashing until the progress bar appears; try a data cable; confirm p= port and b=921600 are correct
⚠️ cd reports No such file or directory / no Makefile found
Cause: make ran outside the example project directory, or the SDK install path differs from the tutorial
Fix: cd ~/Ai-Thinker-WB2/applications/system/blog_demo first, then run make; if ~/Ai-Thinker-WB2 doesn't exist, find the SDK with find ~ -name "Ai-Thinker-WB2"
Self-Check
The serial outputs 7 groups of logs under different level settings, and higher levels (e.g. ERROR) filter out more logs — the blog log system is verified.

