Controlling A Raspberry Pi From A Web Browser With Vapor 3

Introduction

In this tutorial you will learn how to access and control a Raspberry Pi development board from a web browser. I will show you how to control 3 LEDs, but this implementation can be easily extended to access and control much more. A basic understanding of electronics and programming is expected along with some familiarity with the Raspberry Pi platform. We will be using the Swift programming language along with the Vapor 3 server-side library for our implementation. If you are new to Swift, Vapor, or the Raspberry Pi platform or would just like to refresh your knowledge, check out our Hello World: Writing Your First Swift Program, Writing A Server-Side Program In Swift Using Vapor, and Blink: Making An LED Blink On A Raspberry Pi tutorials first before proceeding with this one. All resources used in this tutorial are available on GitHub for your reference.

My development board is the Raspberry Pi 3 Model B running the Raspbian (November 2018 release) operating system. My version of Swift is 4.1.2. If you are using a different model or a different OS that is similar to Raspbian, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.

If you need help with your specific setup, post a question in the comments section below and someone can try to help you.

Building The Circuit

Before connecting anything to your Raspberry Pi, shutdown and disconnect the Raspberry Pi from power. This avoids accidental damage during wiring. Build the circuit shown below to connect the LEDs to the Raspberry Pi.

Attach the red LED to the breadboard by connecting the anode of the LED to one of the breadboard’s terminal strips and the cathode to the ground bus strip. The anode (positive terminal) of an LED is longer than the cathode (negative terminal). Connect one end of a 330 Ω resistor to the terminal strip containing the red LED’s anode and the other end of the resistor to an adjacent terminal strip.

Follow the same process as above for wiring the yellow and green LEDs as well.

Attach the male end of a jumper wire to the open end of the resistor connected to the red LED. Attach the other (female) end to GPIO21 (physical pin 40) on the Raspberry Pi header. In a similar fashion, connect jumper wires for the yellow and green LEDs to pins GPIO20 (38) and GPIO16 (36) respectively. Also attach a jumper wire from the ground bus strip on the breadboard to one of the ground (GND) pins on the Raspberry Pi header.

Once the circuit is completed, it should look like the photo below.

Raspberry Pi LEDs setup with attached GPIO Breadboard Interface Board

You can now connect power to your Raspberry Pi and boot it up.

Setting Up The Vapor Server

If you haven’t done so already, this is the time to make sure that your Raspberry Pi is already configured to access your local network.

As stated previously, Swift and Vapor are are not quite ready for primetime. For instance, the Vapor Toolbox helper application, used for easily creating and managing Vapor based projects, is not yet available when running Raspbian on the Raspberry Pi. At least I was not able to figure out how to install it. I even tried to compile it locally, but it requires Swift 4.2, which itself is not yet available. Since we are not able to utilize the Toolbox just yet, we will have to add the boilerplate code ourselves, do some of the operations manually, and even “fix” a line of code in one of the sub-libraries used by Vapor.

Let’s now get Vapor up and running.

Create a new project directory named VaporLEDs and go into that directory.

% mkdir VaporLEDs
% cd VaporLEDs

Create a new Swift based project with the following Swift Package Manager command.

% swift package init --type executable

We now need to add the libraries we will use in this project as dependencies. Vapor is a server-side library, Leaf is an HTML templating library, and SwiftyGPIO is a library for controlling the GPIOs on the Raspberry Pi. Edit the Package.swift file and add the following dependencies to the package

// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "VaporLEDs",
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/vapor/vapor.git", .exact("3.1.0")),
.package(url: "https://github.com/vapor/leaf.git", .exact("3.0.2")),
.package(url: "https://github.com/uraimo/SwiftyGPIO.git", .exact("1.1.0")),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "VaporLEDs",
dependencies: ["Vapor","Leaf","SwiftyGPIO"]),
]
)

Once your Package.swift file has been updated and saved, update (load) all of the packages (libraries) with the following command.

% swift package update

Next, we are going to utilize the Leaf templating language and create an HTML template for our home page. By default, the Leaf library looks for templates within the Resources/Views directory, so we need to create it first.

% mkdir Resources
% mkdir Resources/Views

Then create a new file named base.leaf within that directory with the following contents.

This file contains the base structure for an HTML file and will be used as the foundation for or any subsequent templates we create. The #get() construct allows us to pass data into the template through variables, title and body in this case.

Now create a new file named home.leaf, within the same directory as base.leaf, with the following contents.

Here, we are embedding the base.leaf template, using the #embed() construct, and passing in the title and body variables, using the #set() construct. Once we have our server implemented, we will see the Raspberry Pi saying hello to us.

Now that our templates are set up, it is time to create our program. Replace the contents of the Sources/VaporLEDs/main.swift file with the following minimal Vapor based program.

