Skip to content

Concepts First

  • HTTPS: HTTP + TLS encryption, default port 443. Data is encrypted and the server's identity is verified with digital certificates — the standard for modern websites and cloud services.
  • CA certificate: the "root certificate" that signs server certificates. With the CA embedded, the client can verify whether a server certificate is trustworthy.
  • Certificate verification: the example passes ca_pem = NULL by default, using the firmware's certificate bundle (CONFIG_X509_CERTIFICATE_BUNDLE) to verify public certificates; with CONFIG_TLS_MUTUAL_AUTH it also does mutual (two-way) authentication using the embedded test certificates.
  • vs HTTP: the request code is nearly identical — only the scheme changes from http:// to https://, adding one TLS handshake (more latency).

Example Overview

This page covers the wifi_https example in the official Bouffalo SDK (examples/wifi/sta/wifi_https):

  • wifi_https_test <url> creates an https_client task;
  • first issues GET: req.method = HTTP_GET, req.buffer_size = 4096, 8-second timeout;
  • waits 10 seconds, then issues POST (chunked, same as wifi_http);
  • both responses are printed via response_cb;
  • the file embeds test CA/client certificates (ca_cert / own_cert / private_cert) for mutual-auth scenarios.
  • Sibling example: the plaintext variant is HTTP GET/POST; replacing https:// with http:// in the URL uses the wifi_http example.

Operation Steps

1
Enter the Example Directory

Open a terminal and enter the SDK HTTPS example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/wifi/sta/wifi_https
2
Build the Project

Both Ai-M61 and Ai-M62 use bl616:

make CHIP=bl616 BOARD=bl616dk
3
Flash the Firmware

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

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Connect Wi-Fi and Issue an HTTPS Request

Serial tool at 2000000 baud. Wait for bouffalolab />, connect to the router, then run the HTTPS request (the example does GET first, then POST):

wifi_sta_connect Your_SSID 12345678
wifi_https_test https://www.gov.cn
5
Run and Verify

With an 8-second timeout, the example prints Http client GET request server success and Http client POST request server success after success, and prints the server’s response to the serial port.

Code Execution Flow

The complete HTTPS GET/POST flow from boot to response:

APIs Used by the Example

https_client_request(&req, timeout, user_data)

Synchronously issues one HTTPS request. req.ca_pem = NULL uses the firmware certificate bundle; req.buffer_size sets the receive buffer (4096 in the example).

Parameters:

  • &req: struct https_client_request
  • timeout: timeout in ms (8000 in the example)

Return: >= 0 on success; < 0 on failure

req.ca_pem / req.ca_len()

Custom CA certificate. With NULL/0, the firmware certificate bundle (CONFIG_X509_CERTIFICATE_BUNDLE) is used — suitable for public HTTPS sites.

Parameters:

  • ca_pem: PEM CA certificate
  • ca_len: certificate length

Return: none (struct fields)

req.client_cert_pem / req.client_key_pem()

Client certificate and private key for mutual TLS (mTLS), effective only with CONFIG_TLS_MUTUAL_AUTH; the example embeds test certificates to demonstrate this.

Parameters:

  • client_cert_pem / client_cert_len: client certificate
  • client_key_pem / client_key_len: client private key

Return: none (struct fields)

response_cb(rsp, final_data, user_data)

Response callback, prints the server response chunk by chunk.

Parameters:

  • rsp: struct http_response
  • final_data: HTTP_DATA_MORE / HTTP_DATA_FINAL

Return: none

Complete Code

The full wifi_https_client.c source, identical to the official example (examples/wifi/sta/wifi_https). Collapsed by default; click to expand:

