Concepts First
- Power-off saving (KV storage): storing "key-value" data (Wi-Fi credentials, device config, etc.) in Flash so it survives power loss and can be read back after reboot.
- EasyFlash: a lightweight KV database component, adapted by the SDK on top of LittleFS; its interfaces start with
ef_. - PSM partition: EasyFlash can only operate on the partition named PSM in the partition table (the example comment shows a config snippet). If the partition is too small, storage-full errors occur.
- Cache first, then persist:
ef_set_envonly updates the in-memory cache;ef_save_envactually writes Flash;ef_set_and_save_envcombines both.
Example Overview
This page is based on the easyflash example in the official Bouffalo SDK (examples/easyflash), which demonstrates writing, reading, enumerating, and clearing KV data:
- Initializes MTD and EasyFlash, verifying the PSM partition is usable;
- Writes 4 key-value pairs:
wifi.ssid/wifi.passwd/g/hwaddr/mac_aabb//root/aa/bbb/; - Reads them back with
ef_get_env_blob, and demonstrates offset reads withef_get_env_blob_offset; ef_foreach_envenumerates all keys;ef_print_envprints the current key-value set;ef_env_set_default()clears everything and verifies the enumeration count drops to zero.- The example depends on MTD / partition / LittleFS components; the code shown is the example's own
main.c.
Note
EasyFlash can only operate on the PSM partition in the partition table. If the firmware/partition table has no PSM partition, easyflash_init fails; the partition-config snippet in the example's main.c comment (type = 3, name = "PSM") is the required part.
Operation Steps
No external wiring is needed for this page. Open a terminal and enter the SDK EasyFlash example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/easyflashRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616 (the example depends on CONFIG_BFLB_MTD, CONFIG_PARTITION, and CONFIG_LITTLEFS, which are enabled in the example’s defconfig):
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 prints easyflash_init test pass., writes and reads back wifi.ssid, wifi.passwd, g/hwaddr/mac_aabb, /root/aa/bbb/ (printing ssid:helloworld, passwd:helloworld2023 test pass., etc.), then enumerates all keys (4 total) and clears them with ef_env_set_default(), finishing with easyflash case success. After reset or power cycle, the written keys still exist — that is the point of power-off saving.
Code Execution Flow
The complete flow from startup through KV verification is shown below (diamonds are checks):
APIs Used by the Example
bflb_mtd_init()
Initializes MTD (Memory Technology Device, the Flash-partition abstraction); EasyFlash relies on it to access the PSM partition.
Parameters: none
Return: 0 on success; negative error code on failure
easyflash_init()
Initializes EasyFlash KV storage (mounts the PSM partition). Must be called after bflb_mtd_init().
Parameters: none
Return: EfErrCode; EF_NO_ERR means success
ef_set_and_save_env(key, value)
Writes a key-value pair and immediately saves it to Flash (combines ef_set_env and ef_save_env).
Parameters:
key: key name, e.g."wifi.ssid"value: string value
Return: EfErrCode; EF_NO_ERR means success
ef_get_env_blob(key, buf, len, saved_len)
Reads data by key (binary-safe; the example reads strings).
Parameters:
key: key namebuf: destination bufferlen: buffer sizesaved_len: returns the actual saved length (can beNULL)
Return: number of bytes read; 0 if missing or on failure
ef_get_env_blob_offset(key, buf, len, saved_len, offset)
Reads starting at an offset into the stored value; the example uses it to verify offset reads.
Parameters:
key: key namebuf: destination bufferlen: buffer sizesaved_len: returns the actual saved length (can beNULL)offset: starting offset
Return: number of bytes read; 0 on failure
ef_foreach_env(cb, arg)
Enumerates all keys, calling cb(name, arg) for each.
Parameters:
cb: callback functionarg: argument passed through to the callback (used for counting in the example)
Return: EfErrCode
ef_print_env / ef_env_set_default()
ef_print_env prints all current key-value pairs; ef_env_set_default clears them all (factory default).
Parameters: none
Return: ef_print_env returns nothing; ef_env_set_default returns EfErrCode
Complete Code
The complete source below matches the official example (examples/easyflash) verbatim. Collapsed by default, click to expand:
📜 Click to expand easyflash/main.c full code
#include "bflb_mtimer.h"
#include "board.h"
#include "bflb_mtd.h"
#include "easyflash.h"
uint8_t test_data[] = { "1234567890" };
uint8_t read_buffer[100];
#define WIFI_SSID_KEY "wifi.ssid"
#define WIFI_PASSWD_KEY "wifi.passwd"
#define TEST_KEY1 "g/hwaddr/mac_aabb"
#define TEST_KEY2 "/root/aa/bbb/"
static EfErrCode env_foreach_cb(const char *name, void *arg) {
uint32_t *count = (uint32_t *)arg;
printf("foreach key %d: %s\n", (*count)++, name);
return EF_NO_ERR;
}
int main(void)
{
EfErrCode ret;
board_init();
/* Partition and boot2 must be use, and we can only operate partition **psm** with easyflash
*
* partition_cfg with psm:
*
[[pt_entry]]
type = 3
name = "PSM"
device = 0
address0 = 0x3E9000
size0 = 0x8000
address1 = 0
size1 = 0
# compressed image must set len,normal image can left it to 0
len = 0
# If header is 1, it will add the header.
header = 0
# If header is 1 and security is 1, It will be encrypted.
security= 0
*/
bflb_mtd_init();
if (easyflash_init() == EF_NO_ERR) {
printf("easyflash_init test pass.\n");
} else {
printf("errno: %d\r\n", errno);
printf("easyflash_init test failed.\n");
}
memset(read_buffer, 0, sizeof(read_buffer));
ret = ef_set_and_save_env(WIFI_SSID_KEY, (const char *)"helloworld");
if (ret != EF_NO_ERR) {
printf("test case %d failed.\n", __LINE__);
while(1);
}
ret = ef_set_and_save_env(WIFI_PASSWD_KEY, (const char *)"helloworld2023");
if (ret != EF_NO_ERR) {
printf("test case %d failed.\n", __LINE__);
while(1);
}
ret = ef_set_and_save_env(TEST_KEY1, (const char *)"11223344");
if (ret != EF_NO_ERR) {
printf("test case %d failed.\n", __LINE__);
while(1);
}
ret = ef_set_and_save_env(TEST_KEY2, (const char *)"deadbeef");
if (ret != EF_NO_ERR) {
printf("test case %d failed.\n", __LINE__);
while(1);
}
ret = ef_save_env();
if (ret != EF_NO_ERR) {
printf("test case %d failed.\n", __LINE__);
while(1);
}
char ssid[33];
char passwd[65];
char hwaddr[33];
ret = ef_get_env_blob(WIFI_SSID_KEY, ssid, sizeof(ssid), NULL);
if (ret > 0) {
ssid[ret] = 0;
printf("ssid:%s, test pass.\r\n", ssid);
} else {
printf("test case %d failed.\n", __LINE__);
while(1);
}
ret = ef_get_env_blob(WIFI_PASSWD_KEY, passwd, sizeof(passwd), NULL);
if (ret > 0) {
passwd[ret] = 0;
printf("passwd:%s test pass.\r\n", passwd);
} else {
printf("test case %d failed.\n", __LINE__);
while(1);
}
ret = ef_get_env_blob(TEST_KEY1, hwaddr, sizeof(hwaddr), NULL);
hwaddr[ret] = 0;
if (ret == 0) {
printf("read key1 failed\r\n");
while(1);
} else {
printf(TEST_KEY1 ":%s, pass\r\n", hwaddr);
}
ret = ef_get_env_blob_offset(TEST_KEY1, hwaddr, sizeof(hwaddr), NULL, 2);
hwaddr[ret] = 0;
if (ret == 0) {
printf("read key1 failed\r\n");
while(1);
} else {
printf(TEST_KEY1 "+2:%s, pass\r\n", hwaddr);
}
ret = ef_get_env_blob_offset(TEST_KEY1, hwaddr, sizeof(hwaddr), NULL, 3);
hwaddr[ret] = 0;
if (ret == 0) {
printf("read key1 failed\r\n");
while(1);
} else {
printf(TEST_KEY1 "+3:%s, pass\r\n", hwaddr);
}
ret = ef_get_env_blob_offset(TEST_KEY1, hwaddr, sizeof(hwaddr), NULL, 100);
hwaddr[ret] = 0;
if (ret == 0) {
printf("test case %d pass.\n", __LINE__);
} else {
printf(TEST_KEY1 "+100:%s, failed\r\n", hwaddr);
while(1);
}
ret = ef_get_env_blob("aa/bb", hwaddr, sizeof(hwaddr), NULL);
hwaddr[ret] = 0;
if (ret == 0) {
printf("test non-exists key pass\r\n");
} else {
printf("aa/bb:%s, failed!\r\n", hwaddr);
while(1);
}
ret = ef_get_env_blob(TEST_KEY2, hwaddr, sizeof(hwaddr), NULL);
hwaddr[ret] = 0;
if (ret == 0) {
printf("read key2 failed\r\n");
while(1);
} else {
printf(TEST_KEY2 ":%s, pass\r\n", hwaddr);
}
printf("foreach all env:\n");
uint32_t count = 0;
ef_foreach_env(env_foreach_cb, &count);
printf("foreach all env: done, total: %d\n", count);
if (count == 4) {
printf("ef_foreach_env test pass.\n");
} else {
printf("ef_foreach_env test failed.\n");
while(1);
}
ef_print_env();
printf("clear all kv\r\n");
/* reset all kv */
ef_env_set_default();
ef_print_env();
count = 0;
ef_foreach_env(env_foreach_cb, &count);
if (count == 0) {
printf("ef_env_set_default test pass.\n");
} else {
printf("ef_env_set_default test failed.\n");
while(1);
}
printf("easyflash case success\r\n");
while (1) {
}
}FAQ
easyflash_init test failed
The most common cause is a missing PSM partition (EasyFlash can only operate on PSM). Check that partition_cfg contains the name = "PSM" entry shown in the example comment, and that CONFIG_BFLB_MTD, CONFIG_PARTITION, and CONFIG_LITTLEFS are enabled.
Storage full after writing many keys
The PSM partition is limited (0x8000 = 32 KB in the example config); when full, writes return EF_ENV_FULL. Enlarge the PSM partition or periodically delete unneeded keys.
Key-values lost after power cycle
Make sure you used ef_set_and_save_env (or ef_set_env followed by ef_save_env); ef_set_env alone only updates the in-memory cache and data is lost on power-off.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

