Tuesday, September 24, 2013

Home Automation Project #3 Light Switch Cont. - WebUI-Source with WebSockets!

For the best and quickest response times with my webgui I decided to go with websockets. This was my first websocket experiment and it took many, many tutorials and websocket engines for me to finally come to this conclusion. I went with autobahn websockets because of its ease of use and ability to do reactor.callLater() which is the equivelent of a setTimout in javascript from what I understand. With that I used it to constantly check the outputs of my PiFace and send the socket message to the client webapp based off of its changes. So if anyone else in your house turns the lights on or off physically, you can instantly see it change on your phone. Now the only problem is older devices (like my wife's phone) are not all html5 websocket compatible. You may have to create a python cgi script to use as a fallback.

If you are kinda lost and need to recap on the lightswitch series here are the links and summaries:
Setting up the Piface communication listener and the button listener scripts HERE
Pics and description of the WebUI HERE

Setup:
Need to install Twisted, AutoBahnPython, and for the static pages I use lighttpd.

sudo apt-get install python-pip
sudo easy_install autobahn
sudo apt-get install twisted lighttpd


Websocket Server Code:

needs to run on boot after out PifaceListener script (source found here) has started.

Webpage Code:

I copied to my /var/www/ directory:

Sunday, September 22, 2013

Home Automation Project #3 - WebApp

I posted last week about the Raspberry Pi Light Switch (link here) and wanted to share the web pages I've developed to controlling these home automation devices. Starting with the Lighting:



As you see in the pics the status light changed on the Lamps button.  It happened when I pushed the Lamps button on the web app which then turned the Lamps on and indicated they were on the web app.  If I were to physically press the button the webapp would indicate the change within a second of the light coming on (thanks to html5 server push and AJAX).  I've made my router a vpn server so I can securely do this from anywhere I have an internet connection or cell service.  (And now the wife's asking me why I keep playing with the lights....I am writing this at work, oops. )

If you have questions or comments don't be afraid to ask.



 Here's a sneak peak of what's coming up in the series:



Tuesday, September 17, 2013

Home Automation Project #2 Rpi Light Switch

Lightswitch Replacement

Parts: PiFace $30,
Rpi Model A $25,
Wifi Adapter $10,
120v to 5vdc adapter $5ish,
Low voltage 2 gange wall box $5
Blank wall plate $2
2x low profile sanwa arcade buttons $4

This was one of my first pi projects in my home automation endeavor. I wanted something simple and that wasn't too expensive. I decided to go with the PiFace add-on board.  
One really important thing I wanted was to be able to control the light switch via the web. I ran into a problem with the webapp though. Every time I opened it up it would reset the piface card. I found it was reinitializing the card every time it opened my cgi script. I then decided I needed a python program that would run on boot that would listen for commands to execute against the piface card. I then came upon Pyro, which came in very handy.

Source for my pyro piface

import Pyro.core
import piface.pfio as cmd
import os
from time import sleep
from sys import exit
from os import fork,chdir,setsid,umask


class Command(Pyro.core.ObjBase):
        def __init__(self):
          Pyro.core.ObjBase.__init__(self)
        def outputStatus(self):
          list1=cmd.read_output()
          status = '{0:08b}'.format(list1)[::-1]
          return status
 #output command for piface outputs
        def outputCmd(self,pin,value):
             cmd.digital_write(pin,value)
             return self.outputStatus()
#command to return input statuses
        def inputStatus(self):
          list1 = cmd.read_input()
          status = '{0:08b}'.format(list1)[::-1]
          return status
#read cpu temperature
        def pitemperature(self):
          return open('/sys/class/thermal/thermal_zone0/temp','r').read()
def main():
        Pyro.core.initServer()
        cmd.init()
        daemon = Pyro.core.Daemon()
        uri = daemon.connect(Command(),"cmd")
        print "cmd port: ", daemon.port
        print "cmd uri:", uri
        daemon.requestLoop()

if __name__ == "__main__":
  try:
    pid = fork()
    if pid > 0:
      exit(0)
  except OSError, e:
    exit(1)

  chdir("/")
  setsid()
  umask(0)

  try:
    pid = fork()
    if pid > 0:
      exit(0)
  except OSError, e:
    exit(1)
  main()


