Skip to content

First, What Is It ​

  • A project = application code + build configuration + flash configuration: main.c does the work; CMakeLists.txt/Makefile/Kconfig/defconfig tell the build system how to compile; flash_prog_cfg.ini tells the flasher what and where to burn.
  • In-SDK project: the project directory lives under SDK examples/, and the Makefile locates the SDK root via ../.. — exactly how official examples are organized.
  • Fastest files to edit: main.c for functionality, defconfig for components, CMakeLists.txt for the project name.

Operation Steps ​

1
Enter examples and create a project directory

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_project
2
Create CMakeLists.txt

The 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)
3
Create Makefile

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.build
4
Create Kconfig

The config-menu entry (make menuconfig); content is fixed, copy it:

mainmenu "SDK Configuration"

source "$BL_SDK_BASE/Kconfig"
5
Create defconfig

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 =y
6
Write main.c

The 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);
    }
}
7
Build the project

Run from the project directory (Ai-M61/Ai-M62 both use bl616; BOARD is the board type):

make CHIP=bl616 BOARD=bl616dk
8
Flash the firmware

Hold BOOT, tap EN/RST to enter download mode, then flash. The firmware is under build/build_out/:

make flash CHIP=bl616 COMX=/dev/ttyUSB0

What Each File Is For (File-by-File) ​

CMakeLists.txt: the build entry ​

cmake
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.cmake in 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 contains main; the SDK links it with the startup code.
  • project(...): the project name, which appears in the firmware file name.

Makefile: the make entry ​

make
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 ​

text
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 ​

text
CONFIG_FREERTOS =y      # enable FreeRTOS
CONFIG_SHELL =y         # enable serial shell
CONFIG_BFLB_LOG =n      # logging toggle
  • Each CONFIG_XXX toggles a component/feature: =y on, =n off, commented = default.
  • You can override temporarily: make CONFIG_BFLB_LOG=y without editing files.
  • Examples have long defconfigs to enable everything they use; your own project can be minimal.

main.c: application entry ​

c
#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; also LOG_E/W/D/T for other levels.
  • For multitasking, create tasks with xTaskCreate and call vTaskStartScheduler() (see the FreeRTOS example).

flash_prog_cfg.ini: flash configuration (optional) ​

ini
[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 address

It 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:

ApproachWhen to useKey syntax
Approach 1: standalone library (recommended)Reusable code, decoupled from main.csdk_generate_library() + add_subdirectory()
Approach 2: drop into app (simplest)One or two files, no separate directorysdk_add_include_directories() + target_sources(app PRIVATE ...)

How it works: the SDK's project() macro links every library registered via sdk_generate_library() into the firmware in one go (--whole-archive), so you do not need to call target_link_libraries manually.

To add a my_lib library, the project tree becomes:

text
my_project/
├── CMakeLists.txt
├── Makefile
├── Kconfig
├── defconfig
├── main.c
└── my_lib/
    ├── CMakeLists.txt
    ├── include/
    │   └── my_lib.h
    └── src/
        └── my_lib.c

Approach 1 steps:

1
Write the library's own CMakeLists.txt

Create a CMakeLists.txt inside my_lib/:

  • sdk_generate_library(): creates a static library libmy_lib.a (named after the directory) and registers it in the SDK global link list automatically.
  • sdk_add_include_directories(include): adds my_lib/include to 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)
2
Include the library in the top-level CMakeLists.txt

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)
3
Call it from main.c

#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
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 ...): compiles my_lib.c directly into app; separate multiple sources with newlines/spaces.
  • This produces no separate .a; the code is compiled into libapp.a together with main.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

Released under the MIT License. Build Time 2026-09-30 17:31:25