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 (any Pi should work, I recommend using Raspberry Pi 3) – read Best Raspberry Pi Starter Kits
- SD Card (minimum size 8Gb and class 10)
- Micro USB Power Supply
- Ethernet cable or WiFi dongle
- Breadboard
- 2x LEDs
- 2x 470Ω Resistors
- Jumper wires
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.
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)
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>
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!
Demonstration
Open your Raspberry Pi address in your browser by entering its IP address, in my case: http://192.168.1.98/.
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
I like this tutorial!
Thanks Rui for taking time to prepare this post.
You are welcome, thank you for reading Sam!
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…
I set this up exactly per you instructions and it works! I am impressed.
I can also control the LED’s through my iMAC per the RasPi
rlp
Still works as a linux computer.
So easy! I will try soon it!
Usually I use Django, but I’d like to try Flask
Thank you for this
Awesome! Let me know your results Andrew 🙂
Great resource Rui.
I hope to add an analog input with display of value.
If anyone gets there first please publish code !
Thanks.
That should be an upcoming tutorial, thanks for your suggestion 🙂
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
OOps! above due to spontaneous wifi disconect. Now it downloads.
I’m glad it’s working now! 🙂
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
You’re welcome.
Thanks for reading Samian!
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.
Hi JJ,
1. I wasn’t aware of that glitch, because it never happened to me…
2. You can definitely change the code to do that, I recommend that you take a look at how templates work: jinja.pocoo.org/docs/dev/templates/
Thanks for reading,
Rui
Thanks Rui
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.
I don’t know any library for the Raspberry Pi with the RFID reader
It would be great to see you use WebSockets for this. I see there’s some code for WebSockets with Flask.
Thanks for the idea!
I have installed this on my Pine64. Using the RPi.GPIO-PineA64 module found on github. Same wiring. Thanks for the tutorial.
Thanks for letting me know Jim, I’m glad it worked!
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.
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.
I don’t have any information on that exact subject.
Thanks,
Rui
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
Hi Matt,
I’m glad you enjoyed it. Here’s the complete list of tutorials: https://randomnerdtutorials.com/tutorials
Feel free to choose the next project from that list!
Thanks for reading,
Rui
sir how can i deploy this web page on internet by which i can access raspberry from anywhere?
There’s not an easy way to do that. You might need to setup port forwarding with your router, but that’s not secure and I don’t recommend it.
Thanks,
Rui
Thanks for this tutorial.
How about having boostrap in a directory instead of link ?
Thank you .
You can do it… Simply follow the getbootstrap website guide, download and set the path to your directory in the HTML file
How can I put basic authentication to this project?
Thanks, mate. Great tutorial, it helps me a lot despite I dont use a rapsberry pi, instead an orange pi.
Thanks. This is a more elaborated example than those in the official RPi site or on sparkfun. It works.
Hi. I’m glad it works!
Thanks 🙂
Thank you so much! This is just what I’ve been looking for.
You’re welcome! 🙂
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.
Yes.
You can do that, as long as the phone and the Raspberry Pi are connected to the same network.
Regards,
Sara 🙂
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!
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.
Just rasbery pi to phone.
Hi Kevin.
You can do that if you set your Raspberry Pi as an access point.
Regards,
Sara 🙂
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.
Hi Frank.
I’m really glad you got this project working.
Unfortunately, we don’t have any tutorial about controlling servos with slider for Raspberry Pi.
We have something about that, but it is with the ESP32: https://randomnerdtutorials.com/esp32-servo-motor-web-server-arduino-ide/
The Raspberry Pi camera streaming project can be found here: https://randomnerdtutorials.com/video-streaming-with-raspberry-pi-camera/
Thank you for your interest in our tutorials.
Regards,
Sara 🙂
Well i have verified the code and it is working. thanks to the trainer.
Hi,
First of all, I love your project. At this moment it works fine, but I have a small question.
Is there a way to change the order on the web page?
At this moment it is from top to bottom lamp 4, 2, 3 and 1.
Is it possible to change this to 1, 2, 3 and 4?
Thanks in advance.
Kind regards, Milan
how to start on boot?