OpenHarmony实战开发——有氧拳击之设备端开发

一、简介

在一个风和日丽,阳光明媚的下午,码农们都像往常一样正在专注地码代码。突然前面的小哥哥站起来,手握开发板,来回出拳。这是怎么回事?

原来这是一款拳击互动游戏,本文将带你一同解开其中的奥秘。开发者从中不仅能体验到学习知识的愉悦,还能享受到健身的乐趣。依托OpenAtom OpenHarmony(以下简称"OpenHarmony")3.2 Beta1操作系统,样例分为应用端和设备端两部分。本文主要介绍设备端的实现,后续会分享应用端的开发。

设备端:采用Hi3861开发板,处理加速度计传感器数据。

应用端:采用DAYU200(RK3568)开发板,主要处理显示及音效。

如下图,左侧为设备端,右侧为应用端:开发者手握设备端开发板,观察屏幕,根据应用端APP显示,在指定的时间完成挥拳动作;挥拳信息经无线传递到应用端,应用端APP对挥拳时机有相应的计分规则,最后统计出总分。

二、原理

相比正常状态下,挥拳动作会引起手臂较大的加速度变化。根据这个特征,我们使用BearPi-HM_Nano开发板的扩展模块E53_SC2,它内部集成了MPU6050传感器,能够读取加速度的大小。

做挥拳动作实验,统计数据,得到挥拳时加速度的阈值。程序执行时,把实时的数据与阈值进行比较,判断是否触发了挥拳动作。再经过无线通信,实时把数据发送到应用端。

三、加速度计传感器使用说明

设备端的开发关键在对加速度计传感器的使用,主要涉及两点:1、重力加速度g的理解;2、如何把MPU6050寄存器的数据转化为有单位的数据?

1、样例使用的加速度传感器是MPU6050,它有±2g、±4g、±8g和±16g四个量程可以选择。一个g是指一个重力加速度,代表9.8米/秒²大小。举个例子:假如设备从高处掉落,其加速计测量到的加速度将为0g,因为传感器没有受到力的挤压,处在失重状态;假如设备水平放在桌面上,则加速计测量出的加速度为1g(9.8米/秒²),我们可以理解为受到1g的压力;

2、MPU6050采用16位的ADC采样。16位的ADC采样是什么意思?举个例子:如果量程选择(通过寄存器选择)是±2g,16位的ADC采样,表示的含义是用65536(即2的16次方)种情况去表达-2到+2g的情况。如下datasheet截图显示,AFS_SEL=0,表示±2g量程,当数据寄存器的数据为16384,对应表示受到1g的力。例如:数据寄存器读取到的值为X,对应受到的力的大小为Y,则Y=X/16384,单位是g。

四、代码解析

设备端代码主要分为两个线程:1、传感器数据处理线程;2、TCP通信线程;它们之间通过事件的方式进行同步通信。

1、传感器数据处理线程主要函数说明:

