Skip to content

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_env only updates the in-memory cache; ef_save_env actually writes Flash; ef_set_and_save_env combines 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 with ef_get_env_blob_offset;
  • ef_foreach_env enumerates all keys; ef_print_env prints 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

1
Enter the Example Directory

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/easyflash
2
Build the Project

Run 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=bl616dk
3
Flash the Firmware

Connect 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/ttyUSB0
4
Run and Verify

Open 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 name
  • buf: destination buffer
  • len: buffer size
  • saved_len: returns the actual saved length (can be NULL)

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 name
  • buf: destination buffer
  • len: buffer size
  • saved_len: returns the actual saved length (can be NULL)
  • 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 function
  • arg: 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
c
#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

Released under the MIT License. Build Time 2026-09-11 14:52:23