Raspberry Pi und industrielle Benutzeroberfläche (HMI) mittels Modbus

Will man in rauer Umgebung mit einem Raspi kommunizieren, ist der Einsatz eines industriellen Touchpanels eine gute Idee. Ich setze hierfür gerne die Geräte  von  Proface ein. Die Geräte gibt es in vielen Größen und Ausführungen .Mittels eines Softwaretools (GP ProEX) lassen sich leicht und schnell ansprechende Oberflächen erstellen.  Hier will ich in einem kurzen Beispiel zeigen, wie man mir Hilfe eines ProFace-Touchpanels mit dem Raspberry Pi kommuniziert und die GPIOs des Raspis schaltet sowie den Zustand seiner Eingänge anzeigt.Schnittstellen

Schnittstellen

Die Proface Panels verfügen über eine unglaubliche Anzahl von Treibern für nahezu alle am Markt verfügbaren Steuerungen und einer Vielzahl weiterer Geräte wie z.B. Frequenzumricher (Inverter) oder auch Temperaturregler. Für unsere Anwendung bietet es sich an, den Modbus als Protokoll zum Informationsaustausch zwischen Raspi und Panel zu nutzen. Als physikalisches Übertragungsmedium soll hierbei das Ethernet genutzt werden, sowohl der Pi als auch die ProFace-Panels bieten entsprechende Schnittstellen hierfür.

Qelltext für die Modbus-Kommunikation mit den ProFace – Panels

#!/usr/bin/env python
'''
Pymodbus Server With Updating Thread
--------------------------------------------------------------------------
This is an example of having a background thread updating the
context while the server is operating. This can also be done with
a python thread::
    from threading import Thread
    thread = Thread(target=updating_writer, args=(context,))
    thread.start()
'''
#---------------------------------------------------------------------------#
# import the modbus libraries we need
#---------------------------------------------------------------------------#
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
import RPi.GPIO as GPIO

#---------------------------------------------------------------------------#
# import the twisted libraries we need
#---------------------------------------------------------------------------#
from twisted.internet.task import LoopingCall

#---------------------------------------------------------------------------#
# configure the service logging
#---------------------------------------------------------------------------#
import logging
logging.basicConfig()
log = logging.getLogger()
#log.setLevel(logging.DEBUG)


# RPi.GPIO Layout verwenden (wie Pin-Nummern)
GPIO.setmode(GPIO.BOARD)

# Pin 13 (GPIO 27) auf Output setzen
GPIO.setup(37, GPIO.OUT)
GPIO.setup(35, GPIO.OUT)
GPIO.setup(33, GPIO.OUT)
GPIO.setup(31, GPIO.OUT)
GPIO.setup(29, GPIO.OUT)