📜 Click to expand wifi_https/main.c full code
c
/****************************************************************************
 *
 * 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 "timers.h"

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

#include "wifi_mgmr_ext.h"

#include "bflb_irq.h"
#include "bflb_uart.h"

#include "rfparam_adapter.h"

#include "board.h"
#include "shell.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"

struct bflb_device_s *gpio;

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

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

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

volatile uint32_t wifi_state = 0;
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;
        case CODE_WIFI_ON_GOT_IP: {
            wifi_state = 1;
            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: {
            wifi_state = 0;
            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();

    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");

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

    vTaskStartScheduler();

    while (1) {
    }
}
📜 Click to expand wifi_https_client.c full code
c

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <lwip/api.h>
#include <lwip/arch.h>
#include <lwip/opt.h>
#include <lwip/inet.h>
#include <lwip/errno.h>
#include <netdb.h>

#include "shell.h"
#include "utils_getopt.h"
#include "bflb_mtimer.h"
#include "https_client.h"

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif 

const char ca_cert[] =
    "-----BEGIN CERTIFICATE-----\n"
    "MIIDAzCCAeugAwIBAgIUSIynf9c+C4TGvBNrpOGW0EsWKRUwDQYJKoZIhvcNAQEL\n"
    "BQAwETEPMA0GA1UEAwwGTXlDQV8xMB4XDTI0MDUyMzAxNTIzOFoXDTI1MDUyMzAx\n"
    "NTIzOFowETEPMA0GA1UEAwwGTXlDQV8xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"
    "MIIBCgKCAQEAj7Xp94L7VLyo6m4AdvKIa1pOjZQUzPnfgkAJhdHmupIMhdx8/L/i\n"
    "Yn55tZhPOzk7S+CYuYoH1Bx/tvmP+HJ9J8qeNOPm+QbiYNUSJcs2vXkKMFgiSqh4\n"
    "zE6B80+clSSR0de80s7td0PTL9k/emQSu+mV1q2JrXyhcP6/fumt1TzrmTb2SxiB\n"
    "rMQh76v8NfV0a0Y78QDd+S0JIhJ27S610DCSnl+B1pgI8sMuwtSSNOyNYFbym2PO\n"
    "LYUYaN9Lup7i2pxPi/p/tIwBq3mbs01JawBMFArTkidxIK+xkf4NVUT1weurw+s8\n"
    "3yC+pomUtvKpCNxdgSslnCxNhoUYxxCrawIDAQABo1MwUTAdBgNVHQ4EFgQUz1kI\n"
    "khTZxHjVYlOk16H7v57pJCowHwYDVR0jBBgwFoAUz1kIkhTZxHjVYlOk16H7v57p\n"
    "JCowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEASyul5PoQ8V0M\n"
    "+lKOCxeL8qbYTTLJzcIGcN7FQzTWHYqfmdlc+9IZ7mHk1RR+fxNll3EpHaqA/U9J\n"
    "bRUiBIzsMRWaCl0g3NCcZOILQ/hfgh6TgFyo5bOUKNOjBKGrV4/mnOOyPvPc5n/H\n"
    "BW3LjqmfE2CFeUruXutOPIkY8YDiPRFwCSaNapO4Bc6+kgYQz7dPEWOF0b2IcJ7O\n"
    "vD2JPeKSoUxPs71TUNzGBQx+mbXxULrhFkSs9jRdPUJQpJqaq+SVLLXOU+RsQab0\n"
    "dqud+4N3VgjA1tI/Oi23sF4xMotaWH4VrWIxuu0O8I3GYx+ZhFyhRa33ma7JxZtu\n"
    "VlA0gHsH5Q==\n"
    "-----END CERTIFICATE-----\n";

const char own_cert[] =
    "-----BEGIN CERTIFICATE-----\n"
    "MIIC9DCCAdygAwIBAgIUZp6RfB5tG+5MCi/Wtjk2eAxtk3QwDQYJKoZIhvcNAQEL\n"
    "BQAwETEPMA0GA1UEAwwGTXlDQV8xMB4XDTI0MDUyMzAxNTIzOFoXDTI1MDUyMzAx\n"
    "NTIzOFowEzERMA8GA1UEAwwIY2xpZW50XzEwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n"
    "DwAwggEKAoIBAQCo2b69q96nG1eO7/PZCYRg78mk1Z9KbWLtQftZDJJIuIv/Xa/P\n"
    "suSUajK8bJJ5CUlP05cowcccBqZ41mlP4nC0iGqesnJd5rDo/QIo09Oms67KkW9y\n"
    "wB8vGgo5nQjeT/q05iX4di2Bo3Npj5brKocbkH4rTWidKh1LoDETW709IJICWTkU\n"
    "4Sj5IQnAuY8a9MREBkjo03KctDWG2qY9JFOdb5E6K+UmbXL7t68ItOdAlK80HUvn\n"
    "+AyhXjabfeP9JD3/UABm37Lmm3hyfbGMCJgCkVt/TXTZun+30fHzfE5Ok9+iKdki\n"
    "DZMw9DfSp+7jh4lyb01S5VN+T/5pOpEUjfi3AgMBAAGjQjBAMB0GA1UdDgQWBBSE\n"
    "t5Zai9tEI2DNfbocINJowkpNAjAfBgNVHSMEGDAWgBTPWQiSFNnEeNViU6TXofu/\n"
    "nukkKjANBgkqhkiG9w0BAQsFAAOCAQEAcIzEp40BLHawKNzEQ18iO4MWcSVmCvvG\n"
    "CqPrmuHSQ5TUkKUwlu4Kf1SqRlhy2W5dt04Ws4z7olHu6pIzKKGSvHUVRPM85+CH\n"
    "ZzfT2zA7mD4UgX8LO9g2HhhBzNKZQFzZgt+FPy1XxMRD+zunnDDXrmTNW2whBZsP\n"
    "AKIro78SpJbwUpQW/Go6a2YYMYY6QGIXcoNFyhkL2Wk6SIMqmvrHXQwF/LqQZupQ\n"
    "CYVdac/j7bQPKweIclsq7b9O1dixurCCSjJ6V7CBTZbUM53jBs2BZA60lRmwVHFl\n"
    "oGtU9KCUhH7IJXQYAm1sqxnfICaVJdxb0a5ahCj5LMsZQXFvabOWNg==\n"
    "-----END CERTIFICATE-----\n";

const char private_cert[] =
    "-----BEGIN PRIVATE KEY-----\n"
    "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCo2b69q96nG1eO\n"
    "7/PZCYRg78mk1Z9KbWLtQftZDJJIuIv/Xa/PsuSUajK8bJJ5CUlP05cowcccBqZ4\n"
    "1mlP4nC0iGqesnJd5rDo/QIo09Oms67KkW9ywB8vGgo5nQjeT/q05iX4di2Bo3Np\n"
    "j5brKocbkH4rTWidKh1LoDETW709IJICWTkU4Sj5IQnAuY8a9MREBkjo03KctDWG\n"
    "2qY9JFOdb5E6K+UmbXL7t68ItOdAlK80HUvn+AyhXjabfeP9JD3/UABm37Lmm3hy\n"
    "fbGMCJgCkVt/TXTZun+30fHzfE5Ok9+iKdkiDZMw9DfSp+7jh4lyb01S5VN+T/5p\n"
    "OpEUjfi3AgMBAAECggEAQcs/IOdUxibIUoE7somb1K37E8jN3hGLp8m7KDLW2ZFX\n"
    "s/UMqdEacp6DEJr55EHzGDDpyM7hSBFrUtCxjpg8tIwnh5kfKWnX66GS8te/tEh1\n"
    "xqcvFza3sAHklBiVuRLPLSg1CYD7MHXBZhO2igIzTVFbaIIsTnl3mt1b4iAwOB3S\n"
    "3rE5yRDvYXo9SM8aoL7iDaQoWd4z8hLiQmxMBDwI9sjp0BFUTLBjWu/qcikcWwOx\n"
    "Z9MtYwwl6Dldvf3tJ9cTY4/6wQKMMsLqS3Jov2k8xV0DvGU0Xkq6N96L++iaE3Iz\n"
    "bz+AbWz1J3UAHJ5T844TJtn2KxNWdqIgUryVkbqlKQKBgQDoYsBUYZnth735AKnl\n"
    "kLaP0jBKbVMaMa3ihA1qNUcCKkKfxP1xenrbS94UWRJzLq/txehb4YsptJ1AJ624\n"
    "HeMUm85A9Ka4vFxfBmMOYyxMx9OMFQbxxijv2oRP+3irupBAAEVuAbpaI4YWL4Wn\n"
    "mfeFzLFggt4bTGiUO7jvQOT4CwKBgQC6AjLtep8iUSQz/xo5ULnw/lHMf8lOin5t\n"
    "LqoA3qwx/ootOZp5WG9rVaKimELxdQwO4DtH9ZGJfbKeJDh6AbePjcHShfK2UzI0\n"
    "/xvZLzI+Wgz2ybX9HJ6y/rUy83eXMI5/G5zJEXRjgLZdDBkKP5FUllzn9dYQKq4n\n"
    "froofr0xhQKBgGUEsIi92NUJwgphQHm7u7CvdTCVb5+eYvFp6A74VALb7hRX5LiU\n"
    "vp1f0I1V3xBVBkM9WfG/DOi3S/hch0bXLySBSlqwP11k/F6Ofzb844AJCawGY/tr\n"
    "4dXKTuQZ8+3q7tPzijr041ZVxO8V/2rJInF2dtD2cqi5QWjWwWC3/BDbAoGBALhw\n"
    "Zb9Ez3Zfrt5AHeinOzjTdJiqPRo8CZCJrNif826/TpP5gkrAffqYN2OyRu7VeNdn\n"
    "UGDSBzWmBe75S2bvPOyDS6VMXJadeDNEooCAY/mfoSs0Z9hLALHMDjAvam3oH+O5\n"
    "pYVaYWYowqsQy+N1lM2jKwx2n4P4TieraTuuMDthAoGBALNnxVZyGsUj4pqTIbnh\n"
    "9CbfDkf2os2KwuLQTtkmMTRQe88mDHaVlY7dFBeuAhF9gf3Z/TapiesBlgPxehiu\n"
    "1aNylDK6ScLXxzq/ZEIX7Ou4gP3hBAHrtHbRYS8klYkV7tNCW0fmiz1M3C/8HP0V\n"
    "FHFBlzbNHqryPsFm7VVC4TyY\n"
    "-----END PRIVATE KEY-----\n";

static int payload_cb(int sock, struct http_request *req, void *user_data)
{
    const char *content[] = {
        "foobar",
        "chunked",
        "last"
    };
    char tmp[64];
    int i, pos = 0;

    for (i = 0; i < ARRAY_SIZE(content); i++) {
        pos += snprintf(tmp + pos, sizeof(tmp) - pos,
                "%x\r\n%s\r\n",
                (unsigned int)strlen(content[i]),
                content[i]);
    }

    pos += snprintf(tmp + pos, sizeof(tmp) - pos, "0\r\n\r\n");

    (void)zsock_send(sock, tmp, pos, 0);

    return pos;
}

static int log_output(void *ptr, size_t size)
{
    size_t i;
    for (i = 0; i < size; i++) {
        putchar(((char *)ptr)[i]);
    }
    return i;
}

static void response_cb(struct http_response *rsp,
            enum http_final_call final_data,
            void *user_data)
{
    log_output(rsp->recv_buf, rsp->data_len);
    if (final_data == HTTP_DATA_MORE) {
        //printf("Partial data received (%zd bytes)\r\n", rsp->data_len);
    } else if (final_data == HTTP_DATA_FINAL) {
        //printf("All the data received (%zd bytes)\r\n", rsp->data_len);
    }
}

#define PING_USAGE                                \
    "wifi_https_test [url]\r\n"        \
    "\t url: url or dest server ip\r\n" \

static void https_client_task_entry(void *arg)
{
    int ret;
    char *url;
    struct https_client_request req;
    const char *headers[] = {
        "Transfer-Encoding: chunked\r\n",
        NULL
    };

    url = (char *)arg;

    memset(&req, 0, sizeof(req));
    req.method           = HTTP_GET;
    req.url              = url;
    req.protocol         = "HTTP/1.1";
    req.response         = response_cb;
    req.ca_pem           = NULL;  /* Use cert_bundle from firmware if CONFIG_X509_CERTIFICATE_BUNDLE is enabled */
    req.ca_len           = 0;
