Skip to content

Concepts First

  • Log: text printed to the serial port while the program runs, used to observe state and locate bugs — the program's "diary".
  • Log levels: severity classes. From highest to lowest, the example uses F (fatal), E (error), W (warning), I (info), D (debug), T (trace).
  • Level colors: each level has its own color so severity is visible at a glance. The colors are defined by the actual BFLB_LOG_COLOR_* macros (bflb_log_conf.h; bflog_conf_user.h in the example directory is a reference copy): F magenta, E red, W yellow, I no color (BFLB_LOG_SGR_RESET), D white, T dim white (BFLB_LOG_SGR_FAINT). In the example config the green INFO line is commented out, so INFO prints without color.
  • Log tags: names for log groups (e.g. MAIN, TEST); tags can be enabled/disabled globally for filtering, so you can watch only the part you care about.
  • Sync / async mode: sync mode outputs immediately; async mode (the freertos_async sibling example) queues log messages and outputs them from a dedicated thread, which suits high log volume.

Example Overview

This page is based on the barebone_sync example in the official Bouffalo SDK (examples/bflog/barebone_sync), which demonstrates BFLB_LOG logging, levels, and tag filtering:

  • Creates a synchronous log recorder backed by a 4096-byte memory pool (BFLB_LOG_MODE_SYNC);
  • Creates a UART stream outputter (with colors) and connects its output function to uart0;
  • The main loop prints F/E/W/I/D/T level logs every second and cycles three filter states: all enabled → only MAIN → all disabled;
  • log_test.c defines the TEST tag, showing how tags are defined across multiple files and filtered.
  • Sibling example (examples/bflog/): freertos_async (FreeRTOS multi-thread async logging).

Operation Steps

1
Enter the Example Directory

No external wiring is needed for this page. Open a terminal and enter the SDK logging example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/bflog/barebone_sync
2
Build the Project

Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:

make CHIP=bl616 BOARD=bl616dk
3
Flash the Firmware

Connect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Run and Verify

Open a serial tool (baud rate 2000000). Every second the program prints enable all output (both MAIN and TEST tags visible), then enable only MAIN output (only MAIN visible), then disable all output (neither visible), cycling forever. Each round prints the six levels F/E/W/I/D/T (hello world this is ... and hello test this is ...).

Code Execution Flow

The complete execution flow from startup to running is shown below (loop arrows mean repeated execution):

APIs Used by the Example

bflb_log_create(log, pool, size, mode)

Creates the log recorder, which receives logs and dispatches them to outputters.

Parameters:

  • log: the bflb_log_t recorder object (example_recorder in the example)
  • pool: log memory pool pointer (4 KB aligned example_pool array)
  • size: pool size in bytes (EXAMPLE_LOG_POOL_SIZE = 4096)
  • mode: BFLB_LOG_MODE_SYNC (synchronous) or async mode

Return: 0 on success; negative error code on failure

bflb_log_direct_create(direct, type, color, lock, unlock)

Creates a log outputter (direct); the example uses a stream outputter with colors enabled.

Parameters:

  • direct: the bflb_log_direct_t outputter object
  • type: output type, BFLB_LOG_DIRECT_TYPE_STREAM in the example
  • color: BFLB_LOG_DIRECT_COLOR_ENABLE
  • lock / unlock: mutex callbacks, NULL in the example

Return: 0 on success; negative error code on failure

bflb_log_direct_init_stream(direct, stream_output)

Binds the actual stream output function (the example writes each byte to uart0 via bflb_uart_putchar).

Parameters:

  • direct: the stream outputter object
  • stream_output: output callback, uint16_t (*)(void *, uint16_t)

Return: 0 on success; negative error code on failure

bflb_log_append(log, direct)

Adds the outputter to the recorder; logs are forwarded to the outputter only after this call.

Parameters:

  • log: recorder object
  • direct: outputter object

Return: 0 on success; negative error code on failure

bflb_log_direct_resume / bflb_log_resume(direct / log)

Resumes the outputter / recorder to the working state (both start suspended and must be resumed to output).

Parameters:

  • direct / log: pointer to the corresponding object

Return: 0 on success; negative error code on failure

bflb_log_global_filter("MAIN", enable)

Globally enables/disables output for a given tag.

Parameters:

  • tag_string: tag name, "MAIN" / "TEST" in the example
  • enable: true to enable / false to disable

Return: 0 on success; negative error code on failure

BFLB_LOG_F / E / W / I / D / T(recorder, fmt, ...)

Leveled log macros: F fatal, E error, W warning, I info, D debug, T trace.