I then have either my button listener script(which runs as a daemon on boot) or the web cgi script. Here's the lightswitch.py daemon code:

import sys
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from os import fork,chdir,setsid,umask
from sys import exit
from time import sleep
import time
import os
import Pyro.core

def main():
        loop=True
#connect to the piface pyro daemon we created earlier
        cmd = Pyro.core.getProxyForURI("PYROLOC://localhost:7766/cmd")
        time_pushed=0
        pushed_1=False
        pushed_2=False
        longpush_2=False
        count=0
        client = ModbusClient('ip_address_of_modbus_IO')
        client.close()
        while loop:
                sleep(.05)
                status = cmd.outputStatus()
                inputstatus = cmd.inputStatus()
#toggle button turn relay 1 on
                if inputstatus[0]=='1' and pushed_1 != True:
                        if status[0]=='0':
                           cmd.outputCmd(0,1)
                        else:
                           cmd.outputCmd(0,0)
                        pushed_1=True
                if inputstatus[0]=='0' and pushed_1:
                        pushed_1=False
#toggle button turn relay 2 on
                      if inputstatus[1]=='1' and pushed_2 !=True:
                        pushed_2=True
                        longpush_2=False
                        time_pushed=time.time()
#if both buttons are pressed for 10 second the reboot the pi
                      while inputstatus[0]=='1' and inputstatus[1]=='1':
                        sleep(1)
                        count=count+1
                        if(count==10):
                          os.system("sudo reboot")


#button released
                     if inputstatus[1]=='0' and pushed_2:
#calculate the time the button was pressed to determine what to do
                        time_taken = time.time()-time_pushed
                        pushed_2=False
                        if(time_taken<.5):
                         if status[1]=='0':
                           cmd.outputCmd(1,1)
                         else:
                           cmd.outputCmd(1,0)
#send command to remote i/o using modbus to turn on some lamps if you hold the button longer than 1/2 second
                        if (time_taken>.5):
                         try:
                          if(client.connect()):
                           if(client.read_discrete_inputs(200,1).bits[0]==True):
                             client.write_coil(0,True)
                           else:
                             client.write_coil(2,True)
                           client.close()
                         except:
                           e = sys.exc_info()[0]
                           f=open('error.log','w+')
                           f.write(e+'\n')
                           f.close()
                        time_pushed=0
#turn off light at certain time
                        hour=time.localtime(time.time())[3]
                        minute=time.localtime(time.time())[4]
                        second=time.localtime(time.time())[5]

                if(hour==5 and minute == 0 and second == 0 and status[0]=='1'):
                  cmd.outputCmd(1,0)


#useful daemon-like code:
if __name__ == "__main__":
  try:
    pid = fork()
    if pid > 0:
      exit(0)
  except OSError, e:
    exit(1)

  chdir("/")
  setsid()
  umask(0)

  try:
    pid = fork()
    if pid > 0:
      exit(0)
  except OSError, e:
    exit(1)
  main()



I threw in a bit of modbus to talk to some of my remote i/o to control some lamps throughout the room. The web code is similar to the lightswitch code (without listening for button presses of course). It response to AJAX requests to give the statuses of the outputs. If desired I can post the web code later.

Pics





see part one to this series here:Sprinkler Control

Saturday, September 14, 2013

Raspberry Pi SCADA Part 1, Modbus Temperature Sensor

Raspberry Pi SCADA Part 1, Modbus Temperature Sensor.

One great thing about the Pi is that it is so cost effective in some SCADA applications. With several different languages to be able to present your data. In the many crazy off the wall things I will do in my series here I will start off with using pymodbus,the DS18b20, and a 4.7k resistor temperature probe to get building temperature. Now there are known security flaws with modbus since it is an open protocol, but with this example you can only read data from the addresses.  This will be in holding the register 0x00 and will need to be scaled by 100 afterwords.

My total cost:
$25.00
.05
.20
$10.00
$3.00
Total
$38.25

SETUP

get pymodbus and dependencies:
sudo apt-get install python-pymodbus python-twisted-conch 