#Pins auf Input setzen
GPIO.setup(40, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(38, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(36, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(32, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(22, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)


#---------------------------------------------------------------------------#
# define your callback process
#---------------------------------------------------------------------------#
def updating_writer(a):
    ''' A worker process that runs every so often and
    updates live values of the context. It should be noted
    that there is a race condition for the update.
    :param arguments: The input arguments to the call
    '''
    log.debug("updating the context")
    context  = a[0]
    register = 1
    slave_id = 0x00
    address  = 0x00
    #Wert aus Register 000001 von Button auslesen
    values   = context[slave_id].getValues(register, address)
    if values[0] == False:
        gpio_off(37)
    elif values[0] == True:
        gpio_on(37)

    address  = 0x01
    #Wert aus Register 000002 von Button auslesen
    values   = context[slave_id].getValues(register, address)
    if values[0] == False:
        gpio_off(35)
    elif values[0] == True:
        gpio_on(35)

    address  = 0x02
    #Wert aus Register 000003 von Button auslesen
    values   = context[slave_id].getValues(register, address)
    if values[0] == False:
        gpio_off(33)
    elif values[0] == True:
        gpio_on(33)

    address  = 0x03
    #Wert aus Register 000004 von Button auslesen
    values   = context[slave_id].getValues(register, address)
    if values[0] == False:
        gpio_off(31)
    elif values[0] == True:
        gpio_on(31)

    address  = 0x04
    #Wert aus Register 000005 von Button auslesen
    values   = context[slave_id].getValues(register, address)
    if values[0] == False:
        gpio_off(29)
    elif values[0] == True:
        gpio_on(29)

    # Eingang lesen
    register = 1
    address = 0x05
    if GPIO.input(40) == GPIO.HIGH:
        values = [1]
        #setzt den Datenanzeiger auf den Wert
        context[slave_id].setValues(register, address, values)
    elif GPIO.input(40) == GPIO.LOW:
        values = [0]
        context[slave_id].setValues(register, address, values)

    # Eingang lesen
    register = 1
    address = 0x06
    if GPIO.input(38) == GPIO.HIGH:
        values = [1]
        #setzt den Datenanzeiger auf den Wert
        context[slave_id].setValues(register, address, values)
    elif GPIO.input(38) == GPIO.LOW:
        values = [0]
        context[slave_id].setValues(register, address, values)

    # Eingang lesen
    register = 1
    address = 0x07
    if GPIO.input(36) == GPIO.HIGH:
        values = [1]
        #setzt den Datenanzeiger auf den Wert
        context[slave_id].setValues(register, address, values)
    elif GPIO.input(36) == GPIO.LOW:
        values = [0]
        context[slave_id].setValues(register, address, values)

    # Eingang lesen
    register = 1
    address = 0x08
    if GPIO.input(32) == GPIO.HIGH:
        values = [1]
        #setzt den Datenanzeiger auf den Wert
        context[slave_id].setValues(register, address, values)
    elif GPIO.input(32) == GPIO.LOW:
        values = [0]
        context[slave_id].setValues(register, address, values)

    # Eingang lesen
    register = 1
    address = 0x09
    if GPIO.input(22) == GPIO.HIGH:
        values = [1]
        #setzt den Datenanzeiger auf den Wert
        context[slave_id].setValues(register, address, values)
    elif GPIO.input(22) == GPIO.LOW:
        values = [0]
        context[slave_id].setValues(register, address, values)

def request_data(a):
    log.debug("reading the context")
    context  = a[0]
    register = 2
    slave_id = 0x00
    address  = 0x00

    values = context[slave_id].getValues(register, address, count=15)
    print values

def gpio_on(pin):
    # LED immer ausmachen
    GPIO.output(pin, GPIO.LOW)

    # LED an
    GPIO.output(pin, GPIO.HIGH)

def gpio_off(pin):
    # LED aus
    GPIO.output(pin, GPIO.LOW)
#---------------------------------------------------------------------------#
# initialize your data store
#---------------------------------------------------------------------------#
store = ModbusSlaveContext(
    di = ModbusSequentialDataBlock(0, [0]*100),
    co = ModbusSequentialDataBlock(0, [0]*100),
    hr = ModbusSequentialDataBlock(0, [2]*100),
    ir = ModbusSequentialDataBlock(0, [0]*100))
context = ModbusServerContext(slaves=store, single=True)

#---------------------------------------------------------------------------#
# initialize the server information
#---------------------------------------------------------------------------#
identity = ModbusDeviceIdentification()
identity.VendorName  = 'pymodbus'

#---------------------------------------------------------------------------#
# initialize the server information
#---------------------------------------------------------------------------#
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'

#---------------------------------------------------------------------------#
# run the server you want
#---------------------------------------------------------------------------#
time = 0.2 # 5 seconds delay
time2 = 1
print context[0]
loop = LoopingCall(f=updating_writer, a=(context,))
loop2 = LoopingCall(f=request_data, a=(context,))
loop.start(time, now=False) # initially delay by time
#loop2.start(time2, now=False) # initially delay by time
StartTcpServer(context, identity=identity, address=("192.168.10.38", 502))

Einrichten der Benutzeroberfläche mit GP Pro ex

Protokoll des ProFace Touchpanels auswählen
Modbus-Adresse des Schaltelements festlegen
Herunterladen des Projektes auf das Panel