Introduction: IPhone to Arduino Using Bluetooth 4.0 --

About: I'm a homeless outreach worker in Fort Worth, Texas. I hack away on electronics as a way to deal with the stress; a type of meditation in hopes I don't fall prey to compassion-fatigue or burnout. I spend a…

Ladvien.com

NOTE: This project requires an iOS Developer License. It's $99 a year.

This write-up describes writing an app for Bluetooth 4.0 iOS devices to enable a serial connection to an Arduino. To facilitate the serial connection on the Arduino end a HM-10 acts as a peripheral, which interacts with the Ardunio via a serial connection. Of course, the HM-10 operates on 3.3v and the Arduino 5v, but I created an Instructable on how to convert the HM-10 to 5v.

How to Create an Arduino Compatible Bluetooth 4.0 Module

The HM-10 is a low cost (6.87 USD, shipping ~.20) Bluetooth 4.0 module.

Order from Fastech:

  1. HM-10
  2. HM-11

2013:

I'd been wanting to create a bridge between Arduino and iOS for awhile. Not going to lie, not a huge fan of iOS devices, but since my wife won an iPad Mini I've begrudgingly accepted they fill a need. Though, my hacking has orbited robotics projects and any device I can't connect to a robot frustrate me. Well, a year ago I realized an iPad Mini would connect to an HM-10, and therefore, robot.

Sadly, the app used to connect to the HM-10 was LightBlue, an app with little versatility. However, it allowed me to confirm everything was in place to the connect the iPad Mini to a robot. But then I learned something confirming my disdain for Apple products.


To develop an app for iOS devices you must have an iOS Developer's license and a Mac publish from. This sort of stuff infuriates a hacker. Well, skipping all the melodrama I'm infamous for, I put off developing an app because I figured the total cost for me to get started was ~$400 (cheap Mac and License). Not worth the risk, since I'd never even used a Mac, let a lone coded on one. Well, enter the heroes of the story: Carduino.

Last year Simon Riley from the Carduino project contacted me. He asked what had stopped me from developing an iOS app to connect to the HM-10. My answer was short: Lack of funds. Carduino, in a great act of faith, donated towards the cause.

2014:

Thanks to Simon Riley and the Carduino Project!

Step 1: What We're Going to Make --

What we'll make:

I'm going to keep it pretty simple. I've written a TableViewController which takes care of finding BLE devices, display, and connection. I will walk you through creating a new project and adding my bleModule to it. Then, I'll show you how to implement the following functions:

This is our RX function,

