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:


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=''):
        self.tempDir = '/sys/bus/w1/devices/'
        list = os.listdir(self.tempDir)
        self.fileName = fileName
        self.currentTemp = -999
        self.correctionFactor = 1;
        self.enabled = True

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

                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)

    #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
    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)]

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   = ''
identity.ProductName = 'pymodbus Server'
identity.ModelName   = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'
pi = Temp()
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