GD32VW553-IoT 实现 SoftAP 配网
这有什么用?
启动 wifi_ap_task
任务将会创建一个软 AP 并启动一个 HTTP 服务器,可使用手机等设备连接到这个软 AP,进入配网网页设置网络的 SSID 和密码,提交后将把数据写入 Flash
sys_task_create_dynamic((const uint8_t *)"WiFi AP", 4096, OS_TASK_PRIORITY(1), wifi_ap_task, NULL);
应用实例
这是一个可联网上报数据的盖革计数器 —— 连接 Wi-Fi 失败进入配网模式,在手机设定网络后重启即可连接 Wi-Fi
示例代码
源代码使用 GNU GPLv3 开源许可协议发布,你可以自由使用、修改和分发源代码,仅需保留原始版权声明和许可证信息,并使用相同的许可证
储存数据
#define USER_DATA_ADDRESS 0x08300000 // 用户数据起始地址
#define PAGE_SIZE 0x400 // 每页大小
/*!
\brief 向 Flash 写入配网数据
\param[in] ssid_data: SSID 数据
\param[in] ssid_length: SSID 32 位字长度
\param[in] pass_data: 密码数据
\param[in] pass_length: 密码 32 位字长度
\retval none
*/
void store_user_data(uint32_t *ssid_data, uint32_t ssid_length, uint32_t *pass_data, uint32_t pass_length) {
// 解锁 Flash 操作
fmc_unlock();
// 擦除目标页
uint32_t page_address = USER_DATA_ADDRESS;
fmc_page_erase(page_address);
// 等待擦除完成
while (fmc_flag_get(FMC_FLAG_BUSY));
// 写入 SSID 数据
for (uint32_t i = 0; i < ssid_length; i++) {
fmc_word_program(page_address + (i * 4), ssid_data[i]);
while (fmc_flag_get(FMC_FLAG_BUSY));
}
// 写入 PASSWORD 数据
uint32_t pass_start_address = USER_DATA_ADDRESS + (ssid_length * 4);
for (uint32_t i = 0; i < pass_length; i++) {
fmc_word_program(pass_start_address + (i * 4), pass_data[i]);
while (fmc_flag_get(FMC_FLAG_BUSY));
}
// 锁定 Flash 操作
fmc_lock();
}
/*!
\brief 读取 Flash 中的 SSID
\param[out] buffer: 欲保存到的字符串
\param[in] length: 32 位字长度
\retval none
*/
void get_user_ssid(uint32_t *buffer, uint32_t length) {
// 读取 SSID 数据
for (uint32_t i = 0; i < length; i++) {
buffer[i] = *(uint32_t *)(USER_DATA_ADDRESS + (i * 4));
}
}
/*!
\brief 读取 Flash 中的密码
\param[out] buffer: 欲保存到的字符串
\param[in] length: 32 位字长度
\retval none
*/
void get_user_pass(uint32_t *buffer, uint32_t length) {
// 计算 PASSWORD 的起始地址
uint32_t ssid_max_length = 4; // SSID 最大长度为 16 字节,即 4 个 32 位字
uint32_t pass_start_address = USER_DATA_ADDRESS + (ssid_max_length * 4);
// 读取 PASSWORD 数据
for (uint32_t i = 0; i < length; i++) {
buffer[i] = *(uint32_t *)(pass_start_address + (i * 4));
}
}
/*!
\brief 向 Flash 写入 SSID
\param[in] ssid: SSID
\retval none
*/
void set_user_ssid(const char *ssid) {
uint32_t ssid_data[4] = {0}; // 16 字节数据,存储为 4 个 32 位字
// 将字符串转换为字节数组
for (uint32_t i = 0; i < 4; i++) {
if (i * 4 < strlen(ssid)) {
ssid_data[i] = *(uint32_t *)(ssid + (i * 4));
}
}
// 获取当前存储的 PASSWORD 数据
uint32_t pass_data[16];
get_user_pass(pass_data, 16);
// 存储 SSID 和 PASSWORD 数据
store_user_data(ssid_data, 4, pass_data, 16);
}
/*!
\brief 向 Flash 写入密码
\param[in] pass: 密码
\retval none
*/
void set_user_pass(const char *pass) {
uint32_t pass_data[16] = {0}; // 63 字节数据,存储为 16 个 32 位字
// 将字符串转换为字节数组
for (uint32_t i = 0; i < 16; i++) {
if (i * 4 < strlen(pass)) {
pass_data[i] = *(uint32_t *)(pass + (i * 4));
}
}
// 获取当前存储的 SSID 数据
uint32_t ssid_data[4];
get_user_ssid(ssid_data, 4);
// 存储 SSID 和 PASSWORD 数据
store_user_data(ssid_data, 4, pass_data, 16);
}
软 AP 和 HTTP Server
// 软 AP 信息
#define AP_SSID "GD32_AP"
#define AP_PASSWORD "52GD32IoT"
#define HTTP_PORT 80
// 默认 Wi-Fi
char wifiSSID[16] = "SSID";
char wifiPASS[64] = "PASS";
/*!
\brief 解析 POST 参数
\param[in] input: POST 数据
\param[in] key: 欲提取的键
\param[out] value: 欲保存到的变量
\retval true 表示解析成功,false 表示解析失败
*/
int get_param(const char* input, const char* key, char* value) {
char search_key[64] = {0};
snprintf(search_key, sizeof(search_key), "%s=", key);
const char* start = strstr(input, search_key);
if (start != NULL) {
start += strlen(search_key); // 跳过 "key="
const char* end = strchr(start, '&');
if (end != NULL) {
// 如果找到 & 分隔符,复制到分隔符位置
size_t len = end - start;
memcpy(value, start, len);
value[len] = '\0';
} else {
// 如果没找到 &,说明是最后一个参数,直接复制到末尾
strcpy(value, start);
}
return true;
}
return false;
}
/*!
\brief 处理 HTTP 请求
\param[in] conn_fd: 套接字描述符
\retval none
*/
static void http_server_netconn_serve(int conn_fd) {
char buffer[1024];
int len = read(conn_fd, buffer, sizeof(buffer) - 1);
if (len > 0) {
buffer[len] = '\0';
printf("Received request:\n%s\n", buffer);
// 判断请求方法
if (strncmp(buffer, "GET", 3) == 0) {
// GET 请求,返回包含表单的 HTML 页面
const char *response =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n"
"\r\n"
"<html><head>"
"<title>Wi-Fi Config</title><meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />"
"<style>body{margin:0;padding:20px;background-color:#f0f0f0;}h1{text-align:center;}"
"form{margin:0 auto;background-color:#fff;padding: 20px;border-radius:5px;}"
"div{margin-bottom:15px;}p{color:gray;font-size:10px;text-align:center;}"
"input[type='text']{width:100%;padding:8px;border:1px solid #ddd;}"
"input[type='submit']{width:100%;padding:10px;background-color:#d3d3d3;color:#fff;border:none;}"
"input[type='submit']:hover{background-color: #808080;}</style></head>"
"<body><h1>Wi-Fi Config</h1>"
"<form method='POST' action='/'>"
"<div><label for='ssid'>SSID:</label>"
"<input type='text' name='ssid' placeholder='Enter SSID'></div>"
"<div><label for='pass'>Password:</label>"
"<input type='text' name='pass' placeholder='Enter PASSWORD'></div>"
"<input type='submit' value='Save'>"
"</form></body></html>";
write(conn_fd, response, strlen(response));
printf("HTTP response sent.\n");
} else if (strncmp(buffer, "POST", 4) == 0) {
// POST 请求,处理表单数据
char *content_type = strstr(buffer, "Content-Type: ");
if (content_type != NULL) {
content_type += 13; // 跳过 "Content-Type: "
if (strstr(content_type, "application/x-www-form-urlencoded") != NULL) {
// 找到请求体的起始位置
char *body = strstr(buffer, "\r\n\r\n");
if (body != NULL) {
body += 4; // 跳过 "\r\n\r\n"
printf("Received POST data: %s\n", body);
// 解析 POST 数据
char ssid[16] = {0};
char pass[64] = {0};
get_param(body, "ssid", ssid);
get_param(body, "pass", pass);
printf("SSID: %s\n", ssid);
printf("Password: %s\n", pass);
set_user_ssid(ssid);
set_user_pass(pass);
// 返回响应
const char *response =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n"
"\r\n"
"<html><head><title>FGC Wi-Fi Config</title><meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />"
"<style>body{margin:0;padding:20px;background-color:#f0f0f0;}h1{text-align:center;}"
"p{color:gray;font-size:10px;text-align:center;}"
"input[type='submit']{width:100%;padding:10px;background-color:#d3d3d3;color:#fff;border:none;}"
"input[type='submit']:hover{background-color: #808080;}</style></head>"
"<body><h1>Wi-Fi Configured</h1>"
"<p>Please reboot your device.</p>"
"<input type='submit' value='< BACK' onclick='history.back()'>";
write(conn_fd, response, strlen(response));
printf("HTTP response sent.\n");
}
}
}
} else {
// 其他请求方法,返回 405 Method Not Allowed
const char *response =
"HTTP/1.1 405 Method Not Allowed\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n"
"\r\n"
"<html><head><title>405 Method Not Allowed</title></head>"
"<body><h1>405 Method Not Allowed</h1></body></html>";
write(conn_fd, response, strlen(response));
printf("HTTP response sent.\n");
}
} else {
printf("No data received.\n");
}
close(conn_fd);
printf("Client connection closed.\n");
}
/*!
\brief HTTP Server 任务
\param[in] arg: arg
\retval none
*/
static void http_server_task(void *arg) {
int listen_fd, conn_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// 创建 socket
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
printf("Failed to create socket.\n");
return;
}
printf("Socket created successfully.\n");
// 配置 socket 地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(HTTP_PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定 socket
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
printf("Failed to bind socket.\n");
close(listen_fd);
return;
}
printf("Socket bound to port %d.\n", HTTP_PORT);
// 监听连接
if (listen(listen_fd, 5) < 0) {
printf("Failed to listen on socket.\n");
close(listen_fd);
return;
}
printf("HTTP server is listening on port %d\n", HTTP_PORT);
while (1) {
// 接受连接
conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (conn_fd < 0) {
printf("Failed to accept connection.\n");
continue;
}
printf("New client connected. IP: %s\n", inet_ntoa(client_addr.sin_addr));
// 处理 HTTP 请求
http_server_netconn_serve(conn_fd);
}
}
/*!
\brief Wi-Fi SoftAP 任务
\param[in] param: param
\retval none
*/
static void wifi_ap_task(void *param) {
// 启动软 AP
printf("Starting soft AP...\n");
if (wifi_management_ap_start((char *)AP_SSID, (char *)AP_PASSWORD, 1, AUTH_MODE_WPA, 0) != 0) {
printf("Failed to start soft AP.\n");
goto exit;
}
printf("Soft AP started. SSID: %s, Password: %s\n", AP_SSID, AP_PASSWORD);
// 启动 HTTP 服务器
printf("Starting HTTP server...\n");
http_server_task(NULL);
exit:
printf("The ap task has ended.\n");
sys_task_delete(NULL);
}
- 分类:技术
暂无评论