patternpythonMinor
Web server to switch GPIO pin
Viewed 0 times
gpiowebserverswitchpin
Problem
I am running a simple Python webserver to toggle a GPIO pin
on a Raspberry Pi. All seems to work well, but I have a few
questions about my code (see below).
I am using the
The HTML file just sends out Ajax requests using buttons when clicked.
both ways. Is it mandatory to send a response?
Python code:
on a Raspberry Pi. All seems to work well, but I have a few
questions about my code (see below).
I am using the
urlparse module to obtain the URL arguments.The HTML file just sends out Ajax requests using buttons when clicked.
- I've commented out the send_response lines, and it seems to work
both ways. Is it mandatory to send a response?
- Is the code below easy to exploit? Are there improvements I can make to make it a bit more robust?
Python code:
import time
import SimpleHTTPServer
import SocketServer
from urlparse import *
ledpin=18
myport=8123
# Turn LED on => http://ip_of_pi:port/control.html?led=on
# Turn LED off => http://ip_of_pi:port/control.html?led=off
class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
urlcomp=urlparse(self.path) # Split URL in components
query = parse_qs(urlcomp.query) # Get args as dictionary
if len(query)==0 or query.has_key("led")==False:
self.path=urlcomp.path
# self.send_response(200)
elif query["led"] == ["on"] :
GPIO.output(ledpin,True)
# self.send_response(200)
return
elif query["led"] == ["off"] :
GPIO.output(ledpin,False)
# self.send_response(200)
return
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(ledpin,GPIO.OUT)
Handler = MyRequestHandler
httpd = SocketServer.TCPServer(('0.0.0.0', myport), Handler)
httpd.serve_forever()Solution
Import mess
You fail to bring
Global layout mess
It is good practice to put top-level code into functions and call them in a
Note that I also turned your constants into parameters with default values. In this case it can make testing much more easier.
You also don't need your alias (
Request handler
There is a more idiomatic way of checking if
It is recommended that you call the
If you want to send back data to the browser on top of the status code, you can write whatever content you want into
If you want to read the body from a file, just use regular python file manipulation:
You fail to bring
GPIO into the namespace by using something along the lines of from RPi import GPIO. You also import time but don't use it. And you import everything from urlparse using from urlparse import *. This form is frowned upon as it polutes your namespace and may override something else. Better use import urlparse or from urlparse import urlparse, parse_qs.Global layout mess
It is good practice to put top-level code into functions and call them in a
if __name__ == '__main__' clause. That way you can start an interactive session and import your script for testing purposes without having code running as a side effect of your import:def init_GPIO(ledpin=18):
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(ledpin,GPIO.OUT)
def init_webserver(port=8123):
httpd = SocketServer.TCPServer(('0.0.0.0', port), MyRequestHandler)
httpd.serve_forever()
if __name__ == "__main__":
init_GPIO()
init_webserver()Note that I also turned your constants into parameters with default values. In this case it can make testing much more easier.
You also don't need your alias (
Handler = MyRequestHandler) and can use the class directly.Request handler
There is a more idiomatic way of checking if
'led' is in the query string; by using EAFP:class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
urlcomp = urlparse(self.path) # split url in components
query = parse_qs(urlcomp.query) # Get args as dictionary
try:
led_state = query['led']
except KeyError:
pass
else:
turn_on = led_state == ["on"]
GPIO.output(ledpin, turn_on)
self.send_response(200)It is recommended that you call the
send_response method since, even if the raspberry-pi handled the request and turned the led on or off, your browser is still waiting for a reply of your webserver. Not calling self.send_response(200) should result in a timeout of your browser at some point.If you want to send back data to the browser on top of the status code, you can write whatever content you want into
self.wfile which will stream is to your browser. You can use it to make it simpler to control your raspi. You can also cusomize the message based on how you handled the action.class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
urlcomp = urlparse(self.path) # split url in components
query = parse_qs(urlcomp.query) # Get args as dictionary
try:
led_state = query['led']
except KeyError:
message = "No commands processed"
else:
if led_state in (["on"], ["off"]):
GPIO.output(ledpin, led_state == ["on"])
message = "Led turned {}".format(led_state[0])
else:
message = "Unknown action {}".format(led_state)
# Build links whatever the action was
message += """
Turn on the led
Turn off the led
"""
self.send_response(200)
# Custom headers, if need be
self.send_header('Content-type', 'text/html')
self.end_headers()
# Custom body
self.wfile.write(message)If you want to read the body from a file, just use regular python file manipulation:
with open('control.html') as html:
self.wfile.write(html.read())Code Snippets
def init_GPIO(ledpin=18):
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(ledpin,GPIO.OUT)
def init_webserver(port=8123):
httpd = SocketServer.TCPServer(('0.0.0.0', port), MyRequestHandler)
httpd.serve_forever()
if __name__ == "__main__":
init_GPIO()
init_webserver()class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
urlcomp = urlparse(self.path) # split url in components
query = parse_qs(urlcomp.query) # Get args as dictionary
try:
led_state = query['led']
except KeyError:
pass
else:
turn_on = led_state == ["on"]
GPIO.output(ledpin, turn_on)
self.send_response(200)class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
urlcomp = urlparse(self.path) # split url in components
query = parse_qs(urlcomp.query) # Get args as dictionary
try:
led_state = query['led']
except KeyError:
message = "<p>No commands processed</p>"
else:
if led_state in (["on"], ["off"]):
GPIO.output(ledpin, led_state == ["on"])
message = "<p>Led turned {}</p>".format(led_state[0])
else:
message = "<p>Unknown action {}</p>".format(led_state)
# Build links whatever the action was
message += """<p>
<a href="/control.html?led=on">Turn on the led</a>
</p><p>
<a href="/control.html?led=off">Turn off the led</a>
</p>"""
self.send_response(200)
# Custom headers, if need be
self.send_header('Content-type', 'text/html')
self.end_headers()
# Custom body
self.wfile.write(message)with open('control.html') as html:
self.wfile.write(html.read())Context
StackExchange Code Review Q#112222, answer score: 2
Revisions (0)
No revisions yet.