Build (compile) the project with the following Swift Package Manager command. Make sure you are in the top level project directory (VaporLEDs) before running any Swift Package Manager commands or you may get erroneous results.

% swift build

The compile will take a little while, and once finished, we should see the following warning and error displayed.

We can ignore the warning, but we need to fix the error so that our project will successfully compile. Normally, I would strongly discourage editing any of the source package files directly, but in this case we need to fix the error in order to proceed. The error is stating that the init member is expecting a result type of UnsafeMutableRawPointer!, but a type of ImplicitlyUnwrappedOptional<Wrapped> is received instead. Luckily we can fix this fairly easily by casting the result to the expected type.

Edit the SSLContext.swift file using the full path shown in the compilation output and replace the following on line 315

We are now using Swift and Vapor to serve web pages from the Raspberry Pi! How cool is that? It’s amazing how we can get excited about some of the most simple things. When you are ready to exit the program, press CTRL-C to terminate it.

Integrating An LED Into The Program

Now let’s control an LED using our new website.

The first thing we need to do in incorporate the SwiftyGPIO library to access the onboard GPIO of the Raspberry Pi. Edit the main.swift file again and import the library just below the Leaf and Vapor imports at the beginning of the file.

import SwiftyGPIO

Add the SwiftyGPIO configuration, shown below, just after the Vapor configuration section.

This route lets us send the URL commands /redLED/on and /redLED/off to turn on and off the red LED. It also returns the status of the red LED as determined by the Raspberry Pi for us to verify it is working as planned.

Once you have saved your main.swift file, let’s give it a try. Again from the top level project directory, build and run the program.

We have made good progress. We are now able to turn on and off three LEDs from a web browser. That is all well and good, but I think we can do better.

Bringing It All Together

Wouldn’t it be nice if we had a single page showing us the status and allowing us to control all of the LEDs? I think so, so let’s make it happen.

Replace the contents of the home page Leaf template (Resources/Views/home.leaf) with the code shown below to add a nice looking table for this capability. The first column provides the component names: Red, Yellow, and Green LEDs. The second column provides the status of each LED: ON (in green color) or OFF (in red color). The third, and last, column provides us the ability to turn ON or OFF each LED.

Don’t forget to save the file when you are done.The /redLED/on, /redLED/off, etc. routes we used to control the LEDs still leave us on their own individual status web pages. Let’s instead make them redirect back to the home page for a cleaner user interface. To do this we need to change the return types from String to Response for the routes that control the LEDs along with adding the actual redirects themselves. For the redLED, this entails editing the main.swift file again and replacing the lines

router.get("redLED", String.parameter) { req -> String in // redLED

with

router.get("redLED", String.parameter) { req -> Response in // redLED

at the beginning of the route and

return "redLED: \(redLED.value == 1 ? "On" : "Off")"

with

return req.redirect(to: "/")

at the end of the route. Then do the same for the yellow and green LED routes as well.

Once you’re done making edits, save, build, and run the program again. Open the http://raspberrypi.local:8080 URL to view and test the new format. Looking quite nice isn’t it?

Everything is looking pretty good, but we are not quite done yet. There is still one little problem that I would be remiss if I didn’t share and explain how to resolve it.

We have been exiting the program (CTRL-C) with the Raspberry Pi GPIO pins left in the same state as was used by the program. This could lead to potential issues with other programs or circuitry. We should be good citizens and reset the utilized pins back to their default states upon exiting.

Most programs run through various paths of the code and eventually exit once the main function has completed. We would, in that case, just put our GPIO cleanup code at the end of the main function. In this case, we find ourselves in an endless loop when using the Vapor library, hence, we have to use CTRL-C to exit the program. To solve this, we need to incorporate a signal interrupt handler that detects the CTRL-C operation and gives us the path to a clean exit.

Let’s first create a function, named resetGPIO(), that handles all of our GPIO cleanup duties. Then we need to create the signal interrupt handler and call our resetGPIO() function from within it. Add the following code just below the SwiftyGPIO configuration section and above the Routes section.

Now when we exit the program, the pins will go back to their default states. Give it a shot; save, build, and run the program again. If one or more of the LEDs are turned on when exiting, you should now see all of them turn off.

Summary

In this tutorial we learned how to view the status and control three LEDs on a Raspberry Pi development board from a web browser. Although this may be a simple example, this approach can be extended to control a wide variety of operations and/or components. The endpoints (routes) used here can even be used within scripts, smartphone applications, etc. for even more functionality.

The complete project and the schematic are available on GitHub for your reference.

Thank you for joining me in this journey and I hope you enjoyed the experience. Please feel free to share your thoughts in the comments section below.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.

He knew he wanted to work with technology from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).

He put himself through college (University of Texas at Austin) by working at Motorola where he worked for many years after that in Research and Development.

John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.

Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.