Parameters:

  • recorder: recorder object (&example_recorder in the example)
  • fmt, ...: printf-style format string and arguments

Return: none

BFLB_LOG_DEFINE_TAG / BFLB_LOG_GET_TAG(name, string, enable)

Defines a log tag: BFLB_LOG_DEFINE_TAG(MAIN, "MAIN", true) places the tag into a dedicated section; BFLB_LOG_GET_TAG(MAIN) fetches it and assigns it to BFLB_LOG_TAG, so log macros in this file carry the tag.

Parameters:

  • name: tag variable name
  • string: tag string (used when printing and filtering)
  • enable: initial enabled state

Return: none

Complete Code

The complete source below matches the official example (examples/bflog/barebone_sync) verbatim. Collapsed by default, click to expand:

📜 Click to expand barebone_sync/main.c full code
c
#define DBG_TAG "MAIN"

#include "bflb_mtimer.h"
#include "bflb_uart.h"
#include "bflb_rtc.h"
#include "bflb_clock.h"
#include "board.h"
#include "bflb_log.h"

/*!< Adding the BFLB_LOG tag allows the use of tag filtering functionality */
/*!< If not added, the tag can still be displayed, but tag filtering will not work */

/*!< Define a BFLB_LOG tag */
BFLB_LOG_DEFINE_TAG(MAIN, DBG_TAG, true);

/*!< Cancel the previous BFLB_LOG tag */
#undef BFLB_LOG_TAG

/*!< Define the BFLB_LOG tag as a new tag */
#define BFLB_LOG_TAG BFLB_LOG_GET_TAG(MAIN)

/*!< Bflog bare-metal synchronization routine */
/*!< bflb_log barebone sync example */

struct bflb_device_s *uart0 = NULL;
static struct bflb_device_s *rtc = NULL;

/*!< Current UTC Timestamp 2022-12-16 17:52 */
uint32_t timestamp_base = 1671184300;

/** @defgroup   example_bflb_log_port port
-----------------------------------------------------------------------------
* @{
----------------------------------------------------------------------------*/
uint64_t bflb_log_clock(void)
{
    return bflb_mtimer_get_time_us();
}

uint32_t bflb_log_time(void)
{
    return BFLB_RTC_TIME2SEC(bflb_rtc_get_time(rtc)) + timestamp_base;
}

char *bflb_log_thread(void)
{
    return "";
}
/*---------------------------------------------------------------------------
* @}            example_bflb_log_port port
----------------------------------------------------------------------------*/

#define EXAMPLE_LOG_POOL_SIZE 4096

bflb_log_t example_recorder;
static uint32_t example_pool[EXAMPLE_LOG_POOL_SIZE / 4];
bflb_log_direct_stream_t example_uart_stream;

uint16_t example_uart_stream_output(void *ptr, uint16_t size)
{
    for (size_t i = 0; i < size; i++) {
        bflb_uart_putchar(uart0, ((char *)ptr)[i]);
    }
    return size;
}

void example_log_init(void)
{
    void *record = (void *)&example_recorder;
    void *direct = (void *)&example_uart_stream;

    /*!< Create a logger, configure the memory pool, set the memory pool size, and set the mode to synchronous.*/
    /*!< create recorder */
    if (0 != bflb_log_create(record, example_pool, EXAMPLE_LOG_POOL_SIZE, BFLB_LOG_MODE_SYNC)) {
        printf("bflb_log_create faild\r\n");
    }

    /*!< Create an outputter, of the stream outputter type, enable color output, and set the mutex to NULL. */
    /*!< create stream direct */
    bflb_log_direct_create(direct, BFLB_LOG_DIRECT_TYPE_STREAM, BFLB_LOG_DIRECT_COLOR_ENABLE, NULL, NULL);
    /*!< Configure the output function of the stream output */
    bflb_log_direct_init_stream((void *)direct, example_uart_stream_output);

    /*!< Add the outputter to the logger */
    /*!< connect direct and recorder */
    bflb_log_append(record, direct);

    /*!< Restore the outputter to working mode*/
    /*!< resume direct */
    bflb_log_direct_resume(direct);

    /*!< Restore the logger to working mode */
    /*!< resume record */
    bflb_log_resume(record);
}

extern void test_log(void);

