====== Siemens TD-3511 ======
{{ :hardware:channels:meters:power:siemens:td-3511.png?200|TD-3511}}
Beim TD-3511 von Siemens 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.\\
User in Oberösterreich nutzen diese Anleitung:\\ [[/hardware/channels/meters/power/edl-ehz/siemens_td3511_in_oberoesterreich|Siemens TD-3511 in Oberösterreich]]\\
===== Hardware =====
Gelesen werden kann mittels [[hardware:controllers:ir-schreib-lesekopf|IR-Schreib-Lese-Kopf]] bei [[hardware:controllers:ir-schreib-lesekopf#grundlagen|300bd 7E1]], das Zuleitungskabel geht nach unten weg.\\
===== Kommunikation =====
Die Datenübertragung erfolgt gemäß Hersteller-Anleitung per IEC 62056-21 in Mode "C".\\
Das im vzlogger zu konfigurierende Protokoll ist "d0".\\
Der Zähler arbeitet im Pull-Modus, d.h. er sendet die Daten erst nach Anforderung.
Die initiale Kommunikation muss mit einer baudrate 300Bit/s erfolgen, erst nach dem vom Zähler gesendeten Identifikationstelegramm kann per Quittierungs-/Optionsauswahltelegramm auf eine höhere Baudrate umgestellt werden.
----
Der Zähler erwartet die Initialisierungssequenz
/?!\r\n (hex 2F 3F 21 0D 0A)
und antwortet mit seinem Identifikationstelegramm.
Nach der an den Zähler gesendeten Startsequenz ("Optionsauswahltelegramm") "ACK 0 Z 0 CR LF"
ACK0\r\n (hex 06 30 zz yy 0D 0A)
sendet er bei Option "Y" = 0 ("Daten auslesen") die Daten in der mit "Z" ausgewählten Baudrate.\\
Beispiel: "ACK050\r\n" (hex 06 30 35 30 0D 0A) --> Daten werden vom Zähler mit 9.600Bit/s gesendet.
Ausschnitt aus der Hersteller-Anleitung (s. Quellen - Benutzerhandbuch (Siemens AG Österreich)):
>Folgende Bitraten in Abhängigkeit des Wertes des Zeichens "Z" sind im Mode "C" definiert:
>
Code Übertragungsrate
0 300 Bit/s
1 600 Bit/s
2 1.200 Bit/s
3 2.400 Bit/s
4 4.800 Bit/s
5 9.600 Bit/s
6 19.200 Bit/s
9 115.200 Bit/s
>Der Zähler meldet sich aus Kompatibilitätsgründen mit der normkonformen Baudratenkennung "6". Eine maximal mögliche Baudrate von 115.200 Bd wird unterstützt.
----
Wahrscheinlich funktionieren auch die Befehle des [[hardware:channels:meters:power:edl-ehz:elster_AS1440]]. Die Umschaltung der Geschwindigkeit auf 19200bps funktioniert auf jeden Fall, dazu die Startsequenz verwenden:
\x06060\r\n
----
Je nach Messstellenbetreiber kann auch eine niedrigere, als die vom Hersteller angegebene, maximal mögliche Baudrate parametriert sein.\\
Falls die Kommunikation bei einer höheren Baudrate nicht funktioniert, sollte in der Startsequenz manuell die niedrigste Baudrate von 300Bit/s eingestellt werden (automatische Erkennung deaktivieren).
----
===== Beispielkonfiguration =====
Im Gegensatz zu anderen Zählern müssen beim TD-3511 die OBIS-Kennzahlen in der Form "C.D.E" (Bsp: 1.7.0) und nicht in der Form "A-B:C.D.E" (Bsp: 1-1:1.7.0) eingetragen werden.
{
"verbosity": 10,
"log": "/var/log/vzlogger/vzlogger.log",
"retry": 30,
"local": {
"enabled": false,
"port": 8081,
"index": true,
"timeout": 0,
"buffer": -1
},
"meters": [
{
"enabled": true,
"allowskip": false,
"protocol": "d0",
"device": "/dev/ttyAMA0",
"aggtime": -1,
"interval": 10,
"pullseq": "2F3F210D0A", // "/?!\r\n" = 2F 3F 21 0D 0A
"ackseq": "063034300D0A", //needs to be in accordance to parameter 'baudrate_read'
//"auto" for auto detection
"baudrate": 300,
"baudrate_read": 4800,
"baudrate_change_delay": 300,
"parity": "7e1",
"use_local_time": false,
"read_timeout": 10, //?? 2200ms according to manual?
"dump_file": "/home/pi/D0_pullSeq.txt",
"channels": [
{
"identifier": "1.7.0", //Positive active instantaneous power (A+) [kW]
"uuid": "00000000-0000-0000-0000-000000000000",
"api": "influxdb",
"host": "http://localhost:8086",
"database": "vzlogger",
"measurement_name": "vz_measurement",
"duplicates": 600,
"username": "",
"password": ""
}, {
"identifier": "1.8.1", //Positive active energy (A+) in tariff T1 [kWh]
"uuid": "00000000-0000-0000-0000-000000000000",
"api": "influxdb",
"host": "http://localhost:8086",
"database": "vzlogger",
"measurement_name": "vz_measurement",
"duplicates": 600,
"username": "",
"password": ""
}, {
"identifier": "2.8.1", //Negative active energy (A+) in tariff T1 [kWh]
"uuid": "00000000-0000-0000-0000-000000000000",
"api": "influxdb",
"host": "http://localhost:8086",
"database": "vzlogger",
"measurement_name": "vz_measurement",
"duplicates": 600,
"username": "",
"password": ""
}
]
}
]
}
----
===== Auslesen mit anderen Methoden als vzlogger =====
==== Auslesen per PHP ====
Dieses Script übergibt die Daten ganz normal an die Middleware.
Das Script verwendet als Geschwindigkeit 9600. Wer 19200 möchte muss die beiden Zeilen im Script einkommentieren.
'xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb',
'2.7.0(' => 'xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb',
'1.8.1(' => 'xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb',
'1.8.2(' => 'xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb',
'2.8.1(' => 'xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb'
);
error_reporting(E_ALL);
function setSerial($device,$bps) {
echo "setze Schnittstelle auf $bps bps\n";
$output=array();
$returnVar=0;
$cmd="stty $bps -F $device";
exec($cmd, $output,$returnVar);
echo "Ergebnis vom Setzen der seriellen Schnittstelle per stty:\n";
print_r($output);
} // function initSerial
function setSerialInital($device,$bps) {
echo "setze Schnittstelle auf $bps bps\n";
$output=array();
$returnVar=0;
$cmd="stty $bps -F $device 1:4:da7:a30:3:1c:7f:15:4:10:0:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0";
exec($cmd, $output,$returnVar);
echo "Ergebnis vom Setzen der seriellen Schnittstelle per stty:\n";
print_r($output);
} // function initSerial
function checkLine($line) {
global $channels;
global $urlBase;
echo "Line: '$line'\n";
$line=trim($line);
if ($line=='!') die("\n bin durchgelaufen\n");
foreach(array_keys($channels) as $orbis) {
if(substr($line,0,strlen($orbis))==$orbis ) {
echo "match für $orbis\n";
$part=(float)substr($line,6);
// 2.8.0(885.259*kWh)
$part=strstr(substr($line,6),'*',true);
if (('1.7.0(' == $orbis) or ('2.7.0(' == $orbis)) $part=$part*1000;
echo "ermittelter Wert: $part\n";
$url=$urlBase.$channels[$orbis].'.json?operation=add&value='.$part;
echo "rufe $url auf.\n";
$dummy=curl_file_get_contents($url);
} // if
} // foreach
} // function checkLine
function curl_file_get_contents($URL)
{
$c = curl_init();
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_URL, $URL);
$contents = curl_exec($c);
curl_close($c);
if ($contents) return $contents;
else return FALSE;
} // function curl_get_file_contents
function getTimestamp() {
$seconds = microtime(true); // false = int, true = float
return round( ($seconds * 1000) );
}
setSerialInitial($device,300);
$fp=fopen($device,'c+');
if (!$fp) {
echo "Konnte Port nicht öffnen\n";
die;
} else {
echo "Port öffnen OK\n";
} // if
echo "Request senden ...";
$out = "/?!\r\n";
fwrite($fp, $out);
echo "Request OK.\n";
echo "Lese eine Zeile\n";
echo fgets($fp);
echo "gelesen\n";
/*
echo "Lese eine Zeile\n";
echo fgets($fp);
echo "gelesen\n";
*/
echo "sicherheitshalber etwas warten\n";
usleep(500 * 1000);
echo "bps-Rate-Request senden ...";
$out = "\x06\x30\x35\x31\x0D\x0A";
$out = "\x06050\r\n";
// fuer 19200:
// $out = "\x06060\r\n";
fwrite($fp, $out);
echo "BPS Request OK.\n";
echo "warte bis Zeichen ausgegeben wurden...\n";
usleep(500 * 1000);
//setSerial($device,9600);
echo "schließe Port\n";
fclose($fp);
setSerial($device,9600);
// für 19200
// setSerial($device,19200);
echo "öffne Port\n";
$fp=fopen($device,'c+');
if (!$fp) {
echo "Konnte Port nicht öffnen\n";
die;
} else {
echo "Port 9600 OK\n";
} // if
echo "lese Rest ein\n";
while (!feof($fp)) {
echo $line=fgets($fp, 128);
checkLine($line);
} // while
fclose($fp);
?>
==== Auslesen per C zu MySQL ====
Das folgende C Programm kann alle Daten mit dem [[hardware:controllers:ir-schreib-lesekopf-usb-ausgang|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
#include
#include
#include
#include
#include
#include
/*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;
}
==== Test mit HTerm ====
Zum Testen kann [[http://www.der-hammer.info/terminal/|hterm]] verwendet werden.\\
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 mit Perl-Script ====
#!/usr/bin/perl
#
# (m)ein Stromzähler mit IR-Schnittstelle blubbert nach einem "Aufforderung-
# 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
#
# 20171230: Andreas Schulze: speed :-)
use warnings;
use strict;
use utf8;
use Device::SerialPort;
use Time::HiRes qw(usleep);
# functions
sub tty_init($);
sub tty_baudrate($$);
sub tty_close($);
sub send_telegram($$);
sub receive_line($);
# global vars
my $PORT='/dev/ttyUSB0';
my $aufforderung = "/?!";
my $speed = chr(6) . "090";
my $tty;
my $answer;
# main
$tty = tty_init($PORT);
send_telegram($tty, $aufforderung);
print receive_line($tty);
usleep(250*1000);
send_telegram($tty, $speed);
usleep(250*1000);
tty_baudrate($tty, 115200);
do {
$answer = receive_line($tty);
print $answer;
} until $answer eq "!\r\n";
tty_close($tty);
exit(0);
# parameter: tty name
# return : a tty handle
# on error : die
sub tty_init($) {
my ($device) = @_;
my $tty = new Device::SerialPort($device) || die "can't open $device: $!";
$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->read_const_time(10);
#$tty->debug(1);
$tty;
}
# parameter: a tty handle
# on error : die
# return : -
sub tty_close($) {
my ($tty) = @_;
$tty->close || die "can't close tty: $!";
}
# parameter: a tty handle
# parameter: new baudrate
# return : -
# on error : die
sub tty_baudrate($$) {
my ($tty, $baudrate) = @_;
$tty->baudrate($baudrate) || die 'fail setting baudrate';
$tty->write_settings || die 'fail write settings';
};
# parameter: a tty handle
# parameter: string to send
# return : -
# on error : die
sub send_telegram($$) {
my ($tty,$telegram) = @_;
print "send '$telegram' ...\n";
$telegram .= "\r\n";
my $num_out = $tty->write($telegram);
die "write failed\n" unless ($num_out);
die "write inclomplete\n" unless ($num_out == length($telegram));
print "$num_out Byte written ...\n";
}
# parameter: a tty handle
# return : received string including the final "\n"
# on error : -
sub receive_line($) {
my ($tty) = @_;
my ($line, $num_read, $c);
do {
($num_read, $c) = $tty->read(1);
$line .= $c;
} until !defined($c) || $c eq "\n";
$line;
}
===== Quellen =====
[[https://www.ng-o.com/kunden/downloads/netzkunden/messwesen/strom/Bedienungsanleitung_Siemens_Smart_Meter.pdf|Benutzerhandbuch]] (Netze NGO)\\
{{:hardware:channels:meters:power:siemens:AMIS_TD-351x_BHBK.pdf|Benutzerhandbuch}} (Siemens AG Österreich)\\