w801通过蓝牙更新wifi并实现上电自动重连的一点分享

发布于 2022-04-19 12:30:25

一.项目流程

项目整体思路比较简单,W801上电后读取内部flash中保存在固定位置的Wifi账号密码,同时开始蓝牙,每当蓝牙接收到命令后,就对其进行解析,如果为连接wifi命令,则断开当前wifi,并进行新的wifi连接,连接成功后将账号密码更新至内部flash中固定位置,用于下次上电连接。大致的流程图如下:
1.png

二.代码解析

【1】使用说明:本代码使用的基本都是基于SDK中代码进行的修改
【2】主程序:

void UserMain(void)
{
    printf("\n-----------------------PROJECT Version1.6.5---------------------------\n");
    printf("\n-------------------------------BLE Start--------------------------------\n");    //这里在wm_main中开启了蓝牙任务,可以参考官方提供的蓝牙说明书
    tls_os_time_delay(1000);
    printf("\n-------------------------------WiFi Start--------------------------------\n");
    mqtt_start();
    get_wifi_params(ssid, pwd);                                                               //获取内部flash中保存的最近一次成功连接的wifi账号密码
    wifi_connect_net(ssid, pwd);                                                              //进行wifi连接,这个使用的就是demo中的链接函数,做了一点修改
    tls_os_time_delay(3000);
    printf("\n-------------------------------APP Start--------------------------------\n");
    tls_os_task_create(app_handle, "app", app_task,
                           NULL, (void *)app_task_stk, 
                           APP_TASK_SIZE * sizeof(u32),
                           APP_TASK_PRIO, 0);                                                      //主任务
}

【3】蓝牙命令处理的铺垫:要实现蓝牙命令的解析,首先要知道蓝牙接受到命令后在那个位置保存,这个很多大佬已经进行了分享,我这里再简单说明下如下图所示,为了方便后续更多蓝牙命令的处理因此,我单独创建了一个ble_cmd_excute函数用于命令解析执行。
2.png

【4】蓝牙命令的定义:为了我后续添加更多的功能,因此我采用的Json格式来定义蓝牙命令,具体格式如下:

{"method":"xxxx","params":{"param1":"xxxxx","param2":"xxxxx"}}

其中method代表的是具体的命令类型,params则是该命令的参数,因为后续命令可能带有很多参数,因此params的键值设置了一个Json体方便填入更多参数,例如本次wifi连接接用到的命令为:

{"method":"wifi_connect","params":{"ssid":"xxxxx","pwd":"xxxxx"}}

【5】蓝牙处理函数代码解析:

/**
 * @brief   蓝牙命令解析
 */ 
int ble_cmd_excute(char *cmd)
{
    /*解析Json字符串
     * root: 下行命令解析得到的JSON体
     * str_method: 解析后得到的下传方法名
     * json_params: 解析后得到的下传参数JSON体
     * json_p1: 解析后得到第一个参数的JSON体
     * json_p2: 解析后得到第二个个参数JSON体
     * 
     */
    cJSON *root = 0;
    cJSON *json_params, *json_p1, *json_p2;
    char *str_method;
    char temp[100];
    root = cJSON_Parse(cmd);
    if (!root)
    {
        BLE_CMD_DEBUG("[INFO]: JSON格式错误: %s\r\n", cJSON_GetErrorPtr());
    }
    else
    {
        //解析命令
        str_method = cJSON_GetObjectItem(root, "method")->valuestring;
        json_params = cJSON_GetObjectItem(root, "params");
        
        //BLE_CMD_DEBUG("str_method: %s\n", str_method);
        //BLE_CMD_DEBUG("%s\n", cJSON_Print(json_params));

        if (strcmp(str_method, "wifi_connect") == 0)      //先解析method,判断是否是进行wifi链接的命令,如果是再调用ble_set_wifi命令进行具体参数解析以及操作
        {
            ble_set_wifi(json_params);
        }
        
        
        tls_os_time_delay(1000);
        ble_send_rel();
    }
    
    return WM_SUCCESS;
}
/**
 * @brief   解析完成返回数据
 */ 
