Benutzer-Werkzeuge

Webseiten-Werkzeuge


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

Dies ist eine alte Version des Dokuments!


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 müssen sich neuerdings beim Energielieferanten registrieren:
http://strom.netzgmbh.at/eag_at/page/284094835607631929_0_1060376991645160446,de.html
um den Zähler per IR-Schnittstelle auszulesen. Der notwendige AES-Schlüssel muss beim Netzbetreiber beantragt werden.
Siehe auch den Thread im Photovoltaikforum

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

Beispielkonfiguration vzlogger

Vzlogger ist in der Lage diesen Zähler auszulesen.

   "meters": [
        // example for OMS based device
        {
            "enabled": true,
            "protocol": "oms",
            "device": "/dev/ttyUSB0",
            "key": "123456789012345667890",
            "channel": {
                "uuid": "a7b84340-249d-11e5-b65e-f13edf25e0a4",
                "identifier": "1.7.0",
                "middleware": "http://localhost/middleware.php",
                }
        },

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+hterm

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: $!";
hardware/channels/meters/power/edl-ehz/siemens_td3511.1476724092.txt.gz · Zuletzt geändert: 2016/10/17 19:08 von jau