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

Der Zähler erwartet die Initialisierungssequenz

/?!\r\n

nach Startsequenz

\x06\x30\x35\x31\x0D\x0A
\x06050\r\n

sendet er die Daten mit 9600bd.

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

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: 2018/01/25 15:41 von jau