Stromzähler per S0-Schnittstelle mit RaspberryPi auslesen

Kurz bevor ich in meine Wohnung eingezogen bin, hatte ich die Möglichkeit dem Elektriker beim Anschließen des Sicherunsgkastens über die Schulter zu schauen. Und was mir damals gleich aufgefallen ist: Der verbaute Stromzähler, ein Eltako dsz12e, hat einen S0-Ausgang. Als ich 2013 meine Bachelorarbeit mit dem sperrigen Thema “Analyse der Wirtschaftlichkeit von PV-Anlagen in Verbindung mit Speichern für Privathaushalte ohne EEG-Vergütung” geschrieben habe, war eines der Probleme überhaupt an Daten zu kommen, zu welchen Zeitpunkten in Privathaushalten elektrische Energie verbraucht wird. Letztendlich habe ich ein Verbrauchsprofil aufgrund einer VDI-Richtline erzeugt – aber auch das beinhaltete “nur” werte für 15 minuten – eine noch genauere Auflösung wäre aber wünschenswert gewesen. Der S0-Ausgang an meinem Stromzähler hat deshalb sofort mein Interesse geweckt.

Allerdings waren andere Bastelprojekte erstmal wichtiger – und anfangs hat mich auch abgeschreckt, dass der S0-Ausgang laut Spezifikation eine angelegten Spannung von 5V benötigt, der Raspberry liefert aber nur 3,3V. Aber irgendwann als ich mal wieder zu dem Thema gegoogelt habe, bin ich über einen Blogartikel von Johannes Weber gestoßen, der mit dem Raspberry Pi seinen S0-Stromzähler (den gleichen, der auch bei mir verbaut ist) ausließt – und mein Interresse war wieder geweckt.

Letzte Woche hab ich es dann geschaft. Der S0-Ausgang des Stromzählers wurde wie von Johannes beschrieben mit dem Pi verbunden (ebenfalls mit Gelb/Weiß, damit ich nicht durcheinander komme) – und ich konnte mit seinem Testprogramm erste Messswerte aufzeichnen.

Allerding unterscheidet sich ab hier unsere Zielsetzung: Johannes will die Daten mit seinem Monitoring-Server aufzeichnen – ich hätte gerne zuerstmal einfache CSV-Daten. Wie ich diese konkret später mal verwenden werde ist noch nicht klar – aber mit CSV-Dateien bleib ich flexibel. Deshalb habe ich Johannes Stromzählerprogramm für meine Zwecke angepasst – es erzeugt jetzt für jeden Tag eine CSV-Datei mit werten für jede Minute:

/*
 * isr.c:
 *	Wait for Interrupt test program - ISR method
 *
 *	How to test:
 *	Use the SoC's pull-up and pull down resistors that are avalable
 *	on input pins. So compile & run this program (via sudo), then
 *	in another terminal:
 *		gpio mode 0 up
 *		gpio mode 0 down
 *	at which point it should trigger an interrupt. Toggle the pin
 *	up/down to generate more interrupts to test.
 *
 * Copyright (c) 2013 Gordon Henderson.
 ***********************************************************************
 * This file is part of wiringPi:
 *	https://projects.drogon.net/raspberry-pi/wiringpi/
 *
 *    wiringPi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    wiringPi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
 ***********************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <time.h>     //for filename & time in CSV-file

//  What GPIO input are we using?
//  This is a wiringPi pin number

#define	BUTTON_PIN	9

//  globalCounter:
//  Global variable to count interrupts
//  Should be declared volatile to make sure the compiler doesn't cache it.

static volatile int globalCounter = 0 ;


/*
 * myInterrupt:
 *********************************************************************************
 */

void myInterrupt (void)
{
  ++globalCounter ;
}


/*
 *********************************************************************************
 * main
 *********************************************************************************
 * adaptet by Jonathan Schneider (http://langhaarschneider.net) to save input-Values of every minute to CSV-files for every day.
 * inspired by Johannes Weber (http://blog.webernetz.net/2014/10/13/stromzahler-mit-s0-schnittstelle-vom-raspberry-pi-auswerten/)
 */

