Products for USB Sensing and Control
Products for USB Sensing and Control

MURVV Mobile Robot with iOS

Creating an iOS app (written in Objective-C) to control a mecanum robot.


by Lucas

Source Code

Download

Note: when opening the Xcode project included for this project, you will have to change some settings before it will work. These include:

  • Header search path
  • Other linker flags

There is a detailed guide on how to change these here.

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_openWaitForAttachmentopen 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"]]];

Completed project. Next up: hire UI designers...