Contributed by WT_0213, curated by Ai-Thinker
Currently, the device automatically turns on AP mode after power-up. After connecting to the wireless AP, open the web page, submit the WiFi name and WiFi password you want to connect to, and click Submit. It responds to the WiFi name and password submitted via POST.
The following code has comments at key points to make it easier to understand.
My posting points have recovered, and I'm full of motivation again. 😄
Demo
First power on the device. After power-up, you can see the following hotspot on your computer or phone:

Click to connect.

Then it will ask you to enter the password.

The password here is: 1234567879
Then enter the following in the browser:
192.168.169.1
You can see the following interface; I simply wrote a configuration page:

Enter the WiFi name and password to connect to, then click Submit.

On success, it returns the submitted WiFi name and password;

Implementation Approach and Ideas
Turning on AP Mode
static void start_ap(void)
{
wifi_mgmr_ap_params_t config = { 0 };
config.channel = 3;
config.key = USER_AP_PASSWORD;
config.ssid = USER_AP_NAME;
config.use_dhcpd = 1;
if (wifi_mgmr_conf_max_sta(2) != 0) {
return 5;
}
if (wifi_mgmr_ap_start(&config) == 0) {
return 0;
}
}Starting the HTTP Server
void mhttp_server_init()
{
//常用变量
int ss, sc;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int snd_size = 0; /* 发送缓冲区大小 */
socklen_t optlen; /* 选项值长度 */
// int optlen;
int err;
socklen_t addrlen;
// int addrlen;
//建立套接字
ss = socket(AF_INET, SOCK_STREAM, 0);
if (ss < 0)
{
printf("socket error\n");
}
/*设置服务器地址*/
bzero(&server_addr, sizeof(server_addr));
/*清零*/
server_addr.sin_family = AF_INET;
/*协议族*/
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*本地地址*/
server_addr.sin_port = htons(80);
/*服务器端口*/
/*绑定地址结构到套接字描述符*/
err = bind(ss, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err < 0)
{
printf("bind error\n");
return -1;
}
/*设置侦听*/
err = listen(ss, 7);
if (err < 0)
{
printf("listen error\n");
return -1;
}
addrlen = sizeof(struct sockaddr_in);
MYPARM parm11;
while (1)
{
printf("accept start\r\n");
sc = accept(ss, (struct sockaddr *)&client_addr, &addrlen);
if ((sc < 0) || (mysemaphoreflag > 0))
{
printf("accept fail sc is:%d semaphore is:%d\r\n", sc, mysemaphoreflag);
if (sc > 0)
{
close(sc);
}
continue;
}
parm11.sc = sc;
parm11.buf = NULL;
mysemaphoreflag++;
http_server_thread(&parm11);
vTaskDelay(1);
}
}Just wait for a client to connect. To make the device startup state more intuitive, an LED-on-at-boot operation was added: the green LED turns on automatically after startup.
The code is as follows:
gpio = bflb_device_get_by_name("gpio");
bflb_gpio_init(gpio, GPIO_PIN_14, GPIO_OUTPUT| GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
bflb_gpio_set(gpio, GPIO_PIN_14);Main Code Explanation
main.c
int main(void)
{
//中开启时钟
board_init();
// 亮绿灯
gpio = bflb_device_get_by_name("gpio");
bflb_gpio_init(gpio, GPIO_PIN_14, GPIO_OUTPUT| GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
bflb_gpio_set(gpio, GPIO_PIN_14);
//设置中断分组
bflb_irq_set_nlbits(4);
//设置中断优先级
bflb_irq_set_priority(37, 3, 0);
bflb_irq_set_priority(WIFI_IRQn, 1, 0);
// 拿到 gpio
gpio = bflb_device_get_by_name("gpio");
// 拿到 uart
uart0 = bflb_device_get_by_name("uart0");
shell_init_with_task(uart0);
// 初始化tcp ip
tcpip_init(NULL, NULL);
wifi_start_firmware_task();
// 创建http服务
create_http_server_task();
vTaskStartScheduler();
while (1) {
}
}Creating the HTTP Service
void create_http_server_task(void)
{
MuxSem_Handle = xSemaphoreCreateMutex();
if (NULL != MuxSem_Handle)
{
printf("MuxSem_Handle creat success!\r\n");
}
xTaskCreate(http_server_task, (char*)"fw", WIFI_HTTP_SERVER_STACK_SIZE, NULL, HTTP_SERVERTASK_PRIORITY, &http_server_task_hd);
}This uses xTaskCreate, the FreeRTOS task API.
If you're not familiar with it, see the FreeRTOS tasks article in the Ai-M61-32S AP provisioning series: https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=43670
Next is http_server_task
void http_server_task(void* param)
{
// 打开AP
start_ap();
// 启动http服务,也就是响应网页的服务
mhttp_server_init();
}This part is simple.
The AP Code
static void start_ap(void)
{
wifi_mgmr_ap_params_t config = { 0 };
// 通道
config.channel = 3;
// 设置AP热点名称
config.ssid = USER_AP_NAME;
// 设置密码
config.key = USER_AP_PASSWORD;
// 启动dhcp
config.use_dhcpd = 1;
// 设置最大连接数
if (wifi_mgmr_conf_max_sta(2) != 0) {
return 5;
}
// 启动AP模式
if (wifi_mgmr_ap_start(&config) == 0) {
return 0;
}
}WiFi Settings
#define USER_AP_NAME "Ai-M61-32s"
#define USER_AP_PASSWORD "123456789"Then the HTTP service starts:
mlwip_https.c
void mhttp_server_init()
{
//常用变量
int ss, sc;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int snd_size = 0; /* 发送缓冲区大小 */
socklen_t optlen; /* 选项值长度 */
// int optlen;
int err;
socklen_t addrlen;
// int addrlen;
//建立套接字
ss = socket(AF_INET, SOCK_STREAM, 0);
if (ss < 0)
{
printf("socket error\n");
}
/*设置服务器地址*/
bzero(&server_addr, sizeof(server_addr));
/*清零*/
server_addr.sin_family = AF_INET;
/*协议族*/
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*本地地址*/
server_addr.sin_port = htons(80);
/*服务器端口*/
/*绑定地址结构到套接字描述符*/
err = bind(ss, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err < 0)
{
printf("bind error\n");
return -1;
}
/*设置侦听*/
err = listen(ss, 7);
if (err < 0)
{
printf("listen error\n");
return -1;
}
addrlen = sizeof(struct sockaddr_in);
MYPARM parm11;
while (1)
{
printf("accept start\r\n");
sc = accept(ss, (struct sockaddr *)&client_addr, &addrlen);
if ((sc < 0) || (mysemaphoreflag > 0))
{
printf("accept fail sc is:%d semaphore is:%d\r\n", sc, mysemaphoreflag);
if (sc > 0)
{
close(sc);
}
continue;
}
parm11.sc = sc;
parm11.buf = NULL;
mysemaphoreflag++;
http_server_thread(&parm11);
vTaskDelay(1);
}
}Next, Responding to Requests
📜 Click to expand the full http_server_thread code
void http_server_thread(void *msg)
{
// printf("http_server_thread\r\n");
MYPARM *parm11;
parm11 = (MYPARM *)msg;
int sc;
char readbuffer[1024];
int size = 0;
char command[1024];
char head_buf[1000];
memset(command, 0, sizeof(command));
memset(head_buf, 0, sizeof(head_buf));
sc = parm11->sc;
memset(readbuffer, 0, sizeof(readbuffer));
while (1)
{
// printf("read stop\r\n");
size = read(sc, readbuffer, 1024);
// int rc = recv(sc, readbuffer, sizeof(readbuffer), 0);
printf("read len:%d\r\n", size);
printf("get:%s\r\n", readbuffer);
if (size <= 0)
{
printf("size <= 0\r\n");
break;
}
int len = get_http_command(readbuffer, command); //得到http 请求中 GET后面的字符串
printf("get:%s len:%d\r\n", command, len);
if (strcmp(command, "/") == 0)
{
printf("command1\r\n");
streatask = 0;
sprintf(head_buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html;charset=UTF-8\r\n\r\n", sizeof(html_page));
// head_buf
// strlen(index_ov2640_html)
// 返回html页面
int ret = write(sc, head_buf, strlen(head_buf));
if (ret == -1)
{
printf("send failed");
close(sc);
mysemaphoreflag--;
return NULL;
}
ret = write(sc, html_page, sizeof(html_page));
if (ret < 0)
{
printf("text write failed");
}
close(sc);
mysemaphoreflag--;
break;
}
else if (strstr(command, "set"))
{
printf("set\r\n");
streatask = 0;
// 获取POST提交过来的数据,拿到ssid部分
char* wifiCfg = strstr(readbuffer, "ssid");
printf("wifiCfg: %s \r\n", wifiCfg);
sprintf(head_buf, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 433\r\nAccess-Control-Allow-Origin: *\r\n\r\n");
int ret = write(sc, head_buf, strlen(head_buf));
if (ret == -1)
{
printf("send failed");
close(sc);
mysemaphoreflag--;
return NULL;
}
// 将post参数切开,分别拿到 ssid 和 pwd
char* ssid = strtok(wifiCfg, "&");
char* pwd = strtok(NULL, "&");
// 将ssid参数切开,分别拿到 key 和 value
char* ssidKey = strtok(ssid, "=");
char* ssidValue = strtok(NULL, "=");
// 将pwd参数切开,分别拿到 key 和 value
char* pwdKey = strtok(pwd, "=");
char* pwdValue = strtok(NULL, "=");
// ============================================
// 待实现 将 wifi 信息写入 存储
// 开启 sta 模式,连接WIFI
// ============================================
printf("OK ssid:%s, pwd:%s \r\n", ssidValue, pwdValue);
// 创建cJSON
cJSON *json = cJSON_CreateObject();
cJSON_AddStringToObject(json, "ssid", ssidValue);
cJSON_AddStringToObject(json, "pwd", pwdValue);
char data_buf[1024];
sprintf(data_buf, "%s\r\n\r\n", cJSON_Print(json));
printf("OK data_buf:%s \r\n", data_buf);
// static char data_buf[] = "{\"ssid\":1,\"pwd\":0}";
// ret = write(sc, data_buf, 433);
// 将获取到的数据通过json形式返回
ret = write(sc, data_buf, sizeof(data_buf));
if (ret < 0)
{
printf("text write failed");
}
close(sc);
mysemaphoreflag--;
break;
}
else
{
streatask = 0;
close(sc);
mysemaphoreflag--;
}
// write(sc,readbuffer,size);
}
//关中断
// free(parm11->buf);
// vTaskDelete(NULL);
//开中断
}HTML Code - page.h is a very simple interface; there is plenty of room for optimization.
static const unsigned char html_page[] = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WiFi Configuration</title>
<style>
body {
font-family: Arial, Helvetica, sans-serif;
background: #f5f7fa;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
}
.card {
background: #fff;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 32px 40px;
width: 320px;
text-align: center;
}
h1 {
font-size: 20px;
color: #333;
margin: 0 0 8px 0;
}
p.tip {
font-size: 13px;
color: #888;
margin: 0 0 24px 0;
}
input[type="text"] {
width: 100%;
box-sizing: border-box;
padding: 10px 12px;
margin-bottom: 14px;
border: 1px solid #d9d9d9;
border-radius: 6px;
font-size: 14px;
outline: none;
}
input[type="text"]:focus {
border-color: #1677ff;
}
input[type="submit"] {
width: 100%;
padding: 10px 0;
border: none;
border-radius: 6px;
background: #1677ff;
color: #fff;
font-size: 15px;
cursor: pointer;
}
input[type="submit"]:hover {
background: #4096ff;
}
</style>
</head>
<body>
<div class="card">
<h1>WiFi Configuration</h1>
<p class="tip">Enter your router name and password, then click Submit. The device will connect automatically.</p>
<form method="post" action="/set">
<input name="ssid" type="text" placeholder="WiFi Name (SSID)">
<input name="pwd" type="text" placeholder="WiFi Password">
<input type="submit" value="Submit">
</form>
</div>
</body>
</html>
)";At this point, the prerequisites for AP provisioning are basically implemented.
The next step is to save the WiFi information to storage.
WiFi Reference Documentation
WiFi API Guide - Ai-Thinker Technology documentation (wb2-api-web.readthedocs.io)
Wi-Fi Manager - BL IoT SDK release_bl_iot_sdk_1.6.39-238-gf5ba0a7ee docs (bouffalolab.github.io)
Continuing from the previous post, save the WiFi name and password into storage.
Use easyflash to save the WiFi name and password.
Create the storage-related code.
storage/storage.h
#ifndef __CUSTOM_H_
#define __CUSTOM_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define SSID_KEY "SSID"
#define PASS_KEY "PASS"
#ifndef USER_FLASH_H
#define USER_FLASH_H
void flash_init(void);
void flash_erase_set(char* key, char* value);
char* flash_get_data(char* key, int len);
#endif
#ifdef __cplusplus
}
#endif
#endif /* EVENT_CB_H_ */storage/storage.c
/**
* @file user_flash.c
* @author your name ([email]you@domain.com[/email])
* @brief
* @version 0.1
* @date 2023-08-07
*
* @copyright Copyright (c) 2023
*
*/
#include "stdio.h"
#include "string.h"
#include "storage.h"
#include "easyflash.h"
#include "log.h"
void flash_init(void)
{
//init easyflash
bflb_mtd_init();
easyflash_init();
}
/**
* @brief
*
* @param key
* @param value
*/
void flash_erase_set(char* key, char* value)
{
size_t len = 0;
int value_len = strlen(value);
ef_set_and_save_env(key, value);
// bflb_flash_read(key, flash_data, strlen(value));
// printf("writer data:%s\r\n", flash_data);
memset(value, 0, strlen(value));
ef_get_env_blob(key, value, value_len, &len);
LOG_W("flash_erase_set %s: %s", key, value);
}
/**
* @brief
*
* @param key
* @return char*
*/
char* flash_get_data(char* key, int len)
{
static char* flash_data = NULL;
flash_data = pvPortMalloc(len);
memset(flash_data, 0, len);
ef_get_env_blob(key, flash_data, len, (size_t)&len);
LOG_W("flash_get_data %s: %s, len:%d\r\n", key, flash_data, strlen(flash_data));
return flash_data;
}Modify web/mlwip_https.c to add the following:
#include "storage.h"
#include "wifi_event.h"
#include "log.h"
....
// 将 wifi 信息写入 存储
flash_erase_set(SSID_KEY, ssidValue);
flash_erase_set(PASS_KEY, pwdValue);
// 开启 sta 模式,连接WIFI
// 重启设备
LOG_W("system 2s reset ");
vTaskDelay(2000/portTICK_PERIOD_MS);
GLB_SW_System_Reset();Modify proj.conf to add the following:
## easy flash
set(CONFIG_PARTITION 1)
set(CONFIG_BFLB_MTD 1)
set(CONFIG_EASYFLASH4 1)Modify CMakeLists.txt to add the following:
...
sdk_add_include_directories(storage)
...
target_sources(app PRIVATE
storage/storage.c
web/cJSON.c
web/mlwip_https.c
)Some comments were added to the flash_prog_cfg.ini configuration file.
#***************************************************#
## Firmware burning configuration #
## Firmware burning configuration #
#***************************************************#
[cfg]
## 0: no erase, 1: programmed section erase, 2: chip erase
## 0: no erase, 1:programmed section erase, 2: chip erase
erase = 1
## skip mode set first para is skip addr, second para is skip len, multi-segment region with ; separated
skip_mode = 0x0, 0x0
## Reset download function enable
## 0: not use isp mode, #1: isp mode
boot2_isp_mode = 1
## Configure boot2 firmware, otherwise the reset burn function cannot be used
filedir = ./build/build_out/boot2_*.bin
address = 0x000000
## Configure partition firmware, this is necessary
[partition]
filedir = ./build/build_out/partition*.bin
address = 0xE000
## Configure the application firmware address. When creating a new project, you need to change "Project_basic" to the new project name, otherwise flashing may fail
#(To configure the application firmware address, when creating a new project, it is necessary to modify "Project_Basic" to the name of the new project, otherwise it may cause burning failure)
[FW]
filedir = ./build/build_out/smart_config_$(CHIPNAME).bin
address = @partition
address = 0x10000
## [mfg]
## filedir = ./build/build_out/mfg*.binFor easyflash4 related issues, you can refer to:
[FAQ] easyflash4 usage problems and solutions https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=43763
[Reference] AP Web Provisioning on the Ai-M61-32S (Step 2) https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=43766
Next, read the WiFi information and connect to WiFi.
Create the WiFi-related code.
wifi/wifi_event.h
/**
* @file wifi_event.h
* @author your name ([email]you@domain.com[/email])
* @brief
* @version 0.1
* @date 2023-06-29
*
* @copyright Copyright (c) 2023
*
*/
#ifndef WIFI_EVENT_H
#define WIFI_EVENT_H
#include "stdint.h"
int wifi_start_firmware_task(void);
void wifi_event_handler(uint32_t code);
uint8_t wifi_connect(char* ssid, char* passwd);
#endifwifi/wifi_event.c
📜 Click to expand the full wifi_event.c code
/**
* @file wifi_event.c
* @author your name ([email]you@domain.com[/email])
* @brief
* @version 0.1
* @date 2023-06-29
*
* @copyright Copyright (c) 2023
*
*/
#include "FreeRTOS.h"
#include "task.h"
#include "timers.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 "bflb_irq.h"
#include "bflb_uart.h"
#include "bflb_l1c.h"
#include "bflb_mtimer.h"
#include "bl616_glb.h"
#include "rfparam_adapter.h"
#include "board.h"
#include "log.h"
#include "storage.h"
#define DBG_TAG "WIFI EVENT"
#define WIFI_STACK_SIZE (1024*4)
#define TASK_PRIORITY_FW (16)
static wifi_conf_t conf =
{
.country_code = "CN",
};
static TaskHandle_t wifi_fw_task;
static uint32_t sta_ConnectStatus = 0;
xQueueHandle queue;
/**
* @brief WiFi task
*
* @return int
*/
int wifi_start_firmware_task(void)
{
LOG_I("Starting wifi ...");
/* enable wifi clock */
GLB_PER_Clock_UnGate(GLB_AHB_CLOCK_IP_WIFI_PHY | GLB_AHB_CLOCK_IP_WIFI_MAC_PHY | GLB_AHB_CLOCK_IP_WIFI_PLATFORM);
GLB_AHB_MCU_Software_Reset(GLB_AHB_MCU_SW_WIFI);
/* set ble controller EM Size */
GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);
if (0 != rfparam_init(0, NULL, 0)) {
LOG_I("PHY RF init failed!");
return 0;
}
LOG_I("PHY RF init success!");
/* Enable wifi irq */
extern void interrupt0_handler(void);
bflb_irq_attach(WIFI_IRQn, (irq_callback)interrupt0_handler, NULL);
bflb_irq_enable(WIFI_IRQn);
xTaskCreate(wifi_main, (char*)"fw", WIFI_STACK_SIZE, NULL, TASK_PRIORITY_FW, &wifi_fw_task);
return 0;
}
/**
* @brief wifi event handler
* WiFi event callback
*
* @param code
*/
void wifi_event_handler(uint32_t code)
{
sta_ConnectStatus = code;
BaseType_t xHigherPriorityTaskWoken;
switch (code) {
case CODE_WIFI_ON_INIT_DONE:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE", __func__);
wifi_mgmr_init(&conf);
}
break;
case CODE_WIFI_ON_MGMR_DONE:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE", __func__);
}
break;
case CODE_WIFI_ON_SCAN_DONE:
{
char* scan_msg = pvPortMalloc(128);
memset(scan_msg, 0, 128);
wifi_mgmr_sta_scanlist();
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE SSID numbles:%d", __func__, wifi_mgmr_sta_scanlist_nums_get());
sprintf(scan_msg, "{\"wifi_scan\":{\"status\":0}}");
// xQueueSend(queue, scan_msg, );
if (wifi_mgmr_sta_scanlist_nums_get()>0) {
xQueueSendFromISR(queue, scan_msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
vPortFree(scan_msg);
}
break;
case CODE_WIFI_ON_CONNECTED:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED", __func__);
void mm_sec_keydump();
mm_sec_keydump();
}
break;
case CODE_WIFI_ON_GOT_IP:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP", __func__);
}
break;
case CODE_WIFI_ON_DISCONNECT:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT", __func__);
char* queue_buff = pvPortMalloc(128);
memset(queue_buff, 0, 128);
sprintf(queue_buff, "{\"wifi_disconnect\":true}");
xQueueSendFromISR(queue, queue_buff, pdTRUE);
vPortFree(queue_buff);
}
break;
case CODE_WIFI_ON_AP_STARTED:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED", __func__);
}
break;
case CODE_WIFI_ON_AP_STOPPED:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED", __func__);
}
break;
case CODE_WIFI_ON_AP_STA_ADD:
{
LOG_I("[APP] [EVT] [AP] [ADD] %lld", xTaskGetTickCount());
}
break;
case CODE_WIFI_ON_AP_STA_DEL:
{
LOG_I("[APP] [EVT] [AP] [DEL] %lld", xTaskGetTickCount());
}
break;
default:
{
LOG_I("[APP] [EVT] Unknown code %u ", code);
}
}
}
uint8_t wifi_connect(char* ssid, char* passwd)
{
int ret = 255;
// struct fhost_vif_ip_addr_cfg ip_cfg = { 0 };
uint32_t ipv4_addr = 0;
char* queue_buff = pvPortMalloc(128);
memset(queue_buff, 0, 128);
if (NULL==ssid || 0==strlen(ssid)) {
return 1;
}
if (wifi_mgmr_sta_state_get() == 1) {
wifi_sta_disconnect();
}
if (wifi_sta_connect(ssid, passwd, NULL, NULL, 0, 0, 0, 1)) {
return 4;
}
LOG_I("Wating wifi connet");
//wait for connection success
sta_ConnectStatus = 0;
for (int i = 0;i<10*30;i++) {
vTaskDelay(100/portTICK_PERIOD_MS);
switch (sta_ConnectStatus) {
case CODE_WIFI_ON_MGMR_DONE:
// vTaskDelay(2000);
// LOG_I("wifi_mgmr_sta_scan:%d", wifi_mgmr_sta_scan(wifi_scan_config));
vPortFree(queue_buff);
return 3;
case CODE_WIFI_ON_SCAN_DONE:
// LOG_I("WIFI STA SCAN DONE %s", wifi_scan_config[0].ssid_array);
vPortFree(queue_buff);
return 2;
case CODE_WIFI_ON_DISCONNECT: //connection failed (still not connected after exceeding the reconnect count)
LOG_I("Wating wifi connet Faild");
// wifi_sta_disconnect();
vPortFree(queue_buff);
return 4;
case CODE_WIFI_ON_CONNECTED: //connected (in wifi sta state, this means both obtaining an IP via DHCP or using a static IP succeeded)
// LOG_I("Wating wifi connet OK \r\n");
break;
case CODE_WIFI_ON_GOT_IP:
wifi_sta_ip4_addr_get(&ipv4_addr, NULL, NULL, NULL);
LOG_I("wifi connened %s,IP:%s", ssid, inet_ntoa(ipv4_addr));
sprintf(queue_buff, "{\"ip\":{\"IP\":\"%s\"}}", inet_ntoa(ipv4_addr));
flash_erase_set(SSID_KEY, ssid);
flash_erase_set(PASS_KEY, passwd);
xQueueSend(queue, queue_buff, portMAX_DELAY);
LOG_I("Wating wifi connet OK and get ip OK");
vPortFree(queue_buff);
return 0;
default:
//wait for connection success
break;
}
}
vPortFree(queue_buff);
return 14; //connection timeout
}Add the following to main.c:
...
#include "wifi_event.h"
...
/**
* @brief void queue_task(void* arg)
* message queue loop reading
* @param arg
*/
static void queue_task(void* arg)
{
char* ssid = NULL;
char* password = NULL;
ssid = flash_get_data(SSID_KEY, 32);
password = flash_get_data(PASS_KEY, 32);
if (ssid != NULL && strlen(ssid) > 0)
{
printf("read flash ssid:%s password:%s\r\n", ssid, password);
wifi_connect(ssid, password);
}
else {
printf("ssid read value is NULL:%06X\r\n", SSID_KEY);
}
}
int main(void)
{
...
// initialize storage
flash_init();
// initialize tcp ip
tcpip_init(NULL, NULL);
// start the WiFi task
wifi_start_firmware_task();
char* ssid = NULL;
ssid = flash_get_data(SSID_KEY, 32);
if (ssid != NULL && strlen(ssid) > 0) {
xTaskCreate(queue_task, "queue task", 1024*6, NULL, 2, NULL);
}
...
}Modify proj.conf to add the following:
## wifi
set(CONFIG_VIF_MAX 2)
set(CONFIG_STA_MAX 4)
set(CONFIG_MAC_TXQ_DEPTH 32)
set(CONFIG_MAC_RXQ_DEPTH 12)Modify CMakeLists.txt to add the following:
target_sources(app PRIVATE
wifi/wifi_event.c // add wifi-related code
storage/storage.c
web/cJSON.c
web/mlwip_https.c
)The current solution has the following problems:
Operation Steps
Chinese WiFi names cannot be connected
The AP disappears after the WiFi info is set
Wrong WiFi info cannot be fixed, and recompiling doesn’t help either
Solutions:
Operation Steps (continued)
Use and connect to an English-named WiFi when possible
Re-flash the firmware
Erase the chip and flash.
In the flash_prog_cfg.ini configuration file
Change
[cfg] # 0: no erase, 1: programmed section erase, 2: chip erase
#0: no erase, 1:programmed section erase, 2: chip erase
erase = 1
to
[cfg] # 0: no erase, 1: programmed section erase, 2: chip erase
#0: no erase, 1:programmed section erase, 2: chip erase
erase = 2
Then run the make flash COMX=COM? [? stands for your COM number] command to erase the data.
At this point, the AP web provisioning implementation on the Ai-M61-32S is complete.
If you think it’s not bad, you can give it a
+ 1.
Have questions?
For other questions, please visit the unified discussion area: Ai-Thinker Discussions