int main (void)
{
  FILE* ptr_datei;    //pointer to file descriptor
  time_t t;           //timestamp of the current time
  struct tm* ptr_ts;  //pointer to tm-struct (defined in time.h)
  int lastmin = 0;    //int value for the last minute that was written to the CSV-file
  char filename[31];  //Filename including dir:/home/pi/strom/YYYY-MM-DD.csv + \0 means 30 characters needed


  if (wiringPiSetup () < 0)
  {
    fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno)) ;
    return 1 ;
  }

  if (wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0)
  {
    fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno)) ;
    return 1 ;
  }


  for (;;)                                //endless loop...
  {
    do {
      delay (100);                        //sleep 100 ms
      t = time(NULL);                     //get current time
      ptr_ts = localtime(&t);             //create tm-structure
    } while (lastmin == ptr_ts->tm_min);  //loop while the current minute = the last minute, which values are already saved in the logfile
    //create filename. Year has 4 Characters, Month and say may have only one. So it has to be formatted that month and day always will have two characters - maybe with leading zwro.
    //To year 1900 has to be added because it only includes the years from 1900.
    //Month has to be increased by one because it starts with january = 0
    //BE CAREFUL IF YOU CHANGE THE FILENAME! You might have to increase filename[]!
    sprintf(filename, "/home/pi/strom/%i-%02i-%02i.csv", ptr_ts->tm_year+1900, ptr_ts->tm_mon+1, ptr_ts->tm_mday);
    printf("%s", filename);
    ptr_datei = fopen(filename, "a");     //Open file FILENAME. Create it if it doesnt exist. Append things that will be written at the end
    //Print following information into one line: HH:MM;COUNTERVALUE
    //HH & MM have to be formated, that they always will have two characters
    fprintf (ptr_datei, "%02i:%02i;%d\n", ptr_ts->tm_hour, ptr_ts->tm_min, globalCounter);
    globalCounter = 0;                    //after writing the counter-value to the file, reset the counter-value to 0 for the next minute
    fclose (ptr_datei);                   //close the CSV-file
    lastmin = ptr_ts->tm_min;             //set lastmin to the minute for which the CSV-file was written.

  }                                       //And start again

  return 0 ;                              //will hopefully never be reached...
}

Inzwischen wurde auch das Problem der Spannungsversorgung des Pi in Nähe des Sicherungskasten gelöst – und der Pi liefert mir hübsche Messwerte:

pi@raspicounter ~/strom $ cat 2015-04-16.csv
21:10;19
21:11;19
21:12;18
21:13;19
21:14;18
21:15;18
21:16;18
21:17;19
21:18;19
21:19;19
21:20;19

Die angezeigten Werte müssen noch in kWh oder Wh umgerechnet werden – da mein Zähler mir pro kWh 800 Impulse liefert. Ich habe also in den letzten Minuten einen Verbrauch von rund 23,57 Wh pro Minute gehabt.

Das nächste Projekt könnte dann mein Wärmemengenzähler für die Heizung werden. Der Sensus pollucom e lässt sich laut Bedienungsanleitung auch irgendwie auslesen…

Ich freue mich über deine Anmerkungen/Fragen/Kommentare!

