If you landed here, this is part two of a three parts tutorial on how to control a lamp using a Raspberry PI and a relay – this part uses Flask (a web framework for Python) to control the lamp (by controlling the relay).

Here are the other parts of the tutorial:

Web Server

Having a loop to activate or deactivate a relay is neat (Part one of the tutorial), but not very functional. What we want is something that lets us turn on or off the light when we wanted.

Lets create a very basic web app that will allow us to do just that.  Because of processing power constrains, we’re going with Flask for the web pages and Nginx for the web server.

Install the necessary software

We’re going to install this in a virtual environment. It’s has a lot of benefits and allows us to keep everything separated (you later can use this Raspberry PI to create another web app in a different environment and no harm will come to this one).

sudo apt-get install python-virtualenv

For Python3

sudo apt-get install python3-venv

NOTE: If you check out the code on my github account, the following steps are not necessary, EXCEPT CREATING THE VIRTUAL ENVIRONMENT. After that, you can go straight to Configure Nginx.

If you downloaded the code from Github.com, just see if everything is working with:

python app.py

If you get something along the following lines:

(venv) pi@raspberrypi:~/lightup $ python app.py 
app.py:12: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup (relay_pin, GPIO.OUT)
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
 * Restarting with stat
app.py:12: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup (relay_pin, GPIO.OUT)
 * Debugger is active!
 * Debugger PIN: 329-089-878

and can browse to http://<RPi_ip>:8080 and see a light, then everything is working fine. Go to configure Nginx

Now, let’s create the web application.  Create a directory where we’re going to put all the files (don’t need if you checked out the code – directory is already created).

mkdir lightup
cd lightup

Now, let’s create the virtual environment and activate it. Let’s create it in a directory called venv (you can named anything you want)

Our virtual environment will be in a directory called venv. Everything will be separated – virtual environment and Flask app

pi@raspberrypi:~/lightup $ virtualenv venv
Running virtualenv with interpreter /usr/bin/python2
New python executable in venv/bin/python2
Also creating executable in venv/bin/python
Installing setuptools, pip...done.

For Python3

python3 -m venv venv

Activate it

pi@raspberrypi:~/lightup $ . venv/bin/activate
(venv)pi@raspberrypi:~/lightup $

