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

news2025/1/11 16:52:29

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

根据官方文档编译烧录成功后,我们修改下官方例子,进行开发来实现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
在这里插入图片描述

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

登录后平台收到“/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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1519881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

升入理解计算机系统学习笔记

磁盘存储 磁盘是广为应用的保存大量数据的存储设备&#xff0c;存储数据的数量级可以达到几百到几千千兆字节&#xff0c;而基于RAM的存储器只能有几百或几千兆字节。不过&#xff0c;从磁盘上读信息的时间为毫秒级&#xff0c;比从DRAM读慢了10万倍&#xff0c;比从SRAM读慢了…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:GridItem)

网格容器中单项内容容器。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。仅支持作为Grid组件的子组件使用。 子组件 可以包含单个子组件。 接口 GridItem GridItem(value?: GridItemOptions)…

DVWA靶场-CSRF跨站请求伪造

CSRF(跨站请求伪造)简介概念 CSRF&#xff08;Cross—site request forgery&#xff09;&#xff0c;跨站请求伪造&#xff0c;是指利用受害者未失效的身份认证信息&#xff08;cookie&#xff0c;会话等&#xff09;&#xff0c;诱骗其点击恶意链接或者访问包含攻击代码的页面…

UG NX二次开发(C#)-单选对话框UF_UI_select_with_single_dialog的使用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、UF_UI_select_with_single_dialog函数3、实现代码3.1 利用委托创建一个方法3.2 直接调用1、前言 对于单选对话框,采用C++/C写的时候比较容易,也在帮助文档中有示例,但是对于C#开发采…

修复ElementUI中el-select与el-option无法通过v-model实现数据双向绑定的问题

1. 问题描述 需求&#xff1a;在使用ElementUI时&#xff0c;通过el-select和el-option标签实现下拉列表功能&#xff0c;当el-option中的选项被选中时&#xff0c;被选中的选项可以正确回显到已选择的列表中。 对于上面的下拉列表&#xff0c;当我们选中“超级管理员”的选项…

小程序路由跳转---事件通信通道EventChannel(一)

EventChannel是什么&#xff1f; 借助wx.navigateTo方法&#xff0c;在两个页面之间构建起数据通道&#xff0c;互相可以通过“派发事件”及“注册事件监听器”来实现基于事件的页面通信。基础库版本v2.7.3以上支持。 EventChannel中主要的方法 EventChannel.emit( strign e…

【四 (3)数据可视化之 Seaborn 常用图表及代码实现 】

目录 文章导航一、介绍二、安装Seaborn三、导入Seaborn四、设置可以中文显示五、占比类图表1、饼图2、环形图 六、比较排序类1、条形图2、箱线图3、小提琴图 七、趋势类图表1、折线图 八、频率分布类1、直方图 九、关系类图表1、散点图2、成对关系图3、热力图 文章导航 【一 简…

手机备忘录怎么导出到电脑,如何将手机备忘录导出到电脑

备忘录是我们日常生活和工作中常用的工具之一&#xff0c;我们可以在手机上轻松地记录重要的事务、想法和灵感。然而&#xff0c;在某些情况下&#xff0c;我们可能需要将手机备忘录导出到电脑进行更详细的整理和管理。那么&#xff0c;手机备忘录怎么导出到电脑&#xff0c;如…

ASP.NET-Server.HtmlEncode

目录 背景: 1.转义特殊字符&#xff1a; 2.防止跨站脚本攻击&#xff08;XSS&#xff09;&#xff1a; 3.确保输出安全性&#xff1a; 4.保留原始文本形式&#xff1a; 5.与用户输入交互安全&#xff1a; 实例说明: 不用Server.HtmlEncode 效果展示: 用Server.HtmlEnc…

【Numpy】练习题100道(26-50题)

#学习笔记# 在学习神经网络的过程中发现对numpy的操作不是非常熟悉&#xff0c;遂找到了Numpy 100题。 Git-hub链接 1.题目列表 26. 下面的脚本输出什么&#xff1f;(★☆☆) print(sum(range(5),-1)) from numpy import * print(sum(range(5),-1)) 27. 考虑一个整数向量…

TCP的三次握手和4次挥手

一、首先讲一下TCP的由来 最开始&#xff0c;人们考虑到将网络信息的呼唤与回应进行规范&#xff0c;达成一种公认的协议&#xff0c;就好像没有交通规则的路口设定交通规则。 人们设计出完美的OSI协议&#xff0c;这个协议包含七个层次由下到上分别是&#xff1a; 物理层&…

【Unity】进度条和血条的三种做法

前言 在使用Unity开发的时候&#xff0c;进度条和血条是必不可少的&#xff0c;本篇文章将简单介绍一下几种血条的制作方法。 1.使用Slider Slider组件由两部分组成&#xff1a;滑动区域和滑块。滑动区域用于显示滑动条的背景&#xff0c;而滑块则表示当前的数值位置。用户可…

因聚而生 数智有为丨软通动力携子公司鸿湖万联亮相华为中国合作伙伴大会2024

3月14日&#xff0c;以“因聚而生 数智有为”为主题的“华为中国合作伙伴大会2024”在深圳隆重开幕。作为华为的重要合作伙伴和本次大会钻石级&#xff08;最高级&#xff09;合作伙伴&#xff0c;软通动力深度参与本次盛会&#xff0c;携前沿数智化技术成果和与华为的联合解决…

Python之Web开发中级教程----创建Django子应用

Python之Web开发中级教程----创建Django子应用 基于上一个教程的Django项目&#xff08;可以先看上一集&#xff0c;链接如下&#xff1a;&#xff09; https://mp.csdn.net/mp_blog/creation/editor/136724897 2.创建子应用 python manager.py startapp book admin.py&…

蓝桥杯 EDA 组 2021-2022 省赛真题+模拟题原理图解析

本文解析了标题内的原理图蓝桥杯EDA组真题&#xff0c;为方便阅读2023年真题/模拟和国赛部分放到其他章节解析。下文中重复或者是简单的电路节约篇幅不在赘述。 其中需要补充和计算原理图的题目解析都放在最下面 一、2021第十二届真题第一场 1.1 AMS1117 线性稳压器 最常见的1…

工具类实现导出复杂excel、word

1、加入准备的工具类 package com.ly.cloud.utils.exportUtil;import java.util.Map;public interface TemplateRenderer {Writable render(Map<String, Object> dataSource) throws Throwable;}package com.ly.cloud.utils.exportUtil;import java.util.Map;public int…

AI学习笔记之六:无监督学习如何帮助人类挖掘数据金矿和防范网络欺诈

在这个大数据时代&#xff0c;企业和组织在过去几十上百年的经营过程中积累了大量的原始数据&#xff0c;其中蕴含着宝贵的商业价值和见解。然而&#xff0c;要从这些海量的、未经标记和处理的数据中发现隐藏的规律和知识&#xff0c;并不是一件容易的事情。这就好比要从一座巨…

测试用例的设计(1)

目录 1. 测试用例的基本要素 2.测试用例的设计方法 2.1.基于需求设计 2.2根据功能需求测试 2.3非功能测试 3. 具体的设计方法 3.1等价类法 3.2边界值法 3.3判定表 1. 测试用例的基本要素 测试用例是为了实施测试而面向测试的系统提供的一组集合,这组集合包含:测试环境,…

使用FFmpeg源码配置程序configure查看所有支持的编码器/解码器/封装/解封装及网络协议

查看支持编码器: configure --list-encoders 查看支持编码器: configure --list-decoders 查看所有支持的封装: configure --list-muxers 查看所有支持的解封装: configure --list-demuxers 查看所有支持的网络通信协议: configure --list-protocols

数据预处理|数据清洗|使用Pandas进行异常值清洗

数据预处理|数据清洗|使用Pandas进行异常值清洗 使用Pandas进行异常值清洗1. 异常值检测1.1 简单统计分析1.2 散点图方法1.3 3σ原则1.4 箱线图 2. 异常值处理2.1 直接删除2.2 视为缺失值2.3 平均值修正2.4 盖帽法2.5 分箱平滑法2.6 回归插补2.7 多重插补2.8 不处理 使用Pandas…