找回密码
 马上注册

QQ登录

只需一步,快速开始

查看: 5938|回复: 0

【iOS与EV3混合机器人编程系列之五】iOS_WiFi_EV3_Library 剖析之连接EV3

[复制链接]
发表于 2014-7-16 18:18:00 | 显示全部楼层 |阅读模式
本帖最后由 songrotek 于 2014-7-16 18:20 编辑

在上一篇文章中,我们讲解了如何用开源代码库CocoaAsyncSocket来实现iOS上的UDP和TCP数据通信。那么在本文中,我们将介绍在CocoaAsyncSocket的基础如何使用UDP和TCP连接EV3的机制。

之所以我们能够通过无线连接EV3,根本原因在于EV3的源代码内建了一套无线连接通信的机制。
这套机制是这样的:
1)EV3在连接到无线网络后,就不断地从3015端口发送UDP数据,数据的格式如下:
  1. Serial-Number: 0016533f0c1e
  2. Port: 5555
  3. Name: EV3
  4. Protocol: EV3
复制代码
从这个UDP数据中,我们可以获取其ip地址,设备序列号两个重要数据。
2)拥有了ip地址,我们就可以建立TCP连接连接到EV3,端口为5555
3)在连接上TCP后,我们就可以向EV3发送数据了,我们必须先发送一个解锁信息获取控制EV3的权限才能实现对EV3的有效控制,解锁信息格式为:
GET /target?sn=SERIAL_NUMBER VMTP1.0 Protocol: EV3
sn=对应设备的序列号,就是我们从UDP信息中获取的序列号
4)解锁信息发送成功后,EV3会返回一条信息:”Accept:EV340”。如果我们收到了这条信息,就意味着我们已经解锁成功,现在我们就可以发送特定的EV3命令来控制EV3了!!

连接EV3的奥秘就是上面这么几句话了。恭喜你,你已经知道Secret了!那么现在我们要通过具体的程序来实现它。

在代码库中我们建立了一个类EV3WifiManager来管理EV3的连接和数据传输功能,并建立了EV3WifiBrowserViewController的视图控制器来作为EV3连接管理的界面。

为了更好地存储管理EV3设备数据,我们还建立了EV3Device来存储EV3的信息。

关于界面的设计本文就不谈了,仅谈谈实现连接的核心代码。

== Step 1:连接UDP并接收数据 ==
连接方法我们在上一篇文章已经讲了,本文不再谈。
现在主要说明一下数据处理部分:
  1. - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
  2.       fromAddress:(NSData *)address
  3. withFilterContext:(id)filterContext
  4. {
  5.         // 1
  6.         NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  7.         if (msg)
  8.         {
  9.         // 2
  10.         NSString *serialNumber = [msg substringWithRange:NSMakeRange(14, 12)];
  11.         
  12.         NSString *host = [GCDAsyncUdpSocket hostFromAddress:address];
  13.         
  14.         EV3Device *device = [self.devices objectForKey:host];

  15.         if (!device && host.length < 20) {
  16.             
  17. // 3
  18.             EV3Device *aDevice = [[EV3Device alloc] initWithSerialNumber:serialNumber address:host tag:self.devices.count isConnected:NO];
  19.             
  20.             
  21.             
  22.             // 4
  23.             dispatch_queue_t tcpSocketQueue = dispatch_queue_create("com.manmanlai.tcpSocketQueue", DISPATCH_QUEUE_CONCURRENT);
  24.             GCDAsyncSocket *tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:tcpSocketQueue];
  25.             
  26.             aDevice.tcpSocket = tcpSocket;
  27.             
  28.             
  29.             [self.devices setObject:aDevice forKey:aDevice.address];
  30.             
  31.             
  32.             
  33.             if ([self.delegate respondsToSelector:@selector(updateView)]) {
  34.                 dispatch_async(dispatch_get_main_queue(), ^{
  35.                     [self.delegate updateView];
  36.                 });
  37.             }
  38.         }
  39.         
  40.         }
  41.         else
  42.         {
  43.         NSLog(@"Error converting received data into UTF-8 String");
  44.         }
  45.         // 5
  46.         [self.udpSocket sendData:data toAddress:address withTimeout:-1 tag:0];
  47. }
复制代码