#ifdef CONFIG_TLS_MUTUAL_AUTH
    req.client_cert_pem  = own_cert;
    req.client_cert_len  = sizeof(own_cert);
    req.client_key_pem   = private_cert;
    req.client_key_len   = sizeof(private_cert);
#endif
    req.buffer_size      = 4096;

    ret = https_client_request(&req, 8*1000, "IPv4 GET");
    if (ret < 0) {
        printf("Http client get request fail ret:%d\r\n", ret);
    } else {
        printf("Http client GET request server success\r\n");
    }

    /* Add delay to allow WiFi layer to recover */
    /* some web server don't allow quickly access */
    vTaskDelay(pdMS_TO_TICKS(10000));

    memset(&req, 0, sizeof(req));

    req.method           = HTTP_POST;
    req.url              = url;
    req.protocol         = "HTTP/1.1";
    req.payload_cb       = payload_cb;
    req.header_fields    = headers;
    req.response         = response_cb;
    req.ca_pem           = NULL;  /* Use cert_bundle from firmware if CONFIG_X509_CERTIFICATE_BUNDLE is enabled */
    req.ca_len           = 0;
#ifdef CONFIG_TLS_MUTUAL_AUTH
    req.client_cert_pem  = own_cert;
    req.client_cert_len  = sizeof(own_cert);
    req.client_key_pem   = private_cert;
    req.client_key_len   = sizeof(private_cert);
