参考了一篇机器人控制的文章,总结一下:
【iOS与EV3混合机器人编程系列之四】iOS_WiFi_EV3_Library 剖析之一:WiFi UDP和TCP
其中
1)拥有一个连接界面来搜索并连接WiFi局域网内的EV3设备。
2)实时接收EV3的端口数据并且能非常方便地对数据进行处理。
3)能够发送直接命令(Direct Command)到EV3实现对EV3的实时控制。
和路由器的直连需求是一样的
我们只需要使用库中的四个文件:
GCDAsyncSocket.h // 实现TCP传输
GCDAsyncSocket.m
GCDAsyncUdpSocket.h // 实现UDP传输
GCDAsyncUdpSocket.m
1.UDP的使用
Step 1:创建实例instance
GCDAsyncUdpSocket
Step 2:连接UDP到特定端口
这里我们使用这个类中的方法:1)连接API:bindToPort:error:
2)接收数据API:beginReceiving:
3)关闭API:closeStep 3:接收UDP数据
通过delegate方法
UDPsocket已收到数据
Step 4:发送UDP数据
2、TCP的使用
Step 1:创建实例instance GCDAsyncSocket类
Step 2:连接TCP
Step 3:发送TCP数据
Step 4:接收数据
Step 5:数据处理 通过Delegate方法实现
// TCP socket已连接
// TCP socket已断开
// TCP socket已写入数据
支持多机连接,因此我们在每个EV3Device中处理数据。从EV3那边接收的任何数据包含端口数据这是在这里进行处理。
这里可以做一些设备相关的信息存储,比较灵活。
// TCP socket已发送数据
这篇文章也提供了源码:
songrotek/iOS_WiFi_EV3_Library
看一下资源结构
EV3CommandDefinitions.h
这个是枚举的.h文件,将16进制的枚举全部写进来,
typedef enum {
EV3ParameterSizeByte = 0x81, // 1 byte
EV3ParameterSizeInt16 = 0x82, // 2 bytes
EV3ParameterSizeInt32 = 0x83, // 4 bytes
EV3ParameterSizeString = 0x84 // null-terminated string
}EV3ParameterSize;
类似上文,这样写的时候引入枚举就能够进行16进制的拼接转化等就像这样`
- (void)addParameterWithInt8:(Byte)parameter
{
buffer[cursor] = EV3ParameterSizeByte;
cursor++;
buffer[cursor] = parameter;
cursor++;
EV3Device.h
EV3Device.m
这个是设备信息模型,会由EV3WifiManager.h 管理初始化和信息处理,连接到某个设备,以及EV3WifiManager管理多台设备的设备列表
设备来管理TCP的操作,因为每台设备才会和业务相关,因此将业务有关的控制机器人、路由器的操作都在这里 类内部只用了[self.tcpSocket writeData:data withTimeout:-1 tag:MESSAGE_CLEAR];
这个方法
//EV3WifiManager中对设备的操作
// 连接或者断开ev3到iOS。连接之后,就可以发送直接命令了。
- (void)connectTCPSocketWithDevice:(EV3Device *)device;
// udp接收到数据就要建立一个设备,给设备添加一个tcp的连接
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
{
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (msg)
{
NSString *serialNumber = [msg substringWithRange:NSMakeRange(14, 12)];
//主机mac地址
NSString *host = [GCDAsyncUdpSocket hostFromAddress:address];
//取出device设备数据
EV3Device *device = [self.devices objectForKey:host];
//如果没有设备就信息
if (!device && host.length < 20) {
//初始化一个设备
EV3Device *aDevice = [[EV3Device alloc] initWithSerialNumber:serialNumber address:host tag:self.devices.count isConnected:NO];
// 启动 TCP/IP socket
dispatch_queue_t tcpSocketQueue = dispatch_queue_create("com.manmanlai.tcpSocketQueue", DISPATCH_QUEUE_CONCURRENT);
GCDAsyncSocket *tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:tcpSocketQueue];
//把这个tcpSocket 给设备的tcpSocket对象
aDevice.tcpSocket = tcpSocket;
// 将设备添加到设备列表里
[self.devices setObject:aDevice forKey:aDevice.address];
//发送消息去刷新列表
if ([self.delegate respondsToSelector:@selector(updateView)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate updateView];
});
}
}
}
else
{
NSLog(@"Error converting received data into UTF-8 String");
}
//udp发送数据 --------
[self.udpSocket sendData:data toAddress:address withTimeout:-1 tag:0];
}
//tcp发送数据 --------
#pragma mark - GCDAsyncSocket
- (void)connectTCPSocketWithDevice:(EV3Device *)device
{ //获取到设备socket并且建立连接时在这里,这样设备里面可以克控制这个socket ,这些代理方法也能够控制socket,这样变得两个类都能对tcpsocket进行控制,可能需要把当前的这些操作打包给device类来做,来减少依赖关系。
//作者在这里这样使用应该是为了同时能够管理udp连接和tcp设备管理所有的socket问题,
//获取到tcp socket
GCDAsyncSocket *tcpSocket = device.tcpSocket;
// 建立连接
NSError *error = nil;
if (![tcpSocket connectToHost:device.address
onPort:5555
error:&error])
{
NSLog(@"Error connecting: %@", error);
} else {
NSLog(@"Connected");
// write data
NSLog(@"writing...");
NSString *unlockMsg = [NSString stringWithFormat:@"GET /target?sn=%@ VMTP1.0 Protocol: EV3",device.serialNumber];
NSData *unlockData = [unlockMsg dataUsingEncoding:NSUTF8StringEncoding];
//写数据
[tcpSocket writeData:unlockData withTimeout:-1 tag:MESSAGE_UNLOCK];
//读取数据
[tcpSocket readDataWithTimeout:-1 tag:MESSAGE_UNLOCK];
}
}
- (void)disconnectTCPSocketWithDevice:(EV3Device *)device
{
//设备停止扫描
[device stopScan];
//设备断开连接
[device.tcpSocket disconnect];
//设备列表移除这个设备地址-----我的app需求可能不太一样这里
[self.devices removeObjectForKey:device.address];
//刷新界面
if ([self.delegate respondsToSelector:@selector(updateView)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate updateView];
});
}
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString *host = sock.connectedHost;
EV3Device *device = [self.devices objectForKey:host];
[device handleReceivedData:data withTag:tag];
}
EV3DirectCommander.h
EV3DirectCommander.m
16进制操作的类,所有16进制的操作都会在这里,参数的发送都在这里,EV3Device设备管理了业务的收发,而收发消息的过程中,都需要进行16进制的拼接,就引入了这个类,
使用的时候就一直调用+方法,具体例子
NSData *data = [EV3DirectCommander turnMotorAtPort:port power:power];
[self.tcpSocket writeData:data withTimeout:-1 tag:MESSAGE_NO_REPLY];
至于内部---调用初始化方法后添加必要参数返回即可
+ (NSData *)playToneWithVolume:(int)volume frequency:(UInt16)frequency duration:(UInt16)duration
{
EV3DirectCommander *command = [[EV3DirectCommander alloc] initWithCommandType:EV3CommandTypeDirectNoReply globalSize:0 localSize:0];
[command addOperationCode:EV3OperationSoundTone];
[command addParameterWithInt8:(Byte)volume];
[command addParameterWithInt16:frequency];
[command addParameterWithInt16:duration];
return [command assembledCommandData];
}宏定义ios向机器人发送信息的类型
define Ev3WifiFit_EV3MessageType_h
define MESSAGE_UNLOCK 1 // 发送ev3解锁信息
define MESSAGE_NO_REPLY 11
define MESSAGE_GET_SENSOR_TYPE_AND_MODE 12
define MESSAGE_READ_DATA 13
define MESSAGE_SCAN_PORTS 14
define MESSAGE_SCAN_SENSOR_TYPE_AND_MODE 15
define MESSAGE_SCAN_SENSOR_DATA 16
define MESSAGE_CLEAR 17
define MESSAGE_UNDEFINE 0
具体使用:
[self.tcpSocket writeData:data withTimeout:-1 tag:MESSAGE_NO_REPLY];
EV3Sensor.h
EV3Sensor.m
Device设备里的元器件,组成部分,每个设备有8个元器件
EV3WifiBrowserViewController.h
EV3WifiBrowserViewController.m
这个是简单的界面显示控制,对不同状态的连接情况显示不同的图表TableView
EV3WifiManager.h
EV3WifiManager.m
这个是单例管理类 ,添加到控制器中,遵循其代理方法,控制udp管理和设备tcp的管理
// 具体使用
// init ev3WifiManager
self.ev3WifiManager = [EV3WifiManager sharedInstance];
self.ev3WifiManager.delegate = self;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ev3DeviceConnected:) name:EV3DeviceConnectedNotification object:nil];
[self.ev3WifiManager startUdpSocket];
//[self.udpSocket close];
[self.ev3WifiManager stopUdpSocket];
//devices count info
NSUInteger count = [self.ev3WifiManager.devices count];
EV3Device *device = [[self.ev3WifiManager.devices allValues] objectAtIndex:indexPath.row];
//connect device
[self.ev3WifiManager connectTCPSocketWithDevice:device];
[self.ev3WifiManager disconnectTCPSocketWithDevice:device];
#pragma mark - ev3 wifi manager delegate
- (void)updateView
{
[self update];
}
GCDAsyncSocket.h
GCDAsyncSocket.m
GCDAsyncUdpSocket.h
GCDAsyncUdpSocket.m
是socket TCP 和UDP 需要包含的第三方库CocoaAsyncSocket