First, What Is It
- A project = application code + build configuration + flash configuration:
main.cdoes the work;CMakeLists.txt/Makefile/Kconfig/defconfigtell the build system how to compile;flash_prog_cfg.initells the flasher what and where to burn. - In-SDK project: the project directory lives under SDK
examples/, and theMakefilelocates the SDK root via../..— exactly how official examples are organized. - Fastest files to edit:
main.cfor functionality,defconfigfor components,CMakeLists.txtfor the project name.
Operation Steps
Every directory under SDK examples/ is an independent project. Create your own (name with letters, digits and underscores):
cd bouffalo_sdk/examples
mkdir my_project
cd my_projectThe build configuration — tells the build system which SDK to use, which file is the entry, and the project name. Copy the minimal content:
cmake_minimum_required(VERSION 3.15)
find_package(bouffalo_sdk REQUIRED HINTS $ENV{BL_SDK_BASE})
sdk_set_main_file(main.c)
project(my_project)The entry for make; it points to the SDK root and includes the SDK build script project.build:
SDK_DEMO_PATH ?= $(abspath .)
BL_SDK_BASE ?= $(abspath ./../..)
export BL_SDK_BASE
include $(BL_SDK_BASE)/project.buildThe config-menu entry (make menuconfig); content is fixed, copy it:
mainmenu "SDK Configuration"
source "$BL_SDK_BASE/Kconfig"The project’s default configuration: toggle components with CONFIG_XXX =y/n (FreeRTOS, Shell, logging, drivers). Two lines are enough to start:
CONFIG_FREERTOS =y
CONFIG_SHELL =yThe application entry. Always start with board_init(), then init peripherals and loop:
#include "board.h"
#include "bflb_mtimer.h"
#define DBG_TAG "MAIN"
#include "log.h"
int main(void)
{
board_init();
LOG_I("my project start\r\n");
while (1) {
bflb_mtimer_delay_ms(1000);
}
}Run from the project directory (Ai-M61/Ai-M62 both use bl616; BOARD is the board type):
make CHIP=bl616 BOARD=bl616dkHold BOOT, tap EN/RST to enter download mode, then flash. The firmware is under build/build_out/:
make flash CHIP=bl616 COMX=/dev/ttyUSB0What Each File Is For (File-by-File)
CMakeLists.txt: the build entry
cmake_minimum_required(VERSION 3.15) # minimum CMake version
find_package(bouffalo_sdk REQUIRED HINTS $ENV{BL_SDK_BASE}) # find the SDK CMake package
sdk_set_main_file(main.c) # set the application entry
project(my_project) # project name (used in firmware name)find_package(bouffalo_sdk ...): lets CMake find the SDK build package (cmake/bouffalo_sdk-config.cmakein the SDK root).$ENV{BL_SDK_BASE}is an environment variable set by the Makefile.sdk_set_main_file(main.c): tells the SDK which file containsmain; the SDK links it with the startup code.project(...): the project name, which appears in the firmware file name.
Makefile: the make entry
SDK_DEMO_PATH ?= $(abspath .) # current project dir (needed by the SDK scripts)
BL_SDK_BASE ?= $(abspath ./../..) # SDK root: two levels up from examples
export BL_SDK_BASE # export for $ENV{BL_SDK_BASE} in CMake
include $(BL_SDK_BASE)/project.build # include the SDK build script (CMake + build + pack)?= means "use the default only if not defined", so you can override on the command line: make BL_SDK_BASE=/path/to/sdk ....
Kconfig: config-menu entry
mainmenu "SDK Configuration"
source "$BL_SDK_BASE/Kconfig"It is the entry of the make menuconfig graphical menu, referencing the SDK Kconfig tree. In daily use, edit defconfig directly.
defconfig: default configuration
CONFIG_FREERTOS =y # enable FreeRTOS
CONFIG_SHELL =y # enable serial shell
CONFIG_BFLB_LOG =n # logging toggle- Each
CONFIG_XXXtoggles a component/feature:=yon,=noff, commented = default. - You can override temporarily:
make CONFIG_BFLB_LOG=ywithout editing files. - Examples have long defconfigs to enable everything they use; your own project can be minimal.
main.c: application entry
#include "board.h"
#include "bflb_mtimer.h"
#define DBG_TAG "MAIN"
#include "log.h"
int main(void)
{
board_init(); /* init the board (clock/pins/UART) — always first */
LOG_I("my project start\r\n");
while (1) {
bflb_mtimer_delay_ms(1000);
}
}board_init(): the first function of every project, "powering up" the board.LOG_I(...): tagged info log; alsoLOG_E/W/D/Tfor other levels.- For multitasking, create tasks with
xTaskCreateand callvTaskStartScheduler()(see the FreeRTOS example).
flash_prog_cfg.ini: flash configuration (optional)
[cfg]
erase = 1 # 0 no erase / 1 erase programmed section / 2 chip erase
[FW]
filedir = ./build/build_out/my_project_$(CHIPNAME)*.bin # firmware path (wildcards allowed)
address = 0x000000 # start addressIt tells the flasher where the firmware is and where to write it. Building works without it, but make flash needs it.
FreeRTOSConfig.h (on demand)
To customize task stack/priority/heap, put a FreeRTOSConfig.h in the project (copy from another example); otherwise the SDK common config is used.
How to Add a Custom Library to the Project
As your project grows, you'll want to split reusable code (sensor drivers, protocol parsing, utilities, etc.) into standalone libraries instead of piling everything into main.c. bouffalo_sdk offers two ways:
| Approach | When to use | Key syntax |
|---|---|---|
| Approach 1: standalone library (recommended) | Reusable code, decoupled from main.c | sdk_generate_library() + add_subdirectory() |
| Approach 2: drop into app (simplest) | One or two files, no separate directory | sdk_add_include_directories() + target_sources(app PRIVATE ...) |
How it works: the SDK's
project()macro links every library registered viasdk_generate_library()into the firmware in one go (--whole-archive), so you do not need to calltarget_link_librariesmanually.
Approach 1: standalone library (recommended)
To add a my_lib library, the project tree becomes:
my_project/
├── CMakeLists.txt
├── Makefile
├── Kconfig
├── defconfig
├── main.c
└── my_lib/
├── CMakeLists.txt
├── include/
│ └── my_lib.h
└── src/
└── my_lib.cApproach 1 steps:
Create a CMakeLists.txt inside my_lib/:
sdk_generate_library(): creates a static librarylibmy_lib.a(named after the directory) and registers it in the SDK global link list automatically.sdk_add_include_directories(include): addsmy_lib/includeto the global header search path, so any source file can#include "my_lib.h".sdk_library_add_sources(src/my_lib.c): compiles the source into the current library; write multiple lines for multiple files.
For headers that should stay private to the library, use
sdk_add_private_include_directories(...).
sdk_generate_library()
sdk_add_include_directories(include)
sdk_library_add_sources(src/my_lib.c)Add add_subdirectory(my_lib) to the top-level CMakeLists.txt. Key point: it must be placed before project(my_project) — the project() macro links all registered libraries into the firmware at that point, so a late call leaves the library out.
cmake_minimum_required(VERSION 3.15)
find_package(bouffalo_sdk REQUIRED HINTS $ENV{BL_SDK_BASE})
sdk_set_main_file(main.c)
add_subdirectory(my_lib) # include the custom library (must come before project())
project(my_project)#include "my_lib.h" in main.c and call the library function; rebuild with make CHIP=bl616 BOARD=bl616dk. The build log will show an extra line [register library : libmy_lib] ..., confirming the library is registered.
#include "my_lib.h"
int main(void)
{
board_init();
my_lib_init(); /* from the custom library */
while (1) {
bflb_mtimer_delay_ms(1000);
}
}Approach 2: drop into app (simplest)
With only one or two files, skip the library directory and attach the sources straight to the app target (the SDK already created the app library that holds main.c):
cmake_minimum_required(VERSION 3.15)
find_package(bouffalo_sdk REQUIRED HINTS $ENV{BL_SDK_BASE})
sdk_set_main_file(main.c)
sdk_add_include_directories(my_lib) # header directory
target_sources(app PRIVATE my_lib/my_lib.c) # compile the source into app
project(my_project)target_sources(app PRIVATE ...): compilesmy_lib.cdirectly intoapp; separate multiple sources with newlines/spaces.- This produces no separate
.a; the code is compiled intolibapp.atogether withmain.c. It's the least setup — upgrade to Approach 1 when the file count grows.
FAQ
Build says SDK / bouffalo_sdk not found
Check the project location: it must be under examples/ (so ../.. points to the SDK), or explicitly pass make BL_SDK_BASE=<SDK path>.
Where is the firmware?
After a successful build it is under build/build_out/, named with the project and chip (e.g., my_project_...bin).
defconfig changes seem ignored?
Clean and rebuild: make clean && make CHIP=bl616 BOARD=bl616dk; or override on the command line with CONFIG_XXX=y.
After adding a custom library, the build fails with undefined reference / header not found?
Check in order: in the library's CMakeLists.txt, does sdk_add_include_directories point to the header directory and does sdk_library_add_sources list every .c; and in the top-level CMakeLists.txt, is add_subdirectory(my_lib) placed before project(my_project).
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