func peripheral(peripheral: CBPeripheral?, didUpdateValueForCharacteristic characteristic: CBCharacteristic?, error: NSError!) {

This is our TX function,

func peripheralDevice.writeValue(data, forCharacteristic: deviceCharacteristics, type: CBCharacteristicWriteType.WithoutResponse)

Step 2: Getting Started --

Create a New Project 1:

Open Xcode and Select "Create a New Xcode project"


We are going to use the Single View Application template.

Name your App and click Next.

Step 3: Storyboard Layout --

Embed Main View in Navigation Controller 2:

We are going to use a Navigation Controller to help us manage the project. Embed your View Controller into a Navigation Controller by selecting Editor-->Embed In-->Navigation Controller

Add a Table View Controller 3:

Add a Table View Controller to your project. This will be used to display discovered BLE devices.

Setup the Prototype Cell 4:

We need to give the prototype cell an Identifier. Name it "cell"

Step 4: Storyboard Layout --

Add bleTableViewController 5:

I've provided a UI TableViewController that takes care of discovering and connecting the HM-10/11.

Download the zip file. After extracting the file, locate the bleTableViewController.swift in the bleModule directory, then move it to your project as shown below. When asked, make sure "Copy Items if Needed" is selected.

Set Table View class to bleTableViewController 6:

We need to connect the bleTableViewController.swift to our Table View Controller we added. To do this, click on the Table View you added, go to Identity Inspector. Set the Class to bleTableViewController, as shown below.

Create and Connect a "Scan" Button 7:

Switch back to your Main View Controller. We need to add a Bar Item Button (not a regular button), this button will need to go in the top right corner of your Navigation Bar. We will use this button to segue to the BLE device Table View.

Set your Bar Button's title to "Scan." Then, hold down control, click and drag from your Scan button to the Table View Controller. When you let go you'll be given several segue options, select "push"

Step 5: Main View Controller Layout --

Main View Setup 8:

We nee to add two more items to the main view.

  1. Button
  2. Text Field

After they are added, go to this constraints editor and click "Add Missing Constraints" under "All Views in View Controller"

Now we have our interface added, we need to create an action and outlet for the items. You do this by holding Control, clicking on the item, and dragging to your code. You must be under the "Assistant Editor" view.

  1. For the Button create an Action named "sendButton". Make the action perform on "Touch up Inside."
  2. Create an Outlet for the Text Field, name it "sendTextField".

Now we have our project setup, let's jump into the code.

Step 6: Code

Under import UIKit add,

import CoreBluetooth

This loads the BLE Framework.

Change your class definition to look like this,

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate

This connects the Main View to listen for actions performed by the iPhone's BLE and the HM-10.

Since we added the CBCentralManagerDelegate we must add the centralManagerDidUpdateState. This functions watches the iOS device's on Bluetooth device. If it changes states (e.g., Bluetooth is turned off) then this function will run.

func centralManagerDidUpdateState(central: CBCentralManager?) {
    if let central = central{
        if central.state == CBCentralManagerState.PoweredOn {
            println("Bluetooth ON")
        }
        else {
            println("Bluetooth switched off or not initialized")
        }
    }
}

Basically, all we are doing here is printing out to system whether the BLE got turned off. You could change this to do all sorts of fancy stuff, but we are going to keep it simple.

Now, here's where it gets fun! Let's add a function to grab any data coming from the HM-10 and print it to the navigation bar title.

// Get data values when they are updated
func peripheral(peripheral: CBPeripheral?, didUpdateValueForCharacteristic characteristic: CBCharacteristic?, error: NSError!) {
    if let characteristicValue = characteristic?.value{
        var datastring = NSString(data: characteristicValue, encoding: NSUTF8StringEncoding)
        if let datastring = datastring{
            navigationItem.title = datastring as String
        }
    }
}

A few things of note here, var datastring = NSString(data: characteristicValue, encoding: NSUTF8StringEncoding) is actually getting the data from the RX buffer, converting it into an NSString. The if let datastring = datastring is unwrapping the optional, then setting the navigation bar title to the RX string. Nifty, eh?

Mind you, the could make the recipient of the data anything which takes a string. I simply used the navigation bar title so we didn't need to add other items.

Alright, we are getting close to the end. What about sending text?

// Write function
func writeValue(data: String){
    let data = (data as NSString).dataUsingEncoding(NSUTF8StringEncoding)
    if let peripheralDevice = peripheralDevice{
        if let deviceCharacteristics = deviceCharacteristics{
            peripheralDevice.writeValue(data, forCharacteristic: 
	deviceCharacteristics, type: CBCharacteristicWriteType.WithoutResponse)
        }
    }
}

This function is actually a data preparation function for the real workhorse: peripheralDevice.writeValue(data, characteristic).

The wrapper function I created, writeValue(String), takes a string, it converts it to UTF8 data format, which is the format needed by writeValue(data, characteristic). It then checks if there is a peripheral to write the values. If there is, it calls the writeValue function using the data you passed it.

If that seems confusing, just know, it takes a string and sends it to the receiving device.

You can call this writeValue function from a button, such as the "Send" button we setup.

@IBAction func sendButton(sender: AnyObject) {
    if let text = sendTextField{
        writeValue(sendTextField.text)
    }
}

This causes our button to grab whatever you typed into the Text Field box and send it to the receiving device, by way of the nifty little writeValue(String) function we made.

OK! That's it. (I hope. Gulp.)

When you finish your ViewController's code should looking something like this,

Step 7:

Now for my favorite caveat:

I'm a only nominally professional app developer. Meaning, if you find bugs or areas of improvement, scream them out at me. I'm committed to personal development and I can only do it with honest feedback. That stated, I can't guarantee my code is bug free. But if you find a bug, I do promise to attempt correcting it quickly.

Enjoy!

Some other articles that might be useful,

Coded Creations

Participated in the
Coded Creations