Once inside the virtual environment (you notice because the word (venv) is preceded of the current path:

( venv ) pi@raspberrypi:~/lightup

If you ever want to get out of the virtual environment, just type deactivate and it will exit.

If you ever mess things up with the virtual environment, just delete the venv directory (or the name you gave it) and start all over again

We need to install the necessary libraries. Even if already installed on the system, we need them in our virtual environment. And, we don’t need sudo 🙂

pip install flask uwsgi rpi.gpio

NOTE: Installing uwsgi will take some time, because it is being compiled on the PI and pip will download any dependencies for flask

Now, let’s create the application. For starters, just a typical Hello World to see if everything works. Open your favorite editor (I do love Vim) and start typing.

vi app.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
	return "Hello World of Relays!"

if __name__ == "__main__":
	app.run(host='0.0.0.0', port=8080)

Close and save the file.

To run it:

python app.py

In a browser, just type your RPi IP address and specified port (in this case 8080):

Great, it works !

Now, let’s add some functionality and turn on and off the llight

Change the code and add some routes

from flask import Flask
import RPi.GPIO as GPIO

app = Flask(__name__)

relay_pin = 23
GPIO.setmode (GPIO.BCM)
GPIO.setup (relay_pin, GPIO.OUT)

@app.route("/on")
def turn_light_on():
	GPIO.output (relay_pin, GPIO.LOW)
	return "Light on!"

@app.route("/off")
def turn_light_off():
	GPIO.output (relay_pin, GPIO.HIGH)
	return "Light off!"

if __name__ == "__main__":
	app.run(host='0.0.0.0', port=8080, debug=True)

Now, run it again:

python app.py

In a browser, put the following URL to turn the light on

http://<rpi_ip>:8080/on

and to turn the light off

http://<rpi_ip>:8080/off

Flask CODE

What’s happening in the code is, very short version:

We start by importing the Flask class.

Next, we create an instance of the class. First argument is the name of the application. Since we’re using a single module, we’re using __name__.

Next, we use the route() decorator so Flask will know what URL will trigger the application.

The function is given a name which is also used to generate URLs for that particular function and returns a message to the user.

Add some functionality

Now that we now that it works, let’s make it more user friendly using some templates and some buttons.

Create a directory to hold the template for our webpage

mkdir templates
cd templates
vi lights.html

Put the following code in int:

<html>
<body>
        <h2>Lights on RPi Webserver</h2>
        <!-- check pin state -->
        {% if pin == 1 %}
                <p>Light is <b>off</b></p>
                <input type="button" onclick="window.location='/on';" value="Turn on"/>
        {% else %}
                <p>Ligh is <b>On</b></p>
                <input type="button" onclick="window.location='/off';" value="Turn off"/>
        {% endif %}

</body>
</html>

Now, return to the root of the application and change the app.py file

from flask import Flask, render_template
import RPi.GPIO as GPIO

app = Flask(__name__)

#pin connected to relay
relay_pin = 23
#pin state
pin = 1

GPIO.setmode (GPIO.BCM)
GPIO.setup (relay_pin, GPIO.OUT)

#default route, without anything
@app.route("/")
def default():
	# read pin state
	pin = GPIO.input(relay_pin)
	return render_template ('lights.html',pin=pin)

# set a route for action
# light on or off
@app.route("/<status>")
def onAction(status):
	pin = 2
	if status == "on":
		pin = 0
		GPIO.output (relay_pin, GPIO.LOW)
		#message =  "Light on!"
		print ("on")
	if status == "off":
		pin = 1
		GPIO.output (relay_pin, GPIO.HIGH)
		#message = "Light off!"
		print ("off")
	
	# return to the template with new info
	return render_template ('lights.html',pin=pin)

if __name__ == "__main__":
	app.run(host='0.0.0.0', port=8080, debug=True)

What we’ve done was to add some dynamic content to our web server.

Run the application with:

python app.py

Open a browser and put the url of the pi:

http://<RPi_IP:8080/

Hurray ! We got our self’s a neat web application to control a light…

Flash code

We now started to render templates with flask.  Flask uses Jinja2 for that.

To render a template, we use the render_template() method. Just give the name of the template and any variable we wish to pass to the template – we do and it is called pin.

Flask will look for the template in the templates directory (that we’ve created above).

Inside the template, we use {% %} to put some Python code within and check the pin value, to change the button value accordingly.

CSS

Now, we have a beginning of a web application, only that… it’s ugly.. Let’s make it hansom !

Skeleton is a CSS boilerplate with support for desktop and mobile layouts. We’re going to use it to give a great face to our webpage.

Go to http://getskeleton.com/ and download a copy.

Create a folder named static in the root of the application folder.  The result should be this:

pi@raspberrypi:~/lightup $ ls
app2.py  app.py  static  templates
pi@raspberrypi:~/lightup $

Copy the file (at this date, the file is Skeleton-2.0.4.zip)  to the Raspberry PI and move it to the folder static.

Unzip the file. It should have been created a Skeleton-2.0.4 folder with other folders inside.

Archive:  Skeleton-2.0.4.zip
   creating: Skeleton-2.0.4/
   creating: Skeleton-2.0.4/css/
  inflating: Skeleton-2.0.4/css/normalize.css  
  inflating: Skeleton-2.0.4/css/skeleton.css  
   creating: Skeleton-2.0.4/images/
 extracting: Skeleton-2.0.4/images/favicon.png  
  inflating: Skeleton-2.0.4/index.html

We need now to move those folders to the static folder and remove the Skeleton-2.0.4 folder.

cd Skeleton-2.0.4/
pi@raspberrypi:~/lightup/static/Skeleton-2.0.4 $ ls
css  images  index.html
pi@raspberrypi:~/lightup/static/Skeleton-2.0.4 $ mv css/ images/ ..
pi@raspberrypi:~/lightup/static/Skeleton-2.0.4 $ cd ..
pi@raspberrypi:~/lightup/static $ ls
css  images  Skeleton-2.0.4  Skeleton-2.0.4.zip
pi@raspberrypi:~/lightup/static $

Now, remove the Skeleton-2.0.4.zip and the Skeleton-2.0.4 folder. We’re going to have two folders and our lights.html

We’re going to create a html file to be used as a header for our lights.html as well others that we might create.

create a file, named header.html and put the following code in there

<!DOCTYPE html>
<meta charset="UTF-8">
<html>
        <head>
                <title>Light Up</title>
                <meta name="description" content="">
                <meta name="author" content="">

                <link href='//fonts.googleapis.com/css?family=Raleway:400,300,600' rel='stylesheet' type='text/css'>

                <link rel="stylesheet" href="static/css/normalize.css">
                <link rel="stylesheet" href="static/css/skeleton.css">
                <link rel="icon" type="image/png" href="static/images/favicon.png">
        </head>
        <body>
                {% block content %}
                {% endblock %}
        </body>
</html>

Now, edit the file lightup.html and change the code to be:

{% extends "header.html" %}
{% block content %}
        <h2>Lights on RPi Webserver</h2>
        <!-- check pin state -->
        {% if pin == 1 %}
                <p>Light is <b>off</b></p>
                <input type="button" onclick="window.location='/on';" value="Turn on"/>
        {% else %}
                <p>Ligh is <b>On</b></p>
                <input type="button" onclick="window.location='/off';" value="Turn off"/>
        {% endif %}
{% endblock %}

Now, if we execute the application and check the browser, we’re already going to see some differences.

python app.py

Code

What we’ve used here is something called Template Inheritance. We’ve build a base skeleton (header.html) that contains all the common elements. If we had more than one page (we only have lights.html) putting the common things in the same place will save code and make things more clean.

The {% extends %} tag is key. It tells the template engine that it will extend another template, in this case  header.html.

{% block content %} in the header.html will be replaced by our code between {% block content %} inside the lights.html file.

Now, let’s add a last fancy detail.

Go to the static folder.  Create a new folder named js. We’re going to add a bit of javascript to the page using jquery.

cd static
mkdir js
cd js
wget https://code.jquery.com/jquery-3.3.1.min.js

Now, go to the templates folder

Edit the header.html file and add the following lines:

<script src="static/js/jquery-3.3.1.min.js"></script>
<script>
        var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>

The lines above are needed for Flask to load jQuery. More info here.

The complete file should be:

<!DOCTYPE html>
<meta charset="UTF-8">
<html>
        <head>
                <title>Light Up</title>
                <meta name="description" content="">
                <meta name="author" content="">

                <link href='//fonts.googleapis.com/css?family=Raleway:400,300,600' rel='stylesheet' type='text/css'>

                <link rel="stylesheet" href="static/css/normalize.css">
                <link rel="stylesheet" href="static/css/skeleton.css">
                <link rel="icon" type="image/png" href="static/images/favicon.png">
                <script src="static/js/jquery-3.3.1.min.js"></script>
                <script>
                        var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
                </script>
        </head>
        <body>
                {% block content %}
                {% endblock %}
        </body>
</html>

What we’re going to do is to show a light bulb image lit when LED on and Off when LED off, removing the message.

Now, just edit the lights.html file and change the code:

{% extends "header.html" %}
{% block content %}
        <script type="text/javascript">
                $(function() {
                        $('#lighton').click(function() {
                                $('#light').attr("src","static/images/lampon.png");
                        
                        });
                        $('#lightoff').click(function() {
                                $('#light').attr("src","static/images/lampoff.png");
                        });
                });
        </script>
        <div class="container"> 
                <div class="row">
                        <div class="two-thirds columns">
                                <h2>Lights on RPi</h2>
                                <!-- check pin state -->
                                {% if pin == 1 %}
                                        <img src="static/images/lampoff.png" id="light"></p>
                                        <input type="button" onclick="window.location='/on';" value="Turn on"/ id="lighton">
                                {% else %}
                                        <img src="static/images/lampon.png" id="light"></p>
                                        <input type="button" onclick="window.location='/off';" value="Turn off"/ id="lightoff">
                                {% endif %}
                        </div>
                </div>
        </div>
{% endblock %}

Start the application with Python3 and refresh the browser page

python app.py

 

 

Nginx to serve Flask using uwsgi

Now that we have the application terminated, let’s configure Nginx to serve a Flask application

Configure nginx

Before serving anything, Nginx must be installed and configured.

Install Nginx

Because this is a Raspberry PI and we don’t need a lot of things, let’s install the light version of Nginx

sudo apt-get install nginx-light

First, we need to remove the default configuration of sites-enabled (because we’re going to write our own). This is just a symlink, so, remove it:

sudo rm /etc/nginx/sites-enabled/default

Now, create a new configuration for nginx. In the directory of our application, create a new config file for nginx:

vi lightup_nginx.conf

server {
	listen 80;
	server_name localhost;
	charset	utf-8;
	client_max_body_size	75M;
	location /static {
		root /home/pi/lightup/;
	}

	location / { try_files $uri @lightup; }
	location @lightup {
		include uwsgi_params;
		uwsgi_pass unix:/home/pi/lightup/lightup_uwsgi.sock;
	}
}

Create the symlink for this configuration at nginx

sudo ln -s /home/pi/lightup/lightup_nginx.conf /etc/nginx/conf.d/

Restart nginx to make the configuration effective:

 sudo systemctl restart nginx

Now, create the uwsgi configuration file.

vi lightup_uwsgi.ini

[uwsgi]
# base
base = /home/pi/lightup

# Python module
app = app
module = %(app)
home = %(base)/venv
pythonpath = %(base)

#socket location
socket = /home/pi/lightup/%n.sock

# Permissions
chmod-socket = 666

# variable holds flask app
callable = app

#logfiles
logto = /home/pi/lightup/%n.log

Some things to notice:

the app name must match the file name of our application, in this case app.py

Now, let’s try to start the uwsgi daemon:

(venv)pi@raspberrypi:~/lightup $ uwsgi --ini lightup_uwsgi.ini 
[uWSGI] getting INI configuration from lightup_uwsgi.ini

Now, type the address in the browser (without the port this time)

If you run into problems, check the log files (you have from nginx in /var/log/nginx/error.log and the one from the lightup_uwsgi.log in the app directory).

Now that everything is working, we just need to start the uwsgi instance at boot. Since the latest versions of Raspbian use systemd, let’s create a unit

Go to /etc/systemd/system and create a new unit file, ending in .service

sudo vi lightup.service

[Unit]
Description=uWSGI instance to serve lightup
After=network.target

[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/lightup
Environment="PATH=/home/pi/lightup/venv/bin"
ExecStart=/home/pi/lightup/venv/bin/uwsgi --ini lightup_uwsgi.ini

[Install]
WantedBy=multi-user.target

Now, save and start the unit

sudo systemctl start lightup

You can check if it’s running, by issuing

(venv)pi@raspberrypi:~/lightup/venv/bin $ sudo systemctl status lightup
● lightup.service - uWSGI instance to serve lightup
   Loaded: loaded (/etc/systemd/system/lightup.service; disabled)
   Active: active (running) since Tue 2018-03-27 22:20:37 UTC; 1min 20s ago
 Main PID: 2066 (uwsgi)
   CGroup: /system.slice/lightup.service
           └─2066 /home/pi/lightup/venv/bin/uwsgi --ini lightup_uwsgi.ini

Mar 27 22:20:37 raspberrypi systemd[1]: Started uWSGI instance to serve lightup.
Mar 27 22:20:37 raspberrypi uwsgi[2066]: [uWSGI] getting INI configuration from lightup_uwsgi.ini

Just open your browser and type the Raspberry PI URL and see everything is working.

Now, to make it start at every reboot, just enable it

sudo systemctl enable lightup
Created symlink from /etc/systemd/system/multi-user.target.wants/lightup.service to /etc/systemd/system/lightup.service.

And see if Nginx is also starting on boot

sudo systemctl enable nginx

Reboot your PI and after a while, refresh your browser and see if it loads the page

Here’s the setup, with a LAMP instead of the LED

NOTE: Working with electricity is very dangerous and you must know what are you doing.  Always disconnect the lamp from the mains power before doing something in the relay..

Don’t touch the relay when the lamp is connected to the mains power. You’re the sole responsible if something goes wrong…

Just for information, the wire that’s connected to the relay is the phase, the brown one – at least here in Europe.