NEWS
[Skriptbeispiel] python Aufruf modbus-poll
-
<size size="150">Beispiel für modbus/tcp</size>
(erweiterte Variante für modbus/rtu im 2. Post)
Aufgabenstellung: Auslesen von Verbrauchsdaten aus einem Energiemessgerät Siemens PAC3200 über modbus/tcp und Import in iobroker.
möglich geworden ist dieses Projekt durch das durch bluefox gefixte javascript zur Ausführung von python-scripten.
Voraussetzung: python (hier 2.7.9) mit pip oder easy_install zur Installation von pymodbus.
Meine Wahl fiel auf pymodbus, da dies sehr viel besser als z.B. modbus-tk dokumentiert ist.
pymodbus ist so vielseitig, dass sich nahezu jede Aufgabe im modbus-Bereich lösen lässt.
https://github.com/bashwork/pymodbus
https://code.google.com/p/pymodbus/
Doku https://pythonhosted.org/pymodbus/
dieses Skript in den javascript adapter einfügen
createState('pythonResult', ''); schedule("*/5 * * * *", function () { var python = require('child_process').spawn('python', // second argument is array of parameters, e.g.: ["/opt/python/kwh.py"]); var result = ''; python.stdout.on('data', function(data){ result += data.toString(); }); python.on('close', function(code){ if (code !== 0) { log('Error: ' + code); } else { log(result); setState('pythonResult', result, true); } }); });dadurch wird im 5 Min. Intervall das python Skript /opt/python/kwh.sh aufgerufen, welches via modbus/tcp den Inhalt des Registers 2801 im PAC3200 abruft und den Wert über stdout an das obige javascript zurückgibt.
#!/usr/bin/env python from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadDecoder from pymodbus.client.sync import ModbusTcpClient as ModbusClient siemens = ModbusClient(host='192.168.0.68', port=502) result = siemens.read_holding_registers(2801, 2, unit=1) decoder = BinaryPayloadDecoder.fromRegisters(result.registers, endian=Endian.Big) a= decoder.decode_32bit_float() # etwas Formatierung kilowattstunden print ("%.2f" % (a/1000)) #KWh siemens.close()als Abfallprodukt ergab sich folgende Lösung zum Import der Daten in Zabbix als trapper item mittels zabbix_sender. (naja, eigentlich war dies zuerst - den iobroker-import habe ich davon abgeleitet.)
! –---------------------------------------------------------------------
! #!/usr/bin/env python
! from pymodbus.constants import Endian
! from pymodbus.payload import BinaryPayloadDecoder
! from pymodbus.client.sync import ModbusTcpClient as ModbusClient
! siemens = ModbusClient(host='192.168.0.68', port=502)
! result = siemens.read_holding_registers(2801, 2, unit=1)
! decoder = BinaryPayloadDecoder.fromRegisters(result.registers, endian=Endian.Big)
! a= decoder.decode_32bit_float()
! # active energy Zaehler kilowattstunden
! # formatiert für Zabbix_sender host und key (item - key) müssen in Zabbix vorhanden sein
! # siehe zabbix doku trapper item
! print "host key " + ("%.2f" % (a/1000)) #KWh
! siemens.close()
! -------------------------------------------------------------------------
! shell script durch cron alle 5 min gestartet (bzw. nach Bedarf)
! #!/bin/sh
! python /root/kwh.py > /root/kwh.log
! zabbix_sender -z 127.0.0.1 -i /root/kwh.log >/dev/nul
! -------------------------------------------------------------------------sicher lässt sich dies oder jenes eleganter coden, für mich efüllts den Zweck vollauf.
-
Dank @starfish haben wir nun eine Möglichkeit modbus Daten zu pollen. Das ist uU ganz hilfreich (zumindest bis ein Modbus-Adapter fertig ist).
Ich habe für meine Zwecke das Python-Script angepasst, da ich serielle Geräte am Bus habe. Zudem wollte ich mehrere Werte abfragen.
Hier das Python-Script 'pymodhaus.py' (liegt bei mir in /home/pi/modbus-crawler):
#!/usr/bin/env python import sys, time, json from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadDecoder from pymodbus.client.sync import ModbusSerialClient as ModbusClient from pymodbus.transaction import ModbusRtuFramer def decode32(decval): decoder = BinaryPayloadDecoder.fromRegisters(decval.registers, endian=Endian.Big) return decoder.decode_32bit_float() def decode64(decval): decoder = BinaryPayloadDecoder.fromRegisters(decval.registers, endian=Endian.Big) return decoder.decode_64bit_int() client = ModbusClient(method='rtu', port='/dev/ttyUSB0', stopbits=1, bytesize=8, timeout=0.05, baudrate=19200, parity='N') connection = client.connect() #print "Connection: ", connection * Only for debug try: timestamp = str(time.time()).split('.')[0] # iEM3155 Haus --> Adresse immer 1 kleiner als im Schneider Datenblatt; weiss der Geier warum # HausL01 = decode32(client.read_holding_registers(3028-1, 2, unit=10)) # HausL02 = decode32(client.read_holding_registers(3030-1, 2, unit=10)) # HausL03 = decode32(client.read_holding_registers(3032-1, 2, unit=10)) HausLNM = decode32(client.read_holding_registers(3036-1, 2, unit=10)) HausP01 = decode32(client.read_holding_registers(3054-1, 2, unit=10)) * 1000 HausP02 = decode32(client.read_holding_registers(3056-1, 2, unit=10)) * 1000 HausP03 = decode32(client.read_holding_registers(3058-1, 2, unit=10)) * 1000 HausPower = decode32(client.read_holding_registers(3060-1, 2, unit=10)) * 1000 # HausPowerB = decode32(client.read_holding_registers(3068-1, 2, unit=10)) * 1000 # HausPowerS = decode32(client.read_holding_registers(3076-1, 2, unit=10)) * 1000 HausImport = decode64(client.read_holding_registers(3204-1, 4, unit=10)) / 1000 HausExport = decode64(client.read_holding_registers(3208-1, 4, unit=10)) / 1000 except: print 6 else: json_string = json.dumps({'ts': timestamp, 'HausLNM': round(HausLNM,2), 'HausP01': round(HausP01,0), 'HausP02': round(HausP02,0), 'HausP03': round(HausP03,0), 'HausPower': round(HausPower,0),'HausImport': HausImport, 'HausExport': HausExport}) print json_string client.close()Nun zum iobroker.javascript:
// Create Datenpunkte für PV-Anlage createState('javascript.1.Solar.pi1Result', ''); createState('javascript.1.Solar.modPoll', 1); // Create Datenpunkte für Schneider EnergyMeter 'Haus' createState('javascript.1.Solar.Schneider.HausLNM', 0); createState('javascript.1.Solar.Schneider.HausP01', 0); createState('javascript.1.Solar.Schneider.HausP02', 0); createState('javascript.1.Solar.Schneider.HausP03', 0); createState('javascript.1.Solar.Schneider.HausPower', 0); createState('javascript.1.Solar.Schneider.HausImport', 0); createState('javascript.1.Solar.Schneider.HausExport', 0); createState('javascript.1.Solar.Schneider.HausTimeStamp', 0); schedule({astro: "sunrise"}, function () { setState('javascript.1.Solar.modPoll', 1, true); }); schedule({astro: "sunset"}, function () { setState('javascript.1.Solar.modPoll', 0, true); }); //Schedule Script Run Haus schedule("*/1 * * * *", function (Haus) { var enabled = getState("javascript.1.Solar.modPoll"/*javascript.1.Solar.modPoll*/).val; if (enabled == 1) { var python = require('child_process').spawn('python', ["/home/pi/modbus-crawler/pymodhaus.py"]); // second argument is array of parameters, e.g.: var result = ''; python.stdout.on('data', function(data){ result += data.toString(); }); python.on('close', function(code1){ if (code1 !== 0) { log('Error: ' + code1, 'error'); } else { if (result == 6) { log('Error: Fehler im Modbus Python Script -Haus-', 'warn'); } else { log('Modbus Python Script -Haus- erfolgreich gelaufen, Werte akzeptiert'); setState('javascript.1.Solar.pi1Result', result); var solar1 = JSON.parse(result); setState('javascript.1.Solar.Schneider.HausLNM', solar1.HausLNM, true); setState('javascript.1.Solar.Schneider.HausP01', solar1.HausP01, true); setState('javascript.1.Solar.Schneider.HausP02', solar1.HausP02, true); setState('javascript.1.Solar.Schneider.HausP03', solar1.HausP03, true); setState('javascript.1.Solar.Schneider.HausPower', solar1.HausPower, true); setState('javascript.1.Solar.Schneider.HausImport', solar1.HausImport, true); setState('javascript.1.Solar.Schneider.HausExport', solar1.HausExport, true); setState('javascript.1.Solar.Schneider.HausTimeStamp', solar1.ts, true); } } }); } });Etwas komplexer hat sich dann der Umstand erwiesen, dass ich das Script (mangels tcp) lokal am Solar-PI laufen lassen muss. Daher musste ein iobroker als remote-host auf den PI. Aber dass ist ein ganz anderes Thema.
Version 0.1.0 2015-06-22 First release
Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.
Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.
Mit deinem Input könnte dieser Beitrag noch besser werden 💗
Registrieren Anmelden