Seite 1 von 1
Auslesen eines Elektronischen Stromzählers mit einem Raspi
Verfasst: Montag 26. August 2013, 22:36
von Sporty1200
Hallo zusammen
ich bin neuling in der python Programmierung und interesiere mich sehr dafür besser zu werden
und hoffe ihr könnt mir etwas auf die sprünge helfen
ich besitze 2 elektronische Stromzähler mit optischer schnittstelle
das gesendete Protokoll entspricht der SML (smart meter language).
Gesendet wird der datensatz vom Zähler ca. alle 3 sec.
auslesen möchte ich es mit einem Raspberry PI Modell B
mit einem W&T rs232 auf USB wandler
ich hab folgende Probleme:
mit einem Terminal Programm (Hterm) auf meinem Laptop hab ich den datensatz schon gelesen es sind 396 zeichen
in hex kodiert (0-255)
mit einem kleinen python code hab ich versuch den zähler auszulesen dabei wandelt es direct jedes zeichen was unter 128 ist automatisch in einen Ascii zeichensatz um und "inWaiting" liefert nur 321 zeichen unabhängig ob über Raspi oder PC :K
und falls einer einen tip hat wie ich aus einem teilbereich aus einem byte array wieder einen zähler wert machen kann wäre ich sehr dankbar.
Ich hab in büchern und internet kaum hilfreiches gefunden zu diesem problem.
ich hoffe ich hab es einigermassen darstellen können.
anbei mein testcode der auch nur bedingt funtioniert
Code: Alles auswählen
import serial
import time
import sys ,os
try :
ser = serial.Serial ("/dev/ttyUSB0",9600,timeout=10000) # open first serial port
print (ser.portstr) # check which port was really used
ser.setDTR = 1
# ser.setRTS = 1
except :
print ("gibt es nicht")
byte_wartend = 0
while True:
S = bytes
while ser.inWaiting():
if ser.inWaiting() == byte_wartend & ser.inWaiting()!= 0:
print (ser.inWaiting())
#S = ser.readline()
print(ser.readline())
byte_wartend = 0
else:
byte_wartend = ser.inWaiting()
time.sleep(0.5)
time.sleep(0.05)
Re: Auslesen eines Elektronischen Stromzählers mit einem Ras
Verfasst: Dienstag 27. August 2013, 08:07
von BlackJack
@Sporty1200: Python beziehungsweise das `serial`-Modul konvertieren nichts automatisch von Hexadezimal nach Binär, das macht wohl eher Dein Terminalprogramm.
Wenn das tatsächlich beliebige Binärdaten sein können, kannst Du auch nicht `readline()` verwenden, denn das schaut nach einer Zeilenende-Kennzeichnung, die nur bei Textdaten Sinn macht und in Binärdaten im Grunde überall zufällig vorkommen kann wo eben zufällig ein entsprechender Bytewert steht.
Also muss erst einmal geklärt werden was da tatsächlich gesendet wird, Binärdaten oder Text.
Der Leseteil ist übrigens eigenartig. Das innere ``if`` (mit der bitweisen Und-Verknüpfung) ist ganz fürchterlich verwirrend. Für logische Verknüfungen gibt es das ``and``-Schlüsselwort und man muss auch mitbedenken, dass `ser.inWaiting()` in diesem Ausdruck zweimal aufgerufen wird, dabei aber jedes mal ein anderes Ergebnis liefern kann. Und beim `print()` in dem ``if``-Zweig könnte es schon wieder ein anderes Ergebnis liefern, also ist das nur bedingt in der Form sinnvoll.
`S` entspricht nicht der Namenskonvention aus dem
Style Guide for Python Code und warum die `bytes()`-Funktion daran gebunden wird, erschliesst sich mir auch nicht wirklich.
Edit: Das Protokoll heisst übrigens
Smart *Message* Language. Du hast Dir also vorgenommen ein Binärformat mit verschiedenen Nachrichtentypen auseinander zu nehmen. Plane dafür ein wenig Zeit ein, das ist für den Anfang nämlich nicht das leichteste. Vor allem wenn man mit einer High-Level-Programmiersprache arbeitet, wo man diese Ebene nicht (mehr) braucht um die Programmiersprache zu verstehen.
Re: Auslesen eines Elektronischen Stromzählers mit einem Ras
Verfasst: Dienstag 27. August 2013, 17:46
von Sporty1200
Das Protokoll heisst übrigens Smart *Message* Language.
jap du Hast recht. und danke schonmal für die schnelle Rückmeldung.
der code ist mist! war auch nur gebastel weil ich seit 2 wochen nicht weiterkomme
(googlen, foren und bücherkaufen hatte nur bedingt geholfen).
der code hatte auch nicht den anspruch so später eingesetzt zu werden.
das mit der ascii ausgabe hatte wohl was mit der ausgabe beim "Print"befehl aufgrunde der ausgabe mit b'\xx\xx...' zutuhen
Hier mal die ausgabe an meinem Laptop mit Hterm:
1B 1B 1B 1B 01 01 01 01 76 05 02 BF BE C8 62 00 62 00 72 63 01 01 76 01 01 05 00 EA 94 EE 09 08 05 35 34 2D 4C 92 AD 01 01 63 5A DE 00 76 05 02 BF BE C9 62 00 62 00 72 63 07 01 77 01 09 08 05 35 34 2D 4C 92 AD 07 01 00 62 0A FF FF 72 62 01 65 02 80 C7 0E 7B 77 07 81 81 C7 82 03 FF 01 01 01 01 04 49 53 4B 01 77 07 01 00 00 00 09 FF 01 01 01 01 09 08 05 35 34 2D 4C 92 AD 01 77 07 01 00 01 08 00 FF 65 00 00 01 82 01 62 1E 52 FF 59 00 00 00 00 01 9E BA 41 01 77 07 01 00 01 08 01 FF 01 01 62 1E 52 FF 59 00 00 00 00 01 9E BA 41 01 77 07 01 00 01 08 02 FF 01 01 62 1E 52 FF 59 00 00 00 00 00 00 00 00 01 77 07 01 00 0F 07 00 FF 01 01 62 1B 52 00 65 00 00 00 8C 01 77 07 01 00 15 07 00 FF 01 01 62 1B 52 00 65 00 00 00 01 01 77 07 01 00 29 07 00 FF 01 01 62 1B 52 00 65 00 00 00 89 01 77 07 01 00 3D 07 00 FF 01 01 62 1B 52 00 65 00 00 00 01 01 77 07 01 00 60 05 05 FF 01 01 01 01 65 00 00 01 82 01 77 07 81 81 C7 82 05 FF 01 01 01 01 83 02 2A 10 19 95 CE 5A 87 1E 0C C6 70 20 E4 97 B5 6B 3C A6 8D 4C 7B A1 80 16 DE 8C 6E E0 96 49 AB 4C DF 81 89 F6 B1 AD 2E 29 F0 A4 C6 D2 D4 6C CC 5B 01 01 01 63 2E 37 00 76 05 02 BF BE CA 62 00 62 00 72 63 02 01 71 01 63 29 2C 00 1B 1B 1B 1B 1A 00 19 42
Startsequenz 1B 1B 1B 1B 01 01 01 01 (quelle Wikipedia)
Endsequenz 1B 1B 1B 1B 1A <xx> <yy> <zz>
<xx> : Anzahl der Füllbytes
<yy> <zz> : CRC über die Datei
Hier die ausgabe mit meinem Python code
321
b'\xff\xffrb\x01e\x02\xfd\xd1\xda{w\x07\x81\x81\xc7\x82\x03\xff\x01\x01\x01\x01\x04ISK\x01w\x07\x01\x00\x00\x00\t\xff\x01\x01\x01\x01\t\x08\x0554-L\x92\xad\x01w\x07\x01\x00\x01\x08\x00\xffe\x00\x00\x01\x82\x01b\x1eR\xffY\x00\x00\x00\x00\x01\xdc(\xf6\x01w\x07\x01\x00\x01\x08\x01\xff\x01\x01b\x1eR\xffY\x00\x00\x00\x00\x01\xdc(\xf6\x01w\x07\x01\x00\x01\x08\x02\xff\x01\x01b\x1eR\xffY\x00\x00\x00\x00\x00\x00\x00\x00\x01w\x07\x01\x00\x0f\x07\x00\xff\x01\x01b\x1bR\x00e\x00\x00\x00\x90\x01w\x07\x01\x00\x15\x07\x00\xff\x01\x01b\x1bR\x00e\x00\x00\x00\x04\x01w\x07\x01\x00)\x07\x00\xff\x01\x01b\x1bR\x00e\x00\x00\x00\x89\x01w\x07\x01\x00=\x07\x00\xff\x01\x01b\x1bR\x00e\x00\x00\x00\x01\x01w\x07\x01\x00`\x05\x05\xff\x01\x01\x01\x01e\x00\x00\x01\x82\x01w\x07\x81\x81\xc7\x82\x05\xff\x01\x01\x01\x01\x83\x02*\x10\x19\x95\xceZ\x87\x1e\x0c\xc6p \xe4\x97\xb5k<\xa6\x8dL{\xa1\x80\x16\xde\x8cn\xe0\x96I\xabL\xdf\x81\x89\xf6\xb1\xad.)\xf0\xa4\xc6\xd2\xd4l\xcc[\x01\x01\x01c\xad\x1e\x00v\x05\x03@@\xf7b\x00b\x00rc\x02\x01q\x01c#\xc3\x00\x1b\x1b\x1b\x1b\x1a\x00\x03E\x1b\x1b\x1b\x1b\x01\x01\x01\x01v\x05\x03@@\xf8b\x00b\x00rc\x01\x01v\x01\x01\x05\x01\x15j\xfe\t\x08\x0554-L\x92\xad\x01\x01c\xa4\xc5\x00v\x05\x03@@\xf9b\x00b\x00rc\x07\x01w\x01\t\x08\x0554-L\x92\xad\x07\x01\x00b\n'
falls einer ne idee hat für hilfe wäre ich echt dankbar
danke schon mal
Re: Auslesen eines Elektronischen Stromzählers mit einem Ras
Verfasst: Dienstag 27. August 2013, 18:36
von BlackJack
@Sporty1200: Auf der Wikipediaseite ist die SML-Spezifikation als PDF verlinkt. Ich habe die mal kurz überflogen. Du müsstest das was da drin steht sozusagen von „hinten” implementieren. Also zuerst das Protokoll. Hoffentlich das in der Version 1, denn das ist einfacher. Ansonsten muss man ein paar Escape-Sequenzen mehr interpretieren.
Einen grundlegenden Fehler bei Deinem Code habe ich Dir ja schon genannt: das `readline()`. Dein Ergebnis hört mit einem b'\n' auf, also einem Zeilenende-Zeichen. Das ist aber nicht das Ende des SML-Files, sondern irgendwo mitten drin. Da es sich um Binärdaten handelt, kann dieser Bytewert ja zum Beispiel auch als Teil einer binär kodierten Zahl vorkommen.
Du musst Code zum Empfangen von Daten schreiben, der das als Datenstrom auffasst und die Escape-Sequenzen interpretieren kann, um den Anfang und das Ende eines SML-Files erkennen zu können und alle Bytes eines solchen Files liefern kann, damit man die im nächsten Arbeitsschritt dann parsen kann. Dazu würde ich Funktionen zum parsen der ganzen Einzelteile schreiben, so dass man am Ende eine Funktion hat, welche ein komplettes SML-File in eine allgemeine Datenstruktur parsen kann. Typen und Längen sowohl für einfache als auch für zusammengesetzte Datentypen sind in dem Datenformat kodiert. Das ist also zumindest was Struktur und Typen betrifft erst einmal selbstbeschreibend.
Re: Auslesen eines Elektronischen Stromzählers mit einem Ras
Verfasst: Dienstag 27. August 2013, 21:44
von Sporty1200
@BlackJack
Danke erstmal hab jetzt was gefunden im Bezug auf Datenstrom in der Unterscheidung von Text und binär. Da hab ich jetzt erstmal einen Ansatz mit
Werde die Tage ausprobieren ob ich da was hinkriege
Ich danke dir
Re: Auslesen eines Elektronischen Stromzählers mit einem Ras
Verfasst: Dienstag 27. August 2013, 22:30
von BlackJack
@Sporty1200: Ich sehe nicht wo man da das `io`-Modul braucht. Du bekommst von `Serial` Binärdaten, und das ist ja auch richtig so, denn es handelt sich nicht um ein Textprotokoll. Man könnte einen `io.TextIOWrapper()` verwenden wenn man ein Binärdatei-Objekt hat das kodierten Text „spricht”, aber die Zeichenketten im SML-File sind in Binärdaten eingebettet und können zudem noch in verschiedenen Kodierungen vorliegen.
Re: Auslesen eines Elektronischen Stromzählers mit einem Ras
Verfasst: Sonntag 8. September 2013, 13:39
von Sporty1200
hi ich bin etwas Weiter gekommen
der code ist alles andere als sauber und fertig
mach aber bis jetzt zwei dinge ok
1. datensatz auslesen und zählerstand im datensatz finden
2. eine .txt erstellen mit den zählerständen
danke an BlackJack
Vier dinge muss ich noch hinkriegen
1. auslesen des zählers zu bestimmten zeiten (z.B.4x am Tag) (bis jetzt noch nicht hinbekommen)
2. einmal die woche eine Email mit der .txt versenden (dürfte kein grösseres Problem sein)
4. Code aufräumen und sauber schreien (das wird noch kniffelig)
anbei mein code bis jetzt
Code: Alles auswählen
import serial
import time
datensatzlaenge = 8
Zsposition = 144
Zielordner = "Testdatei.txt"
ser = serial.Serial ("/dev/ttyUSB0",9600,timeout=10000) # open first serial port
ser.close()
def PortON():
ser.open()
def PortOFF():
ser.close()
def Auslesen():
datensatz_auslesen = False
anfangsuchen = True
i = 0
j = 0
while ser.inWaiting() <800:
time.sleep(0.5)
s = ser.read(ser.inWaiting())
while anfangsuchen: #suchen des Datensatzanfang's
if (s[i:i+8])== b'\x1b\x1b\x1b\x1b\x01\x01\x01\x01':
anfangsuchen = False
print ("Der Anfang liegt bei",i)
datensatz_auslesen = True
elif i > 500:
anfangsuchen = False
else:
i = i +1
while datensatz_auslesen:
if j == 0:
S = (s[i+Zsposition+j])
j = j+1
else:
S = (S*256) +(s[i+Zsposition+j]) # den wert *256 nemen um es eine stelle Weiterzu setzen
j = j+1
if j >= datensatzlaenge:
datensatz_auslesen = False
S = (float(S)/10000) #teilen für die Kommastelle
Zaehlerstand = str(S)+";"+'kwh'+";"+str(time.asctime())+"\n"
print (Zaehlerstand)
fobj = open(Zielordner,"a")
fobj.write(str(Zaehlerstand))
fobj.close()
# Main
while True:
PortON()
Auslesen()
PortOFF()
time.sleep (50)