W801/W800-wifi-socket開發(一)-UDP

發布於 2022-04-18 19:28:15

@TOC

**本文使用環境:
主控:W800-KIT (開發板)
兼容:W800 W801 AIR101
開發環境:CDK
SDK:W801/W800的SDK(tls庫)**

==我的聯盛德問答社區主頁==
==我的CSDN文章==
==寫在前面:==
我不準備修改官方的文件,我會直接調用對應的api,因為官方用了大量的回調和消息隊列,一環扣著一環,要改動話費的時間太多了,也沒有太大的必要。

一、項目概述

^^^^程序功能: 使用板載WIFI連接電腦端的服務器(UDP協議,使用網絡調試助手模擬),傳輸數據。

^^^^從本文起,將開始板載wifi的使用。寫之前吐槽一句,開發W80X真就是拿著官方的SDK,一個函數一個函數的啃啊,屬實有點難受啊。。。。

二、socket-udp官方SDK梳理。

^^^^本小節主要用來梳理官方UDP連接的程序設計思路,這都是一個函數一個函數找出來的啊,真的辛苦。希望看官動動你發財的小手,幫忙點個關注,點個贊,點個收藏。
==注意== :這裡寫的有點繞啊,如果新手可能不容易理解,自己多看幾次把。程序設計會放在第三章節。

1、連接路由器

^^^^打開wm_connect_net_demo.c文件,demo中wifi的連接函數在該文件下。
在這裡插入圖片描述如上圖所示,demo_connect_net函數入口參數為wifi名和密碼。
舉個栗子:

demo_connect_net("yyds","1234567890");
//注意,必須要有延時,否者連接會出問題。。。。。
tls_os_time_delay(2000);

^^^執行完成後開發板就自動連接到wifi了,此時串口會打印:
在這裡插入圖片描述
表示已經成功加入路由器,可見路由器給開發板分配IP:192.168.1.74

2、連接UDP

^^^^打開wm_udp_demo.c文件,udp_demp的連接函數在該文件下。
在這裡插入圖片描述
socket_udp_demo()'函數參數分別表示模式(單播和多播)端口IP。本文連接電腦服務器,因此配置如下:

    socket_udp_demo(1,10086,"192.168.1.87");

上述==IP==是我的電腦==IP==,端口是我自定義的端口,可根據實際情況修改。接下來將會詳細的理一下函數的執行思路。

^^^^socket_udp_demo()'函數前半部分全是初始化相關,主要看下面這個結構體:demo_udp結構體如下所示,可以看出該結構體包含連接相關的變量,*sock_rx*sock_tx,是後續需要用到的接收和發送指針,意味著後續的發送可以直接使用該結構體傳送。

/**
 * @typedef struct demo_udp
 */
typedef struct demo_udp
{
    tls_os_queue_t *udp_q;
    struct ip_mreq mreq;
    char *sock_rx;
    char *sock_tx;
    int cast_mode;
    bool socket_ok;
    int socket_num;
    int port;
    u32 ip_addr;
    u32 rcv_data_len;
    int snd_skt_no;
    int snd_data_len;

} ST_Demo_Udp;

^^^^函數中對輸入和輸出都進行了內存的分配,==如果發送數據和接收數據長度很長需要特別注意==。
在這裡插入圖片描述
^^^^接下來直接看以下兩個任務,注意demo_udp這個結構體傳遞給了這個兩個任務。
在這裡插入圖片描述
^^^^第一個是配置和發送相關,第二個是接收數據。

2.1、首先進入demo_udp_task()'函數:(函數中的udp是上一個函數傳進來的,和前文的demo_udp等價)
^^^^判斷網絡是否正常連接。

    if(ethif->status)    /*connected to ap and get IP*/
    {
        tls_os_queue_send(udp->udp_q, (void *)DEMO_MSG_SOCKET_CREATE, 0);
    }
    else
    {
        struct tls_param_ip ip_param;

        tls_param_get(TLS_PARAM_ID_IP, &ip_param, TRUE);
        ip_param.dhcp_enable = TRUE;
        tls_param_set(TLS_PARAM_ID_IP, &ip_param, TRUE);
        tls_wifi_set_oneshot_flag(1);         /*Enable oneshot configuration*/
        printf("\nwait one shot......\n");
    }

