Benutzer-Werkzeuge

Webseiten-Werkzeuge


hardware:channels:meters:power:edl-ehz:siemens_td3511

Siemens TD-3511

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:
Siemens TD-3511 in Oberösterreich

Hardware

Gelesen werden kann mittels IR-Schreib-Lese-Kopf bei 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<Z><Y>\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 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.
vzlogger.conf
{
  "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": "<username>",
          "password": "<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": "<username>",
          "password": "<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": "<username>",
          "password": "<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.

<?php
 
$urlBase='http://localhost/middleware.php/data/';
$device='/dev/serial/by-id/usb-bitteAnpassen';
 
$channels=array(
                '1.7.0(' => '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 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;
 
}

Test mit HTerm

Zum Testen kann 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

Benutzerhandbuch (Netze NGO)
Benutzerhandbuch (Siemens AG Österreich)

hardware/channels/meters/power/edl-ehz/siemens_td3511.txt · Zuletzt geändert: 2023/05/29 13:51 von gregor.wolf