Concepts First
- Coexistence: BL616/BL618 are single-antenna combo chips — Wi-Fi and BLE share the radio and are scheduled by the firmware, so both can work at the same time.
- PS (Power Save): the Wi-Fi idle power-saving mechanism. Once enabled, Wi-Fi sleeps periodically and leaves radio time for BLE — the recommended setup for coexistence scenarios.
- Duty cycle: the percentage of time Wi-Fi stays awake to send/receive; the example sets it to 50% with
wifi_sta_ps_set 50. - iperf: the example builds in Wi-Fi iperf (
CONFIG_WIFI_IPERF =y) for throughput tests in coexistence scenarios.
Example Overview
- This page covers the official wifi_ble example (
examples/wifi/coex/wifi_ble), demonstrating Wi-Fi STA and BLE running simultaneously: while Wi-Fi keeps its connection, BLE can still advertise, scan and connect. - The example = Wi-Fi management (connect/events) + the
btble_cliBLE commands (the port layer lives inbtble_test_port.c, not expanded on this page). - Supported chips: BL602 / BL616 / BL616CL / BL618DG.
- The SDK also ships a set of Python test tools for BL616CL (
BL616cl_test/:ble_test.py,wifi_test.py,coex_test.py) for automated coexistence testing.
Operation Steps
Enter the SDK’s wifi_ble example directory (prerequisite: set up the environment with Quick Start (Linux) or Windows):
cd examples/wifi/coex/wifi_bleAi-M61 and Ai-M62 both use bl616; the example enables Wi-Fi and BLE together by default:
make CHIP=bl616 BOARD=bl616dkHold BOOT, tap EN/RST to enter download mode, then flash:
make flash CHIP=bl616 COMX=/dev/ttyUSB0Open the serial terminal at 2000000 baud, wait for the Wi-Fi stack to initialize, then type (replace SSID and password with your own):
wifi_sta_connect <ssid> <passwd>Turn on Wi-Fi power save and set the Wi-Fi duty cycle to 50% to leave radio time for BLE:
wifi_sta_ps_on
wifi_sta_ps_set 50Start connectable BLE advertising in the same shell — a phone with nRF Connect can scan and connect while Wi-Fi stays connected:
ble_start_adv 0 0 0x80 0x80Code Execution Flow
APIs Used by the Example
tcpip_init(cb, arg)
Initialize the lwIP TCP/IP stack so the module can obtain an IP once Wi-Fi connects.
Parameters:
cb: init-done callbackarg: callback argument
Return value: none
wifi_task_create()
Create the Wi-Fi firmware task and load the Wi-Fi stack.
Parameters: none
Return value: none
fhost_init()
Initialize fhost (the Wi-Fi management framework); afterwards the network can be connected through the wifi_mgmr_* APIs.
Parameters: none
Return value: none
btble_cli_start()
Start BLE CLI as a task (port implementation in btble_test_port.c); internally runs btble_controller_init → hci_driver_init → bt_enable and registers the ble_* commands.
Parameters: none
Return value: none
async_register_event_filter(EV_WIFI, wifi_event_handler, NULL)
Register a Wi-Fi event filter; CODE_WIFI_ON_* events drive the state machine (init done → connected → got IP, etc.).
Parameters:
ev: event typeEV_WIFIcb: event callback
Return value: none
Complete Code
The full main.c of the wifi_ble example, identical to the official SDK, collapsed by default — click to expand (btble_test_port.c, the BLE port layer, is not expanded on this page):
📜 Click to expand wifi_ble/main.c full code
#include "shell.h"
#include <FreeRTOS.h>
#include "task.h"
#include "board.h"
#include "rfparam_adapter.h"
#include "bflb_mtd.h"
#include "easyflash.h"
#include <lwip/tcpip.h>
#include "wifi_mgmr_ext.h"
#ifndef BL602
#include "fhost_api.h"
#include "wifi_mgmr.h"
#endif
#include "mm.h"
#define DBG_TAG "MAIN"
#include "log.h"
#include "async_event.h"
static struct bflb_device_s *uart0;
#define WIFI_STACK_SIZE (1536)
#define TASK_PRIORITY_FW (16)
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 wifi_mgmr_task_start(void);
#endif
#if defined(CONFIG_BLUETOOTH)
extern int btble_cli_start(void);
#endif
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();
#ifndef BL602
LOG_I("Starting fhost ...\r\n");
fhost_init();
#endif
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);
}
}
}
int main(void)
{
board_init();
configASSERT((configMAX_PRIORITIES > 4));
uart0 = bflb_device_get_by_name("uart0");
shell_init_with_task(uart0);
bflb_mtd_init();
/* ble stack need easyflash kv */
easyflash_init();
/* Init rf */
if (0 != rfparam_init(0, NULL, 0)) {
printf("PHY RF init failed!\r\n");
return 0;
}
tcpip_init(NULL, NULL);
xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
#if defined(CONFIG_BLUETOOTH)
btble_cli_start();
#endif
vTaskStartScheduler();
while (1) {
}
}FAQ
BLE cannot connect or throughput drops when Wi-Fi and BLE are both on
First enable Wi-Fi power save and set the duty cycle to 50%: wifi_sta_ps_on + wifi_sta_ps_set 50. With a single antenna the two radios necessarily share RF time, so throughput below the Wi-Fi-only scenario is expected.
How to measure Wi-Fi throughput during coexistence
The example builds in iperf (CONFIG_WIFI_IPERF =y); after connecting, use the iperf commands to test TCP/UDP throughput while BLE keeps advertising or stays connected to observe the coexistence impact.
Can I test BLE alone without connecting Wi-Fi
Yes. Simply skip wifi_sta_connect; the BLE commands (ble_start_adv, ble_start_scan, etc.) still work.
How to use the BL616CL Python test tools
ble_test.py, wifi_test.py and coex_test.py under examples/wifi/coex/wifi_ble/BL616cl_test/ work with the test board; each README describes its test flow. The BLE test cases only support the BLE-only BL616CL chip.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

