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-11 14:52:23