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 = NULLby default, using the firmware's certificate bundle (CONFIG_X509_CERTIFICATE_BUNDLE) to verify public certificates; withCONFIG_TLS_MUTUAL_AUTHit 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://tohttps://, 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 anhttps_clienttask;- 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://withhttp://in the URL uses thewifi_httpexample.
Operation Steps
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_httpsBoth Ai-M61 and Ai-M62 use bl616:
make CHIP=bl616 BOARD=bl616dkHold BOOT, briefly press EN/RST to enter download mode, then flash:
make flash CHIP=bl616 COMX=/dev/ttyUSB0Serial 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.cnWith 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_requesttimeout: 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 certificateca_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 certificateclient_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_responsefinal_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
/****************************************************************************
*
* 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
#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);
#endifFAQ
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

