本帖最后由 lovzx 于 2023-12-6 22:08 编辑
AP + 二维码 + HTTP 配网
运行效果
配网界面
配网成功
运行日志
连接wifi
二维码
二维码最简单使用lvgl自带的库就行了,lvgl配置默认是关闭的,找到lv_conf.h中的代码,八LV_USE_QRCODE定义改成1就启用了二维码模块
#define LV_USE_QRCODE 1
AP
使用SDK里面wifi_mgmr模块管理就好,我简单封装到了wifi.h中
uint32_t get_wifi_event_code();
/**
* @brief 连接wifi
* @param ssid
* @param key
* @return uint8_t
*/
wifi_connect_code wifi_connect(char* ssid, char* key);
/**
* @brief 断开wifi连接
*/
void wifi_disconnect();
/**
* @brief 关闭wifi
* @return int
*/
int wifi_close();
/**
* @brief 打开wifi
* @return int
*/
int wifi_open();
/**
* @brief 重启wifi
* @return int
*/
int wifi_restart();
/**
* @brief 扫描wifi
* @param env
* @param arg
* @param cb
*/
void wifi_scan(void* env, void* arg, ap_scan_item_cb_t cb);
/**
* @brief 获取wifi状态
* @return wifi_status
*/
wifi_status get_wifi_status();
/**
* @brief 设置wifi自动连接
* @param autoconnect
*/
void wifi_autoconnect(bool autoconnect);
/**
* @brief 初始化wifi
*/
void wifi_init();
/**
* @brief wifi event回调
* @param code
*/
void wifi_event_handler(uint32_t code);
/**
* @brief 获取sta ip
* @return char*
*/
char* wifi_get_sta_ip();
#if USE_AP == 1
/**
* @brief 打开ap
* @param ssid
* @param key
* @param akm
* @return int
*/
int ap_start(char* ssid, char* key, char* akm);
/**
* @brief 重启ap
* @param ssid
* @param key
* @param akm
*/
void ap_restart(char* ssid, char* key, char* akm);
/**
* @brief 关闭ap
*/
void ap_close();
/**
* @brief 获取ap状态
* @return ap_status
*/
ap_status get_ap_status();
使用的时候需要在main中调用wifi_init()函数初始化wifi信息,并创建wifi_main任务,和小安派天气里面的wifi_start_firmware_task函数一模一样,包括wifi_event_handler函数都一样复制到了wifi.h中,在wifi.c中稍微有点不一样,wifi.c中保留了wifi中断事件的值,方便其他地方随时读取查询
HTTP
这个没什么好说的,都是socket那一套流程,创建,listen,bind,accept,read,write等函数的调用,相关的代码都在wifi_config_server.c中,里面也有详细的注释,处理了GET "/" 和POST "/configwifi"两个请求,还有个GET "/loading",连接wifi的时候不关闭AP可以看到加载中的简单转圈圈动画
写之前还没看到SDK里面有HTPP server的代码,写好了发现了也懒得改了,有兴趣的可以参考SDK里面的server写法,很全,
地址是 ..\aithinker_Ai-M6X_SDK\examples\peripherals\emac\lwip_http_server
配网思路
配网成功后会自动保存ssid和key到flash中,重启后会自动连接,
在flash_prog_cfg.ini中有配置清除模式
[cfg]
# 0: no erase, 1:programmed section erase, 2: chip erase
erase = 2
如果是1就只会清除固件所占用的空间,flash并不会被擦除,也就是重新刷固件的话,如果前面配置成功过,就会直接读取到wifi信息进行连接就跳过了配网的步骤
如果是2就全片擦除,刷固件会把flash存的数据也给擦除了,上电后会走配网流程
flash配置
需要用到flash保存ssid及密码,需要在proj.conf文件中打开falsh相关的模块,参考SDK/components/easyflash4模块,用到了其中的设置和读取函数
proj.conf配置
set(CONFIG_PARTITION 1)
set(CONFIG_BFLB_MTD 1)
set(CONFIG_EASYFLASH4 1)
设置和读取flash
/**
* 设置数据并提交,还有类似不带save的函数,只设置到缓存数组里面了,不save的话
* 是不会flush到flash中的
* key保存的数据key
* value数据
*/
EfErrCode ef_set_and_save_env(const char *key, const char *value)
/**
* 读取数据放入到buf中,buf不能是NULL或者没有申请的内存地址
*/
size_t ef_get_env_blob(const char *key, void *value_buf, size_t buf_len, size_t *saved_value_len)
修改DHCP地址
需要在include目录里面的lwipopts_user.h文件中通过宏定义DHCPD_SERVER_IP(默认是192.168.169.1)来修改IP,代码引用
在dhcp_server_raw.c,声明定义在lwip/opt.h中
引用配置文件
#if __has_include("lwipopts_user.h")
#include "lwipopts_user.h"
#else
#include "lwipopts.h"
DCHP服务
/* the DHCP server address */
#ifndef DHCPD_SERVER_IP
#define DHCPD_SERVER_IP "192.168.169.1"
#endif
void dhcpd_start(struct netif* netif)
{
err_t res;
if (1)
{
dhcp_stop(netif);
set_if(netif, DHCPD_SERVER_IP, "0.0.0.0", "255.255.255.0");
netif_set_up(netif);
}
//....
}
其他注意事项:
连接wifi失败的时候会执行关闭http server然后再打开的动作,socket会出现listen返回-8的情况,看了下代码,-8对应的是ERR_USE Address in use,设置了socket 的SO_REUSEADDR依然会出现这个bug,代码tcp.c中相关代码如下,
文件位置{SDK}/components/net/lwip/src/core/tcp.c,函数tcp_listen_with_backlog_and_err
把源文件中的if里面取反就没问题了,这段代码的意思是设置了SO_REUSEADDR,如果ip和port都一样就宝ERR_USE(-8)错误,感觉这个是少了取反,应该是没有设置socket端口重用就报端口占用异常,目前我加了取反后正常了,这个bug搞了快一周了。。。一直不知道是哪里的问题,同样的代码,linux里面测试了socket加了SO_REUSEADDR就正常了
if (ip_get_option(pcb, SOF_REUSEADDR)) {
/* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
is declared (listen-/connection-pcb), we have to make sure now that
this port is only used once for every local IP. */
for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if ((lpcb->local_port == pcb->local_port) &&
ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
/* this address/port is already used */
lpcb = NULL;
res = ERR_USE;
goto done;
}
}
}
设置socket端口重用
//端口复用
int opt_value = 1;
err = setsockopt(ss, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt_value, sizeof(opt_value));
LOG_I("socket id: %dnsetsockopt ret: %dn", ss, err);
看日志打印处理结果也是0,说明是设置成功了的,到listen的时候就返回-8异常,如果有遇到就按照上面说的修改tcp.c文件
wifi事件类型
由于wifi事件是中断类型的,不能直接在中断里面搞复杂耗时的操作,所以要用task处理wifi事件
串口打印log乱码
- 打开board.c文件
- 找到console_init函数
- 修改波特率为115200就好了
连上AP无法打开配置页面(仅限Android手机,不熟悉IOS)
由于配网的时候小安派无法联网,手机连接到小安派的热点回去验证wifi能不能上网,不能上网会弹出来无法上网的提示(或者是通知栏),需要点击保持连接
这个demo也仅是能运行,bug也会有不少,写的不好的地方轻喷,哪里有不好的地方欢迎大佬指点
代码下载地址Robot/bl616_S1_ap_wifi (gitee.com)