基于HarmonyOS控制Hi3861小车之信息通信

开发 前端 OpenHarmony
本节主要详细讲述一下通信关键技术,考虑到TCP/UDP协议的特性,两者间通过UDP进行通信是一种必然的选择,UDP一种无连接的协议。

[[422670]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

引言

在鸿蒙应用实际开发中,经常会遇到App与IOT设备间的通信,本节主要详细讲述一下通信关键技术,考虑到TCP/UDP协议的特性,两者间通过UDP进行通信是一种必然的选择,UDP一种无连接的协议,具有资源消耗小,处理速度快的优点,了解UDP是怎么通信的,这对于每一个HarmonyOS开发者也是需要了解的重点知识。

核心类

DatagramSocket、DatagramPacket、EventHandler,下面分别简单介绍下:

1.DatagramSocket:

构造器DatagramSocket(int port, InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。主要方法receive(DatagramPacket p):从该DatagramSocket中接收数据报,send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。

2.DatagramPacket:

构造器DatagramPacket(byte[] buf, int length, InetAddress addr, int port):以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口--这就决定了该数据报的目的地。

3.EventHandler:

是HarmonyOS用于处理线程间通信的一种机制,可以通过EventRunner创建新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,任务又可以得到合理的处理。比如:主线程使用EventHandler创建子线程,子线程做耗时的下载图片操作,下载完成后,子线程通过EventHandler通知主线程,主线程再更新UI。

功能介绍

通过App Demo控制小车运动(前进、后退、左转、右转、停止),主要通过UDP数据包发送命令,来说明它们间是怎么通信的,它们间控制命令以json格式发送。

如:

  1. "mode""CarControl",//控制命令分类 
  2.     "cmd""forward"//具体命令 
  3. }。 

开发指南

1、创建UDP协议的发送命令对象

  1. private UdpManager() { 
  2.         try { 
  3.             mGpsDatagramSocket = new DatagramSocket(); 
  4.         } catch (SocketException e) { 
  5.             e.printStackTrace(); 
  6.         } 
  7.     } 

2、将要发送的数据封装成DatagramPacket对象发送

  1. DatagramPacket sRequest = new DatagramPacket(mInfoArray, mInfoArray.length, 
  2. InetAddress.getByName(getIp()), PORT); 
  3. // 开始发送 
  4. mGpsDatagramSocket.send(sRequest); 

3、构造发送的命令

  1. public void sendMessage(String info) { 
  2.         Gson gson = new Gson(); 
  3.         WifiCommand messageInfo = new WifiCommand(); 
  4.         messageInfo.setCmd(info); 
  5.         //控制类型 
  6.         messageInfo.setMode(); 
  7.         //转换成json 
  8.         String resultJson = gson.toJson(messageInfo); 
  9.         // 创建发送命令SendMessageRunnable对象 
  10.         mSendMessageRunnable = new SendMessageRunnable(); 
  11.         mSendMessageRunnable.setInfoArray(resultJson.getBytes(StandardCharsets.UTF_8)); 
  12.         // 启动发送命令线程 
  13.         mEventHandler.postTask(mSendMessageRunnable); 
  14.         if ("stop".equals(info) || "tripod_on".equals(info) || "tripod_off".equals(info)){ 
  15.             HiLog.info(label, "info = " + info); 
  16.         } else { 
  17.             // 启动发送Gps请求线程和接收信息线程 
  18.             startReceive(); 
  19.             startSendGpsMessage(); 
  20.         } 
  21.         HiLog.info(label, "sendMessage = " + resultJson); 
  22.     } 

实现效果

基于HarmonyOS控制Hi3861小车之信息通信-鸿蒙HarmonyOS技术社区

附上主要源代码

1. MainAbilitySlice

  1. public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener{ 
  2.     private Button iTurnUp,iTurnDown,iTurnLeft,iTurnRight,iTurnRun; 
  3.  
  4.     private UdpManager udpManager; 
  5.     @Override 
  6.     public void onStart(Intent intent) { 
  7.         super.onStart(intent); 
  8.         super.setUIContent(ResourceTable.Layout_ability_main); 
  9.         initComponent(); 
  10.         // 初始化WiFi控制对象 
  11.         udpManager = UdpManager.getInstance(this); 
  12.     } 
  13.  
  14.     private void initComponent(){ 
  15.         iTurnUp = (Button) findComponentById(ResourceTable.Id_i_up); 
  16.         iTurnUp.setClickedListener(this); 
  17.  
  18.         iTurnDown = (Button) findComponentById(ResourceTable.Id_i_down); 
  19.         iTurnDown.setClickedListener(this); 
  20.  
  21.         iTurnLeft = (Button) findComponentById(ResourceTable.Id_i_left); 
  22.         iTurnLeft.setClickedListener(this); 
  23.  
  24.         iTurnRight = (Button) findComponentById(ResourceTable.Id_i_right); 
  25.         iTurnRight.setClickedListener(this); 
  26.  
  27.         iTurnRun = (Button) findComponentById(ResourceTable.Id_i_run); 
  28.         iTurnRun.setClickedListener(this); 

 2. UdpManager

  1. /** 
  2.  * UDP连接类 
  3.  */ 
  4. public class UdpManager { 
  5.     private static final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00134, "UdpManager"); 
  6.     private static final int PORT = 48100; 
  7.     private static final int GET_MESSAGE = 1; 
  8.     private static UdpManager sUdpManager; 
  9.     private static Context sContext; 
  10.     private UdpReceiveCallback mReceiveInformationCallback; 
  11.     private ReceiveMessageRunnable mReceiveMessageRunnable; 
  12.     private SendGpsMessageRunnable mSendGpsMessageRunnable; 
  13.     private SendMessageRunnable mSendMessageRunnable; 
  14.     private DatagramSocket mGpsDatagramSocket; 
  15.     private static  String ip = "192.168.0.1"
  16.  
  17.     /** 
  18.      * 控制是否还需要接收信息控制器 
  19.      */ 
  20.     private boolean flag = false
  21.  
  22.     private final EventHandler mEventHandler = new EventHandler(EventRunner.create()) { 
  23.         @Override 
  24.         protected void processEvent(InnerEvent event) { 
  25.             super.processEvent(event); 
  26.             if (event.eventId == GET_MESSAGE) { 
  27.                 if (mReceiveInformationCallback != null) { 
  28.                     mReceiveInformationCallback.getMessage(event.object); 
  29.                 } 
  30.             } 
  31.         } 
  32.     }; 
  33.  
  34.     private final EventHandler mReceiveEventHandler = new EventHandler(EventRunner.create()) { 
  35.     }; 
  36.  
  37.     private final EventHandler mSendGpsEventHandler = new EventHandler(EventRunner.create()) { 
  38.     }; 
  39.  
  40.     /** 
  41.      * UdpManager的单例 
  42.      * 
  43.      * @return UdpManager单例对象 
  44.      */ 
  45.     public static UdpManager getInstance(Context context) { 
  46.         if (sUdpManager == null) { 
  47.             sUdpManager = new UdpManager(); 
  48.             sContext = context; 
  49.         } 
  50.         return sUdpManager; 
  51.     } 
  52.  
  53.     /** 
  54.      * 构造函数 
  55.      */ 
  56.     private UdpManager() { 
  57.         // 创建UDP协议的发送命令对象 
  58.         try { 
  59.             mGpsDatagramSocket = new DatagramSocket(); 
  60.         } catch (SocketException e) { 
  61.             e.printStackTrace(); 
  62.         } 
  63.     } 
  64.  
  65.     /** 
  66.      * 注册接收信息的回调函数 
  67.      * 
  68.      * @param callback 接收信息回调函数 
  69.      */ 
  70.     public void registerCallback(UdpReceiveCallback callback) { 
  71.         mReceiveInformationCallback = callback; 
  72.     } 
  73.  
  74.     /** 
  75.      * 对外提供的发送命令方法 
  76.      * 
  77.      * @param info 需要发送的命令 
  78.      */ 
  79.     public void sendMessage(String info) { 
  80.         Gson gson = new Gson(); 
  81.         UdpCommand messageInfo = new UdpCommand(); 
  82.  
  83.         // 传进来的控制命令 
  84.         messageInfo.setCmd(info); 
  85.  
  86.         //控制类型 
  87.         messageInfo.setMode(); 
  88.  
  89.         //转换成json 
  90.         String resultJson = gson.toJson(messageInfo); 
  91.  
  92.         // 创建发送命令SendMessageRunnable对象 
  93.         mSendMessageRunnable = new SendMessageRunnable(); 
  94.         mSendMessageRunnable.setInfoArray(resultJson.getBytes(StandardCharsets.UTF_8)); 
  95.  
  96.         // 启动发送命令线程 
  97.         mEventHandler.postTask(mSendMessageRunnable); 
  98.  
  99.         // 启动发送Gps请求线程和接收信息线程 
  100.         if ("stop".equals(info)) { 
  101.             HiLog.info(label, "info = " + info); 
  102.         } else { 
  103.             // 启动发送Gps请求线程和接收信息线程 
  104.             startReceive(); 
  105.             startSendGpsMessage(); 
  106.         } 
  107.         HiLog.info(label, "sendMessage = " + resultJson); 
  108.     } 
  109.  
  110.     public String getIp() { 
  111.         return ip; 
  112.     } 
  113.  
  114.  
  115.     public void setIp(String mIp) { 
  116.         this.ip = mIp; 
  117.     } 
  118.  
  119.     /** 
  120.      * 内部类,用作发送命令 
  121.      */ 
  122.     private class SendMessageRunnable implements Runnable { 
  123.         private byte[] mInfoArray; 
  124.  
  125.         void setInfoArray(byte[] infoArray) { 
  126.             mInfoArray = infoArray; 
  127.         } 
  128.  
  129.         @Override 
  130.         public void run() { 
  131.             HiLog.info(label, "发送线程 = " + Thread.currentThread().getName()); 
  132.  
  133.             // 发送数据 
  134.             try { 
  135.                 // 延时发送50毫秒,因为如果不延时会将小车卡死 
  136.                 Thread.sleep(50); 
  137.  
  138.                 // 将要发送的数据封装成DatagramPacket对象 
  139.                 DatagramPacket sRequest = new DatagramPacket(mInfoArray, mInfoArray.length, 
  140.                         InetAddress.getByName(getIp()), PORT); 
  141.  
  142.                 // 开始发送 
  143.                 mGpsDatagramSocket.send(sRequest); 
  144.             } catch (IOException | InterruptedException e) { 
  145.                 e.printStackTrace(); 
  146.                 HiLog.info(label, "sendMessage error"); 
  147.             } 
  148.         } 
  149.     } 
  150.  
  151.     /** 
  152.      * 内部类,用作接收命令 
  153.      */ 
  154.     private class ReceiveMessageRunnable implements Runnable { 
  155.  
  156.         @Override 
  157.         public void run() { 
  158.             try { 
  159.                 while (flag) { 
  160.                     byte[] buf = new byte[1024]; 
  161.                     DatagramPacket receiveDatagramPacket = new DatagramPacket(buf, buf.length); 
  162.                     if (mGpsDatagramSocket != null && !mGpsDatagramSocket.isClosed()) { 
  163.                         HiLog.info(label, "接收线程开始阻塞" + Thread.currentThread().getName()); 
  164.  
  165.                         // 接收返回数据,会阻塞线程 
  166.                         mGpsDatagramSocket.receive(receiveDatagramPacket); 
  167.  
  168.                         // 将得到的数据转成json 
  169.                         String json = new String(receiveDatagramPacket.getData(), StandardCharsets.UTF_8); 
  170.                         json = json.substring(json.indexOf("{"), json.lastIndexOf("}")+1); 
  171.                         HiLog.info(label, "receiveMessage json = " + json); 
  172.  
  173.                         // 将对象发送给需要接收返回值的地方 
  174.                         mEventHandler.sendEvent(InnerEvent.get(GET_MESSAGE, json)); 
  175.                     } 
  176.                 } 
  177.             } catch (IOException e) { 
  178.                 e.printStackTrace(); 
  179.                 HiLog.error(label, "receiveMessage error"); 
  180.             } 
  181.         } 
  182.     } 
  183.  
  184.     /** 
  185.      * 内部类,用作发送请求Gps命令 
  186.      */ 
  187.     private class SendGpsMessageRunnable implements Runnable { 
  188.  
  189.         @Override 
  190.         public void run() { 
  191.             Gson gson = new Gson(); 
  192.             UdpCommand messageInfo = new UdpCommand(); 
  193.  
  194.             // 传进来的控制命令 
  195.             messageInfo.setCmd("getinfo"); 
  196.  
  197.             //控制类型 
  198.             messageInfo.setMode(); 
  199.  
  200.             //转换成json 
  201.             String resultJson = gson.toJson(messageInfo); 
  202.  
  203.             byte[] infoArray = resultJson.getBytes(StandardCharsets.UTF_8); 
  204.  
  205.             try { 
  206.                 // 将要发送的数据封装成DatagramPacket对象 
  207.                 DatagramPacket sRequest = new DatagramPacket(infoArray, infoArray.length, 
  208.                         InetAddress.getByName(getIp()), PORT); 
  209.  
  210.                 // 开始发送 
  211.                 mGpsDatagramSocket.send(sRequest); 
  212.  
  213.                 // 启动获取Gps命令线程 
  214.                 mSendGpsEventHandler.postTask(mSendGpsMessageRunnable, 2000); 
  215.                 HiLog.info(label, "发送gps"); 
  216.             } catch (IOException e) { 
  217.                 e.printStackTrace(); 
  218.             } 
  219.         } 
  220.     } 
  221.  
  222.     /** 
  223.      * 启动接收消息 
  224.      */ 
  225.     private void startReceive() { 
  226.         if (!flag) { 
  227.             flag = true
  228.  
  229.             // 创建接收命令ReceiveMessageRunnable对象 
  230.             mReceiveMessageRunnable = new ReceiveMessageRunnable(); 
  231.  
  232.             // 启动接收命令线程 
  233.             mReceiveEventHandler.postTask(mReceiveMessageRunnable); 
  234.             HiLog.info(label, "开启接收线程"); 
  235.         } 
  236.     } 
  237.  
  238.     /** 
  239.      * 开始获取gps点 
  240.      */ 
  241.     private void startSendGpsMessage() { 
  242.         // 创建发送Gps命令SendGpsMessageRunnable对象 
  243.         if (mSendGpsMessageRunnable == null) { 
  244.             mSendGpsMessageRunnable = new SendGpsMessageRunnable(); 
  245.         } 
  246.  
  247.         // 启动获取Gps命令线程 
  248.         mSendGpsEventHandler.postTask(mSendGpsMessageRunnable); 
  249.         HiLog.info(label, "开启发送gps请求线程"); 
  250.     } 

 3. UdpCommand

  1. class UdpCommand { 
  2.     // 控制命令:forward,back,leftright 
  3.     private String cmd; 
  4.     // 控制类型 
  5.     private String mode; 
  6.  
  7.     public String getCmd() { 
  8.         return cmd; 
  9.     } 
  10.  
  11.     void setCmd(String cmd) { 
  12.         this.cmd = cmd; 
  13.     } 
  14.  
  15.     public String getMode() { 
  16.         return mode; 
  17.     } 
  18.  
  19.     void setMode() { 
  20.         this.mode = "CarControl"
  21.     } 

 4. UdpReceiveCallback

  1. /** 
  2.  * 接收小车返回数据的回调函数 
  3.  */ 
  4. public interface UdpReceiveCallback { 
  5.     void getMessage(Object value); 

 5. xml布局文件

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DirectionalLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:height="match_parent" 
  5.     ohos:width="match_parent" 
  6.     ohos:alignment="center" 
  7.     ohos:orientation="vertical"
  8.  
  9.     <DirectionalLayout 
  10.         ohos:height="70vp" 
  11.         ohos:width="match_parent" 
  12.         ohos:orientation="horizontal" 
  13.         ohos:layout_alignment="center" 
  14.         ohos:top_margin="10vp" > 
  15.         <Button 
  16.             ohos:id="$+id:i_up" 
  17.             ohos:height="50vp" 
  18.             ohos:width="120vp" 
  19.             ohos:background_element="#FF9F9F9F" 
  20.             ohos:left_margin="60vp" 
  21.             ohos:text_size="25fp" 
  22.             ohos:text="前进"/> 

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2020-11-03 11:39:22

wifi小车

2020-10-30 09:41:44

鸿蒙Hi3861WiFi小车

2023-07-25 10:42:39

鸿蒙遥控3861小车

2022-03-15 15:00:59

Hi3861Pin接口鸿蒙

2021-09-16 10:03:39

鸿蒙HarmonyOS应用

2022-05-30 15:21:27

Hi3861TCP通信

2020-11-30 13:57:48

Hi3861

2022-09-07 15:35:49

设备开发鸿蒙

2020-12-15 11:57:49

Hi3861 HarmonyOS开发板

2020-10-16 09:50:37

Hi3861WiFi热点

2023-05-26 16:07:14

Hi3861Wifi模块

2022-03-07 15:05:58

HTTPHi3861数据解析

2020-10-14 09:41:02

Hi3861GPIO点灯

2020-10-28 10:00:09

海思Hi3861CentOS鸿蒙LiteOS

2021-12-20 21:01:52

鸿蒙HarmonyOS应用

2022-05-09 14:22:40

Hello WorlMNIST鸿蒙

2020-11-02 12:07:11

鸿蒙 GPIO

2021-09-30 10:11:05

鸿蒙HarmonyOS应用

2020-12-31 12:22:15

鸿蒙Hi3861应用开发

2021-04-25 10:06:57

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号