Overview
Flash (in plain words: the board's "hard drive" — data survives power loss) is used for power-off persistence: device configuration, calibration parameters, OTA (over-the-air upgrade, in plain words: updating firmware over the network like a phone system update) flags and more all need to be written to Flash. The Ai-WB2 SDK divides Flash into FW, mfg, media, PSM, KEY, DATA, factory partitions (in plain words: like splitting a hard drive into C drive and D drive, each storing its own data without interference), and recommends operating by partition name to avoid going out of bounds. This tutorial demonstrates partition read/write and raw address read/write two ways, with data consistency checks.
In plain words: Flash is like the board's "hard drive" — photos live on the hard drive, and after turning off and on again the photos are still there; same with the board: write data into Flash, and after power-off restart the data remains. This tutorial demonstrates two ways to "store data": by partition (like putting a file into a designated folder — safe and never messy) and by direct address (like writing directly to a designated spot on the hard drive — flexible, but writing to the wrong spot can damage the system).
This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version
release_bl_iot_sdk_1.6.40) exampleapplications/storage/flash; the code can be found directly in your local SDK.
Open a terminal and enter the official flash example project directory:
cd ~/Ai-Thinker-WB2/applications/storage/flash
Note:
cdis the “change directory” command and~means your user home directory. This enters the flash example project; all subsequentmakecommands must run in this directory. If it saysNo such file or directory, the path is wrong — see the FAQ at the end.
Open flash/main.c. The full code for this step has been moved to the end of this page:
📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (
applications/storage/flash/flash/main.c).
Code highlights:
| Code | Purpose |
|---|---|
hosal_flash_open("DATA", HOSAL_FLASH_FLAG_ADDR_0) |
Open the DATA partition by name; without “opening the door” none of the later reads/writes can start |
hosal_flash_info_get(p_flash, &part) |
Fetch partition info; printed to confirm the partition name, address and size are normal |
hosal_flash_erase_write(p_flash, &offset, wbuf, 1024) |
Erase then write 1KB of data; Flash must be erased before writing, otherwise correct data can’t be stored |
hosal_flash_read(p_flash, &offset, rbuf, 1024) |
Read back what was just written; write and read must match for the save to be a success |
hosal_flash_raw_write/raw_read(buf, addr, len) |
Read/write directly by physical address; bypasses the partition table, writing the wrong address can damage firmware |
💡 The difference between the two ways: the partition way (recommended) locates by partition name — safe, never out of bounds; the raw address way specifies a physical address directly — you must make sure the address is in a writable partition yourself, writing wrong can damage firmware.
Build in the project directory:
make -j8
Note:
makeis the “build” command, translating the source code into machine code the board can run;-j8builds with 8 parallel cores, faster.
On success a firmware build_out/flash.bin is generated (firmware: the program burned into the board after compilation, like the board’s “operating system + your program”).
Keep the board connected via USB, confirm the serial device, and flash:
make flash p=/dev/ttyUSB0 b=921600
Note:
make flashis the “flash” command, writing the compiled firmware into the chip (flashing: the process of writing a program into the chip);p=/dev/ttyUSB0is the serial device — change it to your computer’s actual port (likeCOM3on Windows),b=921600is the flash baud rate (transfer speed).
⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode; wait for the progress bar to complete — that means the flash succeeded. If it keeps waiting or reports the serial port can’t open, see the FAQ at the end.
After flashing, the board automatically restarts and runs. Open a serial assistant (baud rate 921600) and check the logs:
--------flash write/read demo--------
partition name : DATA
partition start address: 0x001f3000
partition length : 20480
hal flash partition data R/W successful!
hal flash addr R/W successful!
Both read/write methods pass the check, meaning data was successfully written into Flash and read back unchanged.
💡 Power-off verification: disconnect the board’s power and power on again — the data is still in Flash (the example rewrites every boot; you can change it to “read first, write later” to verify power-off persistence).
✅ Expected result: seeing
hal flash partition data R/W successful!andhal flash addr R/W successful!means success. IfR/W failed!orno partition name DATA!prints, the read/write failed — see the FAQ at the end.
API Summary for This Tutorial
hosal_flash_open(name, flags)
Finds the partition by name and returns a device handle (must open before reading/writing).
Parameters:
name: partition name (defined in the partition table), values:"FW"(firmware area),"mfg","media","PSM","KEY","DATA","factory"(user data recommended:"media")flags: address mode flag, values:HOSAL_FLASH_FLAG_ADDR_0(0-address offset) /HOSAL_FLASH_FLAG_ADDR_1/HOSAL_FLASH_FLAG_BUSADDR(bus address)
Return: device handle (hosal_flash_dev_t pointer) on success; NULL on failure
hosal_flash_info_get(dev, part)
Reads the partition's name, start address, length and other info.
Parameters:
dev: device handle returned byhosal_flash_openpart:partition_tstruct pointer (output), required. Fields:name(partition name),start_addr(start address),length(length)
Return: 0 on success; negative error code on failure
hosal_flash_erase_write(dev, off_set, data, size)
Erases by sector first, then writes (Flash must be erased before writing), positioned by in-partition offset.
Parameters:
dev: device handleoff_set: in-partition offset (bytes), values:0~partition length, e.g.0(the start)data: pointer to the data to write, requiredsize: byte count to write, values:1~4096(4-byte alignment is better)
Return: 0 on success; negative error code on failure
hosal_flash_read(dev, off_set, data, size)
Reads data from the specified in-partition offset.
Parameters:
dev: device handleoff_set: in-partition offset (bytes)data: receive buffer pointer, requiredsize: byte count to read
Return: 0 on success; negative error code on failure
hosal_flash_raw_write(buf, address, length)
Writes directly by Flash physical address, bypassing the partition table (be careful not to overwrite the firmware area!).
Parameters:
buf: pointer to the data to write, requiredaddress: Flash physical address (from the partition table),0x192000(media partition) in this tutoriallength: byte count to write
Return: 0 on success; negative error code on failure
hosal_flash_raw_read(buf, address, length)
Reads data directly by Flash physical address.
Parameters:
buf: receive buffer pointer, requiredaddress: Flash physical addresslength: byte count to read
Return: 0 on success; negative error code on failure
hosal_flash_close(dev)
Closes the handle and frees resources (call when no longer reading/writing).
Parameters:
dev: device handle
Return: 0 on success; negative error code on failure
blog_info(fmt, ...)
Outputs an INFO-level log (UART0, filtered by level).
Parameters:
fmt: format string, same usage asprintf, required...: variadic args matchingfmtplaceholders, optional
Return: none
Full Code
Below is the complete flash/main.c source, identical to the official example (applications/storage/flash/flash/main.c):
📜 Click to expand the full flash/main.c code
/*
* @Author: Sean Kwok 1026025056@qq.com
* @Date: 2022-10-09 10:53:46
* @LastEditors: Sean Kwok 1026025056@qq.com
* @LastEditTime: 2022-10-31 09:45:55
* @FilePath: /Ai-Thinker-WB2/applications/storage/flash/flash/main.c
* @Description: Flash Write/Read Example
*/
#include <stdio.h>
#include <hosal_flash.h>
#include <stdlib.h>
#include <blog.h>
#define DEMO_HOSAL_FLASH_TEST_BLOCK 1024
#define DEMO_HOSAL_FLASH_TEST_ADDR 0x192000
#define DEMO_HOSAL_FLASH_TEST_LEN 1024
#define DEMO_HOSAL_FLASH_TEST_BLOCK 1024
/**
* hal flash R/W test demo
*/
void flash_partition_rw(void)
{
int i;
uint32_t offset = 0;
uint8_t *p_wbuf, *p_rbuf;
hosal_flash_dev_t *p_flash;
hosal_logic_partition_t part;
/* flash partition open use address 0 */
p_flash = hosal_flash_open("DATA", HOSAL_FLASH_FLAG_ADDR_0);
/* Open the partition table and use the bus physical address of flash. */
// p_flash = hosal_flash_open("DATA", HOSAL_FLASH_FLAG_ADDR_0 | HOSAL_FLASH_FLAG_BUSADDR);
if (p_flash == NULL) {
blog_info("no partition name DATA!");
return;
}
/* get flash partition info */
hosal_flash_info_get(p_flash, &part);
blog_info("partition name : %s", part.partition_description);
blog_info("partition start address: 0x%08x", part.partition_start_addr);
blog_info("partition length : %d", part.partition_length);
p_wbuf = malloc(DEMO_HOSAL_FLASH_TEST_BLOCK);
if (p_wbuf == NULL) {
blog_info("no memory!");
return;
}
p_rbuf = malloc(DEMO_HOSAL_FLASH_TEST_BLOCK);
if (p_rbuf == NULL) {
blog_info("no memory!");
return;
}
for (i = 0; i < DEMO_HOSAL_FLASH_TEST_BLOCK; i++) {
p_wbuf[i] = i & 0xff;
}
/* erase flash partition and write data */
offset = 0;
hosal_flash_erase_write(p_flash, &offset, p_wbuf, DEMO_HOSAL_FLASH_TEST_BLOCK);
/* read flash partition data */
offset = 0;
hosal_flash_read(p_flash, &offset, p_rbuf, DEMO_HOSAL_FLASH_TEST_BLOCK);
/* check flash read data */
if (memcmp(p_rbuf, p_wbuf, DEMO_HOSAL_FLASH_TEST_BLOCK) != 0) {
blog_info("hal flash R/W failed!");
} else {
blog_info("hal flash partition data R/W successful!");
}
/* close the flash partition and free buf */
hosal_flash_close(p_flash);
free(p_wbuf);
free(p_rbuf);
}
/**
* hal flash R/W test demo
*/
void flash_raw_addr_rw(void)
{
int i;
uint8_t *p_wbuf, *p_rbuf;
p_wbuf = malloc(DEMO_HOSAL_FLASH_TEST_LEN);
if (p_wbuf == NULL) {
blog_info("no memory!");
return;
}
p_rbuf = malloc(DEMO_HOSAL_FLASH_TEST_LEN);
if (p_rbuf == NULL) {
blog_info("no memory!");
return;
}
for (i = 0; i < DEMO_HOSAL_FLASH_TEST_LEN; i++) {
p_wbuf[i] = i & 0xff;
}
hosal_flash_raw_write(p_wbuf, DEMO_HOSAL_FLASH_TEST_ADDR, DEMO_HOSAL_FLASH_TEST_LEN);
hosal_flash_raw_read(p_rbuf, DEMO_HOSAL_FLASH_TEST_ADDR, DEMO_HOSAL_FLASH_TEST_LEN);
/* check flash read data */
if (memcmp(p_rbuf, p_wbuf, DEMO_HOSAL_FLASH_TEST_LEN) != 0) {
blog_info("hal flash addr R/W failed!");
} else {
blog_info("hal flash addr R/W successful!");
}
free(p_wbuf);
free(p_rbuf);
}
void main(void)
{
blog_info("--------flash write/read demo--------");
flash_partition_rw();
flash_raw_addr_rw();
}FAQ & Troubleshooting
⚠️ Prints no partition name DATA!
Cause: the partition table was modified, or the project's Flash layout differs from the example
Fix: confirm the partition config in proj_config.mk wasn't changed; restore the official default partition table and re-flash
⚠️ System malfunctions or can't boot after a raw address write
Cause: a protected partition like the firmware area (FW) was written, breaking the boot code
Fix: only write data partitions like media/DATA with the raw address way (0x192000 in the example); re-flash the firmware to recover after a mistaken write
⚠️ Read/write failures after repeated erase/write cycles
Cause: Flash erases in 4KB sectors, so frequent partial writes wear out the Flash lifetime
Fix: batch-write business data as much as possible; for frequently-updated parameters use the PSM partition or the easyflash component (official applications/storage/easyflash)
⚠️ malloc fails when writing larger data
Cause: the example's write/read buffers use dynamic memory, and RAM is insufficient
Fix: use a static array (static uint8_t wbuf[1024]) or reduce the per-call read/write length
⚠️ Serial port won't open / /dev/ttyUSB0 not found
Cause: USB-to-serial driver not installed, port occupied, or (on Linux) no access permission
Fix: on Linux confirm the device is recognized with lsusb, run sudo chmod 666 /dev/ttyUSB0 or add your user to the dialout group and retry; on Windows check the COM port in Device Manager and install the CH340/CP210x driver
⚠️ Flashing stuck waiting / chip not found
Cause: download mode wasn't entered, the cable only charges and can't transfer data, or the baud rate is wrong
Fix: press and hold EN during flashing until the progress bar appears; try a data cable; confirm p= port and b=921600 are correct
⚠️ cd reports No such file or directory / no Makefile found
Cause: make ran outside the example project directory, or the SDK install path differs from the tutorial
Fix: cd ~/Ai-Thinker-WB2/applications/storage/flash first, then run make; if ~/Ai-Thinker-WB2 doesn't exist, find the SDK with find ~ -name "Ai-Thinker-WB2"
Self-Check
The serial prints both read/write methods as successful! — Flash operations are verified.

