Concepts First
- OTA (Over-The-Air): upgrading firmware over the network without a programmer; IoT devices rely on it after shipping.
- A/B partitions (dual backup): Flash has two firmware areas (FW A / FW B). The new firmware is written to the inactive area; after verification the active partition switches; if the new firmware fails to boot, Boot2 rolls back to the old one.
- Partition table: records each area's address and size. The example calls
wifi_ota_dump_partitionat boot; after OTA, theagefield increments. - Version and hash checks: the example enables
CONFIG_OTA_VERSION_CHECK(rejects downgrades/same version) and verifies the OTA header SHA256 — a corrupted file never switches partitions. - Commands:
https_ota_start <url> [reboot](HTTP/HTTPS),tcp_ota_start <ip> <port> [reboot](TCP);rebootdefaults to 1.
Example Overview
This page covers the wifi_ota_by_http example in the official Bouffalo SDK (examples/wifi/sta/wifi_ota_by_http):
- at boot,
wifi_ota_dump_partition()reads and prints the partition table (pt_table_get_active_partition_from_ram); - after Wi-Fi init, OTA is triggered by shell commands;
ota_tls_config.cinjects the CA and client certificates (for HTTPS mutual auth) viaapp_https_ota_fill_config; thehttps_fotacomponent handles download, verification and partition writes;- supports HTTP, HTTPS and TCP channels; the upgrade flow is: read partition table → erase inactive area → connect server → verify OTA header → verify hash → update partition table (
age++) → reboot perrebootflag. - Related reference: partition tables in the system section's Partition Table; the BLE OTA
GenerateOAD.pytool inexamples/btble/peripheral.
Operation Steps
Open a terminal and enter the SDK OTA example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/wifi/sta/wifi_ota_by_httpBoth Ai-M61 and Ai-M62 use bl616. The example embeds PROJECT_SDK_VERSION in the OTA header and rejects downgrades:
make CHIP=bl616 BOARD=bl616dk PROJECT_SDK_VERSION=2.1.5Hold BOOT, briefly press EN/RST to enter download mode, then flash:
make flash CHIP=bl616 COMX=/dev/ttyUSB0On the PC, go to the directory containing the firmware package (with build/build_out/wifi_ota_bl616.bin.ota) and start the HTTP server:
python3 https.pySerial tool at 2000000 baud. Connect to the router, then run (reboot defaults to 1, so the device restarts after a successful upgrade):
wifi_sta_connect Your_SSID 12345678
https_ota_start http://192.168.18.125:5000/build/build_out/wifi_ota_bl616.bin.otaAt boot the example prints the Flash partition table (Active PT:...); during OTA it downloads the firmware, verifies SHA256, updates the partition table (age++) and reboots. After reboot, the partition table’s age in the log increased — the upgrade took effect.
Code Execution Flow
The complete HTTP OTA flow from boot to upgrade:
APIs Used by the Example
pt_table_get_active_partition_from_ram(pt_table_stuff)
Reads the active partition table from RAM and returns the active partition ID; returns PT_TABLE_ID_INVALID on failure.
Parameters:
pt_table_stuff:pt_table_stuff_configarray receiving the table
Return: active partition ID; PT_TABLE_ID_INVALID if invalid
pt_table_set_flash_operation(erase, write, read)
Sets the Flash read/write callbacks used by the partition table (the example passes bflb_flash_erase/write/read).
Parameters:
erase/write/read: Flash operation callbacks
Return: none
app_https_ota_fill_config(url, config)
(in ota_tls_config.c) fills the OTA TLS configuration: CA certificate, client certificate and private key (from https_ota_tls_material.h).
Parameters:
url: OTA firmware URLconfig:struct https_fota_config
Return: 0 on success
https_ota_start / tcp_ota_start(url/ip, port, reboot)
Shell commands that trigger the HTTP(S)/TCP OTA download and upgrade flow; reboot controls whether to restart after success.
Parameters:
url: firmware.otafile address (HTTP/HTTPS)ip/port: TCP server address (TCP channel)reboot:1restart /0stay
Return: none (commands)
Complete Code
The full main.c source, identical to the official example (examples/wifi/sta/wifi_ota_by_http). Collapsed by default; click to expand:
📜 Click to expand wifi_ota_by_http/main.c full code
/****************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include <lwip/tcpip.h>
#include "wifi_mgmr_ext.h"
#include "bflb_uart.h"
#include "bflb_flash.h"
#include "rfparam_adapter.h"
#include "board.h"
#include "shell.h"
#include "partition.h"
#ifndef BL602
#include "fhost_api.h"
#include "wifi_mgmr.h"
#endif
#include "async_event.h"
#include "mm.h"
#define DBG_TAG "MAIN"
#include "log.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
static struct bflb_device_s *uart0;
extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);
#ifdef BL602
extern void wifi_task_create(void);
extern int fhost_init(void);
extern int wifi_mgmr_task_start(void);
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Functions
****************************************************************************/
void wifi_start_firmware_task(void *param)
{
LOG_I("Starting wifi ...\r\n");
async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);
wifi_task_create();
LOG_I("Starting fhost ...\r\n");
fhost_init();
vTaskDelete(NULL);
}
void wifi_event_handler(async_input_event_t ev, void *priv)
{
uint32_t code = ev->code;
switch (code) {
case CODE_WIFI_ON_INIT_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE\r\n", __func__);
wifi_mgmr_task_start();
} break;
case CODE_WIFI_ON_MGMR_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE\r\n", __func__);
} break;
case CODE_WIFI_ON_SCAN_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE\r\n", __func__);
wifi_mgmr_sta_scanlist();
} break;
case CODE_WIFI_ON_CONNECTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED\r\n", __func__);
void mm_sec_keydump();
mm_sec_keydump();
} break;
#ifdef CODE_WIFI_ON_GOT_IP_ABORT
case CODE_WIFI_ON_GOT_IP_ABORT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_ABORT\r\n", __func__);
} break;
#endif
#ifdef CODE_WIFI_ON_GOT_IP_TIMEOUT
case CODE_WIFI_ON_GOT_IP_TIMEOUT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_TIMEOUT\r\n", __func__);
} break;
#endif
case CODE_WIFI_ON_GOT_IP: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP\r\n", __func__);
LOG_I("[SYS] Memory left is %d Bytes\r\n", kfree_size(0));
} break;
case CODE_WIFI_ON_DISCONNECT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STARTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STOPPED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STA_ADD: {
LOG_I("[APP] [EVT] [AP] [ADD] %lld\r\n", xTaskGetTickCount());
} break;
case CODE_WIFI_ON_AP_STA_DEL: {
LOG_I("[APP] [EVT] [AP] [DEL] %lld\r\n", xTaskGetTickCount());
} break;
default: {
LOG_I("[APP] [EVT] Unknown code %u \r\n", code);
}
}
}
static void wifi_ota_dump_partition()
{
pt_table_stuff_config pt_table_stuff[2];
pt_table_id_type active_id;
pt_table_stuff_config *pt_stuff;
/* Set flash operation function, read via xip */
pt_table_set_flash_operation(bflb_flash_erase, bflb_flash_write, bflb_flash_read);
active_id = pt_table_get_active_partition_from_ram(pt_table_stuff);
if (PT_TABLE_ID_INVALID == active_id) {
printf("No valid PT\r\n");
return;
}
printf("Active PT:%d,Age %d\r\n", active_id, pt_table_stuff[active_id].pt_table.age);
pt_stuff = &pt_table_stuff[active_id];
printf("======= PtTable_Config @%p=======\r\n", pt_stuff);
printf("magicCode 0x%08X;", (unsigned int)(pt_stuff->pt_table.magicCode));
printf(" version 0x%04X;", pt_stuff->pt_table.version);
printf(" entryCnt %u;", pt_stuff->pt_table.entryCnt);
printf(" age %lu;", pt_stuff->pt_table.age);
printf(" crc32 0x%08X\r\n", (unsigned int)pt_stuff->pt_table.crc32);
printf(" idx type device active_index name address[0] address[1] length[0] length[1] age \r\n");
for (uint32_t i = 0; i < pt_stuff->pt_table.entryCnt; i++) {
printf("[%02d] ", i);
printf(" %02u", (pt_stuff->pt_entries[i].type));
printf(" %u", (pt_stuff->pt_entries[i].device));
printf(" %u", (pt_stuff->pt_entries[i].active_index));
printf(" %8s", (pt_stuff->pt_entries[i].name));
printf(" 0x%08lx", (pt_stuff->pt_entries[i].start_address[0]));
printf(" 0x%08lx", (pt_stuff->pt_entries[i].start_address[1]));
printf(" 0x%08lx", (pt_stuff->pt_entries[i].max_len[0]));
printf(" 0x%08lx", (pt_stuff->pt_entries[i].max_len[1]));
printf(" %lu\r\n", (pt_stuff->pt_entries[i].age));
}
}
int main(void)
{
board_init();
uart0 = bflb_device_get_by_name("uart0");
shell_init_with_task(uart0);
if (0 != rfparam_init(0, NULL, 0)) {
LOG_I("PHY RF init failed!\r\n");
return 0;
}
LOG_I("PHY RF init success!\r\n");
wifi_ota_dump_partition();
tcpip_init(NULL, NULL);
xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
vTaskStartScheduler();
while (1) {
}
}FAQ
Download fails / verification fails
Confirm https.py and the module are on the same subnet and the URL points to the correct .ota file; corrupted files or versions not higher than the current firmware are rejected (CONFIG_OTA_VERSION_CHECK); for HTTPS, the certificate chain must match what the module embeds.
No change after upgrade / version unchanged
Raise PROJECT_SDK_VERSION when rebuilding (e.g. 2.1.5 → 2.1.6), otherwise the same version is skipped; after a successful upgrade the device reboots and the partition table age in the boot log is +1 — use that to confirm the active partition switched.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