//E53_SC2模块MPU6050传感器数据处理主要流程
static void DataHandleTask(void)
{
    uint8_t ret;
    ret = E53SC2Init();//MPU6050传感器初始化及配置,配置为+---8g量程
    if (ret != 0) {
        printf("E53_SC2 Init failed!\r\n");
        return;
    }
    while (1)  {
        ret = E53SC2ReadData(&data);//MPU6050传感器寄存器数据读取
        if (ret != 0)  {
            printf("E53_SC2 Read Data!\r\n");
            return;
        }
        AccDataHandle(&data);//MPU6050传感器数据处理,转化为单位为g的数据
        if (myCaldata.Accel[ACCEL_X_AXIS] < 0) {
            myCaldata.Accel[ACCEL_X_AXIS] = myCaldata.Accel[ACCEL_X_AXIS] * -1.0;
        }
        if (myCaldata.Accel[ACCEL_Y_AXIS] < 0) {
            myCaldata.Accel[ACCEL_Y_AXIS] = myCaldata.Accel[ACCEL_Y_AXIS] * -1.0;
        }
        if (myCaldata.Accel[ACCEL_Z_AXIS] < 0) {
            myCaldata.Accel[ACCEL_Z_AXIS] = myCaldata.Accel[ACCEL_Z_AXIS] * -1.0;
        }
        //判断实时数据是否大于拳击阈值Boxing_ACC,大于则设置事件
       if (myCaldata.Accel[ACCEL_X_AXIS] > Boxing_ACC ||                          myCaldata.Accel[ACCEL_Y_AXIS] > Boxing_ACC || myCaldata.Accel[ACCEL_Z_AXIS] > Boxing_ACC) {
            printf("MPU set flg\r\n");
            osEventFlagsSet(g_eventFlagsId, FLAGS_MSK1);//触发拳击事件
        }
        usleep(Delay_10ms);
    }
}
#define MAX_POS_NUM 32767
#define LSB 4096.0
//MPU6050传感器数据处理,转化为单位为g的数据
int AccDataHandle(E53SC2Data *dat)
{    //量程为+-8g,所以分辨率为4096
    if (dat->Accel[ACCEL_X_AXIS] <  MAX_POS_NUM) {
        myCaldata.Accel[ACCEL_X_AXIS] = dat->Accel[ACCEL_X_AXIS]/LSB;
    } else {
        myCaldata.Accel[ACCEL_X_AXIS] =(-1)* (dat->Accel[ACCEL_X_AXIS]-MAX_POS_NUM)/LSB;
    }
    if (dat->Accel[ACCEL_Y_AXIS] <  MAX_POS_NUM) {
        myCaldata.Accel[ACCEL_Y_AXIS] = dat->Accel[ACCEL_Y_AXIS]/LSB;
    } else {
        myCaldata.Accel[ACCEL_Y_AXIS] = (-1)*(dat->Accel[ACCEL_Y_AXIS]-MAX_POS_NUM)/LSB;
    }
    if (dat->Accel[ACCEL_Z_AXIS] <  MAX_POS_NUM) {
        myCaldata.Accel[ACCEL_Z_AXIS] = dat->Accel[ACCEL_Z_AXIS]/LSB;
    } else {
        myCaldata.Accel[ACCEL_Z_AXIS] =(-1)*(dat->Accel[ACCEL_Z_AXIS]-                   MAX_POS_NUM)/LSB;
    }
return 0;
}

2、TCP通信线程主要函数说明:

在本样例的网络通信中,Hi3861 开发板作为客户端,DAYU200(RK3568)开发板作为服务端。它们之间采用TCP机制通信。

如下代码:建立好TCP通信后,常规状态下通信线程处在阻塞态,当拳击事件触发后,则会发送信息给服务端:

static void TCPClientTask(void)
{
    // 在sock_fd 进行监听,在 new_fd 接收新的链接
    int sock_fd;
    uint32_t flags;
    struct sockaddr_in send_addr;   // 服务器的地址信息
    socklen_t addr_length = sizeof(send_addr);
    char recvBuf[recvLen];
    memset(recvBuf, '\0', sizeof(recvBuf));
    // 连接Wifi
    WifiConnect(CONFIG_WIFI_SSID, CONFIG_WIFI_PWD);
    // 创建socket
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("create socket failed!\r\n");
        exit(1);
    }
    // 初始化预连接的服务端地址
    send_addr.sin_family = AF_INET;
    send_addr.sin_port = htons(CONFIG_SERVER_PORT);
    send_addr.sin_addr.s_addr = inet_addr(CONFIG_SERVER_IP);
    addr_length = sizeof(send_addr);
    //连接
    connect(sock_fd,(struct sockaddr *)&send_addr, addr_length);
    printf("TCPClient connect success\r\n");
 
 
    while (1) {
        memset(recvBuf, '\0', sizeof(recvBuf));
  //等待事件是否触发
        flags = osEventFlagsWait(g_eventFlagsId, FLAGS_MSK1, osFlagsWaitAny, osWaitForever);
        printf("TCP get flag\r\n");
        sprintf(sendbuf,"right\r\n");
        send(sock_fd, sendbuf, strlen(sendbuf), 0);//tcp发出触发信息
        // 线程休眠一段时间
        usleep(Delay_100ms);//100ms
    }
    closesocket(sock_fd);
}

