MURVV Mobile Robot with iOS
Creating an iOS app (written in Objective-C) to control a mecanum robot.
by Lucas

Source Code
Introduction
In this project, we will be taking advantage of the work done in previous MURVV projects, and create an iOS app that allows us to control the robot as well as view a live webcam feed. MURVV will be controlled via two joysticks implemented in software, and the webcam feed will be displayed via a UIWebView.
Hardware
MURVV is a “robot” driven by four independent wheels, each attached to a DC motor controlled by a DCC1000 motor controller. The motor controllers are attached to a Phidget SBC running the latest Phidget22 libraries and the Phidget22 network server. The SBC has a USB WIFI adapter allowing MURVV to operate untethered.
Software
Libraries and Drivers
This project assumes that you are somewhat familiar with the basic operation of Phidgets (i.e. attaching and opening devices, reading data, etc). If this is your first time building a Phidgets project with iOS, please refer to the iOS page for instructions on how to setup your environment and get started.
Step 1: Controlling MURVV
Connecting to the motor controllers
The first step will be connecting to the the four motor controllers and identifying which motor corresponds to which wheel. We can set up the PhidgetDCMotor objects in the
viewDidLoad
method of our main view controller. In the viewDidLoad
method, we do the following (four times):
- Call
PhidgetDCMotor_create
to create an instance of the PhidgetDCMotor channel - Call
Phidget_setOnAttachHandler
register for attach events - Call
Phidget_setOnDetachHandler
register for detach events - Call
Phidget_setDeviceLabel
specify which Phidget we want to open - Call
Phidget_openWaitForAttachment
open specified Phidget and wait until an attach event is received.
Note the use of Phidget_setDeviceLabel
above. We can use this function instead of Phidget_setDeviceSerialNumber
because the motor controllers have been labeled in a previous project. This makes identifying the motors extremely simple.
Also note that before we try to open a Phidget, we must enable server discovery by calling PhidgetNet_enableServerDiscovery
. This must be done on iOS applications
as there is no way to connect a Phidget physically, therefore a local connection will never be made.
PhidgetDCMotorHandle motors[4];
NSArray *labelArray;
...
- (void)viewDidLoad {
PhidgetReturnCode result;
[super viewDidLoad];
labelArray = [[NSArray alloc] initWithObjects:@"FrontRight",@"FrontLeft",@"RearRight", @"RearLeft", nil];
result = PhidgetNet_enableServerDiscovery(PHIDGETSERVER_DEVICE);
if(result != EPHIDGET_OK){
const char* errorMessage;
Phidget_getErrorDescription(result, &errorMessage);
[self outputError:@"Failed to enable server discovery" message:[NSString stringWithUTF8String:errorMessage]];
}
for(int i = 0; i < 4; i++){
result = PhidgetDCMotor_create(&motors[i]);
if(result != EPHIDGET_OK){
const char* errorMessage;
Phidget_getErrorDescription(result, &errorMessage);
[self outputError:@"Failed to create dc motor" message:[NSString stringWithUTF8String:errorMessage]];
}
result = Phidget_setOnAttachHandler((PhidgetHandle)motors[i], gotAttach, (__bridge void*)self);
if(result != EPHIDGET_OK){
const char* errorMessage;
Phidget_getErrorDescription(result, &errorMessage);
[self outputError:@"Failed to assign attach handler" message:[NSString stringWithUTF8String:errorMessage]];
}
result = Phidget_setOnDetachHandler((PhidgetHandle)motors[i], gotDetach, (__bridge void*)self);
if(result != EPHIDGET_OK){
const char* errorMessage;
Phidget_getErrorDescription(result, &errorMessage);
[self outputError:@"Failed to assign detach handler" message:[NSString stringWithUTF8String:errorMessage]];
}
result = Phidget_setDeviceLabel((PhidgetHandle)motors[i], ((NSString*)[labelArray objectAtIndex:i]).UTF8String);
if(result != EPHIDGET_OK){
const char* errorMessage;
Phidget_getErrorDescription(result, &errorMessage);
[self outputError:@"Failed to set device label" message:[NSString stringWithUTF8String:errorMessage]];
}
result = Phidget_openWaitForAttachment((PhidgetHandle)motors[i], 5000);
if(result != EPHIDGET_OK){
const char* errorMessage;
Phidget_getErrorDescription(result, &errorMessage);
[self outputError:@"Failed to open motor" message:[NSString stringWithUTF8String:errorMessage]];
}
}
}
Setting motor acceleration
In order for MURVV to respond to our commands in a timely fashion, we must ensure that the motor controller's acceleration is set to an appropriate value. We can set the acceleration in the attach event as shown below.
- (void)onAttachHandler:(NSValue *)p{
const char *deviceLabel;
double accel;
PhidgetHandle phid = [p pointerValue];
Phidget_getDeviceLabel(phid, &deviceLabel);
NSLog(@"%@", [NSString stringWithFormat:@"Got Attach: %@", [NSString stringWithUTF8String:deviceLabel]]);
PhidgetDCMotor_getMaxAcceleration((PhidgetDCMotorHandle)phid, &accel); //get maximum acceleration
PhidgetDCMotor_setAcceleration((PhidgetDCMotorHandle)phid, accel/2); //set acceleration to half the maximum for now
}
We now have access to the motor controllers and we have identified which wheel corresponds to which controller. Now we need a way to actually send commands to MURVV. For this, we will use two virtual joysticks.
Step 2: Creating virtual joysticks
Luckily, someone has already done most of the work for this section! We used the analog joystick library by Dmitriy Mitrophanskiy in order to create the virtual joysticks.
Note: the way the joysticks are used in order to control MURVV has already been covered in this project, so it will not be repeated here.
Step 3: Accessing webcam data
Accessing webcam data on a Phidget SBC is extremely simple. Follow these steps:
- Get a compatible webcam and plug it into the SBC
- Navigate to the WebCam tab on the SBC web interface.
- Confirm the webcam is functional and copy the live stream address. Something like this:
(http://192.168.3.190:81/?action=stream) - Paste this URL in the appropriate place in your code (shown below).
For this project we will be displaying webcam data from the Phidget SBC using a UIWebView. All we have to do is write one line of code in our viewDidLoad
method:
IBOutlet UIWebView *webView;
...
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.3.190:81/?action=stream"]]];
