[OpenHarmony Socket通信]DAYU200遥控3861小车 原创 精华

离北况归
发布于 2023-7-20 17:41
浏览
3收藏

笔者最近学习了OpenHarmony Socket通信相关内容,制作了一个DAYU200(OpenHarmony标准系统)遥控3861小车(OpenHarmony轻量系统)的样例


@toc

1. DAYU200和3861小车简单介绍

[OpenHarmony Socket通信]DAYU200遥控3861小车-鸿蒙开发者社区

2. DAYU200遥控HiSpark-Pegasus智能小车样例介绍

  • 本样例通过TCP socket通信实现DAYU200开发板(OpenHarmony标准系统) 遥控 HiSpark-Pegasus智能小车(OpenHarmony轻量系统)前进、后退、左转、右转、停止等操作,实现了一个简单的OpenHarmony南北向开发互通案例。

  • 样例开发分成3568端小车控制hap应用和3861端c代码

2.1 3568端小车控制hap应用代码讲解

1.3568端小车控制hap应用基于 (需要特别注意开发环境!!!OpenHarmony应用开发更新速度很快)

  • OpenHarmony3.2release
  • DevEco Studio 3.1.0.500
  • sdk 3.2.12.2

2.DAYU200通过tcp socket与3861建立通信,hap应用主要实现代码:https://gitee.com/hihope_iot/hispark-pegasus-smart-car/blob/master/DAYU200遥控HiSpark-Pegasus智能小车样例/DAYU200(3568)/car_socket_control/entry/src/main/ets/pages/Index.ets
[OpenHarmony Socket通信]DAYU200遥控3861小车-鸿蒙开发者社区

3.hap应用TCP协议进行通信大致步骤

  • import需要的socket模块。
  • 创建一个TCPSocket连接,返回一个TCPSocket对象
  • (可选)订阅TCPSocket相关的订阅事件
  • 绑定IP地址和端口,端口可以指定或由系统随机分配
  • 连接到指定的IP地址和端口
  • 发送数据/接收数据
  • Socket连接使用完毕后,关闭

4.添加获取wifi信息和网络的权限,在entry\src\main\module.json5中

    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",
        "usedScene": {
          "abilities": [
            "ohos.samples.socket.EntryAbility"
          ],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.GET_WIFI_INFO",
        "usedScene": {
          "abilities": [
            "ohos.samples.socket.EntryAbility"
          ],
          "when": "always"
        }
      }
    ]

5.建立tcp通信需要首先获取本地ip地址和端口号

//为了获取本地IP地址,需要导入@ohos.wifiManager
import wifiManager from '@ohos.wifiManager';

// 解析标准系统开发板(3568)端本地IP地址
export function resolveIP(ip: number): string {
  if (ip < 0 || ip > 0xFFFFFFFF) {
    throw ('The number is not normal!');
  }
  return (ip >>> 24) + '.' + (ip >> 16 & 0xFF) + '.' + (ip >> 8 & 0xFF) + '.' + (ip & 0xFF);
}

// 获取标准系统开发板(3568)端本地IP地址
let localAddress = resolveIP(wifiManager.getIpInfo().ipAddress);

// 标准系统开发板(3568)本地ip地址和端口
let bindAddress = {
  address: localAddress,
  port: 5678, // 绑定端口,如1234
  family: 1
};

...
...

        //用一个test组件显示本地IP地址
        Text(this.message0)
          .fontSize(25)
          .margin({top:0})
          .fontWeight(FontWeight.Normal)
          .backgroundColor(Color.White)

        //用一个test组件显示本地socket端口号
        Text(this.message1)
          .fontSize(25)
          .margin({top:0})
          .fontWeight(FontWeight.Normal)
          .backgroundColor(Color.White)

6.tcp初始化,订阅TCPSocket相关的订阅事件,绑定本地ip地址和端口

            // 订阅TCPSocket相关的订阅事件
            tcp.on('message',value => {
              this.message_recv = this.resolveArrayBuffer(value.message)
              console.log(`TCP_data` + this.message_recv);
            });

            tcp.on('connect', () => {
              console.log("on connect")
            });
            tcp.on('close', () => {
              console.log("on close")
            });

            // 绑定本地ip地址和端口
            tcp.bind(bindAddress, err => {
              if (err) {
                promptAction.showToast({
                  message: '[3861_car_control] bind fail',
                  duration: 3000,
                  bottom: 10
                });
                console.log('[3861_car_control] bind fail');
                return;
              }
              promptAction.showToast({
                message: '[3861_car_control] bind success',
                duration: 3000,
                bottom: 10
              });
              console.log('[3861_car_control] bind success');
            });

7.绑定轻量系统(3861)的ip地址和端口


            // 3861 IP地址和端口
            let connectAddress = {
              address: this.car_ipaddress,
              port: num, // 端口号
              family: 1
            };

            //连接到指定的3861IP地址和端口
            tcp.connect({
              address: connectAddress, timeout: 6000
            }, err => {
              if (err) {
                promptAction.showToast({
                  message: '[3861_car_control] connect'+this.car_ipaddress+' fail',
                  duration: 3000,
                  bottom: 10
                });
                console.log('[3861_car_control] connect'+this.car_ipaddress+' fail');
                return;
              }
              promptAction.showToast({
                message: '[3861_car_control] connect'+this.car_ipaddress+' success',
                duration: 3000,
                bottom: 10
              });
              console.log('[3861_car_control] connect'+this.car_ipaddress+' success');
            });

8.3568发送数据至3861

            tcp.send({
              data: '0'
            }, err => {
             if (err) {
                console.log('[3861_car_control] send fail');
                return;
              }
              console.log('[3861_car_control] send success');
            })

9.3568接收3861发送的数据

  //解析标准系统开发板(3568)接收到的 轻量系统(3861)发送的数据
  resolveArrayBuffer(message: ArrayBuffer): string {
    if (message instanceof ArrayBuffer) {
      let dataView = new DataView(message)
      console.info(`length ${dataView.byteLength}`)
      let str = ""
      for (let i = 0;i < dataView.byteLength; ++i) {
        let c = String.fromCharCode(dataView.getUint8(i))
        if (c !== "\n") {
          str += c
        }
      }
      console.info(`message aray buffer:${str}`)
      return str;
    }
  }

...
...

            // 订阅TCPSocket相关的订阅事件
            tcp.on('message',value => {
              this.message_recv = this.resolveArrayBuffer(value.message)
              console.log(`TCP_data` + this.message_recv);
            });

···
···

        //用一个text显示标准系统开发板(3568)接收到轻量系统(3861)发出的数据
        Text('接收到的3861发送的数据:' + this.message_recv)
          .fontSize(25)
          .margin({top:0})
          .fontWeight(FontWeight.Normal)
          .backgroundColor(Color.White)

10.关闭tcp连接

            tcp.close((err) => {
              promptAction.showToast({
                message: '[3861_car_control] close socket.',
                duration: 3000,
                bottom: 10
              });
              console.log('[3861_car_control] close socket.')
            });
            tcp.off('message');
            tcp.off('connect');
            tcp.off('close');

11.完成代码实现请查看: https://gitee.com/hihope_iot/hispark-pegasus-smart-car/blob/master/DAYU200遥控HiSpark-Pegasus智能小车样例/DAYU200(3568)/car_socket_control/entry/src/main/ets/pages/Index.ets

12.如果要修改hap应用的名称,在car_socket_control\entry\src\main\resources\zh_CN\element\string.json文件中修改EntryAbility_label属性
[OpenHarmony Socket通信]DAYU200遥控3861小车-鸿蒙开发者社区

13.如果要修改应用图标修改car_socket_control\entry\src\main\resources\base\media下icon.png(像素大小为114×114)
[OpenHarmony Socket通信]DAYU200遥控3861小车-鸿蒙开发者社区

2.2 3861端小车代码讲解

代码地址: https://gitee.com/hihope_iot/hispark-pegasus-smart-car/tree/master/DAYU200遥控HiSpark-Pegasus智能小车样例/HiSpark-Pegasus-smart-car(3861)/car_tcp_control

文件名 说明
BUILD.gn OpenHarmony构建脚本
demo_entry_cmsis.c OpenHarmony liteos-m程序入口
net_common.h 系统网络接口头文件
net_demo.h demo脚手架头文件
net_params.h 网络参数,包括WiFi热点信息、端口信息
car_tcp_server_test.c TCP socket控制小车实现代码
wifi_connecter.c OpenHarmony WiFi STA模式API的封装实现文件
wifi_connecter.h OpenHarmony WiFi STA模式API的封装头文件

本篇文章主要讲解主要实现代码car_tcp_server_test.c (笔者在代码提交了比较详细的注释)

1.3861tcp发送数据

        //3861发送数据
        retval = send(connfd, request, strlen(request), 0);
        if (retval <= 0) {

           //3568侧断开与3861的tcp连接后进行的处理
           printf("_______________________________________\r\n");
           printf("send response failed, %ld!\r\n", retval);
           printf("do_reconnect...\r\n");
           sleep(DELAY_1S);
           close(connfd);//关闭与客户端的连接
           sleep(DELAY_1S); // for debug

           break;//跳出while循环
           car_stop();//小车进入停止状态

        }else{
           printf("The data responsed to the client is %s\r\n", request);
        }

2.3861接收数据

       //3861接收数据
       retval = recv(connfd, request, sizeof(request), 0);
       if (retval < 0) {

           //3568侧断开与3861的tcp连接后进行的处理
           printf("_______________________________________\r\n");
           printf("recv request failed, %ld!\r\n", retval);
           printf("do_reconnect...\r\n");
           sleep(DELAY_1S);
           close(connfd);//关闭与客户端的连接
           sleep(DELAY_1S); // for debug
           
           break;//跳出while循环
           car_stop();//小车进入停止状态

        }else{
           printf("_______________________________________\r\n");
           printf("The data received from the client is %s \r\n", request);
        }

3、当3568与3861断开连接后,可以进行重新连接。相关代码:

    while (1) {

    connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);
    if (connfd < 0) {
        printf("accept failed, %d, %d\r\n", connfd, errno);
        continue;
    }else{
    printf("_______________________________________\r\n");
    printf("accept success, connfd = %d!\r\n", connfd);
    printf("client addr info: host = %s, port = %hu\r\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
    }

/***********************************socket收、发的部分************************************************/

       // 后续 收、发 都在 表示连接的 socket 上进行;
       while (1) {
       char request[128] = "";
      .................
      .................
/*************************************************************************************************/

    }

    }

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2023-7-21 09:56:37修改
9
收藏 3
回复
举报
6条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

很详细的实现过程讲解,必须支持

回复
2023-7-20 18:05:00
喝一大口可乐
喝一大口可乐

看视频效果很不错

回复
2023-7-21 11:58:08
FlashinMiami
FlashinMiami

这个遥控现在是有线的吗

回复
2023-7-21 17:38:45
离北况归
离北况归

无线的,通过wifi控制


1
回复
2023-7-21 18:21:12
鸿蒙坚果派
鸿蒙坚果派

赞赞赞

回复
2023-7-22 11:56:18
香菜太难吃了
香菜太难吃了

了解一下如何打通开发板

回复
2023-7-24 10:57:48
回复
    相关推荐