小安派S1 HTTP & 二维码& AP配网

[复制链接]
查看890 | 回复7 | 2023-12-6 20:49:34 | 显示全部楼层 |阅读模式

本帖最后由 lovzx 于 2023-12-6 22:08 编辑

AP + 二维码 + HTTP 配网

运行效果

运行.jpg

配网界面

配网.jpg

配网成功

获取到IP.jpg

运行日志

连接wifi

连接wifi.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

配网思路

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: %dnsetsockopt ret: %dn", ss, err);

看日志打印处理结果也是0,说明是设置成功了的,到listen的时候就返回-8异常,如果有遇到就按照上面说的修改tcp.c文件

wifi事件类型

wifi事件类型.png

由于wifi事件是中断类型的,不能直接在中断里面搞复杂耗时的操作,所以要用task处理wifi事件

串口打印log乱码

  1. 打开board.c文件
  2. 找到console_init函数
  3. 修改波特率为115200就好了

串口115200.png

连上AP无法打开配置页面(仅限Android手机,不熟悉IOS)

由于配网的时候小安派无法联网,手机连接到小安派的热点回去验证wifi能不能上网,不能上网会弹出来无法上网的提示(或者是通知栏),需要点击保持连接 wifi无法上网.jpg

这个demo也仅是能运行,bug也会有不少,写的不好的地方轻喷,哪里有不好的地方欢迎大佬指点

代码下载地址Robot/bl616_S1_ap_wifi (gitee.com)

本帖被以下淘专辑推荐:

回复

使用道具 举报

lza | 2024-3-6 20:06:24 | 显示全部楼层
太强了
回复

使用道具 举报

lza | 2024-3-6 16:01:10 | 显示全部楼层
学习一下。
回复

使用道具 举报

心云 | 2023-12-19 08:54:20 | 显示全部楼层
回复

使用道具 举报

minmin_99 | 2023-12-7 09:29:22 | 显示全部楼层
学习
回复

使用道具 举报

爱笑 | 2023-12-7 09:08:08 | 显示全部楼层
优秀~
用心做好保姆工作
回复

使用道具 举报

lovzx | 2023-12-6 23:00:33 来自手机 | 显示全部楼层
noonezero 发表于 2023-12-6 21:05
恭喜,大佬由代码可以学习下吗

文中更新了下载地址
回复 支持 反对

使用道具 举报

noonezero | 2023-12-6 21:05:12 | 显示全部楼层
恭喜,大佬由代码可以学习下吗
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则