^^^^查看當前連接的狀態。

tls_netif_add_status_event(udp_net_status_changed_event);

^^^^直接進入udp_net_status_changed_event()'函數,官方使用了一個demo_udp消息隊列發送當前需要執行的函數狀態,和udp等價。

static void udp_net_status_changed_event(u8 status )
{
    switch(status)
    {
    case NETIF_WIFI_JOIN_FAILED:
        tls_os_queue_send(demo_udp->udp_q, (void *)DEMO_MSG_WJOIN_FAILD, 0);
        break;
    case NETIF_WIFI_JOIN_SUCCESS:
        tls_os_queue_send(demo_udp->udp_q, (void *)DEMO_MSG_WJOIN_SUCCESS, 0);
        break;
    case NETIF_IP_NET_UP:
        tls_os_queue_send(demo_udp->udp_q, (void *)DEMO_MSG_SOCKET_CREATE, 0);
        break;
    default:
        break;
    }
}

^^^^退出udp_net_status_changed_event()'函數繼續查看demo_udp_task()'函數。一個死循環,接收udp的消息,這裡的switch中的msg來自於前文提到的upddemo_udp(兩者等價)。

for (;;)
    {
        tls_os_queue_receive(udp->udp_q, (void **)&msg, 0, 0);
        printf("\n udp msg =%d\n",msg);
        switch((u32)msg)
        {
        case DEMO_MSG_WJOIN_SUCCESS:
            break;

        case DEMO_MSG_SOCKET_CREATE:
            create_udp_socket_demo();
            break;

        case DEMO_MSG_WJOIN_FAILD:
            if(udp->socket_num > 0)
            {
                udp->socket_num = 0;
                udp->socket_ok = FALSE;
            }
            break;

        case DEMO_MSG_SOCKET_RECEIVE_DATA:
            break;

        case DEMO_MSG_UART_RECEIVE_DATA:
            if (-1 == udp->snd_data_len)
            {
                len = DEMO_UDP_BUF_SIZE;
            }
            else if(udp->snd_data_len != 0)
            {
                len = (udp->snd_data_len > DEMO_UDP_BUF_SIZE) ?
                      DEMO_UDP_BUF_SIZE : udp->snd_data_len;
            }
            else
            {
                break;
            }
            memset(udp->sock_tx, 'u', len);

            if (DEMO_UDP_BROADCAST == udp->cast_mode)
            {
                pin.sin_addr.s_addr = htonl(0xffffffffUL);  //IPADDR_BROADCAST
            }
            else if (DEMO_UDP_MUTICAST == udp->cast_mode)
            {
                MEMCPY((char *) & (pin.sin_addr.s_addr), (char *)MCASTIP, 4);
            }
            else
            {
                pin.sin_addr.s_addr = udp->ip_addr;
            }

            pin.sin_port = htons(udp->port);

            ret = sendto(udp->socket_num, udp->sock_tx, len, 0, (struct sockaddr *)&pin, sizeof(struct sockaddr));
            //       printf("ret = %d\n",ret);
            if (ret < 0)
            {
                printf("send err\n");
                break;
            }
            else
            {
                if (udp->snd_data_len != -1)
                {
                    udp->snd_data_len -= ret;
                }
            }
            if (udp->socket_ok && udp->snd_data_len != 0)
            {
                tls_os_time_delay(8);
                tls_os_queue_send(udp->udp_q, (void *)DEMO_MSG_UART_RECEIVE_DATA, 0);
            }
            break;

        case DEMO_MSG_SOCKET_ERR:
            tls_os_time_delay(200);
            printf("\nsocket err\n");
            create_udp_socket_demo( );
            break;

        default:
            break;
        }
    }

^^^^梳理一下switch的執行順序:首先執行create_udp_socket_demo()',創建連接,這個函數不用再看了,會自動創建成功。然後,就可以進入發送程序。demo中單獨寫了一個發送函數:

