Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Skripten / Logik
    4. JavaScript
    5. [Skriptbeispiel] python Aufruf modbus-poll

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    [Skriptbeispiel] python Aufruf modbus-poll

    This topic has been deleted. Only users with topic management privileges can see it.
    • S
      starfish last edited by

      <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.

      1 Reply Last reply Reply Quote 0
      • Y
        ykuendig last edited by

        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

        1 Reply Last reply Reply Quote 0
        • First post
          Last post

        Support us

        ioBroker
        Community Adapters
        Donate

        1.1k
        Online

        31.7k
        Users

        79.7k
        Topics

        1.3m
        Posts

        2
        2
        3572
        Loading More Posts
        • Oldest to Newest
        • Newest to Oldest
        • Most Votes
        Reply
        • Reply as topic
        Log in to reply
        Community
        Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
        The ioBroker Community 2014-2023
        logo