Ankündigung

Einklappen
Keine Ankündigung bisher.

Pluginentwicklung, Trigger von serieller Schnittstelle

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • eddso
    antwortet
    Danke für den Tipp, die Zeit ist zu lang, kommen crc errors raus, kürzere sleeps bringen auch nichts. Durch zusammenfassen von reads habe ich zwar die last auf ca 10% reduziert aber weiter komme ich mit python nicht. Das schwierige ist das es ein Bus ist auf dem der Master permanent alle Geräte abfragt. Man muss also nach einem Frameanfang mit seiner Adresse lauschen und dann antworten mit ACK usw. Das alles läuft nicht gerade langsam ab. Werde es mit einem C Programm probieren und über Socket an sh anbinden, mal schauen ob es entschärft wird. C Programm hat eine last von 0,3.

    LG
    Eduard

    Einen Kommentar schreiben:


  • callidomus
    antwortet
    Hi,

    ich würde noch ein time.sleep(0.01) in die Endlosschleife packen.
    Das sollte die Last dramatisch drücken.

    Bis bald

    Marcus

    Einen Kommentar schreiben:


  • eddso
    antwortet
    Danke, mache ich wenn ich das Plugin verbessert habe ( wenns geht ). Momentan gefällt mir folgendes nicht: last beim Raspi von ca 13% bei diesem Plugin (habe nicht mit anderen Plugins verglichen) und das die Daten floaten (letzte Nachkommastelle bei Temperaturen springt z.B zwischen .5 und .6). Das erste kommt wohl vom byteweise lesen, habe aber keine andere Lösung dafür. Beim zweiten kommen die Werte vom Bus so, man müsste wohl ein dejitter einbauen damit man nicht alle paar Sekunden ein neuen Wert detektiert.

    Einen Kommentar schreiben:


  • callidomus
    antwortet
    Hallo Eduard,

    schönes Plugin. Auf den ersten Blick ist mir nichts aufgefallen.

    Über ein Readme und einen Pull-Request (https://github.com/mknx/smarthome/tr...v#start-coding) würde ich mich freuen.

    Bis bald

    Marcus

    Einen Kommentar schreiben:


  • Onkelandy
    antwortet
    Sehr schöne Sache.. da kann man schon den einen oder anderen Ansatz herausholen.

    Wäre dennoch genial, wenn du mal in einer langweiligen Stunde noch ein paar Kommentare einfügen könntest. Dann wär's einfacher, die Sache generischer zu gestalten

    Danke vielmals!

    Einen Kommentar schreiben:


  • eddso
    antwortet
    Vielen dank für die Antwort. Ich habe mir die Zeit genommen den ganzen sh Code rein zuziehen um zu verstehen wie alles Funktioniert. Ist einiges klar geworden. Schönes teil hast du da gebaut. Hier mein Plugin für die NIBE SPLIT WP. Ich aktualisiere die Items nur dann wenn sich was ändert. Einige Werte ändern sich relativ oft (Druck, Temperaturen und der WP) aber ich denke das ist ok.
    Code:
    #!/usr/bin/env python3
    # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
    #########################################################################
    #  Copyright 2013 KNX-User-Forum e.V.           https://knx-user-forum.de/
    #########################################################################
    #  NIBE plugin for SmartHome.py.         http://mknx.github.io/smarthome/
    #
    #  This plugin is free software: you can redistribute it and/or modify
    #  it under the terms of the GNU General Public License as published by
    #  the Free Software Foundation, either version 3 of the License, or
    #  (at your option) any later version.
    #
    #  This plugin is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this plugin. If not, see <http://www.gnu.org/licenses/>.
    #########################################################################
    
    import logging
    import serial
    import re
    import termios
    from struct import *
    
    logger = logging.getLogger('NIBE')
    
    class NIBE():
        def __init__(self, smarthome, serialport):
            self._sh = smarthome
            self._nibe_regs = {}
            self._serial = serial.Serial(serialport, 19200, bytesize=serial.EIGHTBITS, stopbits=serial.STOPBITS_ONE, timeout=2)
            iflag, oflag, cflag, lflag, ispeed, ospeed, cc = termios.tcgetattr(self._serial)
            CMSPAR = 0x40000000
            cflag |= termios.PARENB | CMSPAR | termios.PARODD # to select MARK parity
            termios.tcsetattr(self._serial, termios.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
              
        def run(self):
            self.alive = True
            try:
                while self.alive:
                    if bytes(self._serial.read(1))[0] != 0x03:
                        continue
                    if bytes(self._serial.read(1))[0] != 0x00:
                        continue
                    if bytes(self._serial.read(1))[0] != 0x14:
                        continue
                    self._serial.write(b"\x06")
                    self._serial.drainOutput()
                    self._serial.flushInput()
                    
                    frm = bytes()
                    frm += self._serial.read(1) #<C0>
                    if frm[0] == 0x03:
                        continue
                    
                    frm += self._serial.read(3) #<00> <59> <len>
                    l = int(frm[3])
                    frm += self._serial.read(l+1)
                    
                    self._serial.write(b"\x06")
                    self._serial.drainOutput()
                    self._serial.flushInput()
                    
                    crc = 0
                    for i in frm[:-1]:
                        crc ^= i
                    if crc != frm[-1]:
                        logger.warning("frame crc error")
                        continue
                    
                    msg = frm[4:-1]
                    l = len(msg)
                    i=4
                    while i <= l:
                        reg = msg[i-3]
                        if i != l and (msg[i] == 0x00 or i == (l-1)):
                            raw = bytes([msg[i-2],msg[i-1]])
                            i+=4
                        else:
                            raw = bytes([msg[i-2]])
                            i+=3
    
                        if not reg in self._nibe_regs:
                            continue
                        
                        if self._nibe_regs[reg]['raw'] == raw:
                            continue
                            
                        value = self._decode(reg, raw)
                        logger.debug("update_item: reg:{0} = {1}".format(reg,value))
                        self._nibe_regs[reg]['raw'] = raw
                        for item in self._nibe_regs[reg]['items']:
                            item(value, 'NIBE', 'REG {}'.format(reg))
                            
            except Exception as e:
                logger.warning("nibe: {0}".format(e))
    
        def stop(self):
            self.alive = False
            self._serial.close()
            
        def parse_item(self, item):
            if 'nibe_reg' in item.conf:
                logger.debug("parse item: {0}".format(item))
                nibe_reg = int(item.conf['nibe_reg'])
                if not nibe_reg in self._nibe_regs:
                    self._nibe_regs[nibe_reg] = {'items': [item], 'logics': [], 'raw':0}
                else:
                    self._nibe_regs[nibe_reg]['items'].append(item)
            return None
        
        def _decode(self, reg, raw):
            if len(raw) == 2:
                value = unpack('>H',raw)[0]
            else:
                value = unpack('B',raw)[0]
                
            if reg in [0,32,33,34,35,36,38,44,45,46,48,100,101,102,103,104,105]:    
                #0    CPUID
                #32   Zusatzheizung erlaubt   
                #33   Max dF Commpressor   
                #34   Verd. Freq. regP   
                #35   Min Startzeit Freq min
                #36   Minzeit konst. Freq min
                #38   Verd. Freq. GradMin   
                #44   Pumpengeschwindigkeit %
                #45   Bw reg P   
                #46   Bw reg Q   
                #48   Bw reg Wert xP %
                #100  Datum - Jahr
                #101  Datum - Monat
                #102  Datum - Tag
                #103  Uhrzeit - Stunde
                #104  Uhrzeit - Minute
                #105  Uhrzeit - Sekunden
                return int(value)
                
            if reg == 31:
                #31   Status Heizung
                #1 Auto
                #3 Heizung
                #5 Brauchwasser
                #6 Zusatzheizung
                return int(value)
                
            if reg in [4,8]: #signed
                #4    Heizkurvenverschiebung
                #8    Gradminuten
                return int(unpack('h',pack('H',value))[0]/10)
                
            if reg == 25: #unsigned
                #25   Verdichterstarts
                return int(value/10)
                
            if reg in [1,5,6,7,11,12,13,14,15,16,17,18,21,23,27,37]: #signed
                #1    Aussentemp °C        
                #5    Vorlauf Soll °C
                #6    Vorlauf Ist °C
                #7    Ruecklauf °C
                #11   Kondensator aus (MAX) °C
                #12   Brauchwasser oben °C
                #13   Brauchwasser unten °C
                #14   Verd. Temp. Tho-R1 °C
                #15   Verd. Temp. Tho-R2 °C
                #16   Sauggas Temp. Tho-S °C
                #17   Heissgas Temp. Tho-D °C
                #18   Fluessigkeitstemp AMS °C
                #21   Atemp. am AMS Tho-A °C
                #23   Invertertemp. Tho-IP °C
                #27   Vorlauf °C
                #37   Max Diff. soll-ber °C
                return float(unpack('h',pack('H',value))[0]/10)
                
            if reg in [9,10,19,20,22,24]: #unsigned
                 #9    Verd. Freq. Soll Hz
                #10   Verd. Freq. Ist Hz
                #19   Hochdruck bar
                #20   Niederdruck bar
                #22   AMS Phase Ist A
                #24   Verdichterlaufzeit h
                return float(value/10)
                
            if reg in [40,47]: #unsigned
                #40   Hysterese °C
                #47   Bw reg xP
                return float(value/2)
                
            if reg in [43,49,50]:
                #43   Stopptemp. Heizen °C
                #49   Brauchwasser StartTemp °C 1.2
                #50   Brauchwasser StopTemp °C  1.3
                return float(value)
            
            #2    ?
            #3    ?
            #26   ?
            #28   ?
            #29   ?
            #30   ?
            #39   ?
            #41   ?
            #42   ?
            return value
    Über Verbesserungen freue ich mich
    LG
    Eduard

    Einen Kommentar schreiben:


  • callidomus
    antwortet
    Hallo,

    ein Plugin darf seinen Thread behalten/blockieren. Anderes geht es momentan mit einer seriellen auch nicht.

    Keine Ahnung was bei Dir passiert wenn Du die Items änderst. Prinzipiell ist das Backend ziemlich schnell. Die Frage ist wieso willst Du die Änderungen "reinpumpen". Ich würde das zeitlich wahrscheinlich entspannen, da ja auch Logiken durch Änderungen getriggert werden können.

    Bis bald

    Marcus

    Einen Kommentar schreiben:


  • eddso
    antwortet
    Hallo,
    ich habe das gleiche Problem. Ich bastle gerade an einem Plugin für die NIBE Wärmepumpe. Es funktioniert auch soweit. Da ich permanent die serielle Schnittstelle abhören muss (es prasseln unaufhörlich Daten rein die ich filtere) ist meine Frage wie ich das am besten im Plugin einbaue. Ich brauche ja ein Thread der die Daten ausliest und einen der die Daten in die Items schaufelt oder ein Thread welcher beides macht.

    Das Plugin, wie ich verstehe läuft im sh als Thread, darf die run Methode des Plugins blockieren? D.h wenn ich da die schleife mit dem serielle Schnittstelle Auslesen reinlege, so wie mode
    es gemacht hat.

    Was passiert wenn ich item(value, ...) aufrufe? Werden dabei viele Daten verarbeitet. Ich frage weil sehr viele Daten reinkommen in schnellen Intervallen. Wenn item(value,...) viel macht würde es Sinn machen die Daten lokal zu behalten und über den sheduler in kleineren Intervallen in Items zu schaufeln.

    LG
    eddso

    Einen Kommentar schreiben:


  • callidomus
    antwortet
    Hallo,

    Du kannst mehr Debug-Output in die lib/connection.py einbauen um den Fehler einzugrenzen. Ich vermute aber das Problem bei Deinem Plugin/Hardware.

    Bis bald

    Marcus

    Einen Kommentar schreiben:


  • Onkelandy
    antwortet
    Danke für die Info. Wie gesagt löse ich die Sache derzeit so, dass jede Sekunde vom Arduino Daten geholt werden. Hat sich dort nichts geändert, gibt's aber auch keine neuen Info. Dadurch sollte die Kommunikation halbwegs im Zaum gehalten werden.

    Dennoch gibt's immer wieder folgende Fehlermeldung:
    Code:
    2014-06-30 21:44:35 ERROR    Main         Connection polling failed: [Errno 4] Unterbrechung während des Betriebssystemaufrufs
    Traceback (most recent call last):
      File "/usr/local/smarthome/bin/smarthome.py", line 357, in start
        self.connections.poll()
      File "/usr/local/smarthome/lib/connection.py", line 101, in poll
        for fileno, event in self._epoll.poll(timeout=1):
    IOError: [Errno 4] Unterbrechung während des Betriebssystemaufrufs
    2014-06-30 21:44:38 ERROR    Main         Connection polling failed: [Errno 4] Unterbrechung während des Betriebssystemaufrufs
    Traceback (most recent call last):
      File "/usr/local/smarthome/bin/smarthome.py", line 357, in start
        self.connections.poll()
      File "/usr/local/smarthome/lib/connection.py", line 101, in poll
        for fileno, event in self._epoll.poll(timeout=1):
    IOError: [Errno 4] Unterbrechung während des Betriebssystemaufrufs
    Irgendeine Idee? Könnte das evtl. auch von nem anderen Plugin kommen? wie könnte ich da mehr Infos dazu raus kriegen..?

    Dankeschööön!

    Einen Kommentar schreiben:


  • callidomus
    antwortet
    Hallo,

    Zitat von Onkelandy Beitrag anzeigen
    Wollte kurz checken, ob sich in punkto serieller Schnittstelle schon was getan hat oder noch tun wird.
    Nein und Ja.

    Bis bald

    Marcus

    Einen Kommentar schreiben:


  • Onkelandy
    antwortet
    Hallo zusammen!

    Wollte kurz checken, ob sich in punkto serieller Schnittstelle schon was getan hat oder noch tun wird.

    Ich möchte eine Verbindung mit einem Arduino herstellen. Letzterer soll bei einer Änderung der Daten diese an Smarthome.py schicken. Derzeit löse ich das über ein adaptiertes Roomla-Plugin, das jede Sekunde cyclet. Und somit auch immer beim Arduino nach den Daten frägt, der dann zurücksendet.

    Das funktioniert zwar, ist aber sicher nicht die optimale Lösung. CPU-Last von Python ist dadurch 2,3% statt 0,7% und Memory ist bei 4,3%. Soweit kein Drama, aber doch klar ein Unterschied. Die TCP-Verbindung mit meinen anderen laufenden Plugins (adaptiertes Denon-Plugin, etc.) funktioniert einwandfrei ohne zusätzliche Last..

    Einen Kommentar schreiben:


  • callidomus
    antwortet
    Hi Daniel,

    ja das ist ok so.

    Zu Deiner Exception kann ich nichts sagen, es fehlt der Code-Kontext.

    Du solltest aber mehr try: except: einbauen. U.a. um das close()

    Bis bald

    Marcus

    Einen Kommentar schreiben:


  • mode
    antwortet
    Hallo Marcus,

    ich habe am Wochenende mit poll experimentiert. Dabei rausgekommen, dass die Systemlast mit einem blockierendem read() mit Timeout 1s noch geringer ist als mit mit poll(). Möchte daher vorerst so arbeiten.

    Mein Plugin in dem ich die Daten vom EHz lese funktioniert auch schon. Ich bin mir aber nicht sicher, ob das Plugin sich beim beenden von sh.py korrekt verhält.

    Code:
        def __init__(self, smarthome, port = '/dev/ttyUSB0', baudrate = 9600, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE, bytesize = serial.EIGHTBITS, \
                     timeout = 1, length_emh_frame = 392, max_bytes = 500):
            self._sh = smarthome
            self._tarif = {}
            self._port = port
            self._baudrate = baudrate
            self._parity = parity
            self._stopbits = stopbits
            self._bytesize = bytesize
            self._timeout = timeout
            self._length_emh_frame = length_emh_frame
            self._max_bytes = max_bytes
            self._ser = serial.Serial(port = self._port, baudrate = self._baudrate, parity = self._parity, stopbits = self._stopbits, bytesize = self._bytesize, timeout = self._timeout)
            logger.info("EHZ_EMH is connected to {0}".format(self._ser.portstr))
        def run(self):
            self.alive = True
            while self.alive:
                self._ser.flushInput()
                dataset = self._ser.read(self._max_bytes)
                [...] weitere Verarbeitung und relevante items setzen
    
        def stop(self):
            self.alive = False
            self._ser.close()
    Ist das so ok?


    Edit:
    Gerade einer von vielen Stops nicht erfolgreich:

    Console
    Code:
    >>> Unhandled exception in thread started by <bound method Plugin._bootstrap of <Plugin(ehz, stopped 140047022003968)>>
    Log
    Code:
    2013-11-17 00:29:02,754 INFO     Main         Stop Plugins -- plugin.py:stop:70
    2013-11-17 00:29:02,758 DEBUG    Main         KNX: closing socket cgate.ts:6720 -- connection.py:close:302
    2013-11-17 00:29:02,762 ERROR    Dummy-7      Unhandled exception: 'utf-8' codec can't decode byte 0xe4 in position 2805: invalid continuation byte
    <class 'UnicodeDecodeError'>
      File "/usr/lib/python3.2/threading.py", line 713, in _bootstrap
        self._bootstrap_inner()
      File "/usr/lib/python3.2/threading.py", line 753, in _bootstrap_inner
        (self.name, _format_exc()))
      File "/usr/lib/python3.2/traceback.py", line 269, in format_exc
        format_exception(etype, value, tb, limit, chain))
      File "/usr/lib/python3.2/traceback.py", line 186, in format_exception
        list.extend(format_tb(tb, limit))
      File "/usr/lib/python3.2/traceback.py", line 75, in format_tb
        return format_list(extract_tb(tb, limit))
      File "/usr/lib/python3.2/traceback.py", line 100, in extract_tb
        line = linecache.getline(filename, lineno, f.f_globals)
      File "/usr/lib/python3.2/linecache.py", line 15, in getline
        lines = getlines(filename, module_globals)
      File "/usr/lib/python3.2/linecache.py", line 41, in getlines
        return updatecache(filename, module_globals)
      File "/usr/lib/python3.2/linecache.py", line 132, in updatecache
        lines = fp.readlines()
      File "/usr/lib/python3.2/codecs.py", line 300, in decode
        (result, consumed) = self._buffer_decode(data, self.errors, final)
     -- smarthome.py:_excepthook:494
    2013-11-17 00:29:03,226 INFO     Main         Thread: Main, still alive -- smarthome.py:stop:370
    2013-11-17 00:29:03,227 INFO     Main         Thread: Dummy-7, still alive -- smarthome.py:stop:370

    Einen Kommentar schreiben:


  • callidomus
    antwortet
    Hallo Daniel,

    das mit dem Triggern erzeugt unnötig Last. Ich würde das Plugin/den Thread selber loopen lassen und mit select/poll arbeiten.

    Ich habe mir aber überlegt, ob ich serielle Schnittstellen etwas generischer über lib.connection einbinde. Mal sehen.

    Bis bald

    Marcus

    Einen Kommentar schreiben:

Lädt...
X