Raspberry Pi Web Server using Flask to Control GPIOs

In this project you’ll create a standalone web server with a Raspberry Pi that can toggle two LEDs. You can replace those LEDs with any output (like a relay or a transistor).

In order to create the web server you will be using a Python microframework called Flask.

Parts Required

Here’s the hardware that you need to complete this project:

Raspberry Pi 3

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Basic Raspberry Pi Setup

Before you continue reading this project, please make sure you have Raspbian Operating System installed in your Raspberry Pi.

You can read my Getting Started with the Raspberry Pi Guide to install Raspbian and complete the basic setup.

Installing Flask

We’re going to use a Python microframework called Flask to turn the Raspberry Pi into web server.

To install Flask, you’ll need to have pip installed. Run the following commands to update your Pi and install pip:

pi@raspberrypi ~ $ sudo apt-get update
pi@raspberrypi ~ $ sudo apt-get upgrade
pi@raspberrypi ~ $ sudo apt-get install python-pip python-flask

Then, you use pip to install Flask and its dependencies:

pi@raspberrypi ~ $ sudo pip install flask

Schematics

The schematics for this project are fairly straightforward. Simply connect two LEDs to pins GPIO 23 and GPIO 24, as the figure below illustrates.

RPi Web Server outputs_bb

Creating the Python Script

This is the core script of our application. It sets up the web server and actually interacts with the Raspberry Pi GPIOs.

To keep everything organized, start by creating a new folder:

pi@raspberrypi ~ $ mkdir web-server
pi@raspberrypi ~ $ cd web-server
pi@raspberrypi:~/web-server $

Create a new file called app.py.

pi@raspberrypi:~/web-server $ nano app.py

Copy and paste the following script to your Raspberry Pi (this code is based on Matt Richardson great example).

'''

Adapted excerpt from Getting Started with Raspberry Pi by Matt Richardson

Modified by Rui Santos
Complete project details: https://randomnerdtutorials.com

'''

import RPi.GPIO as GPIO
from flask import Flask, render_template, request
app = Flask(__name__)

GPIO.setmode(GPIO.BCM)

# Create a dictionary called pins to store the pin number, name, and pin state:
pins = {
   23 : {'name' : 'GPIO 23', 'state' : GPIO.LOW},
   24 : {'name' : 'GPIO 24', 'state' : GPIO.LOW}
   }

# Set each pin as an output and make it low:
for pin in pins:
   GPIO.setup(pin, GPIO.OUT)
   GPIO.output(pin, GPIO.LOW)

@app.route("/")
def main():
   # For each pin, read the pin state and store it in the pins dictionary:
   for pin in pins:
      pins[pin]['state'] = GPIO.input(pin)
   # Put the pin dictionary into the template data dictionary:
   templateData = {
      'pins' : pins
      }
   # Pass the template data into the template main.html and return it to the user
   return render_template('main.html', **templateData)

# The function below is executed when someone requests a URL with the pin number and action in it:
@app.route("/<changePin>/<action>")
def action(changePin, action):
   # Convert the pin from the URL into an integer:
   changePin = int(changePin)
   # Get the device name for the pin being changed:
   deviceName = pins[changePin]['name']
   # If the action part of the URL is "on," execute the code indented below:
   if action == "on":
      # Set the pin high:
      GPIO.output(changePin, GPIO.HIGH)
      # Save the status message to be passed into the template:
      message = "Turned " + deviceName + " on."
   if action == "off":
      GPIO.output(changePin, GPIO.LOW)
      message = "Turned " + deviceName + " off."

   # For each pin, read the pin state and store it in the pins dictionary:
   for pin in pins:
      pins[pin]['state'] = GPIO.input(pin)

   # Along with the pin dictionary, put the message into the template data dictionary:
   templateData = {
      'pins' : pins
   }

   return render_template('main.html', **templateData)

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

View raw code

Creating the HTML File

Keeping HTML tags separated from your Python script is how you keep your project organized.

Flask uses a template engine called Jinja2 that you can use to send dynamic data from your Python script to your HTML file.

Create a new folder called templates:

pi@raspberrypi:~/web-server $ mkdir templates
pi@raspberrypi:~/web-server $ cd templates
pi@raspberrypi:~/web-server/templates $

Create a new file called main.html.

pi@raspberrypi:~/web-server/templates $ nano main.html

Copy and paste the following template to your Pi:

<!DOCTYPE html>
<head>
   <title>RPi Web Server</title>
   <!-- Latest compiled and minified CSS -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
   <!-- Optional theme -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
   <!-- Latest compiled and minified JavaScript -->
   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
</head>

<body>
   <h1>RPi Web Server</h1>
   {% for pin in pins %}
   <h2>{{ pins[pin].name }}
   {% if pins[pin].state == true %}
      is currently <strong>on</strong></h2><div class="row"><div class="col-md-2">
      <a href="/{{pin}}/off" class="btn btn-block btn-lg btn-default" role="button">Turn off</a></div></div>
   {% else %}
      is currently <strong>off</strong></h2><div class="row"><div class="col-md-2">
      <a href="/{{pin}}/on" class="btn btn-block btn-lg btn-primary" role="button">Turn on</a></div></div>
   {% endif %}
   {% endfor %}