int ble_send_rel(void)
{
    char msg[] = "EXCUTE FINISHED";
    tls_ble_server_demo_api_send_msg((uint8_t *)msg, strlen(msg));   //通过indicate方法返回一个解析完成的反馈给手机
    return WM_SUCCESS;
}

/**
 * @brief   蓝牙配网
 */ 
int ble_set_wifi(cJSON *json_params)
{
    cJSON *json_p1, *json_p2;
    //首先解析参数得到wifi账号密码
    json_p1 = cJSON_GetObjectItem(json_params, "ssid");
    char *wifi_ssid = (int)json_p1->valuestring;
    json_p2 = cJSON_GetObjectItem(json_params, "pwd");
    char *wifi_pwd = json_p2->valuestring;
    BLE_CMD_DEBUG("wifi_ssid:%s wifi_pwd:%s\n", wifi_ssid, wifi_pwd);
    
    //清空ssid和pwd
    memset(ssid, 0, WIFI_PARAMS_LEN);                                
    memset(pwd, 0, WIFI_PARAMS_LEN);
    strcpy(ssid, wifi_ssid);                                                    
    strcpy(pwd, wifi_pwd);
    //连接wifi
    wifi_connect_net(ssid, pwd);
    return WM_SUCCESS;
}

【6】wifi函数处理:这一部分基本就是SDK自带的demo我做了一定的修改,也就是注释部分

int wifi_connect_net(char *ssid, char *pwd)
{
    struct tls_param_ip *ip_param = NULL;
    u8 wireless_protocol = 0;

    if (!ssid)
    {
        return WM_FAILED;
    }

    printf("\n[INFO]:ssid:%s\n", ssid);
    printf("[INFO]:password=%s\n", pwd);
    tls_wifi_disconnect();
    tls_os_time_delay(500);    //这里要添加一个延时,我个人测试了几次发现不添加延时偶尔会出现还没有成功断开上一个wifi连接就开始下一个wifi连接了

    tls_param_get(TLS_PARAM_ID_WPROTOCOL, (void *) &wireless_protocol, TRUE);
    if (TLS_PARAM_IEEE80211_INFRA != wireless_protocol)
    {
        tls_wifi_softap_destroy();
        wireless_protocol = TLS_PARAM_IEEE80211_INFRA;
        tls_param_set(TLS_PARAM_ID_WPROTOCOL, (void *) &wireless_protocol, FALSE);
    }

    tls_wifi_set_oneshot_flag(0);

    ip_param = tls_mem_alloc(sizeof(struct tls_param_ip));
    if (ip_param)
    {
        tls_param_get(TLS_PARAM_ID_IP, ip_param, FALSE);
        ip_param->dhcp_enable = TRUE;
        tls_param_set(TLS_PARAM_ID_IP, ip_param, FALSE);
        tls_mem_free(ip_param);
    }

    tls_netif_add_status_event(con_net_status_changed_event);
    int ret = tls_wifi_connect((u8 *)ssid, strlen(ssid), (u8 *)pwd, strlen(pwd));
    //printf("ret = %d\n", ret);
    printf("[INFO]:等待网络连接中\n");

    return WM_SUCCESS;
}

