Skip to content

Concepts First

  • Assert: a check saying "this must hold here"; if the condition is false, the logic is broken and the program enters the error handler.
  • DBG macros: print variable names and values, arrays, hexdumps, and booleans. They return the expression result, so you can wrap them around any expression without changing the logic.
  • Param vs. function asserts: _ASSERT_*_PARAM targets parameter validation; _ASSERT_*_FUNC targets internal logic checks; both call error_handler on failure.
  • error_handler: the handler run after an assert failure. The SDK provides a weak default; the example redefines it to print error handler (for testing only — production code usually stops the MCU).

Example Overview

This page is based on the log_dbg_assert example in the official Bouffalo SDK (examples/log_dbg_assert), which demonstrates debug print macros and assert macros:

  • DBG_VALUE: prints the variable name and value with type-adaptive formatting (int, string, pointer, etc.);
  • DBG_HEXDUMP / DBG_ARRAY: hexdump printing / array printing;
  • DBG_BOOL: prints an expression as true/false;
  • _ASSERT_PARAM / _ASSERT_FUNC / _ASSERT_TRUE / _ASSERT_FALSE / _ASSERT_ZERO / _ASSERT_EQUAL (each with _PARAM and _FUNC variants): assert an expression is true/false/zero/equal;
  • LOG_F/E/W/I/D/T and LOG_RF/RE/RW/RI/RD/RT: leveled logs and their Raw (no extra formatting) variants.
  • The example disables CONFIG_BFLB_LOG and uses the classic log.h logging system.

Note

The example's error_handler is for testing only: it prints one line after an assert failure and keeps running. In production, follow the comments and enable the infinite loop (or save the crash state and reset), to avoid continuing with corrupted state.

Operation Steps

1
Enter the Example Directory

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

cd examples/log_dbg_assert
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). The program first prints DBG info (message, hexdump, array, factorial values, boolean expressions), then runs 12 _ASSERT_* assertions (all true, so none fails), and finally loops printing F/E/W/I/D/T logs plus their Raw variants. Change an assertion to false (e.g. _ASSERT_PARAM(1 == 0)), rebuild and flash: you will see error_handler print error handler.

Code Execution Flow

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

APIs Used by the Example

DBG_VALUE(expr)

Prints the expression's name and value (type-adaptive) and returns the expression result, so it can wrap other expressions.

Parameters:

  • expr: any expression

Return: the expression's value

DBG_HEXDUMP / DBG_ARRAY(expr, size / length)

DBG_HEXDUMP dumps a buffer in canonical hex format; DBG_ARRAY prints each element of an array. Both return the original expression.

Parameters:

  • expr: buffer / array
  • size / length: byte count / element count

Return: the original expression's value

DBG_BOOL(expr)

Forces boolean output (true/false) for an expression and returns its value.

Parameters:

  • expr: any expression

Return: the expression's value

_ASSERT_PARAM / _ASSERT_FUNC(expr)

Asserts the expression is true (non-zero); on failure it prints assertion info and enters error_handler. _PARAM is for parameter checks, _FUNC for internal logic checks.

Parameters:

  • expr: an expression that must be true

Return: none (on failure, error_handler is entered)

_ASSERT_TRUE / _ASSERT_FALSE / _ASSERT_ZERO / _ASSERT_EQUAL(...)

More semantic asserts: _ASSERT_TRUE asserts true, _ASSERT_FALSE asserts false, _ASSERT_ZERO asserts zero, _ASSERT_EQUAL(val, expr) asserts equality; each has _PARAM and _FUNC variants.

Parameters:

  • val: expected value (only for _ASSERT_EQUAL)
  • expr: the expression being checked

Return: none (on failure, error_handler is entered)

LOG_F / E / W / I / D / T and LOG_RF ... LOG_RT(fmt, ...)

Leveled log macros: F fatal, E error, W warning, I info, D debug, T trace; LOG_R* are the Raw variants without extra formatting.

Parameters:

  • fmt, ...: printf-style format string and arguments

Return: none

Complete Code

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

📜 Click to expand log_dbg_assert/main.c full code
c
#include "bflb_mtimer.h"
#include "bflb_irq.h"
#include "board.h"

