Skip to content

Concepts First

  • Low power: the chip sleeps most of the time and wakes on demand. When Wi-Fi/BLE stay online, power mainly depends on how much time the radio is awake.
  • PDS sleep: one of the BL616 low-power sleep levels; current is extremely low (tens of µA) while sleeping.
  • Adv interval: how often the module advertises — the longer the interval, the lower the power. In this example 0x0c80 ≈ 2 s, 0x0320 ≈ 500 ms, and 0x80 ≈ 125 ms (commonly used for connectable advertising).
  • tickless: the system suspends its tick and sleeps when idle, waking on events (e.g., RF events or timers); enabled by pm_enable_tickless().
  • DTIM: the next beacon at which the AP delivers buffered multicast/broadcast in Wi-Fi power save; a larger DTIM lets the device sleep longer.

Example Overview

  • This page covers the official wl_ble_lp example (examples/pmu/wl_ble_lp), demonstrating BLE low power, Wi-Fi low power and BLE + Wi-Fi dual low power.
  • BLE scenario: start advertising (or establish a connection), then type pm_enter_lp to enter low power while keeping the advertising/connection alive.
  • The example also provides commands such as pm_enter_lp (enter low power), wifi_lp_set_dtim (set DTIM), wakeup_timer (periodic wake-up and power statistics), and hbn_test (HBN test).
  • Power reference from the SDK README (after lowering the BLE TX power table to pwr_table_ble = <0>):
ModeCurrent
Deep sleep50 µA
ADV = 2s428 µA
ADV = 500ms1430 µA

Operation Steps

1
Enter the example directory

Enter the SDK’s wl_ble_lp example directory (prerequisite: set up the environment with Quick Start (Linux) or Windows):

cd examples/pmu/wl_ble_lp
2
Build the project

The default build includes BLE + Wi-Fi (CONFIG_BLE_ENABLE=0 builds Wi-Fi only); CONFIG_IPV6=y is optional — keep it if IPv6 is needed:

make CHIP=bl616 BOARD=bl616dk CONFIG_IPV6=y
3
Flash the firmware

Hold BOOT, tap EN/RST to enter download mode, then flash:

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Run and verify: BLE advertising low power

Open the serial terminal at 2000000 baud and reset the board. Start advertising first (2 s interval: ble_start_adv 0 0 0x0c80 0x0c80; or 500 ms: ble_start_adv 0 0 0x0320 0x0320), then type pm_enter_lp to enter low power.

5
Check the power statistics

The example’s wakeup_timer <timeout_ms> <rx_bcast> command wakes the chip periodically and prints power statistics (PDS sleep time, LPFW/APP active time and predicted current):

wakeup_timer 60000 0

Code Execution Flow

APIs Used by the Example

bl_lp_init()

Initialize the low-power framework (only active in LP_APP mode); coordinates the low-power firmware (LPFW) and the app firmware.

Parameters: none

Return value: none

bl_lp_sys_callback_register(enter, arg, exit, arg)

Register low-power enter/exit callbacks: lp_enter runs before sleeping and lp_exit runs after wake-up (restores CPU clock, UART, timers, etc.).

Parameters:

  • enter: enter-low-power callback
  • exit: exit-low-power callback

Return value: none

pm_enable_tickless()

Enable tickless mode: suspend the system tick and sleep while idle, restoring automatically after wake-up.

Parameters: none

Return value: none

bl_lp_info_get(&lp_info)

Read low-power statistics (PDS sleep time, LPFW/APP active time) to estimate the average current.

Parameters:

  • lp_info: bl_lp_info_t output structure

Return value: none

btble_controller_init(prio)

Initialize the BLE controller (radio and link layer). With BLE enabled the example also calls GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB) to size the controller memory.

Parameters:

  • prio: controller task priority

Return value: 0 on success; negative on failure

Complete Code

The full main.c of the wl_ble_lp example, identical to the official SDK, collapsed by default — click to expand (wl_ble_lp_app.c, which provides the wakeup_timer command, is not expanded on this page):

📜 Click to expand wl_ble_lp/main.c full code
c
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "mm.h"

#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>

#include "bl_fw_api.h"
#include "wifi_mgmr_ext.h"
#include "wifi_mgmr.h"
#include "fhost_api.h"

#include "bflb_irq.h"
#include "bflb_mtimer.h"
#include "board.h"
#include "bl_lp.h"
#include "bl616_pm.h"
#include "bflb_uart.h"
#include "bflb_gpio.h"
#include "bflb_clock.h"
#include "bl616_glb.h"
#include "bl616_glb_gpio.h"
#include "bl616_hbn.h"
#include "bflb_rtc.h"

