The only issue we have encountered, using the BeagleBone
Black with our robot, is the only wireless communication option is
Bluetooth. I know I can communicate from
my laptop to the BeagleBone Black over Bluetooth but we want to control the
robot from our iPhone/iPad/iPods. If you
are familiar with Apple’s iOS Bluetooth stack, you know that it is very limited
and communicating to custom devices like the BeagleBone Black isn’t really an
option.
I could add a WiFi USB adapter but it would draw too much
power when I use the EasyAcc Battery Charger to power the BeagleBone Black. After
doing a bit of research, I stumbled on the Spark Core. The Spark Core is a complete WiFi enabled
development platform that is code-compatible with the Arduino. I believe I can use the Spark Core as a
communication module for our robot but the first thing I needed to do was to
create an iOS library to communicate with Spark’s Web API because Spark does
not currently have one. You can get the
library that I created from my GitHub repository at: https://github.com/hoffmanjon/SparkCoreIOS.
Lets begin by looking at the classes that make up the Spark
Core library. After the class summaries,
I will show how to use the library. If
you download the library there is a complete sample application with it and I
will discuss that sample at the end of this post. The library has two main classes:
- SparkCoreConnector: This
class is the main communication class that handles all communication between an
iOS device and Spark’s Web API. This
class expose one static method: connectToSparkAPIWithTransaction:andHandler:
- SparkTransaction: This class
defines the information that is used to build the URL to Spark’s Web API and
also the parameters that we need to send.
This class is meant to be sub classed for each of the transaction
types. This base class exposes three
public properties, these are:
- baseUrl – the base URL that is used to build the final URL. By default this is: https://api.spark.io/v1/devices.
- accessToken – The access token for your Spark Core.
- deviceId – The id of your Spark Core.
Next lets look at the transaction types that subclass the
SparkTransaction class. Currently I have
two types defined in the library:
- SparkTransactionGet: This library is used to submit a GET request
to Spark’s Web API. This type of request
is used to retrieve data from your Spark Core.
You will need to set the “property”
property to the name of the variable you want to retrieve.
- SparkTransactionPost: This library is used to submit a POST request
to Spark’s Web API. This type of request
is used to submit data/commands to your Spark Core. You will need to set the “functionName” parameter to the function
you want to call and the “parameters”
property to the commands you want to send.
Now lets see how to use the library with some sample code. The code to send a SparkTransactionPost request
would look something like this:
SparkTransactionPost *postTransaction
= [[SparkTransactionPost alloc] initWithAccessToken:ACCESS_TOKEN deviceId:DEVICE_ID functionName:FUNCTION andParameters:parameter];
[SparkCoreConnector connectToSparkAPIWithTransaction:postTransaction andHandler:^(NSURLResponse *response, NSDictionary
*responseDictionary, NSError *error){
if(error == nil) {
NSLog(@"Response: %@",responseDictionary);
} else {
NSLog(@"Error: %@",error);
}
}];
We start by creating a SparkTransactionPost object using the
initWithAccessToken:deviceId:functionName:andParameters:
initializer. We then call the static connectToSparkAPIWithTransaction:andHandler:
method of the SparkConnector class to send the request.
Creating a SparkTransationGet request is very similar to the
Post request. The code looks like this:
SparkTransactionGet *getTransaction
= [[SparkTransactionGet alloc] initWithAccessToken:ACCESS_TOKEN deviceId:DEVICE_ID andProperty:COUNT_VAR];
[SparkCoreConnector connectToSparkAPIWithTransaction:getTransaction andHandler:^(NSURLResponse *response, NSDictionary
*responseDictionary, NSError *error){
if(error == nil) {
NSLog(@"Response: %@",responseDictionary);
} else {
NSLog(@"Error: %@",error);
}
}];
The only difference between the Get and Post requests is we
create a SparkTransactionGet object using the initWithAccessToken:deviceId:andProperty:
initializer instead of the SparkTransactionPost object.
It is that easy to send requests from you iOS device to your
Spark Core with this library.
Lets take a quick look at the sample project that comes with
the library. The first thing we need to
do is to wire up two LEDs and flash the Spark Core. If you do not understand the wiring diagram
or the code that needs to be flashed to the Spark Core, please see Spark’s
documentation here: http://docs.spark.io:
Now that we have the LEDs wired to our Spark Core, we need
to flash it with the following code:
int ledUser = D7;
int led1 = D0;
int led2 = D5;
int myvar = 0;
void setup() {
Spark.function("led", ledController);
pinMode(ledUser, OUTPUT);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
digitalWrite(ledUser, LOW);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
Spark.variable("myvar", &myvar, INT);
}
void loop() {
}
int ledController(String command)
{
myvar =
myvar +1;
int
ledState = 0;
int
pinNumber = (command.charAt(1) - '0');
if
(pinNumber != 0 && pinNumber != 5 && pinNumber !=7) {
return
-1;
}
if(command.substring(3,7) == "HIGH") ledState = HIGH;
else if(command.substring(3,6)
== "LOW") ledState = LOW;
else return
-2;
digitalWrite(pinNumber,ledState);
return 1;
}
Now lets look at our example application. If you run the code you will see a screen
that looks like this:
By flipping the switches, you can turn the LEDs off/on. If you press the count button you will
retrieve the number of times the LEDs have been toggled and display that count.
Lets look at the ViewController.m file to see how this
application works. We start by defining
a number of constants:
#define ACCESS_TOKEN @"123456789"
#define DEVICE_ID @"my-core"
#define FUNCTION @"led"
#define COUNT_VAR @"myvar"
#define LED_USER @"D7"
#define LED_1 @"D0"
#define LED_2 @"D5"
#define STATE_HIGH @"HIGH"
#define STATE_LOW @"LOW"
These constants are:
ACCESS_TOKEN: The access
token for your Spark Core.
DEVICE_ID: The device id for
your Spark Core.
FUNCTION: The name of the
function that we call in our Post requests.
COUNT_VAR: The name of the
variable to request in our Get requests.
LED_USER: The pin for the
user LED on the Spark Core.
LED_1: The pin for the first
external LED that we connected to the Spark Core.
LED_2: The pin for the
second external LED that we connected to the Spark Core.
STATE_HIGH: Defines the
string for the pin’s high state.
STATE_LOW: Defines the
string for the pin’s low state.
Now lets look at our viewDidLoad method. This method is called after the view is
finished loading.
- (void)viewDidLoad {
[super viewDidLoad];
[_led1Switch setOn:NO animated:YES];
[_led2Switch setOn:NO animated:YES];
[_ledUserSwitch setOn:NO animated:YES];
[self getCount];
}
We start off my using the setOn:animated: method of the
UISwitch class and set all of the switches to off since our LEDs will be in the
off state to begin with. We then call
our getCount
method to retrieve the count from the Spark Core. The getCount method looks like this:
-(void)getCount {
SparkTransactionGet *getTransaction = [[SparkTransactionGet alloc] initWithAccessToken:ACCESS_TOKEN deviceId:DEVICE_ID andProperty:COUNT_VAR];
[SparkCoreConnector connectToSparkAPIWithTransaction:getTransaction andHandler:^(NSURLResponse *response, NSDictionary
*responseDictionary, NSError *error){
if(error == nil) {
NSLog(@"Response: %@",responseDictionary);
NSString *cnt = [responseDictionary objectForKey:@"result"];
_countLabel.text = [NSString stringWithFormat:@"Count: %@", cnt];
} else {
NSLog(@"Error: %@",error);
_countLabel.text = @"Error
Getting Count";
}
}];
}
In this method we create a SparkTransactionGet object using
the initWithAccessToken:deviceId:andProperty:
initializer.
We then use the connectToSparkAPIWithTransaction:andHandler:
static method to send the request to Spark’s Web API. For the handler parameter we pass in a block
object that will run after Spark’s Web API returns a response. In the block object we check to see if there
was an error. If there was no error we
display the count. If there was an error
we display the error message. The
request created by this method is the same as using curl like this: curl –G https://api.spark.io/v1/devices/my-core/myvar
–d access_token=123456789
Next we need to create methods that will be called when the
user flips the switches. These methods
look like this:
-(IBAction)ledUserSwitchAction:(id)sender {
NSString *pin = LED_USER;
NSString *state = STATE_HIGH;
if (_ledUserSwitch.on)
state = STATE_LOW;
NSString *param = [NSString stringWithFormat:@"%@:%@",pin,state];
[self sendRequestWithParameter:param];
}
-(IBAction)led1SwitchAction:(id)sender {
NSString *pin = LED_1;
NSString *state = STATE_HIGH;
if (_led1Switch.on)
state = STATE_LOW;
NSString *param = [NSString stringWithFormat:@"%@:%@",pin,state];
[self sendRequestWithParameter:param];
}
-(IBAction)led2SwitchAction:(id)sender {
NSString *pin = LED_2;
NSString *state = STATE_HIGH;
if (_led2Switch.on)
state = STATE_LOW;
NSString *param = [NSString stringWithFormat:@"%@:%@",pin,state];
[self sendRequestWithParameter:param];
}
Each of these methods does the same thing. They begin by setting the “pin” NSString to the pin on the Spark Core that
we want to set. We then set the “state” NSString to High or Low depending on the
state of the switch. We use the “pin” NSString and the “state” NSString to create our “param”
NSString. Finally we call the sendRequestWithParameter:
method to send the request to Spark’s Web API.
Now lets look at the sendRequestWithParameter: method.
-(void)sendRequestWithParameter:(NSString *)parameter {
SparkTransactionPost *postTransaction = [[SparkTransactionPost alloc] initWithAccessToken:ACCESS_TOKEN deviceId:DEVICE_ID functionName:FUNCTION andParameters:parameter];
[SparkCoreConnector connectToSparkAPIWithTransaction:postTransaction andHandler:^(NSURLResponse *response, NSDictionary
*responseDictionary, NSError *error){
if(error == nil) {
NSLog(@"Response: %@",responseDictionary);
} else {
NSLog(@"Error: %@",error);
}
}];
}
This method begins by creating a SparkTransactionPost object
using the initwithAccessToken:deviceId:functionName:andParameters:
initializer. We then call the same connectToSparkAPIWithTransaction:andHandler:
static method that we saw in the getCount method. In the block object that we pass to the
handler, we simply log the response from the Spark’s Web API or we log the
error depending on if we received an error or not. The request created for this method is the
same as using curl like this: curl https://api.spark.io/v1/devices/my-core/led
–d access_token=123456789 -d params=d0,HIGH
I have just started using the Spark Core so I put in the
functionality that I think I need to communicate to my robot. If anyone has any suggestions or
recommendations on what functionality they would like to see in the library,
please let me know. I hope you find this
library useful.
No comments:
Post a Comment