1、将获取的转化为字符串
2、通过截取字符串来获取EV3设备的序列号,并通过hostFromAddress:获取特定连接的名称(每一个连接都有一个host名称)作为EV3设备的key来存储EV3的信息。
3、新建一个EV3Device实例来存储设备信息
4、新建一个TCP socket用于连接EV3。
5、将原UDP数据返回给EV3。(这一步省略也没有关系)

== 连接TCP并解锁 ==
TCP的连接这里不讲,不清楚的童鞋还请看上一篇文章。
在实现中,我们在界面上显示了ip地址,点击后开始连接。
一旦连接成功,我们就立即发送解锁数据,并立即接收数据。代码如下:

  1. - (void)connectTCPSocketWithDevice:(EV3Device *)device
  2. {
  3.    
  4.     GCDAsyncSocket *tcpSocket = device.tcpSocket;
  5.     // connnect
  6.     NSError *error = nil;
  7.     if (![tcpSocket connectToHost:device.address
  8.                                 onPort:5555
  9.                                  error:&error])
  10.     {
  11.         NSLog(@"Error connecting: %@", error);
  12.         
  13.     } else {
  14.         NSLog(@"Connected");
  15.         // write data
  16.         NSLog(@"writing...");
  17.         NSString *unlockMsg = [NSString stringWithFormat:@"GET /target?sn=%@ VMTP1.0 Protocol: EV3",device.serialNumber];
  18.         NSData *unlockData = [unlockMsg dataUsingEncoding:NSUTF8StringEncoding];
  19.         [tcpSocket writeData:unlockData withTimeout:-1 tag:MESSAGE_UNLOCK];
  20.         
  21.         [tcpSocket readDataWithTimeout:-1 tag:MESSAGE_UNLOCK];
  22.         
  23.     }
  24.    
  25.    
  26. }
复制代码

读取到数据后,我们就着手进行数据处理,由于这个代码库要支持多机连接,因此我们在每个EV3Device中处理数据。这里大家要注意就是我们从EV3那边接收的任何数据包含端口数据这是在这里进行处理。

  1. - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
  2. {
  3.     NSString *host = sock.connectedHost;
  4.     EV3Device *device = [self.devices objectForKey:host];
  5.    
  6.     [device handleReceivedData:data withTag:tag];        
  7. }
复制代码

这里我们只看处理解锁数据的过程:
  1. case MESSAGE_UNLOCK:
  2.         {
  3.             NSString *httpResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  4.             NSString *response =[httpResponse substringToIndex:12];
  5.             if ([response isEqualToString:@"Accept:EV340"]) {
  6.                 self.isConnected = YES;
  7.                 NSLog(@"ev3 connected");
  8.                
  9.                 dispatch_async(dispatch_get_main_queue(), ^{
  10.                     [self scanPorts];
  11.                     [[NSNotificationCenter defaultCenter] postNotificationName:EV3DeviceConnectedNotification object:self];
  12.                 });
  13.             }
  14.                 break;
  15.         }
复制代码

需要注意的是我们发送的每一条数据都有特定的标签以便于区分。对于解锁数据的信息,其对应的标签是MESSAGE_UNLOCK。代码方面需要说明的不是很多,核心就是判断接收到的信息是不是Accept:EV340,其余的代码就是直接发送命令让EV3返回端口数据并且发送notification让界面更新。只要接收到了信息,那么我们就已经成功连接并且可以控制EV3了。

需要说明的是有一些情况局域网中无法发送TCP数据,导致虽然连接上了TCP,但无法发送解锁信息。一般我都是用Android手机做热点。iPhone热点连接问题确实值得大家一起好好研究!

OK,那么大家可以看到,连接EV3并不是一件非常困难的事。只要通过上面几步,我们就能做到。

在下一篇文章中,我们讲介绍如何给EV3发送命令,如何创建命令。敬请期待!

【本文为原创文章,版权所有,转载请注明出处,谢谢!songrotek@qq.com


如果您觉得我的帖子对您有用,请不吝给我一个“赞”!
您需要登录后才可以回帖 登录 | 马上注册

本版积分规则

手机版|中文乐高 ( 桂ICP备13001575号-7 )

GMT+8, 2024-12-27 12:50 , Processed in 7.559723 second(s), 22 queries .

Powered by Discuz! X3.5

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表