# bl616_S1_ap_wifi **Repository Path**: jrobot_Q_Q/bl616_-s1_ap_wifi ## Basic Information - **Project Name**: bl616_S1_ap_wifi - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-12-06 - **Last Updated**: 2024-01-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### AP + 二维码 + HTTP 配网 ### 运行效果 ![Alt text](image/README/runing.jpg) #### 配网界面 ![Alt text](image/README/config_page.jpg) #### 配网成功 ![Alt text](image/README/GOT_IP.jpg) ### 运行日志 ![Alt text](https://bbs.ai-thinker.com/data/attachment/forum/202312/06/190321t8i31xxf5fg9hren.png) #### 二维码 二维码最简单使用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** ### 配网思路 ![Alt text](image/README/AP_HTTP1.png) 配网成功后会自动保存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: %d\nsetsockopt ret: %d\n", ss, err); ``` 看日志打印处理结果也是0,说明是设置成功了的,到listen的时候就返回-8异常,如果有遇到就按照上面说的修改tcp.c文件 #### wifi事件类型 ![Alt text](image/README/wifi_event_type.png) 由于wifi事件是中断类型的,不能直接在中断里面搞复杂耗时的操作,所以要用task处理wifi事件 #### 串口打印log乱码 1. 打开board.c文件 2. 找到console_init函数 3. 修改波特率为115200就好了 ![Alt text](image/README/uart0_115200.png) #### 连上AP无法打开配置页面(仅限Android手机,不熟悉IOS) 由于配网的时候小安派无法联网,手机连接到小安派的热点回去验证wifi能不能上网,不能上网会弹出来无法上网的提示(或者是通知栏),需要点击保持连接 ![Alt text](image/README/not_internet.jpg)