The concept is to be able to control an AC motor through a web based application using Python.
There are a lot of ways to implement something like this. Initially, I have chosen to use a Python/CGI script that would do that. I created a Web Form that would pass the user's input to the CGI script running on my RPi Apache Server, and the CGI script would do the rest. The problem I faced with this setup is that the permissions on the CGI script where not enough to be able to trigger the RPi GPIO pins. As I didn't want the Apache to run as root I had to find a different way.
I will now explain thoroughly each one of the above 4 parts.
The PHP form is a simple form that consists of a vertical slider. Since the motor to be controller is used to open/close the window shades at my home I have emulated the vertical windows shades with a vertical HTML5 slider. Based on the user's input, a different message is sent to the mosquitto broker that emulates the opening or closing of the window shades.
|
<head>
<style type="text/css">
input[type=range].vertical
{
writing-mode: bt-lr; /* IE */
-webkit-appearance: slider-vertical; /* WebKit */
width: 8px;
height: 200px;
padding: 0 5px;
}
</style>
</head>
<body>
<form name="email" action="/cgi-bin/mqtt.py" method="get">
<input type="range" name="dir" min="0" max="2" value="0" step="1"
class="vertical" orient="vertical"/>
<input type="submit" value="Submit">
</form>
</body>
|
The css part of the HTML code above is used in order to rotate the slider vertically.
The input type = "range" is the actual slider. What it looks like in Chrome is the following:
As shown in the code above it's initiated at value = "0", emulating WINDOW CLOSED, has a max value of 2, emulating WINDOW OPEN and an intermediate value of 1, emulating WINDOW HALF.
There is also a submit button that will trigger the form submission.
The vital part of the form though is it's action: action="/cgi-bin/mqtt.py"
Based on the above we have set the form to use method GET in order to pass to the mqtt.py script located in the web server's cgi-bin folder the value of the slider. I am running apache2 and the default location of cg-bin on my RPi is located at /usr/lib/cgi-bin/
Up to this point we have created the Web Form that the user will use in order to send her input to the script that will pick it up and publish it to the Broker.
Part 3 - The Publisher
Now we need to create the script that will pick the user's input and will publish our MQTT messages to the Broker, make them available for the subscribers. This is our mqtt.py file located in the "cgi-bin" folder as stated above. Before proceeding I propose to open next to this page the
documentation of the mosquitto python module. This will help you understand in depth all the following.
The actual code is the following:
|
#!/usr/bin/env python
# Import modules for CGI handling & Debugging
import cgi
import cgitb; cgitb.enable()
#Import Python Broker Module
import mosquitto
#Import Other Python Modules
import os
import time
broker = "127.0.0.1"
port = 1883
mypid = os.getpid()
client_uniq = "pubclient_"+str(mypid)
mqttc = mosquitto.Mosquitto(client_uniq)
#connect to broker
mqttc.connect(broker, port, 60, True)
mqttc.loop()
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from form fields
dir = form.getvalue('dir')
#Print the result in HTML
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Publish MQTT Program</title>"
print "</head>"
print "<body>"
print "<h2>Message %s sent to MQTT</h2>" %(dir)
mqttc.publish("motor/action", dir)
print "</body>"
print "</html>"
|
Line 1 states that our script must be executed by Python program
Lines 4-5 imports all the needed CGI related modules so that the program is executed by our Web Server
Line 8 imports the Mosquitto Python modules
Lines 11-12 imports additional Python modules needed in our program
Lines 14-15 sets variables broker & port to our needs
Lines 17-18 help us create a unique id per client that connects to the broker
Line 19 creates a mosquitto client instance
Line 22 connects our client to the broker
Line 23 executes a continuous loop which is need for all Python clients so that the function properly
Line 26 creates a FieldStorage CGI instance so that we can pick form values
Line 29 assigns form value to a variable
Lines 32 - 41 prints the results of the CGI script execution to the browser
Line 39 Will use this pattern mqttc.publish(msg.topic, msg.payload) and will publish "dir" to "motor/action"
Since we have our MQTT messages coming from our HTML form, published to the Broker, we need to create the software that will read these by subscribing to the corresponding MQTT topic and use them accordingly.
Part 4 - The Subscriber & Action Taker
The script below will subscribe to the predefined MQTT topic, will read the topic's payload, will decode it and based on that will execute some GPIO actions. We will set this script to run as a service so that each time a new MQTT message is received (the user is opening/closing the window shades) the script re-executes accordingly.
|
#!/usr/bin/env python
import time
import sys
import os
import mosquitto
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
#GPIO pin to drive OPEN
GPIO.setup(7, GPIO.OUT)
#GPIO pin to drive CLOSE
GPIO.setup(22, GPIO.OUT)
#Motor Move execution function
def trigger(x, y):
GPIO.output(x, 1)
time.sleep(y)
GPIO.output(x, 0)
GPIO.cleanup()
def on_message(mqtts, userd, msg):
#GPIO pin to drive OPEN
GPIO.setup(7, GPIO.OUT)
#GPIO pin to drive CLOSE
GPIO.setup(22, GPIO.OUT)
topic_payload = []
topic_payload.append(msg.payload)
if topic_payload[0] == "1":
print "Changing Window to HALF"
directionPin = 7
durationSecs = 6
trigger(directionPin, durationSecs)
elif topic_payload[0] == "2":
print "Changing Window to FULLY OPEN"
directionPin = 7
durationSecs = 12
trigger(directionPin, durationSecs)
elif topic_payload[0] == "0":
print "Changing Window to FULLY CLOSED"
directionPin = 22
durationSecs = 12
trigger(directionPin, durationSecs)
def main():
broker = "127.0.0.1"
port = 1883
mypid = os.getpid()
sub_uniq = "subclient_"+str(mypid)
mqtts = mosquitto.Mosquitto(sub_uniq)
mqtts.on_message = on_message
mqtts.connect(broker, port, 60)
mqtts.subscribe("motor/action", 0)
try:
rc = 0
while rc == 0:
rc = mqtts.loop()
GPIO.cleanup()
return 0
#CTRL+C keyboard interrupt
except KeyboardInterrupt:
# resets all GPIO ports used
GPIO.cleanup()
return 4
if __name__ == "__main__":
sys.exit(main())
|
Line 1-15 Are self explanatory
Line 30-31 I write the payload to a list, even if not needed for this version of the script, in order to be able to use it in the future versions of the script.
Line 24-47 Each time a message is received the on_message function is called. Based on the message the corresponding GPIO pins are picked (7 for Open, 22 for Close) and corresponding delay times are set (12 secs for complete Open/Close and 6 secs for half)
Line 17-22 Is the function that is responsible for the AC Motor movement.
Line 50-58 Same as in the previous script
Line 56 On Message receipt call on_message
Line 58 Subscription to motor/action topic
Line 60-70 Loop for ever unless keyboard interrupt
Line 72 Call main - Execute Program