Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
Siemens TD-3511
Bei der Installation einer Solaranlage haben die Stadtwerke bei mir Zähler TD-3511 von Siemens installiert. Es handelt sich um Zweirichtungszähler mit IR-Schnittstelle. Der Zähler besitzt auch noch einen frei zugänglichen Steckplatz für einen M-Bus Adapter, der allerdings hier nicht weiter betrachtet wird.
Hardware
Man benötigt den IR-Schreib-Lese-Kopf, da der Zähler erst angesprochen werden muss, bevor er sendet.
Die linke der beiden Punkte im Bereich des Lesekopfes ist der Empfänger. Rechts ist die IR-Sendediode des Zählers. Wenn man den USB-IR-Schreib-Lesekopf wie im Wiki beschrieben nimmt, geht das USB-Kabel nach unten weg.
Wahrscheinlich funktionieren auch die Befehle des elster_AS1440. Die Umschaltung der Geschwindigkeit auf 9600bps funktioniert auf jeden Fall, siehe Test3.
Test #1
Zum Testen habe ich hterm verwendet.
Einzustellen sind:
Port: bei Windows:verwendeter COM-Anschluss des USB-Adapters, bei Linux z.B. /dev/ttyUSB0
Baud: 300
Data: 7
Stop: 1
Parity: Even
Bei „input control“: Send on Enter auf „CR-LF“
Auf „Connect“ klicken, dann im Eingabefeld „/?!“ (ohne Anführungszeichen) eingeben und Enter drücken. Dann sollte der Zähler sofort mit seiner Seriennummer antworten. Nach 2-3 Sekunden fängt der Zähler dann an, Daten auszugeben.
Test #2
Das folgende Perlscript automatisiert die manuelle Bearbeitung des Zählers mit hterm.
#!/usr/bin/perl # # (m)ein Stromzähler mit IR-Schnittstelle blubbert nach einem "Anforderung- # telegramm" Daten raus. Das Telegramm ist mit 300 Baud, 7 Bit, 1 Stoppbit # und gerader Parität zu senden. Das ist der Initialmodus von Geräten, # die das Protokoll IEC 62056-21 implementieren. # # Autor: Andreas Schulze # Bugfix: Eric Schanze # Datum: 20120302 # my $PORT='/dev/ttyUSB0'; my $anforderungstelegramm = "\n/?!\r\n"; use warnings; use strict; use utf8; use Device::SerialPort; my $tty = new Device::SerialPort($PORT) || die "can't open $PORT: $!"; $tty->baudrate(300) || die 'fail setting baudrate'; $tty->databits(7) || die 'fail setting databits'; $tty->stopbits(1) || die 'fail setting stopbits'; $tty->parity("even") || die 'fail setting parity'; $tty->write_settings || die 'fail write settings'; #$tty->debug(1); my $num_out = $tty->write($anforderungstelegramm); die "write failed\n" unless ($num_out); die "write inclomplete\n" unless ($num_out == length($anforderungstelegramm)); print "$num_out Bytes written\n"; my ($num_read, $s); $tty->read_const_time(10); while(1) { ($num_read, $s) = $tty->read(1); print $s if $s; } $tty->close || die "can't close $PORT: $!";
Test #3
Das folgende C Programm kann alle Daten mit dem USB-IR-Schreib-Lesekopf empfangen und in eine MYSQL-Datenbank speichern.
Dabei wird die Übertragungsrate auf 9600bps erhöht.
Das Programm kann über PHP mit pclose(popen(„cmd.exe /c C:/Pfad/programm.exe“, 'r')); aufgerufen werden.
#include <windows.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <mysql.h> #include <math.h> /*http://www.freewebs.com/dragonstein/msql_c.html libmysql.dll von xampp-Ordner in Main.c-Ordner kopieren*/ // globale Variablen definieren // für COM-Ports DCB sDcb; HANDLE com; DWORD dwCount; COMMTIMEOUTS sTo; time_t rawtime; // -------------------------- int main (){ // Hauptprogramm int i=5,j=0,n=0,ci,error=1; unsigned char Data[999],Datar,Datas[9],TempBuffer[999]; //-------------------------- MYSQL initialisieren ------------------------------ MYSQL *conn; // pointer to connection handler MYSQL_RES *result; // holds the result set MYSQL_ROW row; // contains the data of the table row[n] conn = mysql_init(NULL); // initialize connection handler // connect to server time(&rawtime); strncpy(TempBuffer,ctime(&rawtime),strlen(ctime(&rawtime))-1); while (i>0) { if (mysql_real_connect (conn, "localhost", "user", "password", "home", 3306, NULL, 0)) {i=0; error=0;} else { conn = mysql_init(NULL); // initialize connection handler i--; Sleep(1000); } } //------------ENDE---------- MYSQL initialisieren ----------ENDE---------------- //-------------------------- RS232 initialisieren ------------------------------ if (error==0) { // COM-Port initialisieren // COM-Port des IR-Lesekopfs aus DB auslesen mysql_query(conn, "SELECT Value FROM infos WHERE Typ='Stromverbrauch_COM'"); result = mysql_store_result(conn); row=mysql_fetch_row(result); ci=atoi(row[0]); mysql_free_result(result); // SET Parameter sDcb.BaudRate = CBR_300; // Baudrate sDcb.ByteSize = 7; // DatenBits sDcb.Parity = EVENPARITY; // Kein ParityBit sDcb.StopBits = ONESTOPBIT; // ein StopBit sDcb.fBinary=TRUE; sDcb.fDsrSensitivity=FALSE; sDcb.fParity=FALSE; sDcb.fOutX=FALSE; sDcb.fInX=FALSE; sDcb.fNull=FALSE; sDcb.fAbortOnError=FALSE; sDcb.fOutxCtsFlow=FALSE; sDcb.fOutxDsrFlow=FALSE; sDcb.fDtrControl=DTR_CONTROL_DISABLE; sDcb.fDsrSensitivity=FALSE; sDcb.fRtsControl=RTS_CONTROL_ENABLE; sDcb.fOutxCtsFlow=FALSE; // SET timeouts sTo.ReadIntervalTimeout = MAXDWORD; sTo.ReadTotalTimeoutConstant = 50; // ms auf ein Byte warten sTo.ReadTotalTimeoutMultiplier = 0; sTo.WriteTotalTimeoutConstant = 0; sTo.WriteTotalTimeoutMultiplier= 0; wsprintf(TempBuffer, "//./com%d",ci); // COM Port initialisieren com=CreateFile(TempBuffer,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0); // kein COM Port => Abbruch if(com==-1) {error=2;} else { // Parameter setzen if(!SetCommState(com,&sDcb)) {printf("SetCommState-Error: %s\n",MB_OK+MB_ICONERROR);} // timeouts setzen if(!SetCommTimeouts(com,&sTo)) {printf("SetCommTimeouts-Error: %s\n",MB_OK+MB_ICONERROR);} } } //-----------ENDE----------- RS232 initialisieren -----------ENDE--------------- // ------------------------------------- Receive Data --------------------------------------------- if (error==0) { // No_Data Fehler setzen (wird bei Empfang > 500 Bytes 0 gesetzt) error=3; // Daten löschen und Zeit setzen wsprintf(TempBuffer, "UPDATE e_energie SET Data='0000\n', Time='%d', Value='1' WHERE Typ='Stromzaehler_Temp_Data'",time(NULL)); mysql_query(conn, TempBuffer); // Initialisierung senden // / ? ! \r \n Datas[0]=47; Datas[1]=63; Datas[2]=33; Datas[3]=13; Datas[4]=10; for (i=0;i<5;i++) {WriteFile(com,&Datas[i],1,&dwCount,0);} // Zählerantwort abwarten for (i=0;i<10;i++) { ReadFile(com,&Datar,1,&dwCount,0); if (dwCount!=0) {i=0;} } // Übertragungsgeschwindigkeit im Zähler auf 5=9600 Baud setzen // ACK 0 5 0 \r \n Datas[0]=6; Datas[1]=48; Datas[2]=53; Datas[3]=48; Datas[4]=13; Datas[5]=10; for (i=0;i<6;i++) {WriteFile(com,&Datas[i],1,&dwCount,0);} // warten bis Daten gesendet wurden Sleep(250); // Baudrate der COM-Schnittstelle auf 9600 ändern sDcb.BaudRate = CBR_9600; SetCommState(com,&sDcb); // Daten empfangen for (i=0;i<50;i++) { ReadFile(com,&Datar,1,&dwCount,0); if (dwCount!=0) { i=0; // ASCII Sonderzeichen ausfiltern, erlaubt sind nur: // \r \n . ( ) * 0-9 A-Z a-z if ((Datar==10)||(Datar==13)||(Datar==46)||((Datar>39)&&(Datar<43))||((Datar>47)&&(Datar<58))||((Datar>64)&&(Datar<91))||((Datar>96)&&(Datar<123))) { wsprintf(Data, "%s%c",Data,Datar); // append char to string //printf("%c",Datar); // Ausgabe anzeigen // jeweils nach 500 Zeichen Daten in MYSQL übertragen //(über 1000 Zeichen auf einmal sind nicht möglich!!!) if (j>500) { j=0; error=0; // Fehlercode zurücksetzen wsprintf(TempBuffer, "UPDATE e_energie SET Data=concat(Data,'%s') WHERE Typ='Stromzaehler_Temp_Data'",Data); mysql_query(conn, TempBuffer); wsprintf(Data, ""); // Data wieder leeren } j++; } } } } // ---------------ENDE----------------- Receive Data -----------------ENDE------------------------- // wenn keine Daten empfangen werden if (error!=0) {wsprintf(TempBuffer, "UPDATE e_energie SET Data='Read-Error %d (1=MYSQL;2=COM; 3=NO_DATA)', Time='%d', Value='0' WHERE Typ='Stromzaehler_Temp_Data'",error,time(NULL)); mysql_query(conn, TempBuffer);} mysql_close(conn); /* disconnect from server */ CloseHandle(com); /* disconnect from COM-Port */ return 0; }