在前两篇文章中,我们对iOS与EV3混合机器人编程做了一个主要的设想。而且介绍了要完毕项目所需的软硬件准备和知识准备。

那么在今天这一篇文章中,我们将直接真正開始项目实践。

==第一个项目: EV3 Port Viewer==

项目目的:在iOS设备上通过WiFi连接EV3而且读取EV3每个端口的数据。

大家能够在App Store上搜索EV3 Port Viewer,那么我已经做了一个范例App公布了。下载地址为:https://itunes.apple.com/cn/app/ev3-port-viewer/id898298464?

mt=8

应用的基本使用要求:将EV3和iPhone同一时候连接到同一个WiFi网络中。

对于EV3。必须使用NetGear WNA1100 WiFi Dongle。网卡的使用非常easy,仅仅有插在EV3 Brick的USB接口上就能使用了

这里不得不说明的是:使用iOS7及以上版本号的iPhone,EV3无法直接连接到iPhone的热点上!

可能原因:在iOS7之后,iPhone的热点仅仅支持WPA2 PSK的加密格式,而NetGear WNA1100在EV3上则仅仅能使用WPA2或None。眼下我还没有找到有效的解决的方法,大家能够一起研究解决。

这个问题从本质上看严重影响了iOS与EV3混合机器人的体验。这使得我们不得不单独再弄一个路由器,非常麻烦。

==開始==

我已经将iOS与EV3连接及控制的程序编写成库分享到GitHub上,而且本项目的程序也直接分享了。

https://github.com/songrotek/iOS_WiFi_EV3_Library.git

https://github.com/songrotek/EV3PortViewer.git

另外。感谢网友crazypoo江门首席监黄师对库的扩展。他的github在这:

https://github.com/crazypoo/myrobot

大家能够先下下来,然后跟着本教程一步一步编写这个项目。

在这里我将会一步一步地剖析我编写的这个代码库的实现原理。与此同一时候。考虑到阅读本文的读者可能大都不了解iOS开发,因此本文将非常详细的介绍每个开发步骤!

==Step 1:建立项目==

打开Xcode,新建一个项目,选择Single View Application。点击Next。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc29uZ3JvdGVr/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

将项目命名为EV3PortViewer。

Company Identifier选择你们自己的开发人员账号里申请的App ID。

对于没有开发人员账号的童鞋。那么不要考虑这个。

假设大家想要真机測试,那么有两种选择,一个是花99美元申请一个账号,一个是在淘宝上购买一个真机測试的证书。尽管说在淘宝上这样的方式不怎么好。但对于刚刚開始研究iOS开发的童鞋,不失为一个省钱的方式。

接下来Class Prefix留空,然后Device选择iPhone。这里不使用iPad仅仅是由于iPad太大麻烦。

之后也许会考虑出个iPad版本号。

设置好之后,点击Next创建。

==Step 2:加入代码库==

大家下载我的代码库之后,将其加入进来。方法就是点击项目右键,点击 Add Files to “EV3PortViewer”…,例如以下图所看到的:

目录名称为iOS_WiFi_EV3_Library,加入进来后例如以下图所看到的:

文件比較多,大家先不用管,之后会解说。

==Step 3:更改StoryBoard==

这里我们要在主视图中使用Table的方式来显示每个EV3端口的传感器的数据,因此我们要新建一个TableViewController然后把原来的ViewController替换掉。

点击Main.storyboard,点击中间的viewController,删除掉。

单击右側工具栏里面的TableViewController,然后按住拉进storyboard中。

接下来为了加入连接button,我们须要将TableViewController嵌入到NavigationController中。

在已经选择TableViewController之后。点击Editor->Embed in->Navigation Controller。

接下来拉进来两个Bar Button Item到Navigation Bar其中,左右两边各一个,分别命名为Connect和Help。然后双击Navigation Bar中间。将Bar命名为EV3。例如以下图所看到的:

==Step 4:创建Table View Controller==

新建一个文件,选择Objective-C class。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc29uZ3JvdGVr/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

将Class命名为Ev3TableViewController,而且将Subclass of设置为UITableViewController。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc29uZ3JvdGVr/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

然后保存在EV3PortViewer目录中。

Ev3TableViewController新建完毕后。点击storyboard,将里面的tableViewController的Class设置为Ev3TableViewController。

==Step 5:创建Table View Cell==

由于我们要在每个Table View Cell中显示传感器的图片,还有数据,因此我们须要自己定义一个Table View Cell。

同前面的新建文件的方法一样。新建一个文件命名为Ev3Cell,然后Subclass Of 选择UITableViewCell。

Ev3Cell创建好之后。打开storyboard。点击Ev3TableViewController中的tableviewCell,将其类型改为Ev3Cell。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc29uZ3JvdGVr/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="710" alt="" />

==Step 6:连接Button,定义方法Method==

将storyboard中的buttonButton Connect通过按住Ctrl左键点击拉到Ev3TableViewController文件里来定义Action。

OK,一切非代码工作完毕。接下来我们開始进入编程阶段,来实现每个功能。

==Step 7:实现连接功能==

在Ev3TableViewController.m中最上面加入要使用的代码库的文件:

#import "EV3WifiBrowserViewController.h"
#import “EV3WifiManager.h"

说明一下:EV3WifiBrowserViewController专门用来查看和连接EV3设备,而EV3WifiManager用来管理与EV3的通信,包括接收数据和发送控制命令。

接下来在connectOrDisconnect中加入例如以下代码:

- (IBAction)connectOrDisconnect:(id)sender
{
EV3WifiBrowserViewController *browserViewController = [[EV3WifiBrowserViewController alloc] init]; [self presentViewController:browserViewController animated:YES completion:nil];
}

Ok,仅仅要这么简单的代码即可了,如今我们已经能够连接EV3了!

先来測试一下看看!

在iPhone和EV3都连接到WiFi之后,启动App,点击Connect按键。

大家能够看到,App自己主动搜索EV3,并直接显示出EV3的ip地址,点击地址,正常情况下。EV3就直接连接了。

 

连接上之后,事实上App就已经開始接收从EV3那边传过来的各个端口的数据了,只是我们如今还看不到。

要断开连接也非常easy,再点击一下已连接的ip地址,就会弹出是否断开连接的提示。

==Step 8: 显示端口数据==

这是这个App的功能所在,就是相似于在EV3上直接能够看到的Port View,仅仅只是我们把这个功能搬到了iPhone上。

由于我对于这个代码库的封装使得如今要获取端口数据变得易如反掌。

大家立即能够看到。这个程序最大的问题可能在于这个Ev3TableViewController的编写上。

对于对iOS开发全然不了解的童鞋,这里我也无法讲得再细使大家都能理解。建议不了解的童鞋还是先看看iOS开发的基础教程。

下面介绍每一步的实现方法。

Step 7.1 :编写 Ev3Cell

我们要显示每个端口的信息,包括下面几个方面:

1)端口连接的传感器。上图片

2)端口的名称

3)端口连接的传感器的名称

4)端口连接的传感器发送过来的实时数据

因此。打开Ev3Cell.h。加入例如以下代码:

@property (nonatomic,strong) UIImageView *sensorImage;
@property (nonatomic,strong) UILabel *portLabel;
@property (nonatomic,strong) UILabel *nameLabel;
@property (nonatomic,strong) UILabel *dataLabel;

接下来。打开Ev3Cell.h,加入例如以下代码到初始化Method中:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code self.frame = CGRectMake(0, 0, 320, 120); self.sensorImage = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)]; self.portLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 15, 180, 20)]; self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 50, 180, 20)]; self.dataLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 85, 180, 20)]; [self addSubview:self.sensorImage];
[self addSubview:self.portLabel];
[self addSubview:self.nameLabel];
[self addSubview:self.dataLabel]; }
return self;
}

这里我们採用纯手写的方式来编写Ev3Cell,我们还能够使用storyboard或xib通过图形界面来设置,这里不多讲。

OK。我们编写好了Ev3Cell,接下来是Ev3TableViewController

Step 7.2 完好Ev3TableViewController

1)打开Ev3TableViewController.m。加入头文件

#import "EV3WifiBrowserViewController.h"
#import "EV3WifiManager.h"
#import “Ev3Cell.h"

2)加入property

@property (nonatomic,strong) EV3WifiManager *ev3WifiManager;
@property (nonatomic,strong) EV3Device *device;

3)加入初始化代码在ViewDidLoad:方法中

- (void)viewDidLoad
{
[super viewDidLoad]; self.ev3WifiManager = [EV3WifiManager sharedInstance]; [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(refreshData) userInfo:nil repeats:YES];
}

这里由于考虑到我们须要实时更新数据,所以弄了一个定时器NSTimer。让其每0.1s更新一次。

4)加入refreshData方法

- (void)refreshData
{
self.device = self.ev3WifiManager.devices.allValues.lastObject;
[self.tableView reloadData];
}

5)更改tableView的设置

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.device) {
return 8;
} else return 1; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 120;
}

6)更改Cell的内容

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Ev3Cell *cell = [[Ev3Cell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.userInteractionEnabled = NO; if (self.device) { switch (indexPath.row) {
case 0:
cell.portLabel.text = @"PORTA";
cell.nameLabel.text = self.device.sensorPortA.typeString;
cell.imageView.image = self.device.sensorPortA.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPortA.data];
break;
case 1:
cell.portLabel.text = @"PORTB";
cell.nameLabel.text = self.device.sensorPortB.typeString;
cell.imageView.image = self.device.sensorPortB.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPortB.data];
break;
case 2:
cell.portLabel.text = @"PORTC";
cell.nameLabel.text = self.device.sensorPortC.typeString;
cell.imageView.image = self.device.sensorPortC.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPortC.data];
break;
case 3:
cell.portLabel.text = @"PORTD";
cell.nameLabel.text = self.device.sensorPortD.typeString;
cell.imageView.image = self.device.sensorPortD.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPortD.data];
break;
case 4:
cell.portLabel.text = @"PORT1";
cell.nameLabel.text = self.device.sensorPort1.typeString;
cell.imageView.image = self.device.sensorPort1.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPort1.data];
break;
case 5:
cell.portLabel.text = @"PORT2";
cell.nameLabel.text = self.device.sensorPort2.typeString;
cell.imageView.image = self.device.sensorPort2.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPort2.data];
break;
case 6:
cell.portLabel.text = @"PORT3";
cell.nameLabel.text = self.device.sensorPort3.typeString;
cell.imageView.image = self.device.sensorPort3.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPort3.data];
break;
case 7:
cell.portLabel.text = @"PORT4";
cell.nameLabel.text = self.device.sensorPort4.typeString;
cell.imageView.image = self.device.sensorPort4.image;
cell.dataLabel.text = [NSString stringWithFormat:@"Raw Data:%d",self.device.sensorPort4.data];
break; default:
break;
} } else {
cell.textLabel.text = @"Waiting For Connection Of EV3!";
cell.textLabel.textAlignment = NSTextAlignmentCenter;
} return cell;
}

一切OK!

解释一下:

1)self.device = self.ev3WifiManager.devices.allValues.lastObject;

通过这一行代码来获取连接的EV3设备。之所以这么复杂,是由于我编写的这个代码库事实上支持多机连接,每个连接到iPhone的EV3都存在ev3WifiManager的devices中。大家详细看代码能够知道。devices我採用dictionary而不是array

2)获取传感器数据。

非常easy:

device.sensorPortA

通过这个我们能够获取device相应的端口PortA连接的传感器。

每个传感器包括下面属性:

@property (nonatomic,assign) EV3SensorType type; //类型
@property (nonatomic,strong) NSString *typeString; // 类型字符串
@property (nonatomic,strong) UIImage *image; //传感器图片
@property (nonatomic,assign) int mode; // 传感器工作模式
@property (nonatomic,assign) short data; // 传感器数据

基本上传感器数据的刷新频率是60Hz左右。由于我觉得仅仅要一种传感器模式就够了,比方电机的转角,我们仅仅须要角度制而不须要幅度值,因此这边的mode始终是0.

从上面的代码大家应该能够非常清楚的看到获取传感器的方法。

== Ev3 Port Viewer效果==

这就是我们完毕的App的效果!是不是非常easy也非常酷呢??

详细关于开源码库的实现原理,敬请期待下一篇文章讲述!大家也能够先自己研究看看!

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

【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监測EV3port数据的更多相关文章

  1. 【iOS与EV3混合机器人编程系列之四】iOS_WiFi_EV3_Library 剖析之中的一个:WiFi UDP和TCP

    在上一篇文章中.我们通过编写EV3 Port Viewer项目实现了iOS监測EV3的实时端口数据. 程序最核心的部分就是我们的开源码库iOS_WiFi_EV3_Library. 那么,在本文中,我们 ...

  2. 【iOS与EV3混合机器人编程系列之中的一个】iOS要干嘛?EV3能够更酷!

    乐高Mindstorm EV3智能机器人(下面简称EV3)自从在2013年的CES(Consumer Electronics Show美国消费电子展)上展出之后,就吸引了全球广大机器人爱好者的眼球!E ...

  3. 【iOS与EV3混合机器人编程一系列五个】iOS_WiFi_EV3_Library 解剖连接EV3

    在上一篇文章中.我们解说了怎样用开源码库CocoaAsyncSocket来实现iOS上的UDP和TCP数据通信.那么在本文中.我们将介绍在CocoaAsyncSocket的基础怎样使用UDP和TCP连 ...

  4. 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三

       手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                    ...

  5. .NET 4 并行(多核)编程系列之三 从Task的取消

    原文:.NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之三 从Task的取消 前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程 ...

  6. TCP/IP网络编程系列之三(初级)

    TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...

  7. (转载)完成端口(CompletionPort)详解 - 手把手教你玩转网络编程系列之三

    转自:http://blog.csdn.net/piggyxp/article/details/6922277 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何 ...

  8. Java并发编程系列之三十二:丢失的信号

    这里的丢失的信号是指线程必须等待一个已经为真的条件,在開始等待之前没有检查等待条件.这样的场景事实上挺好理解,假设一边烧水,一边看电视,那么在水烧开的时候.由于太投入而没有注意到水被烧开. 丢失的信号 ...

  9. .Net并行编程系列之三:创建带时间限制(Timeout)的异步任务并取得异步任务的结果

    尝试创建基于MVVM三层架构的异步任务: 场景:View层触发ViewModel层的动作请求,ViewModel层异步的从Model层查询数据,当数据返回或者请求超时时正确更新ViewModel层数据 ...

随机推荐

  1. Python 2.7.9 Demo - 005.字符串判空

    #coding=utf-8 #!/usr/bin/python str1 = None; str2 = ''; str3 = ' '; if str1 == None : print("st ...

  2. 用C++画光(三)——色散

    写在前面 源码:https://github.com/bajdcc/GameFramework/blob/master/CCGameFramework/base/pe2d/Render2DScene5 ...

  3. VC2010下Qt5的中文乱码问题

    要搞清楚这个问题,先要弄明白编码.但是编码问题实在太复杂,这里肯定讲不开. 我先找一个例子,比如:“中文” 的 Unicode 码点/UTF8编码/GBK 分别是多少. 先去这个网站,输入 “中文” ...

  4. CPAN镜像使用帮助

    https://lug.ustc.edu.cn/wiki/mirrors/help/cpan ************************************************** 使用 ...

  5. python匹配两个字符串中间的字符串

    问题:使用python正则如何匹配两字符串中间的字符串解决:使用re模块的findall,注意,re.match是只能从开头匹配的方法: import re html_str = '</a> ...

  6. samba实现跨平台文件共享

    前言:Linux/Unix主机之间实现文件共享我们可以使用NFS,那么,Linux/Unix和Windows主机之间共享文件怎么实现呢,samba就是解决这个问题的. Windows网上邻居依赖的协议 ...

  7. JS自动爆炸案例

    学习到了: setTimeout函数的灵活运用. 案例实现讲解: 1.先定义一个全局的变量,赋值为null. 2.然后使用timeout调用bang函数,以达到自动自动调用函数的功能. 3.bang函 ...

  8. 【Unity/Kinect】Kinect一些常用的API

    先开好这个坑,之后用到就补充,方便回顾. 获取用户相对Kinect传感器设备的位置坐标.(在Kinect坐标系中的位置) public Vector3 GetUserPosition(Int64 us ...

  9. u-boot bootz 加载kernel 流程分析

    u-boot 加载 kernel 的流程分析. image重要结构体头文件 // include/image.h * * Legacy and FIT format headers used by d ...

  10. 判断一个string是否以数字开头

    public static void main(String[] args) {        Pattern pattern =null;    String content = "30. ...