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_*_PARAMtargets parameter validation;_ASSERT_*_FUNCtargets internal logic checks; both callerror_handleron 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_PARAMand_FUNCvariants): assert an expression is true/false/zero/equal;LOG_F/E/W/I/D/TandLOG_RF/RE/RW/RI/RD/RT: leveled logs and their Raw (no extra formatting) variants.- The example disables
CONFIG_BFLB_LOGand uses the classiclog.hlogging 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
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_assertRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:
make CHIP=bl616 BOARD=bl616dkConnect 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/ttyUSB0Open 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 / arraysize/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
#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