#endif
    req.buffer_size      = 4096;

    ret = https_client_request(&req, 8*1000, "IPv4 POST");
    if (ret < 0) {
        printf("Http client post request fail ret:%d\r\n", ret);
    } else {
        printf("Http client POST request server success\r\n");
    }

    free(url);
    vTaskDelete(NULL);
}

#ifdef CONFIG_SHELL
#include <shell.h>

int cmd_wifi_https_client(int argc, char **argv)
{
    char *url;

    if (argc < 2) {
        printf("%s", PING_USAGE);
        return -1;
    }
    url = strdup(argv[1]);

    xTaskCreate(https_client_task_entry, "https_client", 2048, (void *)url, 10, NULL);

    return 0;
}

SHELL_CMD_EXPORT_ALIAS(cmd_wifi_https_client, wifi_https_test, wifi https client test);
#endif

FAQ

HTTPS request fails / certificate verification fails

Confirm the URL starts with https:// and the server certificate chain is complete; public sites require the firmware certificate bundle (CONFIG_X509_CERTIFICATE_BUNDLE) to contain the matching root CA; for self-signed servers, put the CA into req.ca_pem.

TLS handshake is slow / occasionally fails

HTTPS adds one TLS handshake (hundreds of ms to seconds) over HTTP; the example deliberately waits 10 seconds between GET and POST. On poor networks, increase the timeout argument of https_client_request.

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