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 條評論

發布
問題