from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
from twisted.internet.task import LoopingCall
from threading import Thread
from time import sleep
import os
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
temperature =0
class Temp(Thread):
    """
     A class for getting the current temp of a DS18B20
    """

    def __init__(self, fileName=''):
        Thread.__init__(self)
        self.tempDir = '/sys/bus/w1/devices/'
        list = os.listdir(self.tempDir)
        if(list[0][:2]=="28"):
         fileName=list[0]
        self.fileName = fileName
        self.currentTemp = -999
        self.correctionFactor = 1;
        self.enabled = True

    def run(self):
        while True:
            if self.isEnabled():
                try:
                    f = open(self.tempDir + self.fileName + "/w1_slave", 'r')
                except IOError as e:
                    print "Error: File " + self.tempDir + self.fileName + "/w1_slave" + " does$
                    return;

                lines=f.readlines()
                crcLine=lines[0]
                tempLine=lines[1]
                result_list = tempLine.split("=")

                temp = float(result_list[-1])/1000 # temp in Celcius
                temp = temp + self.correctionFactor # correction factor
                #if you want to convert to Celcius, comment this line
                temp = (9.0/5.0)*temp + 32

                if crcLine.find("NO") > -1:
                    temp = -999
                self.currentTemp = temp
                #print "Current: " + str(self.currentTemp) + " " + str(self.fileName)
            sleep(1)

    #returns the current temp for the probe
    def getCurrentTemp(self):
        return self.currentTemp

    #setter to enable this probe
    def setEnabled(self, enabled):
        self.enabled = enabled
    #getter
    def isEnabled(self):
        return self.enabled

def updating_writer(a):
    context  = a[0]
    register = 3
    slave_id = 0x00
    address  = 0x00
    #print pi.getCurrentTemp(),str(int(pi.getCurrentTemp()*10))
    values = [int(pi.getCurrentTemp()*100)]
    context[slave_id].setValues(register,address,values)

store = ModbusSlaveContext(
    di = ModbusSequentialDataBlock(0, [0]*100),
    co = ModbusSequentialDataBlock(0, [0]*100),
    hr = ModbusSequentialDataBlock(0, [0]*100),
    ir = ModbusSequentialDataBlock(0, [0]*100))
context = ModbusServerContext(slaves=store, single=True)

identity = ModbusDeviceIdentification()
identity.VendorName  = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl   = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName   = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'
pi = Temp()
pi.start()
time = 5 # 5 seconds delaytime = 5 # 5 seconds delay
loop = LoopingCall(f=updating_writer, a=(context,))
loop.start(time, now=False) # initially delay by time
StartTcpServer(context, identity=identity, address=("localhost", 502))
#change localhost to your ip address.


Check out how to control Raspberry Pi outputs using this library here



Sunday, September 8, 2013

Raspberry Pi PID/PWM 12V Fan Controller

Just for fun project.


So awhile back I purchased a Raspberry Pi.  I'm trying to stray away from the expensive industrial world in my home automation projects (namely the PLC).  I had many many project ideas for this neat little computer.... homemade digital picture frame, turtle habitat controller, light switches, weather station, thermostat, and on and on.  I purchased a PI weather board (found here) which gives you temperature, humidity, barometer , 4 high power dc outputs, and a couple external 1-wire sensors ports.
I had lots of fun with this little board and the PI. I didn't have to put it together which made it a good first project.  I put it in the fridge, on the ac vents, outside, and in the crawlspace to see what the temperatures were. I couldn't think of much more to do until I could get more funds and more parts for the projects I had in mind.

So after toying around for a bit with an LED and PWM I thought it'd be cool to get that working with the high power outputs the weather board has except with a computer fan :-). I found an old 12v 1000mA AC adapter plenty good enough for the fans.


Now that I got that working I wanted it to start cooling the weatherboard sensors and slow down to try and meet the desired set point. I found that in the industrial world there are nice little equations and formulas to help you achieve this. The PID loop (if you want to learn what that is (wiki link) gave me a value which I added to my dutycycle:
python pid code here ( i'll post my code later)

After recording that video the fans adjusted their speed to about 20-30% to maintain the setpoint. This could be a helpful addition to the cooling part of my turtle habitat project.