int udp_send_data_demo(int len)
{
    printf("\nlen=%d\n", len);
    if (NULL == demo_udp)
    {
        return WM_FAILED;
    }
    if (!demo_udp->socket_ok)
    {
        printf("skt not created\n");
        return WM_FAILED;
    }

    demo_udp->snd_data_len = len;
    tls_os_queue_send(demo_udp->udp_q, (void *)DEMO_MSG_UART_RECEIVE_DATA, 0);

    return WM_SUCCESS;
}

該函數只規定了傳輸的長度,並未對內容進行賦值,tls_os_queue_send()'函數發送消息DEMO_MSG_UART_RECEIVE_DATA參數給 demo_udp_task()'代碼的for循環,tls_os_queue_receive()函數接收消息隊列的值,並switch執行。在case DEMO_MSG_UART_RECEIVE_DATA:中才是真正的發生代碼,發送的內容如下:

memset(udp->sock_tx, 'u', len);

上訴代碼是將發送內容全部填充為'u',因此直接修改這裡的udp->sock_tx內容,就能通過socket發送數據。但實際發送的時候不再這裡賦值。。。。。

2.2、接下來查看demo_udp_recv_task()'函數,這個函數沒有什麼講的,接收的數據會自動放到 udp->sock_rx中。

ret = recvfrom(udp->socket_num, udp->sock_rx, DEMO_UDP_BUF_SIZE,
                           0, (struct sockaddr *)&pin, &addrlen);

三、項目設計

^^^^本小節將會部署自己的工程,讀取並發送數據。

3.1、 修改發送和接收代碼
^^^^ ==必須修改:== ,在 wm_udp_demo.c 找到demo_udp_task()' 函數,將 case DEMO_MSG_UART_RECEIVE_DATA: 中的memset屏蔽掉:


必須將這一條的代碼屏蔽
//memset(udp->sock_tx, 'u', len);

//以下代碼可加可不加,主要是打印發送的數據
//添加在 case DEMO_MSG_UART_RECEIVE_DATA: 的 break之前。
//add by zxx start
printf("send_led: %d data:\n",len);
for(int ii = 0;ii < len; ii--)
{
    printf("%x ",udp->sock_tx[ii]);
}printf("\n");
//add by zxx end        
break;        

^^^^ ==不一定要修改:== ,在 wm_udp_demo.c 找到demo_udp_recv_task()'函數,添加打印代碼。

if (ret > 0)
{
    printf("rcv from %s  : port : %d len = %d rev_data: \n", inet_ntoa(pin.sin_addr), htons(pin.sin_port), ret);
    //add by zxx start
    for(int ii = 0; ii < ret; ii++)
    {
        printf("%d ",udp->sock_rx[ii]);
    }printf("\n");
    //add by zxx end
}

3.2、 封裝數據
^^^^在wm_udp_demp.c文件的==最後==添加代碼:

//add by zxx start
void udp_send_data_self(u8 *data,int data_len)
{
//將data的內容全部拷貝到demo_udp->sock_tx中
    memcpy(demo_udp->sock_tx, data, data_len);
    udp_send_data_demo(data_len);
}
//add by zxx end

3.3、 外部聲明
^^^^在wm_demp_console.h文件的==最後==添加代碼:

//add by zxx start
extern void udp_send_data_self(u8 *data,int data_len);
//add by zxx end
#endif /*__WM_DEMO_CMD_H__*/

3.4、 主函數
^^^^在==你的任務==中寫上如下代碼:

void your_task()
{
    u8 test_data[10] = {0,1,2,3,4,5,6,7,8,9};
    demo_connect_net("yyds","1234567890");
    //必須要
    tls_os_time_delay(2000);
    socket_udp_demo(1,10086,"192.168.1.87");
    while(1)
    {
        //發送數據
        udp_send_data_self(test_data,10);
        tls_os_time_delay(5000);
    }
}

四、測試

1、 模擬服務器
^^^^打開網絡調試助手,按照如下進行配置,IP根據自己實際情況進行配置。
在這裡插入圖片描述

2、程序下載至開發板
^^^^延時時間結束後,開發板和服務器互發數據。
^^^^開發板
在這裡插入圖片描述

^^^^服務器。
在這裡插入圖片描述

5 條評論

發布
問題