#define DBG_TAG "MAIN"
#include "log.h"

static int factorial(int n)
{
    if (DBG_BOOL(n <= 1)) {
        return DBG_VALUE(1);
    } else {
        return DBG_VALUE(n * factorial(n - 1));
    }
}

int main(void)
{
    board_init();

    char message[] = "hellokszhdfoiasjdkjnskjxnvuiolashdfoinaskjldfnvklasjhdfi213489o71234589073298dfl;asjdlfnkjzxncvhasdlkfljhasjkldjhnvjkaslndfvkljashdlifhui";

    /*!< All DBG macros will return the variables passed in */

    /*!< Print various types of variables using DBG_VALUE, type adaptive */
    DBG_VALUE(message);
    /*!< Use DBG_HEXDUMP to print data in canonical format, type adaptive */
    DBG_HEXDUMP(message, sizeof(message));

    const int a = 2;
    const int b = DBG_VALUE(3 * a) + 1;

    /*!< Use DBG_ARRAY to print arrays, type adaptive */
    int numbers[512] = { b, 13 };
    DBG_ARRAY(numbers, 512);

    DBG_VALUE(factorial(4));

    /*!< Printing Boolean expressions using DBG_BOOL */
    DBG_BOOL(1 == factorial(4));
    DBG_BOOL(24 == factorial(4));
    DBG_BOOL(1 == 0);
    DBG_BOOL(1 == 1);

    /*!< Assert whether the expression is true or not */
    _ASSERT_PARAM(1 == factorial(4));
    _ASSERT_FUNC(1 == factorial(4));
    /*!< Assert whether the expression is true or not */
    _ASSERT_TRUE_PARAM(1 == factorial(4));
    _ASSERT_TRUE_FUNC(1 == factorial(4));
    /*!< Assert whether the expression is false or not */
    _ASSERT_FALSE_PARAM(24 == factorial(4));
    _ASSERT_FALSE_FUNC(24 == factorial(4));
    /*!< Assert whether the expression is 0 or not */
    _ASSERT_ZERO_PARAM(factorial(4));
    _ASSERT_ZERO_FUNC(factorial(4));
    /*!< Assert whether two values are equal */
    _ASSERT_EQUAL_PARAM(1, factorial(4));
    _ASSERT_EQUAL_FUNC(1, factorial(4));

    while (1) {
        LOG_F("hello world fatal\r\n");
        LOG_E("hello world error\r\n");
        LOG_W("hello world warning\r\n");
        LOG_I("hello world information\r\n");
        LOG_D("hello world debug\r\n");
        LOG_T("hello world trace\r\n");
        LOG_RF("hello world fatal raw\r\n");
        LOG_RE("hello world error raw\r\n");
        LOG_RW("hello world warning raw\r\n");
        LOG_RI("hello world information raw\r\n");
        LOG_RD("hello world debug raw\r\n");
        LOG_RT("hello world trace raw\r\n");
        bflb_mtimer_delay_ms(1000);
    }
}

/*!< You can leave this function undefined */
/*!< and use the weak default handler */
void error_handler(void)
{
    /*!< assertion faild handler */
    printf("error handler\r\n");

    /*!< For testing purposes only, the following  */
    /*!< comments should be uncommented under normal  */
    /*!< circumstances, so that the MCU is stuck */

    // uintptr_t irq = bflb_irq_save();
    // volatile unsigned char dummy = 0;
    // while (dummy == 0) {
    // }
    // bflb_irq_restore(irq);
}

FAQ

The program keeps running after an assert fails

The example's error_handler only prints a line — that is for testing. In production, follow the comment and enable the while (dummy == 0) loop, or save the crash state and reset, to avoid running with corrupted state.

Do the DBG macros change program logic

No: all DBG macros return the original expression result (DBG_VALUE(x) behaves like x), so they can wrap any expression. When CONFIG_LOG_LEVEL < 3 they degrade to ((void)(expr)) without changing values.

LOG_* vs. BFLB_LOG_*

This example uses the classic log.h system (LOG_*) and disables CONFIG_BFLB_LOG; the "blog Log" page uses the BFLB_LOG system (BFLB_LOG_*, which supports tag filtering). Pick one; mixing them can duplicate output or create config conflicts.

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