Skip to content

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) example applications/system/blog_demo; the code can be found directly in your local SDK.

🎯Page GoalThrough the blog level printing and level-filtering experiment, master APIs like blog_debug/info/warn/error and the level setting methods.
🧰Prerequisites① Ai-WB2 development board (Type-C data cable) ② Environment set up per [SDK Installation](../sdk/sdk_intro).
🔗RelatedSerial output: [UART (Serial)](../basic/uart); the log port is UART0 (TX GPIO4 / RX GPIO3).

Enter the Example Project

Open a terminal and enter the official blog_demo example project directory:

cd ~/Ai-Thinker-WB2/applications/system/blog_demo

Note: cd is the “change directory” command and ~ means your user home directory. This enters the blog_demo example project; all subsequent make commands must run in this directory. If it says No such file or directory, the path is wrong — see the FAQ at the end.

Write the Code

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_debug doesn’t output while blog_info and above do; with BLOG_LEVEL_NEVER everything is masked.

Build the Project

Build in the project directory:

make -j8

Note: make is the “build” command, translating the source code into machine code the board can run; -j8 builds 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”).

Flash the Firmware

Keep the board connected via USB, confirm the serial device, and flash:

make flash p=/dev/ttyUSB0 b=921600

Note: make flash is the “flash” command, writing the compiled firmware into the chip (flashing: the process of writing a program into the chip); p=/dev/ttyUSB0 is the serial device — change it to your computer’s actual port (like COM3 on Windows), b=921600 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. If it keeps waiting or reports the serial port can’t open, see the FAQ at the end.

Run and Verify

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_FILE in the component config header blog_cfg.h to BLOG_LEVEL_DEBUG.

Expected result: multiple groups of logs appear, with higher levels (e.g. BLOG_LEVEL_ERROR) printing fewer logs, and setting BLOG_LEVEL_NEVER making 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 as printf, required
  • ...: variadic args matching fmt placeholders, 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
c
/**
 * @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.

Released under the MIT License. Build Time 2026-09-11 14:52:23