static void con_net_status_changed_event(u8 status )
{
    switch(status)
    {
    case NETIF_WIFI_JOIN_SUCCESS:
        printf("[INFO]:WIFI网络连接成功\n");
        wifi_status_led(0);    //led点亮用于指示wifi连接成功,这个可以自由修改
        clear_wifi_params();   //清空wifi所在内部flash存储区域
        save_wifi_params();    //将成功连接的wifi账号密码保存至flash
        break;
    case NETIF_WIFI_JOIN_FAILED:
        printf("[INFO]:WIFI网络连接失败\n");
        wifi_status_led(1);
        break;
    case NETIF_WIFI_DISCONNECTED:
        printf("[INFO]:WIFI网络已断开连接\n");
        wifi_status_led(1);
        break;
    case NETIF_IP_NET_UP:
    {
        struct tls_ethif *tmpethif = tls_netif_get_ethif();
        print_ipaddr(&tmpethif->ip_addr);
//如果有IPV6,在demo/wm_demo.h里面打开TLS_CONFIG_IPV6
#if TLS_CONFIG_IPV6
        print_ipaddr(&tmpethif->ip6_addr[0]);
        print_ipaddr(&tmpethif->ip6_addr[1]);
        print_ipaddr(&tmpethif->ip6_addr[2]);
#endif
    }
    break;
    default:
        //printf("UNKONWN STATE:%d\n", status);
        break;
    }
}

【7】内部flash存储:这部分基本可以参考demo的读写函数,不多赘述,代码如下

/**
 * @brief   清空WIFI链接参数Flash空间
 */ 
int clear_wifi_params(void)
{
    u8 write_buf[WIFI_PARAMS_LEN];
    memset(write_buf, 0, WIFI_PARAMS_LEN);
    tls_fls_write(0x1F0303, (u8 *)write_buf, WIFI_PARAMS_LEN);
    tls_fls_write(0x1F0303+WIFI_PARAMS_LEN, (u8 *)write_buf, WIFI_PARAMS_LEN);
    
    return WM_SUCCESS;
}

/**
 * @brief   保存WIFI链接参数
 */
int save_wifi_params(void)
{    
    tls_fls_write(0x1F0303, (u8 *)ssid, WIFI_PARAMS_LEN);
    tls_fls_write(0x1F0303+WIFI_PARAMS_LEN, (u8 *)pwd, WIFI_PARAMS_LEN);
    
    return WM_SUCCESS;
}

/**
 * @brief   读取WIFI链接参数
 */
int get_wifi_params(char *temp_ssid, char *temp_pwd)
{
    u8 *read_buf;
    read_buf = tls_mem_alloc(WIFI_PARAMS_LEN);
    if (NULL == read_buf)
    {
        printf("\nmalloc read buf error\n");
        return WM_FAILED;
    }
    memset(read_buf, 0, WIFI_PARAMS_LEN);
    tls_fls_read(0x1F0303, read_buf, WIFI_PARAMS_LEN);
    strcpy(temp_ssid, read_buf);
    //printf("ssid: %s\n", (char *)read_buf);
    
    memset(read_buf, 0, WIFI_PARAMS_LEN);
    tls_fls_read(0x1F0303+WIFI_PARAMS_LEN, read_buf, WIFI_PARAMS_LEN);
    strcpy(temp_pwd, read_buf);
    //printf("pwd: %s\n", (char *)read_buf);
    tls_mem_free(read_buf);
    
    return WM_SUCCESS;
}

三.实验结果以及需要注意的地方

【1】手机端使用的软件:nRF_connect
【2】使用蓝牙发送的需要注意事项:因为蓝牙默认的MTU(最大交换数据量)为18,而我的蓝牙命令比较长,因此必须要request MTU让蓝牙和W801进行MTU的交换,最后会取二者中小的,w801最大的MTU为256,如下图所示:
4.jpg
【3】试验结果:
6.jpg
7.jpg

四.一些补充和后续遇到的小bug

【1】这个小demo中遇到过的问题,我都在问答社区中有提问过,感谢回答的大佬们的解答,如果遇到相似的问题,可以点击下面的链接进行参考。
W801,使用蓝牙接受命令更新wifi账号密码并重连失败
w801使用过程中遇到的3个问题

【2】后续可以进行的优化:这里的wifi链接参数,我保存在了内部flash中,内部flash空间比较紧张,且偶尔会出现乱码的问题,应该是程序运行多了之后,某些地方被覆盖了。因此最好后续保存在外部flash,或者用fatfs挂载一个SD卡,用.txt文件保存,类似于esp32。

【3】最后还是要感谢下,问答社区和群里的大佬的热心解答。

0 条评论

发布
问题