#include "rfparam_adapter.h"

#include "board.h"
#include "board_rf.h"
#include "shell.h"

#include "bflb_mtd.h"
#include "easyflash.h"
#include "clock_manager.h"
#include "pm_manager.h"

#define DBG_TAG "MAIN"
#include "log.h"
#include "async_event.h"

#if defined(CFG_BLE_ENABLE)
#include "bluetooth.h"
#include "conn.h"
#include "conn_internal.h"
#include "btble_lib_api.h"
#include "bl616_glb.h"
#include "hci_driver.h"
#include "hci_core.h"
#include "async_event.h"
#endif

//#include "bflb_mtd.h"
//#include "easyflash.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define WIFI_STACK_SIZE  (1536)
#define TASK_PRIORITY_FW (16)

#define PM_MEM_POOL_SIZE    (1460 *2)

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Data
 ****************************************************************************/
static struct bflb_device_s *uart0;

TaskHandle_t wifi_fw_task;
static TaskHandle_t app_start_handle;
struct bt_conn *bleapp_default_conn;

#if defined(CFG_BLE_ENABLE)
static void ble_connected(struct bt_conn *conn, u8_t err)
{
    if(err || conn->type != BT_CONN_TYPE_LE)
    {
        return;
    }
    if (!bleapp_default_conn) {
        bleapp_default_conn = conn;
    }
}

static void ble_disconnected(struct bt_conn *conn, u8_t reason)
{
    if(conn->type != BT_CONN_TYPE_LE)
    {
        return;
    }
    if (bleapp_default_conn == conn) {
        bleapp_default_conn = NULL;
    }
    printf("%s",__func__);
}
static void ble_conn_param_updated(struct bt_conn *conn, u16_t interval,
			     u16_t latency, u16_t timeout)
{
    if(conn == bleapp_default_conn)
    {
        printf("%s: int 0x%04x lat %d to %d \r\n", __func__, interval, latency, timeout);
    }
}
static struct bt_conn_cb ble_conn_callbacks = {
    .connected  =   ble_connected,
    .disconnected   =   ble_disconnected,
    .le_param_updated = ble_conn_param_updated,
};

void bt_enable_cb(int err)
{
    if (!err) {
        bt_addr_le_t bt_addr;
        bt_get_local_public_address(&bt_addr);
        printf("BD_ADDR:(MSB)%02x:%02x:%02x:%02x:%02x:%02x(LSB) \r\n",
                bt_addr.a.val[5], bt_addr.a.val[4], bt_addr.a.val[3], bt_addr.a.val[2], bt_addr.a.val[1], bt_addr.a.val[0]);
        bt_conn_cb_register(&ble_conn_callbacks);
        //ble_cli_register();
    }
}
#endif
static void app_start_task(void *pvParameters)
{
    app_clock_init();
#if defined(CFG_BLE_ENABLE)
    btble_controller_init(configMAX_PRIORITIES - 1);
    hci_driver_init();
    bt_enable(bt_enable_cb);
#endif
    vTaskDelete(NULL);
}

extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/****************************************************************************
 * Functions
 ****************************************************************************/

void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize)
{
    /* If the buffers to be provided to the Idle task are declared inside this
    function then they must be declared static - otherwise they will be allocated on
    the stack and so not exists after this function exits. */
    static StaticTask_t xIdleTaskTCB;
    static StackType_t uxIdleTaskStack[1024];

    /* Pass out a pointer to the StaticTask_t structure in which the Idle task's
    state will be stored. */
    *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;

    /* Pass out the array that will be used as the Idle task's stack. */
    *ppxIdleTaskStackBuffer = uxIdleTaskStack;

    /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
    Note that, as the array is necessarily of type StackType_t,
    configMINIMAL_STACK_SIZE is specified in words, not bytes. */
    *pulIdleTaskStackSize = 1024;
}

static void wifi_start_firmware_task(void *pvParameters)
{
    (void)pvParameters;
    LOG_I("Starting wifi ...\r\n");

    /* set ble controller EM Size */
#ifdef CFG_BLE_ENABLE
    GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);
#endif

    if (0 != rfparam_init(0, NULL, 0)) {
        LOG_I("PHY RF init failed!\r\n");
        vTaskDelete(NULL);
    }

    LOG_I("PHY RF init success!\r\n");

    async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);

    wifi_task_create();

    vTaskDelay(500);

    LOG_I("Starting fhost ...\r\n");

    fhost_init();

    vTaskDelete(NULL);
}