19 Gedanken zu „Stromzähler per S0-Schnittstelle mit RaspberryPi auslesen“

  1. Hallo, nettes Projekt.
    Ich nutze auch wiringPi und IRC.c. Momentan schreibe ich den GlobalCounter fortlaufend in eine Datei .
    Ich möchte nun, für mich ausreichend, einmal täglich eine Datei mit dem Tagesgesamtertrag erzeugen und speichern.
    Da ich in C total noch keinen Plan habe, könntest du mir einen Tipp oder den modifizierten Quellcode vielleicht verraten? Gern auch an meine E-Mail.
    Gruß Markus

    1. Hallo Markus,
      auch wenn C nicht ganz so einfach ist: Es ist ein gutes Gefühl wenn man grob weis, was das Programm macht. Deshalb will ich dir keine fertige Lösung geben, sondern einen Tipp wie du den obrigen Quellcode für dich adaptieren könntest. Den Rest darfst du selber machen 🙂

      1. In Zeile 100 überprüfe ich ob die letzte Minute in der was gespeichert wurde noch die gleiche Minute ist, die aktuell ist. Wenn du hier nicht auf Minuten sondern auf Tage (http://www.tutorialspoint.com/c_standard_library/c_function_gmtime.htm) überprüfst, wird die While-Schleife nur einmal am Tag um 0:00 verlassen und eine Datei erzeugt.
      2. Die Zeile 110 kannst du so anpassen dass nurnoch der Zählerstand in die Datei geschrieben wird
      3. Die Zeile 113 muss dann natürlich auch angepasst werden
      4. Der Variablenname Lastmin macht dann natürlich keinen Sinn mehr…
      5. In Zeile 97 könntest du den Wert beim Schlafen auch erhöhen um die Last zu senken da du sowieso nicht so genau auflösen willst. Aber eigentlich sollte der EInfluss nicht so groß sein..

      Liebe Grüße,
      Jonathan

      PS: Ich war länger im Urlaub – ich hab vor hier wieder regelmäßiger und schneller zu antworten 🙂

  2. Hallo, danke für deine Anleitung!

    ich habe einen Stromzähler mit 10000Imp/kWh und möchte in einem Diagramm die kW ´s darstellen. Dazu muss ich die Impulse mit 0.006 multiplizieren. Und daran scheitere ich 🙁
    Habe schon eine Variable mit “float” (wegen der Kommastellen) erstellt aber irgendwie fehlt es mir an Wissen.
    Hättest du nen Tip?

    1. Hallo Sebi,

      ich gehe mal davon aus. dass du wie ich alle Werte in einer TXT-Datei stehen haben willst um dir später daraus Diagramme zu basteln. Deshalb ist für dich die Zeile 110 von besonderem Interesse. Es müsste reichen wenn du hier den GlobalCounter mit deinem Faktor multiplizierst, bevor du ihn in die Datei schreibst. Allerdings musst du dann den Formatstring auch entsprechend anpassen, damit der einen Fließkommawert erwartet und so ausgibt wie du es wünschst. Dafür einfach mal nach fprintf googeln, die Beschreibung der Funktion lesen, versuchen zu verstehen, das Skript anpassen – und schon müsste es funktionieren. Eine Float-Variable bräuchtest du vermutlich nicht einmal.

      Liebe Grüße,
      Jonathan

        1. Gerne!
          Wie gesagt – die Variable könntest du dir auch sparen. Einfach direkt in der fprintf-Zeile die Multiplikation vornehmen – und natürlich auch den Formatstring wie du es hast übernehmen.
          Mit was zeichnest du die Diagramme?

  3. Wäre der formatstring das “int” in zeile 50?
    zum zeichnen nehme ich erstmal gnuplot.
    Als nächstes möchte ich noch versuchen einen Temperaturwert mit in die Datei zu schreiben. Hier liegt noch ein DS12B80 Sensor rum. 🙂

    1. Hi,

      nein – Zeile 50 so lassen. Einfach in Zeile 100 das %dn durch dein %.3fn ersetzen und die Multiplikation auch in der selben Zeile bei der Übergabe der Werte durchführen.

      Temperatur möchte ich auch mal noch loggen, aber dann am Besten gleich mehrere Sensoren (Außen, Keller, Wohnzimmer, Dachboden,…) die per Funk ausgelesen werden. Aber davor ist erstmal mit dem Wärmemengenzähler im Keller an der Reihe 🙂

  4. Hi,
    hat jemand Erfahrung mit Siemens-Stromzaehlern (Serie 7KT1500)?
    Ich habe ein Mikro-Blockheizkraftwerk laufen, vor das ein Zaehler Siemens 7KT1530 geschaltet ist.
    Der Zaehler misst den vom BHKW verbrauchten und den erzeugten Strom und zeigt diese Werte auch auf dem Display an (aktueller Wert, aufsummiert, etc.).
    Ich moechte nun einen Pi ueber die S0-Schnittstelle anbinden und die Werte graphisch anzeigen.
    Leider werde ich aus den Datenblaettern des Zaehlers nicht schlau. 🙁
    Was bedeutet ein S0-Impuls? Verbrauchter Strom? Erzeugter Strom? Differenz?

    Vielen Dank,
    Florian

    1. Hallo Florian,

      die S0-Schnittstelle liefert grundsätzlich Impulse pro kWh – d.h. dass wenn 1 kWh durch den Zähler durchgeflossen ist, hat er X Impulse abgegeben. In deinem Fall sind es laut Datenblatt 1000 Impulse/kWh. Wie das allerdings in deinem Fall genau ist mit zufließenden und abfließenden Energiemengen – ob für beide die Impulse abgegeben werden – oder nur für einen der beiden Fälle, kann ich dir leider nicht sagen. Das musst du am besten mal selbt ausprobieren…

      Bitte schreib doch auch wie dein Ergebniss aussieht!

      Liebe Grüße,

      Jonathan

  5. Hey Jonathan, wollte mich auf diesem Weg kurz bei dir bedanken!

    Dank deiner Hilfe bzw insbeondere deiner Modifikationen des Codes von J.Weber konnte ich nun auch mein Projekt ein ganzes Stückchen voran treiben!

    Gruß
    Flocke

  6. Hi,

    wollte fragen ob du das Projekt Wärmemengenzähler schon angefangen hast?
    Ich habe nämlich noch keinen, möchte aber einen Einbauen und Frage mich daher welchen ich nehmen soll. Er sollte auf alle Fälle mittels Raspberry&Co auswertbar sein. Bist du mit deinem Zufrieden?
    Wo genau wird der dann eigentlich eingebaut?

    Danke
    lg
    Chris

    1. Hallo Chris,

      der Wärmemengenzähler ist im Keller eingebaut und misst den Durchfluss, die Vorlauf und die Nachlauftemperatur des Heizwärmekreislaufs in meiner Wohnung und errechnet daraus die Wärmemenge. Leider ist das bei mir installierte Modell nicht direkt auslesbar (ich kann keinen Draht direkt anklemmen) – aber theoretisch müsste er sich per IR-Schnittstelle auslesen lassen. Nachdem ich auf folgenden Forenthread gestoßen bin https://www.mikrocontroller.net/topic/113984 hab ich mir vor einiger Zeit schon einen USB IR-Schreib/Lesekopf (http://wiki.volkszaehler.org/hardware/controllers/ir-schreib-lesekopf-usb-ausgang) besorgt. Seitdem stockt das Projekt allerdings. Vielleicht schaff ich es in den nächsten ruhigen Tagen mich nochmal etwas damit auseinander zu setzen…
      LG,
      Jonathan

  7. Hallo,

    vielen Dank für die super Anleitung. Bin purer Anfänger mit dem Raspberry gewesen und nun logge ich damit meinen Stromzähler 🙂
    Leider habe ich es noch nicht geschafft die Datei so anzupassen dass sie mir für jede Minute die Leistung in “W” anzeigt. Ich müsste den Globalcounter mit 60 Multiplizieren.
    Wie komme ich auf die 60? 1000 Impulse pro kW/h entspricht in der Minute 0,06. Ich möchte aber Watt also 0,06 x 1000 = 60.
    %.3fn habe ich abgeändert und gleich hinter Globalcounter*60 eingetragen. Es kommt dann aber eine sehr lange Zahl heraus die überhaupt keinen Sinn ergibt,

    Vielleicht kann mir dabei wer helfen was ich genau anpassen müsste?

    VG

    Florian

  8. Hallo,

    Hab zwar bereits ein Kommentar versucht zu schreiben aber irgendwie ist es nicht da 🙁
    Erstmal super Anleitung und tolle Kommentare.
    Ich habe es dank der Anleitung geschafft das Programm zum laufen zu bekommen und dass es automatisch startet.
    Zu meiner Frage und ich bin dabei am verzweifeln.
    Wie schaffe ich es dass der Zähler gleich auf Watt umrechnen tut. Ich habe bereits das mit dem %.3f/n versucht und bei globalcounter*60 gesetzt. Jedoch kommt dann bei der Datei eine irre lange Zahl raus. Warum ich mal 60 multiplizieren hat den folgenden Grund. 1000 Impulse pro kwh, auf Minute und Watt heruntergerechnet würde es Impuls mal 60 bedeuten. Das stimmt auch wenn ich am Zähler den aktuellen Verbrauch ansehen und die aufgezeichneten Impulse mal 60 nehme.
    Aber wie bringe ich diese Multiplikation in die c datei unter?
    Für Hilfe bin ich sehr dankbar

    VG Florian

  9. Hallo,

    gibt es eine Möglichkeit den S0 Ausgang zu prüfen?
    Irgendwie will das bei mir einfach nicht funktionieren und ich glaube jetzt schon das vielleicht der S0 Ausgang defekt ist.

    Danke
    Chris

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert