首页 > 代码库 > 【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监測EV3port数据

【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监測EV3port数据

在前两篇文章中,我们对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。

技术分享

技术分享

将项目命名为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。

技术分享


技术分享


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

技术分享


然后保存在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。

技术分享


==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数据