</body>
</html>

View raw code

Launching the Web Server

To launch your Raspberry Pi web server move to the folder that contains the file app.py:

pi@raspberrypi:~/web-server/templates $ cd ..

Then run the following command:

pi@raspberrypi:~/web-server $ sudo python app.py

Your web server should start immediately!

python launch web server

Demonstration

Open your Raspberry Pi address in your browser by entering its IP address, in my case: http://192.168.1.98/.

rpi web server browser

Here’s a video demo of the web server in action.

I hope you’ve found this tutorial useful.

Share this post with a friend that also likes electronics!

You can contact me by leaving a comment. If you like this post probably you might like my next ones, so please support me by subscribing my blog.

Thanks for reading,

-Rui Santos


Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

47 thoughts on “Raspberry Pi Web Server using Flask to Control GPIOs”

  1. Well, that is pretty interesting.
    I did buy a Raspi and set it up as a computer.
    Does your use here permit the RasPi to continue to act as a computer too or does the webserver take over the RasPi? (like OSMC)
    I know I could use separate SD card and re install, but…

  2. sudo pip install flask results in, “cannot fetch index base URL…Cannot find any downloads that satisfy the requirement flask.” The same results if I capitalize Flask.
    Using RasPi 2B and Raspian Jessie

  3. Wow I have been waiting for a decent tutorial on how to do this for a while… Perfect… I knew it could be done some how

  4. Hi Rui,

    I like this tutorial and it is exactly what I needed for my HA Project. I am a hobbiest and not a programmer, so please hear me out. I have a few questions if you do not mind:

    1. Sometimes if you change the state of GPIO 23 and then GPIO 24 directly after each other, the web-server does not respond untill you try and change the state again. Is there a way to refresh the web-server (page refresh [F5]) after each state change?

    2. Instead of having the {% for pin in pins %} loop, can I use it more specifically, i.e. just the if-loop with specific pin plugged in… like this: {% if pins[18].state == true %}?

    Here is what I’m trying to achieve: dropbox.com/s/grxvjqxzus6nz5o/main.html?dl=0

    Sorry if these are total NOOB questions, but I am one… Thanks in advance.

  5. Hello, Can I use Flask to get my data from RC522 rfid tag? The thing is I’ve already could read data from RC522 but the result shows up in Raspberry Terminal screen. My aim is show this data in webserver using flask. Is it possible?
    In short, I want to this: When I read the card to Rc522 the card’s ID shows up in webserver.
    Thanks so much in advice.

  6. It would be great to see you use WebSockets for this. I see there’s some code for WebSockets with Flask.

  7. I have installed this on my Pine64. Using the RPi.GPIO-PineA64 module found on github. Same wiring. Thanks for the tutorial.

  8. Hello Rui,
    My name is Will. Really appreciate your project and your effort to explain stuff thoroughly. Just wondering how to do the port forwarding if I want to send the requested packet outside of LAN? I’m not familiar with Python or Java or HTML but the problem is that I’m using the RP 3 for my graduation project.I know that you need to forward the packet to port 80 but I don’t know exactly how to do that. Any comment and detailed guide would be greatly appreciated. Thank you.

  9. Hi Rui, my name is Will. I’m just wondering if you could help me to write code so that it will forward packet to port 80? Thank you.

  10. Brand new to Raspberry Pi with a web development background. Very good tutorial. Thanks for taking the time to comment on each line. Learnt a lot and great results!

    Point me in the direction of more of your tutorials!

    Thanks, Matt

  11. Does this allow you to access the webpage with a phone via wifi? I want to monitor some weather conditions on water and access the pi with my phone via wifi.

  12. Hey Rui,
    Thanks for the tutorial but I’m not able to turn on the gpio in the web browser. No activity of blinking can be seen when I turn on the gpio pins!
    Also I’ve 2 codes (one for LED blinking and one to show up a message on a 16*2 LCD) that should be simultaneously called when I turn on the GPIO pins!
    Can you please help me with this!?

    • Can you access the web server? What happens when you press the buttons (do you see any message in the Terminal window)? Can you double-check the pins? Can you try with another LED? Thanks!

  13. I want to access the Raspberry Pi via Wifi from the phone and not having to go through my network in the house. Is that possible?

    The reason is so I can monitor weather conditions on the water and look at the sensor reading from my phone on the bank.

  14. This is my first Flask project that worked – thanks.

    PLEASE offer a web page 2-slider control to steer two servos for a webcam.
    A few sites offer help but none have worked for me – yours always work!

    BTW mjpg-streamer seems by far the best webcam streamer and takes very little resource – I would (try to) add that to your servo control page.

Leave a Reply to Sara Santos Cancel reply

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.