Using RFduino to Create a Bluetooth Music Remote: Part 2

In part one of this series of posts on building a remote control with RFduino, I covered the hardware and software involved in making the remote itself. In this post, we’ll get into the details of the iOS app that will receive commands from the RFduino remote. The full source for the iOS app and the RFduino code is available on GitHub here.

Before I get started, I should point out that since I use Spotify on my iPhone for music, I actually had to embed some basic music playback capabilities into my app. This is because it isn’t possible to send commands to the Spotify app from another app (as far as I know). I ended up using CocoaLibSpotify to stream music from my Spotify playlists within the app, which works very nicely except for some finicky offline playback caching issues. I did need a Spotify subscription to do this, which I already had.

If I just wanted to use the built-in iOS music app, it would have greatly simplified things since the MPMusicPlayerController class can be used to control the built-in music app from within another app, as shown below:

// get the iPod music player and skip to the next track
MPMusicPlayerController *musicPlayer = [MPMusicPlayerController iPodMusicPlayer]
[musicPlayer skipToNextItem];

I’m not going to go into detail on CocoaLibSpotify, as that is a whole topic of its own, but code similar to the above could be substituted in the places where CocoaLibSpotify is used.

RFduino Helper Classes

There are some nice helper classes in the RFduino GitHub repository that make it very easy to implement basic Bluetooth functionality in an iOS app. The files of interest are located here – I simply copied these files into an ‘rfduino’ directory in my iOS project in Xcode.

I did have to modify the ScanViewController.m slightly to suit my purposes. I removed all references to AppViewController, which is the main view controller in the RFduino sample apps. I also removed the code that registers ScanViewController as the delegate for rfduinoManager, since I want my main view controller to play that role. However, I left three of the RFduinoManagerDelegate methods in ScanViewController, because that controller will need to be notified of things related to device discovery and disconnection. ASDMainViewController will call those methods when its own corresponding delegate methods are called, like this:

- (void)didDiscoverRFduino:(RFduino *)rfduino
{
    // ASDMainViewController is the delegate for the rfduinoManager, but the scanViewController still needs
    // to be notified about this particular event
    if (scanViewController != nil && [scanViewController respondsToSelector:@selector(didDiscoverRFduino:)])
        [scanViewController didDiscoverRFduino:rfduino];
}

One other thing to note is that “Uses Bluetooth LE accessories” must be enabled under the Capabilities -> Background Modes section of the project settings. I also enabled the “Audio and AirPlay” background mode since I am streaming music in the app from CocoaLibSpotify. However, this may not be necessary if the MPMusicPlayerController method described above is used.

Making the Initial Connection to RFduino

The ScanViewController is used to display a list of available RFduino devices that can be connected to the iOS device. I placed a ‘connect’ button in the main view that will display the scan view, like this:

- (void)showScanView {
    scanViewController = [[ScanViewController alloc] init];
    [[self navigationController] pushViewController:scanViewController animated:YES];
}

When the user clicks a device in the scan view list and a connection is made, both the didConnectRFduino and the didLoadServiceRFduino delegate methods are called.

- (void)didConnectRFduino:(RFduino *)rfduino
{
    NSLog(@"didConnectRFduino");

    if (scanViewController != nil && [scanViewController respondsToSelector:@selector(didConnectRFduino:)])
        [scanViewController didConnectRFduino:rfduino];

    [rfduinoManager stopScan];
}

- (void)didLoadServiceRFduino:(RFduino *)rfduino
{
    if (scanViewController != nil && [scanViewController respondsToSelector:@selector(didLoadServiceRFduino:)])
        [scanViewController didLoadServiceRFduino:rfduino];

    currentRFduino = rfduino;
    currentRFduino.delegate = self;
    [[self navigationController] popViewControllerAnimated:YES];
    [self setupDisconnectBtn];
}

In the first method, we just pass the message on to the corresponding method of the scanViewController (if it exists), and then tell the rfduinoManager to stop scanning for new devices. In the second method we store a reference to the current rfduino device object, and designate the main view controller as its delegate.

When the connection is lost, the didDisconnectRFduino delegate method is called, which is fairly self-explanatory.

- (void)didDisconnectRFduino:(RFduino *)rfduino
{
    NSLog(@"didDisconnectRFduino");

    [rfduinoManager startScan]; // start scanning for connections again
    if (scanViewController != nil && [scanViewController respondsToSelector:@selector(didDisconnectRFduino:)])
        [scanViewController didDisconnectRFduino:rfduino];
    [self setupConnectBtn];
}

Receiving Commands from RFduino

Now that the groundwork is laid, receiving data from RFduino is simple. All that is needed is to implement the didReceive delegate method from RFduinoDelegate.

- (void)didReceive:(NSData *)data {
    if ([data length] > 0) {
        const unsigned char *bytes = [data bytes];
        NSLog(@"Received data: %d", (int)bytes[0]);
        switch (bytes[0])
        {
            case 0:
                [self.playbackManager nextTrack];
                break;
            case 1:
                [self.playbackManager prevTrack];
                break;
        }
    }
}

All we are doing here is looking at the value of the first byte received to check if it is a 0 or a 1, and then switching to the next or previous track.

Conclusion

And that concludes this series on creating a Bluetooth remote with RFduino. I realize there is a lot of code in the app that I didn’t cover, but most of that is related to streaming music from Spotify, which may be a topic for a another blog post!

1 Comment


  1. Hi, I was wondering if you have any plans on updating this to the new Spotify? I’m interested in getting this working. :)

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *