【本文正在参加2023年第一期优质创作者激励计划】
基于标准系统的TCP和WAP使用-实现一个远程示波器
1 综述
近期在学习标准系统wifi能力子系统过程中同时也在调试一台OpenHarmony小车底盘机,因为不能像以前调试STM32使用 Cube monitor进行方便的数据观测,进行PID和其他控制算法调参,完成小车的闭环自动控制,所以自己使用wpa子系统构建wifi访问,在本地widows主机上基于easyX构建了一套示波器用于PID参数调测,链路传输使用TCP进行数据传输。
2 关于标准系统南向设备应用程序开发的建议
一般对于新操作系统,尤其是类似OHOS这类不能够支持本地化编程,目前还没能移植VIM,GCC等工具进行非侵入式编程的操作系统,一般会使用linux本地机器进行程序验证,再加载到新操作系统的环境当中进行适配编译,这样可以有效减少编译浪费的时间。
3WPA子系统配置自启动开发
3.1WPA子系统简介
OpenHarmony的wifi能力使用基于第三方开源wifi软件WPA-supplicant子系统完成,这部分在OpenHarmony系统中进行了深度移植和融合
wpa_supplicant是跨平台的开源软件,支持WPA、WPA2、WPA3(IEEE 802.11i)等Wlan接入技术,可用于台式机、笔记本电脑、手机和其他嵌入式系统。
wpa_supplicant
支持多种网络管理器,如 NetworkManager 和 wicd,也可以通过命令行界面直接使用。用户可以通过编辑 wpa_supplicant
配置文件来配置连接的 Wi-Fi 网络的参数,例如 SSID、密码、加密类型等。
除了作为 Wi-Fi 客户端,wpa_supplicant
还支持将 Linux 计算机配置为 Wi-Fi 热点,使其他设备能够通过该计算机连接到 Wi-Fi 网络。
在OpenHarmony中,wpa_supplicant用来提供Wi-Fi接入和Wi-Fi热点开启的协议栈。

我们使用WPA相关的配置和设置来进行wifi配置,
接口层 :提供wpa_supplicant与Wi-Fi服务通信的接口。
wpa协议栈 :提供Wi-Fi接入与热点功能,其中wpa_supplicant负责Wi-Fi接入,hostapd负责热点功能。
我们主要使用接口层的能力,进行两种类型的配置:
- wifi连接 即本机设置为STA模式,连接到其他热点
- 热点模式 即本机设置为AP模式,其他设备可以链接到本机
配置为任意一种形式取决于当前场景的需要接下来对这两种配置模式进行分析和实操
3.2STA模式
此处需要wpa配置文件从而使能联网配置,如果使用STA模式需要创建一个 wpa_supplicant.conf 配置文件其中包含要连接的 Wi-Fi 网络的 SSID 和密码等信息
注意替换 your_SSID
和 your_password
为你要连接的 Wi-Fi 网络的 SSID 和密码。
在文件处理阶段有个核心问题,如果我们想把WIFI配置作为初始文件写入系统,无法在编码阶段创造好该文件,在打包结束后我们自己写的定义设置文件会丢失或者无法找到相对路径,这时候可以通过重新添加文件分区的方式进行添加,需要用到如下工具,当然如果非必须要开机初始化,可以使用hdcstd工具send功能将文件传输到系统当中,并作为参数文件调用。
natinusala/linux-amlogic-toolkit: Allows to unpack and repack AMLogic images for Android 7 without the Customization Tool (github.com)
如果重新创建分区要使用到芯片厂商的打包工具以及开源打包工具mtd 通过以下指令安装和打包,打包后可以将原本的OpenHarmony.img通过上述工具amologic-toolkits进行解包,再重写进行打包,完成后可以重新烧录
(第二段指令将userfs文件打包为userfs.img镜像文件)
在开发板上电后连接到终端控制台
输入指令
执行wpa_supplicant 脚本,配置conf文件到系统中,连接到指定网络。
3.3AP模式(未测试)
将conf文件编辑为
重复在STA模式中配置的方式,用以下命令启动热点
-B 表示在后台执行 ,-i表示参数后跟无线接口名称, -c 是conf文件的路径。
3.4WPA命令行常用指令
3.5查询IP地址
使用ipconfig指令查询当前网络地址
4TCP 实现
4.1TCP基本概念和主要特点
TCP(Transmission Control Protocol)是一种传输层协议,用于在网络上可靠地传输数据。TCP 是一种面向连接的协议,使用三次握手建立连接,并使用四次挥手终止连接。
TCP 的主要特点包括:
- 可靠性:TCP 通过序列号和确认应答机制,确保数据传输的可靠性和完整性。如果数据包在传输过程中丢失或损坏,TCP 会自动重传该数据包,直到接收方收到正确的数据为止。
- 流量控制:TCP 使用滑动窗口机制来控制数据的流量,确保发送方和接收方之间的数据传输速度相匹配,防止数据包拥塞和丢失。
- 拥塞控制:TCP 可以检测网络拥塞情况,并采取相应的措施,例如降低数据传输速度,以避免拥塞的发生。
- 面向连接:TCP 使用三次握手建立连接,并使用四次挥手终止连接,确保数据传输的可靠性和完整性。
4.2使用C实现TCP的基本操作
在LINUX中使用socket通信方式的TCP协议来实现数据传输,需要分别实现服务器端,和客户端,在这次的样例实现当中,九联UnionPi开发板作为服务端,因为有多个设备需要接入。
4.2.1TCP服务端样例代码
4.2.2TCP客户端样例代码
在案例中实现了简单的TCP客户端和服务端,客户端向服务端发送HELLO。
5OHOS端实现线程通信部署和数据发送
5.1目录结构
在之前的开发中对于小车的IMU数据进行了收取,以及小车底盘,蓝牙模块等进行了控制,通过工程化设计,分别划分在不同的线程当中,并进行了低耦合高内聚的函数化设计。
usr_pthread 负责进行所有线程的实现,main负责线程入口控制和初始化设置。线程间使用消息队列进行通信,因为存在IMU加速度计等信息高频刷新需要进行数据低延迟发送。serial_base和serial_protocal分别实现了串口的底层驱动和串口基础协议。
5.2 消息队列
消息队列样例演示线程1 向线程2发送数据:
在所有的线程当中都实现了类似的消息队列结构来保证数据的完整性和实效性。
绑定了socket通信格式和维特智能提供的IMU SDK后架构如下,不详细展开描述,只描述与示波器相关部分。