int main(void)
{
    board_init();

    /*!< uart0 already initialized in bsp/board */
    uart0 = bflb_device_get_by_name("uart0");
    rtc = bflb_device_get_by_name("rtc");
    bflb_rtc_set_time(rtc, 0);

    example_log_init();

    uint8_t test = 0;

    while (1) {
        if (test == 0) {
            bflb_log_global_filter("MAIN", true);
            bflb_log_global_filter("TEST", true);
            printf("\r\n============================== enable all output\r\n\r\n");
        } else if (test == 1) {
            bflb_log_global_filter("MAIN", false);
            bflb_log_global_filter("TEST", true);
            printf("\r\n============================== enable only MAIN output\r\n\r\n");
        } else if (test == 2) {
            bflb_log_global_filter("MAIN", false);
            bflb_log_global_filter("TEST", false);
            printf("\r\n============================== disable all output\r\n\r\n");
        }

        if (++test >= 3) {
            test = 0;
        }

        BFLB_LOG_F(&example_recorder, "hello world this is fatal error\r\n");
        BFLB_LOG_E(&example_recorder, "hello world this is error\r\n");
        BFLB_LOG_W(&example_recorder, "hello world this is warning\r\n");
        BFLB_LOG_I(&example_recorder, "hello world this is information\r\n");
        BFLB_LOG_D(&example_recorder, "hello world this is degug information\r\n");
        BFLB_LOG_T(&example_recorder, "hello world this is trace information\r\n");
        test_log();
        bflb_mtimer_delay_ms(1000);
    }
}
📜 Click to expand barebone_sync/log_test.c full code
c
#define DBG_TAG "TEST"

#include "bflb_log.h"

BFLB_LOG_DEFINE_TAG(TEST, DBG_TAG, true);
#undef BFLB_LOG_TAG
#define BFLB_LOG_TAG BFLB_LOG_GET_TAG(TEST)

extern bflb_log_t example_recorder;

void test_log(void)
{
    BFLB_LOG_F(&example_recorder, "hello test this is fatal error\r\n");
    BFLB_LOG_E(&example_recorder, "hello test this is error\r\n");
    BFLB_LOG_W(&example_recorder, "hello test this is warning\r\n");
    BFLB_LOG_I(&example_recorder, "hello test this is information\r\n");
    BFLB_LOG_D(&example_recorder, "hello test this is degug information\r\n");
    BFLB_LOG_T(&example_recorder, "hello test this is trace information\r\n");
}

FAQ

How do I enable blog logging (BFLB_LOG)

Enable it at three levels:

  1. Build time (project config): set CONFIG_BFLB_LOG =y in the project's defconfig so the SDK compiles the bflb_log component (already enabled in this example). Without it, the LOG_* macros degrade to no-ops and no logs appear.
  2. Header (log levels): components/utils/log/bflb_log/bflb_log_conf.h is the default config (bflog_conf_user.h in the example directory is a reference copy). Its macros control what is compiled and output:
    • BFLB_LOG_ENABLE: master switch; commenting it out excludes log code from the build;
    • BFLB_LOG_LEVEL_ENABLE: the highest level compiled into the firmware (BFLB_LOG_LEVEL_TRACE in the example, i.e. all of F/E/W/I/D/T);
    • BFLB_LOG_LEVEL_DEFAULT / BFLB_LOG_DIRECT_LEVEL_DEFAULT: default filter level of the recorder/outputter; logs below it are not recorded/output.
  3. Runtime (tag filter): the enable argument of BFLB_LOG_DEFINE_TAG(name, tag, enable) decides whether that tag outputs initially; at runtime use bflb_log_global_filter("MAIN", true/false) to toggle tags dynamically (the example cycles three filter states every second).

For your own project, start by copying the example's defconfig and bflog_conf_user.h; once levels 1 and 2 are enabled, logs are emitted on uart0.

No log output at all

First confirm the baud rate is 2000000; then check that both the recorder and the outputter were resumed (suspended objects output nothing), and that the global filter is not disabling both tags (bflb_log_global_filter with both tags false means no output).

The serial tool shows plain text instead of colors

Colors rely on ANSI escape sequences, so use a serial/terminal tool that supports ANSI colors (e.g. MobaXterm, Xshell, Windows Terminal), and confirm the outputter was created with BFLB_LOG_DIRECT_COLOR_ENABLE (enabled in the example). The actual colors follow the BFLB_LOG_COLOR_* macros in the code (F magenta / E red / W yellow / I none / D white / T dim white); the example's reference config has the green INFO definition commented out — uncomment it and rebuild if you want green INFO.

TEST tag logs are invisible

In the enable only MAIN output filter state TEST is disabled — that is the tag-filtering behavior being demonstrated. Keep both tags true to see both groups.

Log timestamps look wrong

The timestamp comes from bflb_log_time(), which is RTC time plus the fixed timestamp_base. If the RTC is not calibrated or the base does not match the current time, the display drifts, but functional verification is unaffected.

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