iOS + Swift Server + Raspberry Pi = Madness

Image taken from raspberrypi.org

This blog post is a continuation of my previous blog posts exploring Swift on the Raspberry Pi platform.

In my previous adventure, I implemented controlling an LED attached to a Raspberry Pi through Swift code. It was cool but you know what would be more fun than blinking LEDs with Swift? Controlling LEDs and other devices with an iPhone through a Swift server running on a Raspberry Pi!

I started out with drawing a rough diagram of the architecture of the system. It looks something like this:

System architecture

The basic idea is that an iOS app will communicate with a server running on the Pi through standard HTTP methods and the server will control the hardware through the GPIO library. Simple as that.

First, I implemented the iOS app which is my bread and butter. What the app does is it first scans the local network looking for the Pi. Once the Pi is found you configure the app with its IP address. Then the only thing left to do is to implement simple api calls that will be executed on the Rasberry on each action like switching the LED on/off or blinking it.

I took a look at the server frameworks available for Swift on Linux. Out of the most popular frameworks (Vapor, Kitura, Perfect) I selected Vapor since it seemed the most user-friendly at the time. It had command line tools for creating projects and a nice api.

For controlling the hardware I used SwiftyGPIO. To make it all easier to use I wrapped the whole Raspberry system into an object-oriented interface by defining a protocol:

protocol RaspberryApi: class {

func switchLedOn()

func switchLedOff()

func startLedBlink()

func stopLedBlink()

func setLedIntensity(_ intensity: Double)

func displayDigit(_ digit: OneDigitSegmentDisplay.Digit)

func switchDigitDisplaySegmentOff()

func toggleRGBLedColor(_ color: RGBLed.Color)

}

I implemented two concrete classes conforming to the procol. The MockRaspberry for testing and running on the Mac (SwiftyGPIO is not available on the Mac), and the Raspberry class that is actually responsible for controlling the hardware on Linux. The concrete class was selected at runtime given the underlying os the code was running. For this I implemented a factory class:

final class RaspberryFactory {



static func getRaspberry() -> RaspberryApi {

let raspberry: RaspberryApi



#if os(Linux)

raspberry = Raspberry.shared

#else

raspberry = MockRaspberry.shared

#endif



return raspberry

}

}

I was pleasantly surprised by how easy it was to implement the api with Vapor. A couple of lines of code gave me a working api that the iOS app could communicate with!

import Vapor let drop = Droplet()

let raspberry = RaspberryFactory.getRaspberry() drop.get { req in

return "Success"

} drop.patch("led", "switch_on") { (request) in

raspberry.switchLedOn()

return "OK"

} drop.patch("led", "switch_off") { (request) in

raspberry.switchLedOff()

return "OK"

} drop.patch("led", "start_blink") { (request) in

raspberry.startLedBlink()

return "OK"

} drop.patch("led", "stop_blink") { (request) in

raspberry.stopLedBlink()

return "OK"

} drop.patch("led", "intensity") { (request) in

guard let intensity = request.query?["intensity"]?.double else {

throw Abort.badRequest

}



raspberry.setLedIntensity(intensity)

return "Ok"

} drop.patch("digitSegment", Int.self) { (request, digit) in

guard let digit = OneDigitSegmentDisplay.Digit(rawValue: digit) else {

throw Abort.badRequest

}

raspberry.displayDigit(digit)

return "OK"

} drop.patch("digitSegment", "switchOff") { (request) in

raspberry.switchDigitDisplaySegmentOff()

return "OK"

} drop.patch("rgbLed", RGBLed.Color.self, "toggle") { (request, color) in

raspberry.toggleRGBLedColor(color)

return "OK"

} drop.run()

And that’s it! I was able to communicate with the server running on the Pi with my iPhone. Now I could control my precious little LEDs and Segment Displays even when I was in the other room or the balcony. So convenient!

This is the system in action:

Conclusion:

Almost all of the technology used to create this system is experimental at this point. Starting with Swift on ARM Linux, through SPM, Vapor and the GPIO library.

That said I was really pleasantly surprised by how little problems I had with setting this up. The whole coding for this took a couple of hours at most and the effect is really cool. I was really surprised by how simple the server side code turned out to be. Having mostly done iOS work before I thought that the server will be the most difficult part to implement but it turned out just ~50 lines of code. Way to go Vapor!

Next steps:

Having the basics figured out I can now create more complex systems based on this proof of concept one. Some nice projects that come to mind would be a humidity sensor attached to a flower that would send text messages to my iPhone when the humidity is to low. Or controlling a motor that would water the plant through my iPhone.

Let me know if you have any cool ideas :)