/**********************************************************
  * wifi event handler
 ***********************************************************/
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);
        }
    }
}

/**********************************************************
    lp enter callback func
 **********************************************************/
static int lp_enter(void *arg)
{
    return 0;
}

/***********************************************************
    shell 
 ***********************************************************/
extern void uart_shell_isr();
extern struct bflb_device_s *uart_shell;
extern void vPortSetupTimerInterrupt(void);

static void set_cpu_bclk_80M_and_gate_clk(void)
{
    uint32_t tmpVal = 0;

    GLB_Set_MCU_System_CLK_Div(0, 3);
    CPU_Set_MTimer_CLK(ENABLE, BL_MTIMER_SOURCE_CLOCK_MCU_CLK, Clock_System_Clock_Get(BL_SYSTEM_CLOCK_MCU_CLK) / 1000000 - 1);

    /* clk gate,except DMA&CPU&UART0&SF&EMI&WIFI&EFUSE */
    tmpVal = 0;
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_M_CPU, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_M_DMA, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_M_SEC, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_M_SDU, 1);
    BL_WR_REG(GLB_BASE, GLB_CGEN_CFG0,tmpVal);

    tmpVal = 0;
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1_EF_CTRL, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1_SF_CTRL, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1_DMA, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1A_UART0, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1A_UART1, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1_SEC_ENG, 1);
    BL_WR_REG(GLB_BASE, GLB_CGEN_CFG1,tmpVal);

    tmpVal = 0;
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S2_WIFI, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1_EXT_EMI_MISC, 1);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, GLB_CGEN_S1_EXT_PIO, 1);

    BL_WR_REG(GLB_BASE, GLB_CGEN_CFG2, tmpVal);
}

GLB_GPIO_Type pinList[4] = {
    GLB_GPIO_PIN_0,
    GLB_GPIO_PIN_1,
    GLB_GPIO_PIN_2,
    GLB_GPIO_PIN_3,
};

static int lp_exit(void *arg)
{
    int wakeup_reason;
    extern TaskHandle_t rxl_process_task_hd;

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    set_cpu_bclk_80M_and_gate_clk();

    /* recovery system_clock_init\peripheral_clock_init\console_init*/
    board_recovery();

    //GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);
    //bflb_sys_em_config();

    //board_rf_ctl(BRD_CTL_RF_RESET_DEFAULT, 0);

    vPortSetupTimerInterrupt();

    bflb_uart_rxint_mask(uart_shell, false);
    bflb_irq_attach(uart_shell->irq_num, uart_shell_isr, NULL);
    bflb_irq_enable(uart_shell->irq_num);

    wakeup_reason = bl_lp_get_wake_reason();
    if (wakeup_reason & LPFW_WAKEUP_WIFI_BROADCAST) {
        vTaskNotifyGiveFromISR(rxl_process_task_hd, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    } else {
        pm_alloc_mem_reset();
    }

    //GLB_GPIO_Func_Init(GPIO_FUN_JTAG, pinList, 4);

    return 0;
}

int bflb_pm_app_check(void)
{
    return pm_pbufc_check();
}

#ifdef CONFIG_SHELL
static void cmd_tickless(int argc, char **argv)
{	
    int broadcast = 0;

    if ((argc > 1) && (argv[1] != NULL)) {
        printf("%s\r\n", argv[1]);
        broadcast = atoi(argv[1]);
    } else {
        broadcast = 0;
    }

    if (broadcast) {
        enable_multicast_broadcast = 1;
    } else {
        enable_multicast_broadcast = 0;
    }

    pm_enable_tickless();
}

static void cmd_set_dtim(int argc, char **argv)
{
    int dtim = 10;

    if ((argc > 1) && (argv[1] != NULL)) {
        printf("%s\r\n", argv[1]);
        dtim = atoi(argv[1]);
    } else {
        dtim = 10;
    }
    
    set_dtim_config(dtim);
}

static int test_tcp_keepalive(int argc, char **argv)
{
    int sockfd;
    // uint8_t *recv_buffer;
    struct sockaddr_in dest, my_addr;
    char buffer[51];
    uint32_t pck_cnt = 0;
    uint32_t pck_total = 0;

    /* Create a socket */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("Error in socket\r\n");
        return -1;
    }

    /*---Initialize server address/port struct---*/
    memset(&my_addr, 0, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_addr.s_addr = INADDR_ANY;
    my_addr.sin_port = htons(50001);

    memset(&dest, 0, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(50001);
    inet_aton(argv[1], &dest.sin_addr);

    if (argc == 4) {
        pck_cnt = atoi(argv[3]);
        printf("keep alive pck:%ld\r\n");
    }

    printf("tcp server ip: %s\r\n", argv[1]);

    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) != 0) {
        printf("Error in bind\r\n");
        close(sockfd);
        return -1;
    }

    /*---Connect to server---*/
    if (connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) != 0) {
        printf("Error in connect\r\n");
        close(sockfd);
        return -1;
    }

    /*---Get "Hello?"---*/
    memset(buffer, 'A', sizeof(buffer) - 1);

