Thursday, April 3, 2014

Using MQTT protocol and Mosquitto Message Broker to control an AC motor through a web app with Python.

ATTENTION - IN THIS POST MAINS (AC) CURRENT IS USED. AC CURRENT CAN KILL YOU. MAKE SURE THAT YOU KNOW WHAT YOU ARE DOING IF YOU FOLLOW THE BELOW INSTRUCTIONS.

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.

The way to go was MQTT & Mosquitto.

"Before going any further I would like to thank Dougie Lawson, whose post inspired me and from which I have used some code too & Roger Light who is developing Mosquitto and also replied to my questions."

This project's S/W part consists of 3 parts:
  1. Installation of all needed modules.
  2. The HTML Form that the user is accessing in order to control the motor.
  3. The S/W that receives user's form input and publishes it as an MQTT message to the Broker.
  4. The S/W that subscribes to the Broker, runs as a service at the background, reads the MQTT messages published to the Broker and acts accordingly, based on the MQTT messages.
I will now explain thoroughly each one of the above 4 parts.

Part 1. Modules Installation

First of all we need to install the Debian Mosquitto broker module and the Python Mosquitto module.
A "sudo apt-get install mosquitto" will handle the first one. After installation the broker will automatically start as service. A "sudo pip install mosquitto" will handle the second one. If you don't have PIP for Python installed please refer to this link Install PIP for Python before proceeding.
Now that we have all the prerequisites in place lets write some code!

Part 2. The HTML form

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
Line 73 sys.exit()

But what actually is between the Raspberry Pi and the AC Motor?

No comments :

Post a Comment