五、代码构建、编译及烧录

1、OpenHarmony 3.2 Beta1源码下载,地址参考文章结尾处链接;

2、在源码根目录下的vendor目录下,新建team_x文件夹;

3、把boxing文件夹,拷贝到team_x目录下,如下图所示:

4、在源码目录下,输入hb set,然后选择当前文件路径,即输入.(点),然后通过方向键选取team_x下的boxing,如下图:

5、输入hb build -f,开始编译,编译成功后,会在根目录下的out/bearpi_hm_nano/boxing目录生成Hi3861_wifiiot_app_allinone.bin,如下图:

6、用HiBurn工具烧录程序,烧录参考链接在文章结尾处;

烧录成功后,可以本地验证项目是否成功:

1、电脑端使用网络调试助手软件,建立TCP服务端,电脑端建立服务端需要注意以下几点:

(1)电脑与BearPi-HM Nano开发板连入同一个Wi-Fi热点,如图:电脑与开发板都连入热点"YYYYYY";

(2)BearPi-HM Nano开发板程序设置的IP,电脑的IP,网络调试助手服务端的IP,三者保持一致,如下图"192.168.1.100";

(3)点击网络调试助手的"连接"按钮,即先启动服务端。

2、BearPi-HM Nano开发板串口接入电脑,设置波特率为115200;

3、复位BearPi-HM Nano开发板,复位后,串口会打印Wi-Fi连接成功、TCP连接成功等信息,如下图(右侧);

4、手握开发板,尝试出拳(即挥动开发板)。能看到网络助手的TCP服务端窗口,成功接收到同步挥拳信息"right",如下图(左侧):

六、总结

本文主要讲述了拳击互动游戏中,关于设备端的开发,使用 Hi3861开发板硬件,在相关基础例程上做了二次开发。本设备端开发,使用了OpenHarmony的线程、事件、GPIO、IIC、TCP通信等相关基础知识,再结合加速度计传感器的使用,实现与应用端同步交互的功能。

本样例是OpenHarmony知识体系工作组(相关链接在文章末尾)为广大开发者分享的样例。知识体系工作组结合日常生活,给开发者规划了各种场景的Demo样例,如智能家居场景、影音娱乐场景、运动健康场景等;欢迎广大开发者一同参与OpenHarmony的开发,更加完善样例,相互学习,相互进步。

为了帮助到大家能够更有效的学习OpenHarmony 开发的内容,下面特别准备了一些相关的参考学习资料:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ......

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ......

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

相关推荐
NiNg_1_2341 小时前
基于Hadoop的数据清洗
大数据·hadoop·分布式
思忖小下1 小时前
梳理你的思路(从OOP到架构设计)_简介设计模式
设计模式·架构·eit
隔着天花板看星星2 小时前
Spark-Streaming集成Kafka
大数据·分布式·中间件·spark·kafka
Damon小智6 小时前
HarmonyOS NEXT 技术实践-基于基础视觉服务的多目标识别
华为·harmonyos
技术路上的苦行僧6 小时前
分布式专题(8)之MongoDB存储原理&多文档事务详解
数据库·分布式·mongodb
龙哥·三年风水7 小时前
workman服务端开发模式-应用开发-后端api推送修改二
分布式·gateway·php
小小工匠7 小时前
分布式协同 - 分布式事务_2PC & 3PC解决方案
分布式·分布式事务·2pc·3pc
邓校长的编程课堂7 小时前
基于树莓派Pico和声音传感器实现声控风扇的技术分享
物联网·嵌入式开发·树莓派pico·编程入门·c++编程·声音传感器·c++趣味编程
一个儒雅随和的男子8 小时前
微服务详细教程之nacos和sentinel实战
微服务·架构·sentinel
腾讯云开发者8 小时前
AI时代,需要怎样的架构师?腾讯云架构师峰会来了!
架构