Navigation

    全志在线开发者论坛

    • Register
    • Login
    • Search
    • Categories
    • Tags
    • 在线文档
    • 社区主页

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

    Wireless & Analog Series
    1
    1
    966
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • X
      xisecret LV 3 last edited by xisecret

      一、开发板编译、功能介绍
      根据官方文档编译烧录成功后,我们修改下官方例子,进行开发来实现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",
         ]
      }
      

      main.c

      #include <stdio.h>
      #include <string.h>
      
      #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); 
      
      

      三、效果演示
      服务器用了网上下载的个WebssocketMan工具来模拟,网盘链接
      链接:https://pan.baidu.com/s/1t82q_1evB5Zla-Hc1m4SYA
      提取码:4pxu
      1.png
      登陆成功后打印显示:
      2.png
      登录后平台收到“/light/open”消息为点灯,“/light/close”为关灯
      演示截图:
      3.png

      四、问题记录
      在打开wifi例子,编译过程中遇到了分区叠加的错误,具体如图(感谢群里的朋友、社区以及论坛的帮助)
      4.png
      因为内存分配不够,官方文档描述
      device\xradio\xr806\xr_skylark\project\image_cfg\readme.md
      5.png
      按照网上说的修改:
      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

      1 Reply Last reply Reply Quote Share 0
      • 1 / 1
      • First post
        Last post

      Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

      行为准则 | 用户协议 | 隐私权政策