5.3 TCP线程
(暂时这部分还有一些BUG,以及没有完整封装,在构建完成后会重新更新代码仓库)
通过一个线程使用socket通信获取PID控制器返回的数据存入发送缓冲区,再将其发送出去
整套小车控制程序在实现小车的偏航角闭环,此时要发送到客户端示波器的数据是当前角度。
我们现在将pid.actangle发送到17.20.10.1
6Windows端实现示波器
6.1 使用easyX图形库绘制波形图
由于熟悉的绘制波形图的图形库easyX在windows VS才能提供有效支持,所以暂时使用Windows的easyX图形库进行绘制
easyX图形库使用链接EasyX Graphics Library for C++

![image-20230327203843023]
6.2 使用Windows socket完成网络通信
6.3 结合两者完成示波器


切分为两个文件并把从socket读取到的数据放入示波器中进行展示


grsp.cpp
#include"grsp.h"
void num2WSTR(double x, wchar_t*& buffer)
{
std::wstringstream ss;
ss << x;
size_t size = ss.str().length();
buffer = new wchar_t[size + 1];
ss >> buffer;
buffer[size] = 0;
}
DynaWin::DynaWin(double XLIM, double YLIM, unsigned short xSegment, unsigned short ySegment, int xResolution)
{
this->XLIM = XLIM;
this->YLIM = YLIM;
this->xSegment = xSegment;
this->ySegment = ySegment;
this->xResolution = xResolution;
this->DynaQue = new double[xResolution + 1];
this->xAxis = new AxisX[xSegment + 1];
}
void DynaWin::InitBackGround()
{
initgraph(1100, 400, EX_SHOWCONSOLE);
setlinecolor(RED);
setlinestyle(PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3);
setorigin(CENTERX, CENTERY);
setaspectratio(1, -1);
rectangle(0, 150, 1000, -150);
line(0, 0, 1000, 0);
setbkmode(TRANSPARENT);
settextcolor(YELLOW);
setaspectratio(1, 1);
settextstyle(30, 0, L"宋体");
outtextxy(450, -185, L"EasyPlot");
settextstyle(10, 0, L"宋体");
outtextxy(850, 160, L"Author:Dylan");
outtextxy(870, 180, L"Version:1.0");
unsigned short yVertex = 2 * ySegment + 1;
double yUnit = YLIM / ySegment;
setbkmode(TRANSPARENT);
settextcolor(YELLOW);
settextstyle(15, 0, L"10");
setlinestyle(PS_DASH, 1);
for (int i = 1; i <= yVertex; ++i)
{
double label = YLIM - (i - 1) * yUnit;
wchar_t* buffer;
num2WSTR(label, buffer);
outtextxy(-30, (int)((i - ySegment - 1) * (ROWS / ySegment)) - 10, buffer);
delete[] buffer;
buffer = (wchar_t*)NULL;
line(0, (int)((i - ySegment - 1) * (ROWS / ySegment)), 1000, (int)((i - ySegment - 1) * (ROWS / ySegment)));
}
setaspectratio(1, -1);
canvas = GetWorkingImage();
getimage(&local, -50, -160, 1100, 320);
}
void DynaWin::UpateWin(double y)
{
++DynaNum;
UpdateAxisX();
if (DynaNum <= xResolution + 1)
DynaQue[DynaNum - 1] = y;
else
{
for (int i = 0; i <= xResolution - 1; ++i)
{
DynaQue[i] = DynaQue[i + 1];
}
DynaQue[xResolution] = y;
}
}
void DynaWin::ShowWin()
{
SetWorkingImage(canvas);
putimage(-50, -160, &local);
setlinecolor(RED);
setlinestyle(PS_DASH, 2);
for (int j = 0; j <= xSegment - 1; ++j)
{
line((int)xAxis[j].index, -150, (int)xAxis[j].index, 150);
setaspectratio(1, 1);
setbkmode(TRANSPARENT);
settextcolor(YELLOW);
settextstyle(15, 0, L"10");
double label = xAxis[j].label;
wchar_t* wstr;
num2WSTR(label, wstr);
outtextxy((int)xAxis[j].index - 10, 0, wstr);
delete[] wstr;
wstr = NULL;
setaspectratio(1, -1);
}
setlinecolor(WHITE);
setlinestyle(PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 2);
if (DynaNum <= xResolution)
{
for (int i = 0; i <= DynaNum - 2; ++i)
{
line((int)(i * COLS / xResolution), (int)(DynaQue[i] / YLIM * ROWS), (int)((i + 1) * COLS / xResolution), (int)(DynaQue[i + 1] / YLIM * ROWS));
}
}
else
{
for (int i = 0; i <= xResolution - 1; ++i)
{
line((int)(i * COLS / xResolution), (int)(DynaQue[i] / YLIM * ROWS), (int)((i + 1) * COLS / xResolution), (int)(DynaQue[i + 1] / YLIM * ROWS));
}
}
}
void DynaWin::UpdateAxisX()
{
if (DynaNum <= xResolution)
for (int i = 0; i <= xSegment - 1; ++i)
{
xAxis[i].index = (i + 1) * COLS / xSegment;
xAxis[i].label = (i + 1) * XLIM / xSegment;
}
else
{
for (int i = 0; i <= xSegment - 1; ++i)
{
xAxis[i].index -= COLS / xResolution;
}
if (xAxis[0].index <= 0)
{
int j;
for (j = 0; j <= xSegment - 2; ++j)
{
xAxis[j].index = xAxis[j + 1].index;
xAxis[j].label = xAxis[j + 1].label;
}
xAxis[j].index = xAxis[j - 1].index + COLS / xSegment;
xAxis[j].label = xAxis[j - 1].label + XLIM / xSegment;
}
}
}
- 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.
grsp.h
socket.c
7总结与注意事项
注意事项
- 在socket中进行地址族设置时,服务器端将地址设置为本地地址,客户端将地址设置为服务器端地址。
- easyX的环境配置要注意属性设置和父类集成
- 示波器响应速度提升可以通过减少冗余数据实现,以及车机滤波器响应参数调高,IMU的频率调高,可以实现更加有效的参数调制。
- OpenHarmony的wpa子系统使用与linux的基本一致,可以很方便的完成wifi配置
车机迭代图片记录
第一代 OpenHarmony作为ROS节点运行


第二代 OpenHarmony作为车体主控平台运行

总结
使用OpenHarmony进行车机控制可以获得比较好的实时性,比普通liunx响应速度要快,代码体量子系统复杂度小,但是编译和代码测试时间久,各有优劣,目前完成的OpenHarmony版本全向运动小车底盘还将加入更多功能,包括,视觉识别,人体跟踪等,在近期的下一步工作中将小车底盘适配为线性控制模型,加入LQR算法和MPC算法验证小车控制以及UnionPi OpenHarmony开发板进行车机控制的算力支持情况。
整个搭建过程非常完整,必须点赞支持一下
OpenHarmony的使用比之前方便多了
这个小车的功能真是丰富
小车型号能分享下吗
没有看到怎么自启动
总结的很全面