#ifdef LP_APP
    if (argc > 2) {
        cmd_tickless(0, NULL);
    }
#endif

    int ret = 0;

    while (1) {
        pck_total++;
        snprintf(buffer, sizeof(buffer), "SEQ = %ld  ", pck_total);

        buffer[sizeof(buffer) - 2] = '\n';
        ret = write(sockfd, buffer, sizeof(buffer) - 1);
        if (ret != sizeof(buffer) - 1) {
            printf("write error: %d\n", ret);
            break;
        }
        printf("**********************************\n");
        printf("SEQ:%ld WRITE SUCCESS %d\n", pck_total, ret);

        if (pck_cnt && (pck_total >= pck_cnt)) {
            bl_pm_event_bit_set(PSM_EVENT_APP);
            break;
        }
#if 0
        ret = read(sockfd, buffer, sizeof(buffer)-1);
        buffer[sizeof(buffer) -1] = 0;
        printf("read ret: %d, %s\r\n", ret, buffer);
#endif
        vTaskDelay(pdMS_TO_TICKS(30 * 1000));
    }

    close(sockfd);
    return 0;
}

static void cmd_hbn_test(int argc, char **argv)
{
    if (argc <= 5) {
        bl_lp_hbn_fw_cfg_t hbn_test_cfg = {
            .hbn_sleep_cnt = 32768 * 5,
            .hbn_level = 0,
        };
        bl_lp_hbn_enter(&hbn_test_cfg);
    } else {
        if ((argv[1] != NULL) && (argv[2] != NULL) && (argv[3] != NULL)) {
            printf("wdt_pin %d\r\n", atoi(argv[1]));
            printf("max_continue_times %d\r\n", atoi(argv[3]));
            bl_lp_hbn_init(1, atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
        }

        if ((argv[4] != NULL) && (argv[5] != NULL)) {
            printf("hbn sleep %ds\r\n", atoi(argv[4]));
            printf("hbn_level %d\r\n", atoi(argv[5]));
            vTaskDelay(1);
            bl_lp_hbn_fw_cfg_t hbn_test_cfg = {
                .hbn_sleep_cnt = 32768 * atoi(argv[4]),
                .hbn_level = atoi(argv[5]),
            };
            bl_lp_hbn_enter(&hbn_test_cfg);
        }
    }
}

static void cmd_io_dbg(int argc, char **argv)
{
    if (argc != 2) {
        printf("cmd_io_dbg err\r\n");
        return;
    }

    if (atoi(argv[1]) <= 34) {
        iot2lp_para->debug_io = atoi(argv[1]);
    } else {
        iot2lp_para->debug_io = 0xFF;
    }
}

static void cmd_set_clock_source(int argc, char **argv)
{
    uint8_t source;

    // Check argument count
    if (argc != 2) {
        printf("Usage: set_clock_source <source>\r\n");
        printf("  1: Internal RC oscillator\r\n");
        printf("  2: External passive crystal\r\n");
        printf("  3: External active crystal\r\n");
        return;
    }

    // Parse source value from argument
    source = (uint8_t)atoi(argv[1]);

    // Validate source value
    if (source < 1 || source > 3) {
        printf("Error: Invalid clock source value. Must be 1, 2, or 3.\r\n");
        return;
    }

    // Call the API function to set clock source
    int ret = app_set_clock_source(source);
    if (ret == 0) {
        printf("Clock source updated to %d.\r\n", source);
        // The system should reboot automatically after this
    } else {
        printf("Failed to set clock source, error code: %d\r\n", ret);
    }
}

static void cmd_get_clock_source(int argc, char **argv)
{
    uint8_t source;
    int ret;

    // Call the API function to get current clock source
    ret = app_get_clock_source(&source);

    if (ret == 0) {
        printf("Current clock source: %d (", source);

        // Print descriptive name of the clock source
        switch (source) {
            case 1:
                printf("Internal RC oscillator");
                break;
            case 2:
                printf("External passive crystal");
                break;
            case 3:
                printf("External active crystal");
                break;
            default:
                printf("Unknown");
                break;
        }
        printf(")\r\n");
    } else {
        printf("Failed to get clock source, error code: %d\r\n", ret);
        printf("Using default source: Internal RC oscillator\r\n");
    }
}

SHELL_CMD_EXPORT_ALIAS(cmd_tickless, pm_enter_lp, cmd tickless);
SHELL_CMD_EXPORT_ALIAS(cmd_set_dtim, wifi_lp_set_dtim, cmd_set_dtim);
SHELL_CMD_EXPORT_ALIAS(test_tcp_keepalive, lpfw_tcp_keepalive, tcp keepalive test);
SHELL_CMD_EXPORT_ALIAS(cmd_hbn_test, hbn_test, hbn test);
SHELL_CMD_EXPORT_ALIAS(cmd_io_dbg, io_debug, cmd io_debug);
SHELL_CMD_EXPORT_ALIAS(cmd_set_clock_source, set_clock_source, Set system clock source (1:RC, 2:Passive XTAL, 3:Active XTAL));
SHELL_CMD_EXPORT_ALIAS(cmd_get_clock_source, get_clock_source, Get current system clock source);
#endif

/**********************************************************
    proc_hellow_entry task func
 **********************************************************/
#if 0
static void proc_hellow_entry(void *pvParameters)
{
    vTaskDelay(500);

    while (1) {
        printf("task run.\r\n");
        vTaskDelay(5000);
    }
    vTaskDelete(NULL);
}
#endif

void tcpip_init_done(void *arg)
{
}

int main(void)
{
    uint8_t soc_v, rt_v, aon_v;

    board_init();

    uart0 = bflb_device_get_by_name("uart0");
    shell_init_with_task(uart0);

    tcpip_init(tcpip_init_done, NULL);
    xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 27, NULL);

    hal_pm_ldo11_cfg(PM_PDS_LDO_LEVEL_SOC_DEFAULT, PM_PDS_LDO_LEVEL_RT_DEFAULT, PM_PDS_LDO_LEVEL_AON_DEFAULT);
    hal_pm_ldo11_cfg_get(&soc_v, &rt_v, &aon_v);
    printf("SOC:%d RT:%d AON:%d\r\n", soc_v, rt_v, aon_v);

    HBN_Enable_RTC_Counter();
    pm_rc32k_auto_cal_init();

    bflb_mtd_init();
    easyflash_init();

    pm_sys_init();

#ifdef LP_APP
    bl_lp_init(); //wifi lowpower
    bl_lp_sys_callback_register(lp_enter, NULL, lp_exit, NULL);
#endif
    app_set_clock_source(CLOCK_SOURCE_PASSIVE);

#if 0
    printf("[OS] Starting proc_hellow_entry task...\r\n");
    xTaskCreate(proc_hellow_entry, (char*)"hellow", 512, NULL, 10, NULL);
#endif

    xTaskCreate(app_start_task, (char *)"app_start", 1024, NULL, 15, &app_start_handle);
    
    vTaskStartScheduler();

    while (1) {
    }
}

FAQ

Measured power does not match the README reference data

The reference data was measured with a lowered BLE TX power table: change pwr_table_ble = <13> to pwr_table_ble = <0> in bsp/board/bl616_dk/config/bl_factory_params_IoTKitA_auto.dts and rebuild. Power also depends on the board peripherals, supply, and advertisement payload.

No serial output after pm_enter_lp

After entering low power the system sleeps most of the time, so logs only appear during wake-ups; use wakeup_timer 60000 0 to wake periodically and print the power statistics.

How to test Wi-Fi low power only

Disable BLE at build time: make CHIP=bl616 BOARD=bl616dk CONFIG_BLE_ENABLE=0. Connect to Wi-Fi, set DTIM with wifi_lp_set_dtim 10, then run pm_enter_lp 0 (0 = do not receive broadcast/multicast during low power).

How to test BLE + Wi-Fi dual low power

Connect to Wi-Fi first, then start connectable advertising with ble_start_adv 0 0 0x0320 0x0320, and finally enter dual-mode low power with tickless 10 (keeps Wi-Fi alive while maintaining BLE advertising/connection).

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