Overview

The Micro:bit is an amazing bit of kit for those of you who like building electronic gadgets and would like to control them using smart phones and tablets.

It's ability to use Bluetooth Low Energy, it's ease to program and connect external components, makes the Micro:bit an ideal and cheap way to build interfaces.

While I found examples of software running on Android devices that could communicate and control the Micro:bit, I found little on writing for Apple devices such as iPhones and iPads. I decided to address this by writing a module, written in Swift, that provides a simple interface between Apple applications and the Micro:bit.

See: Microbit-Swift for information on using the Microbit-Swift API and it's associated Playground.

The Micro:bit Musical Instrument Controller is an example of using Microbit-Swift to control an iPhone/iPad application. As you can see from the video I'm not likely to be seen as competing with Stradivari when it comes to building musical instruments, but I hope it demonstrates how a few electronic components can control an iPhone/iPad App.

The block diagram below shows the components of the Musical Instrument Controller.

Communication between the Micro:bit and iPad is accomplished using Bluetooth Low Energy (BLE) and is facilitated by the Bluetooth stack incorporated into the Micro:bit Runtime and Core Bluetooth which part of the iOS operating system.

micro:bit controller overview

Block diagram showing the components of the Micro:bit Musical Instrument

Sensors and Switches

We start with a couple of sensors connected to the micro:bit.

In addition, I've added three simple 'push to make buttons' which can control extra functionality, in this example they will activate a pitch bending facility and control the octave range.

The following shows how I've connected the sensors to the micro:bit.

micro:bit controller overview

Circuit diagram showing the connections to the Micro:bit

VR1 and VR2 are the the FSR and SoftPot respectively, SW2,SW3,SW4 are the push buttons. The values I used for R1, R2 & R3 are relatively arbitrary using resistors I had to hand that would give me reasonable results. You may like to experiment with different values.

All the external components are connected to the micro:bit via a Kitronik Edge Connector Breakout Board.

Musical Instrument Controller

The inside of the micro:bit controller

The Control Program

The control program runs in the micro:bit. It's written using the Javascript Blocks Editor.

Micro:bit control program

The Micro:bit (javascript) control program

By default the editor does not include the Bluetooth package, so you you will need to add it using the Add Package option in the Advanced section of the packages list. For further information on how this is done see this article.

It is recommended that during testing the No Pairing option is selected. To do this, select the cog in the top right of the editor and then select Project Settings. Now select - No Pairing Required: Anyone can connect via Bluetooth.

The program uses the Bluetooth Event Service to send micro:bit pin values to the iOS application. The same thing could be accomplished using the IO Pin Service, in which case the controller program would simply consist of starting the 'bluetooth io pin service'. I did find, however, that reading values from the SoftPot could be inaccurate probably because too many readings were being taken before the pressure on the device had stabilised. Raising events containing values from an analogReadPin appeared to give more predictable results helped by introducing a delay of 1 millisecond between each read. Also, this approach allows the full range of the micro:bit 10 bit DAC (0 - 1023) to be transmitted where as the io pin service scales the value to be held in a single byte giving a range of 0 - 255.

As the Bluetooth Event Service is enabled by default, no specific bluetooth start command is required.

The program will raise 5 events:

To prevent unnecessary events being raised a simple filter consisting of checking if the new value on a pin matches its last value is incorporated in the program.

I thought it might be fun to demonstrate the Bluetooth accelerometer service so I've included a pitch bend facility that bends a note depending on the angle of the controller. From the control program perspective, all that is needed is a startAccelerometerService command. This allows accelerometer data to be transmitted at a rate determined by the iOS application.

Microbit-Swift API

Central to this project is to demonstrate the Microbit-Swift application programming interface allowing Apps written for Apple operating systems to communicate with a Micro:bit.

For information about the API see : Micro:bit Swift Bluetooth LE.

The API is written in the Swift programming language and exploits the Apple Core Bluetooth framework. It's easily installed in any Xcode project by simply copying Microbit.swift into the project.

Micro:bit Midi Controller

The controller is a simple iOS application that exploits the CoreMIDI framework to create a MIDI virtual source that can be discovered by GarageBand.

The controller constructs MIDI messages -

based on sensor information from the micro:bit and sends the messages to GarageBand via the MIDI virtual source.

The controller communicates with the micro:bit via the Microbit-Swift API.

Example Microbit-Swift API calls

The following shows examples of API calls to communicate with the Micro:bit Musical Controller.

Initialise a Microbit object.
let microbit = Microbit("BBC micro:bit [tizip]")
Set a delegate to receive call backs from the API
microbit.delegate = self
Start looking for the micro:bit named in the Microbit constructor and if found connect to it.
microbit.startScanning()
Specify what happens when the Accelerometer and Event services are detected.
extension ViewController:MicrobitDelegate {
    func serviceAvailable(service: ServiceName) {
        switch service {
        case .Accelerometer:
            microbit.accelerometer(period: .p160)
        case .Event :
            microbit.registerEvents(events: [9010,9011,9012,9013,9014])
        default :
            break
        }
    }
Specify what happens when the app receives one of the registered events.
extension ViewController:MicrobitDelegate {
        func microbitEvent(type: Int16, value: Int16) {
        //print ("Event = \(type) Value = \(value)")
        switch type {
        case 9010 :
            midiController.stringPosition(value: Int(value))
            stringPosLabel.text = String(value)
            noteNameLabel.text = midiController.noteName
        case 9011 :
            midiController.volumePosition(value: Int(value))
            volumeLabel.text = String(value)
        case 9012 :
            if value == 1 {
                if midiController.pitchBendOn {
                    midiController.setPitchBendOff()
                    pitchBendOnLabel.text = "OFF"
                } else {
                    midiController.pitchBendOn = true
                    pitchBendOnLabel.text = "ON"
                }
            }
        case 9013 :
            if value == 1 {
                if midiController.octave < MAX_OCTAVE {
                    midiController.octave += 1
                    modeLabel.text = String(midiController.octave)
                }
            }
        case 9014 :
            if value == 1 {
                if midiController.octave > 0 {
                    midiController.octave -= 1
                    modeLabel.text = String(midiController.octave)
                }
            }
        default :
            print ("Invalid Event")
        }
    }
Specify what happens when accelerometer data is received.

In this project only the 'y' value is used to determine the angle of the instrument. If the neck is horizontal the value should be 0, the more the neck is tilted upwards 'y' will increase it's negative value. This value can then be used to construct a MIDI pitch bend message.

extension ViewController:MicrobitDelegate {
    func accelerometerData(x: Int16, y: Int16, z: Int16) {
        midiController.pitchPosition(value: Int(y))
        pitchLabel.text = String(y)
    }