Annoyatron - Part II: The Code
After explaining the hardware build in Part I, it is finally time to get crackin on a description of the software that runs the Annoyatron. The Linkit Smart 7688 computer that is the brains of the Annoyatron is running OpenWRT Linux and this means that I had my choice of programming languages with which to hack together some code. I opted for Python.
To control the relays via GPIO, it was first necessary to create a class to represent the GPIO pins. The Smart 7688 ships with the MRAA library that provides easy asccess to GPIO functionality. However, for the basic needs of this project, a 3rd party library wasn't needed.
The Class to Control the IO for the Relays
Nothing too fancy here, it is mostly just a bunch of writing to a file.class relay_pin:
value_path = ""
#init the pin with a GPIO number
def __init__(self,num):
#convert the number to a string
num = str(num)
#unexport the pin by writing the pin number to /sys/class/gpio/export
f = open("/sys/class/gpio/unexport",'w')
f.write(num)
f.close()
#export the pin by writing the pin number to /sys/class/gpio/export
f = open("/sys/class/gpio/export",'w')
f.write(num)
f.close()
pin_path = "/sys/class/gpio/gpio"+num
self.value_path = pin_path+"/value"
#set the direction
f = open(pin_path+"/direction", "w")
f.write("out")
f.close()
#default to off
self.off()
def set_high(self):
f = open(self.value_path, "w")
f.write("1")
f.close()
def set_low(self):
f = open(self.value_path, "w")
f.write("0")
f.close()
#setting 'low' turns on
def on(self):
self.set_low()
def off(self):
self.set_high()
#turn off if the class is destroyed
def __del__(self):
self.off()
The Main loop of the Code
In order to know when to turn GPIO pins on or off, the main controller for the Annoyatron runs a loop which will:
- look for a file containing commands
- parse commands from the file
- run the commands
- delete the file
#!/usr/bin/env python
import sys, signal, os, time
from relay_pin import relay_pin
#where is this script?
script_dir = os.path.dirname( os.path.realpath(__file__) )
#what is the path to the command file?
cmd_file = os.path.join(script_dir, 'cmd_file')
#function to run when CTRL+c is pressed
def ctrl_c_quit(signal, frame):
print "so long, buddy!"
sys.exit(0)
#connect ctrl+c to the quit function
signal.signal(signal.SIGINT, ctrl_c_quit)
#create the relay pins
relay_1 = relay_pin(0)
relay_2 = relay_pin(3)
loop = True
print "starting main loop... \n"
while loop:
#does the file exist
if os.path.isfile(cmd_file):
#open the file for reading
f = open(cmd_file, 'r')
#loop through the lines
for L in f:
l = L.strip()
#### get the data
# data is in `PIN_NUM BINARY_VALUE`
#default to no value for the Pin and Binary val
p = b = None
try:
(p,b) = l.split(" ")
except:
# if the line can't be split, the data is bunk
pass
#are p and b not None?
if p !=None and p!=None:
if p == '1':
if b=='1':
print "1 on"
relay_1.on()
elif b=='0':
print "1 off"
relay_1.off()
elif p =='2':
if b=='1':
print "2 on"
relay_2.on()
elif b=='0':
print "2 off"
relay_2.off()
#close the file
f.close()
#delete the file
os.remove(cmd_file)
time.sleep(0.1)
A Web Server/API for Writing Commands to the Command File
What good is the ability to control GPIO from commands in a file if there is no way for a user to create the file? Since Python is already running the main code for the Annoyatron, I decided to use Python again for creating a web API to write the commands file.Using the [Bottle](http://bottlepy.org/docs/dev/) micro-framework, a rather small bit of code quickly gave me a working API as well as the ability to serve static content.
#!/usr/bin/env python
from bottle import Bottle, run, static_file, redirect
app = Bottle()
def write_cmd_file(string):
f = open('cmd_file', "w")
f.write(string)
f.close()
@app.route('/')
def root():
return static_file('index.html', root='public')
@app.route('//')
def action(relay, value):
#convert value to an int and then a string
v = str(int(value))
if relay == "both":
cmd = "1 %s\n2 %s" % (v,v)
else:
r = str(int(relay))
cmd = "%s %s" % (r,v)
write_cmd_file(cmd)
redirect("/")
run(app, host='0.0.0.0', port=8080)
A Web UI for the Web Server
Now to make some static HTML and CSS content to load into a web browser.<html>
<head>
<meta content='width=device-width, initial-scale=1' name='viewport'>
<title>Annoyatron</title>
<style type='text/css'>
body {
color: white;
}
.btn {
border-radius: 5px;
border: 2px solid green;
padding: 3px 10px;
color: white;
text-decoration: none;
}
h2 {
margin-bottom: 5px;
padding: 0px;
}
.group {
margin-bottom: 10px
}
</style>
</head>
<body bgcolor='black'>
<div class='group'>
<h2>Both</h2>
<a class='btn' href='both/1'>On</a>
<a class='btn' href= 'both/0'>Off</a>
</div>
<div class='group'>
<h2>Lights</h2>
<a class='btn' href='1/1'>On</a>
<a class='btn' href= '1/0'>Off</a>
</div>
<div class='group'>
<h2>Beep</h2>
<a class='btn' href='2/1'>On</a>
<a class='btn' href= '2/0'>Off</a>
</div>
</body>
</html>
The Web Interface in Firefox on Android
Simple, and it gets the job done.
Cheers,
Jezra :)