• 【XR806开发板试用】基于WEBSOCKET实现人机交互(控制开关灯)以及开发问题记录


    一、开发板编译、功能介绍

    根据官方文档编译烧录成功后,我们修改下官方例子,进行开发来实现websocket。
    整体流程:开发板先自动寻找指定的wifi并且连接,连接成功后,通过websocket来与服务端连接,连接成功后,服务端就可以控制开发板开关灯。

    二、代码编写以及功能实现

    进入目录 device/xradio/xr806/ohosdemo/
    修改BUILD.gn文件新增demo:app_demo
    group(“ohosdemo”) {
    deps = [
    #“hello_demo:app_hello”,
    #“iot_peripheral:app_peripheral”,
    #“wlan_demo:app_WlanTest”,
    #“LED:app_led”,
    “demo:app_demo”,
    ]
    }
    创建demo目录 tree -L 1
    ├── BUILD.gn
    └── main.c
    BUILD.gn内容:

    import("//device/xradio/xr806/liteos_m/config.gni")
    
    static_library("app_demo") {
       configs = []
    
       sources = [
          "main.c",
       ]
    
       cflags = board_cflags
    
       include_dirs = board_include_dirs
       include_dirs += [
          "//kernel/liteos_m/kernel/arch/include",
          "//kernel/liteos_m/kernel/arch/include",
          "//base/iot_hardware/peripheral/interfaces/kits",
          "//utils/native/lite/include",
          "//foundation/communication/wifi_lite/interfaces/wifiservice",
          "//device/xradio/xr806/xr_skylark/project",
       ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    main.c

    #include 
    #include 
    
    #include "ohos_init.h"
    #include "kernel/os/os.h"
    #include "iot_gpio.h"
    
    #include "wifi_device.h"
    #include "wifi_hotspot.h"
    
    #include "net/libwebsockets/libwebsockets.h"
    
    
    #define WIFI_DEVICE_CONNECT_AP_SSID "um"  //wifi名称
    #define WIFI_DEVICE_CONNECT_AP_PSK "12345678" //wifi密码
    
    
    #define WEBSOCKET_HOST      "192.168.10.21"    //服务器IP
    #define WEBSOCKET_PORT      9999                //服务器端口
    #define WEBSOCKET_PATH      "/ws"               //websocket路径
    
    #define LIGHT_OPEN          "/light/open"       //开灯指令
    #define LIGHT_CLOSE         "/light/close"               //关灯指令
    
    static OS_Thread_t g_main_thread;
    
    #define GPIO_ID_PA21 21
    
    
    #define MAX_PAYLOAD_SIZE  10 * 1024
    
    /**
     * 会话上下文对象,结构根据需要自定义
     */
    struct session_data {
        int msg_count;
        unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];
        int len;
    };
     
    /**
     * 某个协议下的连接发生事件时,执行的回调函数
     *
     * wsi:指向WebSocket实例的指针
     * reason:导致回调的事件
     * user 库为每个WebSocket会话分配的内存空间
     * in 某些事件使用此参数,作为传入数据的指针
     * len 某些事件使用此参数,说明传入数据的长度
     */
    int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len ) {
        struct session_data *data = (struct session_data *) user;
        switch ( reason ) {
            case LWS_CALLBACK_CLIENT_ESTABLISHED:   // 连接到服务器后的回调
                lwsl_notice( "Connected to server ok!\n" );
                break;
     
            case LWS_CALLBACK_CLIENT_RECEIVE:       // 接收到服务器数据后的回调,数据为in,其长度为len
                lwsl_notice( "Rx: %s\n", (char *) in );
                if (strncmp(in, LIGHT_CLOSE, len))
                {
                    //开灯操作
                    IoTGpioSetOutputVal(GPIO_ID_PA21, 1);
                }
                else if (strncmp(in, LIGHT_OPEN, len))
                {
                    //关灯操作
                    IoTGpioSetOutputVal(GPIO_ID_PA21, 0);
                }
                break;
            case LWS_CALLBACK_CLIENT_WRITEABLE:     // 当此客户端可以发送数据时的回调
                if ( data->msg_count < 3 ) {
                    // 前面LWS_PRE个字节必须留给LWS
                    memset( data->buf, 0, sizeof( data->buf ));
                    char *msg = (char *) &data->buf[ LWS_PRE ];
                    data->len = sprintf( msg, "你好 %d", ++data->msg_count );
                    lwsl_notice( "Tx: %s\n", msg );
                    // 通过WebSocket发送文本消息
                    lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );
                }
                break;
        }
        return 0;
    }
     
    /**
     * 支持的WebSocket子协议数组
     * 子协议即JavaScript客户端WebSocket(url, protocols)第2参数数组的元素
     * 你需要为每种协议提供回调函数
     */
    struct lws_protocols protocols[] = {
        {
            //协议名称,协议回调,接收缓冲区大小
            "ws", callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,
        },
        {
            NULL, NULL,   0 // 最后一个元素固定为此格式
        }
    };
    
    //连接wifi,并且打印ip
    static int wifiConnect()
    {
        const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID;
        const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK;
    
        if (WIFI_SUCCESS != EnableWifi()) {
            printf("Error: EnableWifi fail.\n");
            return -1;
        }
        
        OS_Sleep(1);
        
        if (WIFI_STA_ACTIVE == IsWifiActive())
            printf("Wifi is active.\n");
    
        OS_Sleep(1);
        
        if (WIFI_SUCCESS != Scan()) {
            printf("Error: Scan fail.\n");
            return -1;
        }
        OS_Sleep(3);
    
        WifiScanInfo scan_results[30];
        unsigned int scan_num = 30;
        if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) {
            printf("Error: GetScanInfoList fail.\n");
            return -1;
        }
    
        WifiDeviceConfig config = { 0 };
        int netId = 0;
    
        int i;
        for (i = 0; i < scan_num; i++) {
            if (0 == strcmp(scan_results[i].ssid, ssid_want_connect)) {
                memcpy(config.ssid, scan_results[i].ssid,
                       WIFI_MAX_SSID_LEN);
                memcpy(config.bssid, scan_results[i].bssid,
                       WIFI_MAC_LEN);
                strcpy(config.preSharedKey, psk);
                config.securityType = scan_results[i].securityType;
                config.wapiPskType = WIFI_PSK_TYPE_ASCII;
                config.freq = scan_results[i].frequency;
                break;
            }
        }
    
        if (i >= scan_num) {
            printf("Error: No found ssid in scan_results\n");
            return -1;
        }
    
        if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) {
            printf("Error: AddDeviceConfig Fail\n");
            return -1;
        }
    
        if (WIFI_SUCCESS != ConnectTo(netId)) {
            printf("Error: ConnectTo Fail\n");
            return -1;
        }
        printf("ConnectTo Success\n");
    
        OS_Sleep(15);
        
        return 0;
    }
    
    void wifiDisconnect()
    {
        printf("\n=========== DisConnect Test Start ===========\n");
        if (WIFI_SUCCESS != Disconnect()) {
            printf("Error: Disconnect Fail\n");
            return;
        }
        printf("Disconnect Success\n");
    
        if (WIFI_SUCCESS != DisableWifi()) {
            printf("Error: DisableWifi fail.\n");
            return;
        }
        printf("DisableWifi Success\n");
        printf("\n=========== DisConnect Test End ===========\n");
    }
    
    static void MainThread(void *arg)                                               
    {
        int s32Ret = 0;
    
        s32Ret = wifiConnect();
        if (0 != s32Ret)
        {
            printf("wifiConnect failed\n");
            return;
        }
    
        IoTGpioInit(GPIO_ID_PA21);
        IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT);
     
        // 用于创建vhost或者context的参数
        struct lws_context_creation_info ctx_info = { 0 };
        ctx_info.port = CONTEXT_PORT_NO_LISTEN;
    //    ctx_info.iface = NULL;
        ctx_info.protocols = protocols;
        ctx_info.gid = -1;
        ctx_info.uid = -1;
        
        // 创建一个WebSocket处理器
        struct lws_context *context = lws_create_context( &ctx_info );
    
        char address[] = WEBSOCKET_HOST;
        int port = WEBSOCKET_PORT;
        char addr_port[256] = { 0 };
        sprintf(addr_port, "%s:%u", address, port & 65535 );
        
        // 客户端连接参数
        struct lws_client_connect_info conn_info = { 0 };
        conn_info.context = context;
        conn_info.address = address;
        conn_info.port = port;
        conn_info.path = WEBSOCKET_PATH;
        conn_info.host = addr_port;
        conn_info.origin = addr_port;
        conn_info.protocol = protocols[ 0 ].name;
     
        // 下面的调用触发LWS_CALLBACK_PROTOCOL_INIT事件
        // 创建一个客户端连接
        struct lws *wsi = lws_client_connect_via_info( &conn_info );
                    
        while (1) {
        
            // 执行一次事件循环(Poll),最长等待1000毫秒
            lws_service( context, 1000 );
            /**
             * 下面的调用的意义是:当连接可以接受新数据时,触发一次WRITEABLE事件回调
             * 当连接正在后台发送数据时,它不能接受新的数据写入请求,所有WRITEABLE事件回调不会执行
             */
            lws_callback_on_writable( wsi );
        }
        // 销毁上下文对象
        lws_context_destroy( context );
    
        wifiDisconnect();
    }
    //连接websocket 进行开灯,关灯操作
    
    //wifi 连接
    void demoMain(void)                                                              //(2)
    {
        printf("websocket Test Start\n");
    
        if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL,
            OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK)
        {
            printf("[ERR] Create MainThread Failed\n");
        }
    }
    
    SYS_RUN(demoMain); 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261

    三、效果演示

    服务器用了网上下载的个WebssocketMan工具来模拟,网盘链接
    链接:https://pan.baidu.com/s/1t82q_1evB5Zla-Hc1m4SYA
    提取码:4pxu
    在这里插入图片描述

    登陆成功后打印显示:
    在这里插入图片描述

    登录后平台收到“/light/open”消息为点灯,“/light/close”为关灯
    演示截图:
    在这里插入图片描述
    在这里插入图片描述

    四、问题记录

    在打开wifi例子,编译过程中遇到了分区叠加的错误,具体如图(感谢群里的朋友、社区以及论坛的帮助)
    在这里插入图片描述

    因为内存分配不够,官方文档描述
    device\xradio\xr806\xr_skylark\project\image_cfg\readme.md
    在这里插入图片描述

    按照网上说的修改:
    device\xradio\xr806\xr_skylark\project\demo\wlan_ble_demo\image\xr806\image_wlan_ble.cfg 分配不能够生效
    最终解决:xr806\device\xradio\xr806\xr_skylark\project\demo\audio_demo\image\xr806\目录中将 image_auto_cal.cfg替换image.cfg

  • 相关阅读:
    【WFA】【Enhanced open】CT_OWE_DHgroup_STA_NoAssociation-AllGroupsRejected_10338_1
    Shiro学习笔记_02:shiro的认证+shiro的授权
    华为认证 | 这门HCIA认证正式发布!
    react router v6实现useHistory与自定义history设计思路
    sql注入Less-2
    mysql建数据库时我们的字符集如何选择
    跨境电商指南:如何处理客户投诉
    离线部署神器yumdownloader
    【Python】常量和变量类型
    2023下半年软件设计师考试知识点大全思维导图
  • 原文地址:https://blog.csdn.net/weixin_47569031/article/details/136731836