Energiedatenlogger mit C-Control Mega 128

Projektbeschreibung

Zunehmender Stromverbrauch und wachsende Kosten für elektrische Energie führen zu den bekannten Sparmaßnahmen wie Einsatz von Geräten mit optimiertem Verbrauch oder auch Wechsel des Stromanbieters. Für eine drastische Reduzierung der Kosten bietet sich darüber hinaus die eigene Stromproduktion zum Beispiel über eine Photovoltaik-Anlage an. Zu diesem Schritt habe ich mich Ende 2011 durch den Einbau einer 5,3 kWhp Solaranlage entschlossen. Eine Bilanz kann frühestens nach Ablauf eines Jahres gezogen werden, dann wird sich zeigen ob die Energiekosten auf 0 reduziert werden können oder sich sogar ein Überschuss ergibt.

Um die Effizienz der neuen Anlage zu beurteilen wurde ein Aufzeichnungsgerät SolarLog 100 installiert. Über WLAN oder auch Internet kann hiermit jederzeit am PC die aktuelle Produktion sowie die Historie beobachtet werden. Letztlich spiegelt das SolarLog-System jedoch nur die Sonnenstunden und die damit verbundene Stromerzeugung wieder. Da der Anschluss von handelsüblichen Verbrauchszählern nicht möglich ist, kann keine Gesamtaussage über Verbrauch und Kosten ist gemacht werden.

Neben der Solarstromerzeugung sind also auch der Strombezug vom Energieversorger sowie die Einspeisung ins Netz zu erfassen. Für dieses Datenlogging sollte ein Microcontroller eingesetzt werden, da der hohe Stromverbrauch eines PCs einen Dauereinsatz für diese Aufgabe nicht rechtfertigt. Lediglich die Auswertung der periodisch aufgezeichneten Daten soll dann am PC mit Hilfe von EXCEL durchgeführt werden.

Als Microcontroller wurde ein C-Control Pro Mega 128 ausgewählt, da mit diesem System schon Erfahrungen aus einem anderen Projekt vorlagen. Ziel hier war nun das Auslesen zwei unterschiedlicher Stromzähler incl. Anzeige und Speicherung der Daten. Da das SolarLog 100 Gerät nach Auskunft des Herstellers nicht extern ausgelesen werden kann, mussten die erforderlichen Daten vom offiziellen Zähler der Solaranlage abgegriffen werden. Der vom RWE eingebaute Zweirichtungszähler verfügt über eine Infrarot-Schnittstelle, hierüber waren Verbrauch und Einspeisung zu erfassen. Weiterhin sollten die Temperatur, der Füllstand des Öltanks der Heizung sowie die Betriebsstunden der Heizung erfasst werden.

In den folgenden Kapiteln werden die verwendete Hardware und Software beschrieben und es wird auf die Erfahrungen und Probleme beim Aufbau und Betrieb eingegangen.

Messgeräte und Schnittstellen

Digitaler EMH eHZ Zweirichtungs-Stromzähler

Zur Erfassung des Strombezuges sowie der Stromeinspeisung in das öffentliche Netz wurde vom RWE im November 2011 der elektronische Zweirichtungszähler EMH eHz-HW8E2A5LOEQ2P eingebaut.

EHZ-Zähler Technische Daten

Der EMH-Zähler verfügt auf der Vorderseite über einen IR-Port. Der Datenaustausch erfolgt über einen entsprechenden Schreib-/Lesekopf, dieser ist von den max. Abmessungen nach DIN-EN 62056-21 genormt:

EHZ IR-Port

Die Datenübertragung erfolgt asynchron seriell ohne zusätzlichen Taktimpuls. Der EMH eHz arbeitet mit 9600 Baud, 8 Bit, keine Parität und einem Stopbit. Für die vorliegende Aufgabe muss die Schnittstelle nur als Lesekopf ausgeführt werden, da der Zähler keine Eingaben erwartet. Der Zähler sendet ein Datenprotokoll als Smart Message Language (SML), die Sendung des Datensatzes erfolgt lastabhängig alle 1...4 Sekunden.

Die Impulse der IR-Sendediode können über eine Photodiode oder einen Phototransistor erfasst werden. Für Versuchszwecke wurde mit vorhandenen Bauteilen folgende Schaltung aufgebaut:

Empfangsschaltung

Der Phototransistor BPW40 erfasst die Blinksignale der IR-Diode im Zähler, d.h. bei auftretendem Licht schaltet der Transistor durch und es gelangen positive Impulse auf den nachfolgenden Komparator LM 393. Durch Nachschalten eines invertierenden Schmitt-Triggers hinter den Komparator wird das Signal invertiert und die Flanken zusätzlich geschärft. Der zweite Komparator im LM 393 wird genutzt, um das Blinksignal der IR-Diode durch eine LED anzuzeigen.

Der Schaltungsausgang wurde zu Testzwecken mit dem Eingangsport RXD einer seriellen Schnittstelle (RS232) eines PCs verbunden. Über ein Terminalprogramm (HTerm.exe, http://www.der-hammer.info/terminal/) konnten HEX-Daten sichtbar gemacht werden und der strukturelle Aufbau einer SML-Nachricht war zu erkennen. Leider war aus den Daten jedoch nicht die gewünschte Information über den Zählerstand ablesbar.

Da weitere Experimente mit der oben aufgeführten Anordnung erfolglos blieben, wurde auf eine in http://wiki.volkszaehler.org/hardware/controllers/ir-schreib-lesekopf veröffentliche Schaltung zurückgegriffen:

Schreib- Lesekopf

Die Schaltung enthält einen Schreib- und einen Lesekopf und wird als Bausatz mit Platine und Gehäuse für gut 10,- € zuzüglich Versand angeboten (udo1@gmx.net). Da die IR-Schnittstelle auf der Frontseite der eHZ-Zähler per DIN-Definition nur die Empfangsrichtung zulässt, braucht man nur die IR-Lesevorrichtung. Man muss die Platine daher nur mit den Bauteilen der Lesevorrichtung bestücken, in jedem Falle erhält man nach Einbau in das genormte Gehäuse mit Magnetring eine fast perfekte Lösung zum Anbau an den Zähler. Die Einschränkung bezieht sich auf die minimal hervorstehenden Wölbungen der Dioden am Zähler sowie am Schreib-/Lesekopf. Um hier ein Wippen zu vermeiden muss man z.B. mit einem Schaumstoffring leicht ausgleichen.

Die Datenübertragung erfolgt seriell und es werden Logic-Pegel geliefert. Um sowohl das Eingangssignal, als auch das Ausgangssignal invertieren zu können, wurde beiden Stufen ein Exklusiv-Oder-Glied vorgeschaltet bzw. nachgeschaltet. Dieses ermöglicht durch einfaches Umschalten eines seiner Eingänge auf High oder Low das Ausgangs-Signal entweder zu invertieren oder nicht. D2 dient als Verpolungsschutz.

Der nichtinvertierte Ausgang des Lesekopfes wurde nun wieder mit dem Eingangsport RXD einer seriellen Schnittstelle (RS232) eines PCs verbunden. Über das oben erwähnte Terminalprogramm war nun nicht nur die Struktur der SML-Nachricht erkennbar, sondern die empfangenen HEX-Daten konnten auch ausgewertet werden.

Das HEX-Datentelegramm enthält insgesamt 356 Zeichen, von denen aber zur Ablesung der Zählerstände nur einige wenige benötigt werden. Wie die nachfolgende Tabelle zeigt, enthalten die Zeichen 143 – 147 die Information für Bezug und die Zeichen 166 – 170 die Information für Einspeisung.

Für den Bezugswert ist die Hexadezimalzahl 00007D362E in das Dezimalsystem umzuwandeln. Jede Stelle der Zahl hat den Wert der entsprechenden Potenz von 16, die rechte Ziffer entspricht 16º=1, die zweite von rechts 16¹=16 usw. Jede Ziffer bzw. ihr Zahlenwert (A=10, B=11, ...) wird mit der entsprechenden Potenz multipliziert und anschließend summiert:

Umwandlung Hex < Dez

Unter Berücksichtigung der Kommastelle ergibt sich somit der Zählerstand für Bezug zu 820,5870 kWh. Den Zählerstand für Einspeisen wird analog ermittelt.

SML Protokoll

Siemens Drehstromzähler mit Ferraris-Scheibe

Im Rahmen der Installation der Photovoltaik-Anlage wurde zur Erfassung der erzeugten Energie ein Siemens Drehstromzähler eingebaut. Da zu dieser Zeit kein elektronischer Zähler verfügbar war, arbeitet der eingebaute Zähler nach dem alten Prinzip mit Ferraris-Drehscheibe und verfügt somit auch über keine Schnittstelle.

Ferraris-Zähler Ferraris-Prinzip
Grundsätzlicher Aufbau eines mechanischen Wechselstromzählers

Bei den üblichen Wechselstromzählern, auch bekannt als Ferraris- bzw. Induktionszähler, werden die Spannungen und Ströme mit Spulen, Bremsmagneten und Läuferscheibe über Magnetfelder und Ströme analog multipliziert. Dieses Produkt aus Spannung mal Strom entspricht dann in Form der Drehgeschwindigkeit der Drehscheibe der elektrischen Leistung. Die Drehbewegung der Drehscheibe entsteht durch die mittels der Spannungs- und Stromspule erzeugten Wirbelströme in der Aluminiumscheibe. Dazu erzeugen die am Stromnetz angeschlossene Spannungsspule mit ihrem Eisenkern sowie die vom Laststrom durchflossene Stromspule mit ihrem Kern einen um exakt 90 Grad phasenverschobenen Wechselfluss, der zu eben diesem Wirbelstrom in der Scheibe führt. Das Prinzip des Ferrariszählers berücksichtigt, bedingt durch den Aufbau, sogar den Leistungsfaktor cos ρ und bringt somit nur den Wirkstrom zur Anzeige. Die Umdrehungen der Scheibe, die mit einem Rollenzählwerk registriert werden, sind ein Maß für die geleistete Arbeit. Das Verhältnis von Umdrehung zu kWh wird als Zählerkonstante bezeichnet. Diese Zählerkonstante ist auf dem Zähler (beispielsweise 75 U/kWh beim abgebildeten Siemes-Zähler) angegeben. Dies bedeutet, dass 75 Umdrehungen eine verbrauchte kWh widerspiegeln. Zusätzlich lassen sich auch Daten aus der Geschwindigkeit der Drehscheibe gewinnen. Die Umdrehungszeit (von Rot bis Rot) wird gemessen und ergibt beispielsweise zehn Sekunden. Wenn nun die Anzahl der Umdrehungen auf eine Stunde hochgerechnet (= 360 Umdrehungen pro Stunde) und dann durch die Zählerkonstante geteilt wird, so lässt sich die aktuell verbrauchte Leistung ermitteln:

Leistungsformel

Damit nun aber nicht die einmal beschleunigte Scheibe nachläuft, wenn der Stromverbrauch geringer wird oder gar ausbleibt, ist ein Bremsmagnet installiert, der die Beschleunigung kompensiert und so die Umdrehungszahl der Scheibe genau proportional zum Stromfluss hält. Er sorgt z. B. auch für einen sofortigen Stopp der Scheibe, wenn kein Stromfluss mehr vorhanden ist.

Die Ferraris-Scheibe des Solarzählers wird mit einer Reflex-Lichtschranke abgetastet. Die silberne Scheibe reflektiert gut, d.h. am Ausgang liegen +5V an. Bei Durchlauf der roten Markierung ist die Reflektion schlecht, es entsteht eine negative Flanke, der Optokoppler sperrt. Diese Impulse müssen gezählt werden und als Verbrauchswert aufsummiert werden.

75 Impulse entsprechen 1 kWh. Es darf bei jedem Durchlauf nur ein Impuls gezählt werden, dies muss durch entsprechende Ausführung der Software sichergestellt werden.

Beim elektronischen Zähler werden die Zählerstände im Zähler gespeichert und dort bei Bedarf abgerufen, es steht also immer der genaue Zählerstand zur Erfassung im Datenlogger zur Verfügung. Im Gegensatz dazu sind beim Drehscheibenzähler die einzelnen Impulse zu erfassen und müssen aufsummiert werden. Falls einzelne Impulse verlorengehen ist es nicht mehr möglich einen genauen Zählerstand zu ermitteln. Genau dieser Fehler aber trat bei meinem Datenlogger auf, da der Datentransfer über die serielle Schnittstelle offenbar so zeitintensiv war, dass während dieser Übertragung immer wieder Impulse des Drehscheibenzählers verschluckt wurden. Der erfasste Zählerstand lag permanent unter dem tatsächlichen Zählerstand. Es lag also der Gedanke nahe, die Impulse in einem externen Baustein zu summieren und dann bei Bedarf abzufragen, ohne einen einzigen Impuls zu verlieren. Zu diesem Zweck wählte ich einen CCTools-Bausatz (CCTools I2C-CNT2 HS Nr. 1828), der zwei 8Bit-Counter enthält. Mit jedem Impuls wird der Zähler um Eins erhöht. Die Zählerstände können über den I2C-Bus abgefragt werden.

Die folgende Schaltung zeigt die Ausführung mit dem Reflexkoppler CNY70 und dem Zählerbaustein:

Reflexkoppler Montage des Reflexkopplers

Der Reflexkoppler muss auf der Glasscheibe des Zählers so angebracht werden, dass die Linie Fotodiode / IR-LED senkrecht verläuft. Die optimale Position auch in horizontaler Richtung ist durch Versuche zu ermitteln. Befestigungsversuche mit Klebeband oder Gummiband sind gescheitert, da sich die Platine auf Dauer verschoben hat und keine saubere Messung mehr möglich war. Die Platine wurde daher in ein selbstgefertigtes Gehäuse eingebaut. Als Basis hierfür diente der Einbaurahmen eines defekten digitalen Messgerätes. Auf dem Zählergehäuse wurden mit 2-Komponenten Kleber entsprechende Gewinde¬bolzen (4 mm) aufgeklebt. Das Gehäuse konnte so sicher angeschraubt werden, durch Schlitzlöcher war eine genaue Justierung möglich.

Für die Übertragung von Steuerungssignalen innerhalb eines Gebäudes ist die Länge des reinen I²C-Busses jedoch zu gering. Hier wurde auf den Baustein P82B715, ein I²C Bus Buffer des Herstellers Philips, zurückgreifen. Somit sind Leitungslängen bis zu 50 Meter kein Problem mehr – eine Länge, die durchaus ausreichend erscheint, selbst wenn man auf jedem Stockwerk im Haus I²C-Slaves unterbringen möchte. Das folgende Datenblatt zeigt die technischen Details sowie die Bezugsquelle für die I²C-Verstärkerplatine.

I²C Verstärkerplatine

Trotz Einsatz der vorgenannten Verstärkerplatine kam es auf dem I²C-Bus immer wieder zu Störungen und die Signale wurden nicht sauber übertragen. Abhilfe wurde hier dadurch geschaffen, dass der Zählerbaustein direkt auf der Zusatzplatine im Gehäuse des Datenloggers platziert wurde. Hierzu musste der Baustein entsprechend bearbeitet werden, d.h. durch Entfernen der Klemmleisten konnte die gewünschte Verkleinerung erreicht werden und die Platine nun mittels Stiftsockeln aufgedockt werden. Der Impuls vom Ferraris-Zähler wurde nun über eine separate Leitung zum Zählerbaustein geführt und dort über eine LED angezeigt, die I²C-Verstärkereinheit konnte für diesen Zweck entfallen. Mit dieser Anordnung nun eine genaue Erfassung der Impulse und somit des Zählerstandes möglich.

Füllstandsanzeige für den Öltank

Zur Visualisierung des Füllstandes des Tanks der Ölheizung und damit auch zu einer langfristigen Verbrauchsübersicht wurde im Stutzen für die mechanische Messuhr zusätzlich zur Uhr ein Ultraschallsensor installiert. Hierbei wurde auf ein Fertigmodul mit dem Ultraschall Entfernungsmesser SRF02 zurückgegriffen, ein Modul welches zur Entfernungsmessung zwischen 15 cm und 6 Metern geeignet ist.

Das Modul weist folgende technischen Eigenschaften auf:

  • Betriebsspannung 5V (stabilisiert)
  • Stromaufnahme nur 4mA (typisch)
  • Ultraschallfrequenz 40khz
  • Reichweite 15cm bis 6 Meter
  • Schnittstelle RS232 (TTL) und I2C-Bus
  • Ausgabeeinheit wahlweise mm, inch oder uS
  • Einfachste Verwendung, keine Kalibration/Justierung notwendig, einfach Spannung anlegen und schon kann gemessen werden.
  • Gewicht nur 4,6 g
  • Größe 24mm x 20mm x 17mm
  • Hersteller: Devantech Ltd
  • Distributer: Robotikhardware.de
Ultraschallsensor

Im I²C-Modus wird der Pin Mode nicht beschaltet. Das Modul wurde über die oben beschriebene I²C-Verstärkerplatine an das C-Control Pro Mega 128 Application Board angeschlossen.

Betriebstundenzähler für die Ölheizung

Die Heizungsanlage liefert bei eingeschaltetem Brenner ein 12 V – Signal. Dieses wurde über einen Optokoppler auf den 5 V- Level der C-Control Hardware umgesetzt und mit Port F.4 verbunden. Hier brauchte dann nur noch die Einschaltzeit des Brenners über eine Timer hochgezählt werden.

Temperaturmessung

Für die Messung der Temperatur bietet sich der DS1820 Temperatursensor über den OneWire-Anschluß an. Zunächst wurde ein Sensor zur Erfassung der Außentemperatur installiert.

Auszug aus dem Datenblatt des DS1820:

DS1820 Temperatursensor

Anschlussbelegung:

Anschlussbelegung des Temperatursensors

Hardware

Microcontroller C-Control Pro MEGA 128 Application Board

Als Microcontroller wurde die C-Control Pro MEGA 128 mit Application Board von Conrad eingesetzt. Der Grund hierfür waren vorliegende Erfahrungen aus einem anderen Projekt, sicherlich sind hier auch andere Lösungen möglich. Auf der rechten Seite der Platine wurden im Lochraster¬bereich die Speichereinheit für SD-Karten, der Uhrenbaustein sowie ein SubD Stecker für die Eingangsports installiert. Wichtig ist hier das Entfernen der kaum sichtbaren Verbindungen der äußeren Lochreihen auf der Platine.

C-Control Bauteilseite

C-Control Bestückungsseite

C-Control Lötseite

C-Control Lötseite

Die vier Taster wurden von der Platine entfernt und durch Digitast-Taster ersetzt. Diese wurden auf einer kleinen Lochrasterplatine installiert und im Winkel von 90° an die Basisplatine gelötet. Zur besseren Stabilität wurden die Platinen zusätzlich mit 2-Komponenten Kleber verbunden. Dieser Umbau war erforderlich damit die Bedienbarkeit der Taster beim späteren Einbau in ein Gehäuse gegeben war.

Für die Aufnahme des 4 x 20 LCD-Displays sowie des I²C-Speicherbausteines und des I²C-Verstärkerbausteines wurde eine gedruckte Platine angefertigt. Auf dieser Platine sind auch 6 LEDs angeordnet, hierüber werden z.B. der Impuls des Solarzählers, der Einschaltzustand der Heizung, der Zugriff auf die SD-Card und die Auswahl von 3 möglichen Temperatursensoren angezeigt. Diese Trägerplatine wurde mit Abstandsschrauben auf der Basisplatine der C-Control befestigt.

Die gesamte Elektronik wurde in ein Kunststoffgehäuse mit Klarsichtdeckel eingebaut. Das Gehäuse wurde auf der linken Seite mit einer Aussparung für die Bedien- und Anschlusselemente des Application Board versehen. Im unteren Bereich erfolgte eine Aussparung für die Digitaster und Auf der rechten Seite für den Zugang zur SD-Karte sowie den Stecker der Eingangssignale.

Gesamtansicht des Datenloggers

LCD Display 4 x 20

Der Austausch des LCD-Displays gegen einen Typ mit 4 Zeilen und 20 Zeichen pro Zeile konnte bei gleicher Anschlussbelegung problemlos erfolgen. Hier die Spezifikation des verwendeten Displays:

LCD Display 4 x 20

Zeituhr

Funkuhrmodul DCF77

Für eine genaue Zeitsteuerung wird von Conrad ein DCF77-Funkuhrmodul angeboten. Gegenüber der internen Uhr wäre eine höhere Genauigkeit bei gleichzeitiger automatischer Neujustierung nach einem Stromausfall gegeben.

DCF-Empgänger_01 DCF-Empfänger_02 DCF-Empfänger_03

Die Anschlussbelegung sowie der Schaltplan des DCF77-Funkuhrmoduls gehen aus den vorstehenden Abbildungen hervor. Das Modul wird mit dem nichtinvertierenden Ausgang 3 an den Eingang des Ports angeschlossen. Über eine LED am invertierten Ausgang 4 kann der Impuls des DCF-Signals sichtbar gemacht werden.

Die Inbetriebnahme des DCF-Moduls erwies sich als äußerst problematisch. Hierfür stellten sich letztlich zwei Ursachen heraus:

  • Zunächst war es falsch das DCF-Modul direkt auf dem Application Board zu installieren. Das Modul reagiert sehr empfindlich auf sämtliche Störungen z.B. von PCs, Telefonen und auch wie sich auch zeigte vom Application Board selbst. Abhilfe schafften hier nur der Einbau in einem externen Gehäuse und ein Abstand von mindestens 20 cm zum Application Board.
  • Ebenso wichtig war die Auswahl des Eingangsports für den Anschluss des DCF-Moduls. Standardmäßig ist in der DCF_Lib.cc zwar Port F.0 vorgesehen doch ich hatte mich wegen der günstigeren Lage auf dem Board auf die Ports C.0 – C.7 für meine Eingangsdaten konzentriert. Diese Ports sind jedoch alle noch mit Speicheraufgaben belegt was zwangsläufig zu Störungen führte und kein DCF-Signal detektiert werden konnte. Die Ports F.0 – F.7 erwiesen sich jedoch als sauber und somit konnte der Empfang mit den verfügbaren Beispieldateien hergestellt werden.

Im Zusammenhang mit dem Gesamtprogramm des Datenloggers erwies sich die Funkuhr jedoch nicht als stabil. Offenbar war die Erfassung der Eingangssignale so zeitintensiv, dass es zu Konflikten mit der Software für die Uhr kam. Der Einsatz des DCF-Modules wurde daher wieder verworfen und zunächst auf die interne Uhr zurückgegriffen.

Interne Uhr

Mit dem Einsatz der internen Uhr konnte ein stabiler Betrieb erreicht werden. Das Programm zur Datenerfassung nahm zwar so viel Zeit in Anspruch, dass die Zeit teilweise nur in Sprüngen von mehreren Sekunden angezeigt wurde, insgesamt blieb die Uhr aber im Takt. Die Genauigkeit der internen Uhr wäre somit ausreichend, es war jedoch kein Schutz bei Stromausfall gegeben und die Zeit musste bei jedem Programmstart neu eingegeben werden. Dies war besonders in der Entwicklungsphase sehr lästig und als Dauerlösung war der Einsatz der internen Uhr daher unzureichend. Eine Lösung des Uhrenproblems konnte daher nur in einem externen System liegen, welches neben absoluter Genauigkeit auch batteriegepuffert die Überbrückung eines Stromausfalls gewährleistete.

DS3232 Uhr über I2C-Bus

I²C Uhr Für eine genaue Kontrolle der Uhrzeit bietet sich der Mini DS3232 Uhrenchip an. Neben der absoluten Genauigkeit bietet der Chip eine Batteriepufferung über eine 3 V Knopfzelle an. Der Chip ist als SMD-Bauteil für ca. 10,- € erhältlich, erfordert dann jedoch einen entsprechenden Lötaufwand. Hinzu kommen Kosten für die Knopfzelle und einen entsprechenden Halter. Später habe ich das Ganze als Komplettbaustein fertig montiert auf Platine für 12,90 USD $ im Netz gefunden:

http://www.futurlec.com/Mini_DS3232.shtml

Diese Option ist nicht nur preisgünstiger, sondern stellt auch die bessere Lösung im Hinblick auf den Einbau dar. Die wesentlichen Merkmale des Chips sind in den folgenden Bildern wiedergegeben:

I²C Uhrschaltung

Wie die Abbildungen zeigen ist der Aufwand relativ gering. Das Stellen der Uhr sowie die Abfrage der Uhrzeit erfolgt über den I2C-Bus, die entsprechende Schnittstelle ist auf dem Application Board vorhanden. Aus dem Datenblatt des DS3232 geht das Protokoll für die Ansteuerung hervor, das entsprechende Beispiel ist im Kapitel Software wiedergegeben. Die Uhr wird wie auch die einzelnen Eingangssignale im Rahmen einer Endlosschleife abgefragt, die Uhr selbst belastet somit nicht den Microcontroller und kann unabhängig davon arbeiten.

SD-Card Interface für C-Control Pro

SD-Card Interface Das SD-Card Interface von Conrad für die C-Control Pro wird als Massenspeicher für die aufgezeichneten Daten eingesetzt. Die kleine Platine wurde direkt auf dem Applikation Board installiert und gemäß der mitgelieferten Beschreibung angeschlossen. Die Anschlussbelegung geht aus der folgenden Beschreibung hervor.

Achtung! Der Port B.7 für die LED auf der SD-Card Platine wird auch für das LCD-Display benutzt und kann zu Konflikten führen. Daher wird der Port B.7 nicht für das SD-Card Interface beschaltet, der Zugriff auf die SD-Card kann auch über den Port B.6 und die Power-LED auf der SD-Card Platine oder eine externe LED angezeigt werden.

Anschlüsse für SD-Card Interface

Software


  '      1---------2---------3---------4----------5----------6---------7---------8---------9
  '      Datenlogger für Strom- und Energieverbrauch mit C-Control Pro Mega 128
  '      ======================================================================
  '
  '      Das Gesamtprojekt beinhaltet die Erfassung, Visualisierung und Aufzeichnug von
  '      elektrischen Energieverbräuchen, Temperaturen und Verbrauchsdaten der Ölheizung.
  '
  '      Projektname:         KJS_DataLog.cprj
  '      Aufgabe:             Visualisierung (LCD) und Aufzeichnung (SD-Card) von:
  '                           1. Datum                  DS3232 über I2C-Bus
  '                           2. Uhrzeit                DS3232 über I2C-Bus
  '                           3. Solarenergiezähler     Max7311 über I2C-Bus mit Verstärker
  '                           4. Strombezugszähler      über RS232-Schnittstelle
  '                           5. Stromeinspeisezähler   über RS232-Schnittstelle
  '                           6. Brennerbetriebszeit    über Port F.4
  '                           7. Ölstandsmessung Tank   über I2C-Bus
  '                           8. Temperaturen auslesen  über One Wire Port
  '      Benötigte Libs:      LCD_Lib.cc, IntFunc_Lib.cc
  '      Autor:               Karl-Josef Schneider
  '      Erstelldatum:        12.06.2012
  '      Letzte Bearbeitung:  06.12.2012
  '      Programmtest:        26.11.2012  Programm (Pos.1-5) läuft problemlos ohne Abstürze
  '                           06.12.2012  Pos.6 ergänzt, Datenerfassung und Speicherung OK
  '                           20.12.2012  Pos.7 ergänzt, wenn der Lesebefehl parallel zum
  '                                       Schreiben auf SD-Card erfolgt bleibt das Programm
  '                                       nach dem Schreiben manchmal hängen, daher
  '                           24.12.2012  Lesebefehl bei 15 und 45 min eingefügt
  '                           10.01.2023  Pos.7 Rücksetzbefehl auf 23 Uhr gesetzt
  '                           24.01.2013  Außentemperaturmessung eingefügt
  '      1---------2---------3---------4----------5----------6---------7---------8---------9

  '      Variablen für die DS3232 Uhr
  '      -----------------------------------------------------------------------------------
         Dim Stunde, Minute, Sekunde As Byte          'Zeit definieren
         Dim DoW, Jahr, Monat, Tag As Byte            'Datum definieren
         Dim DaZt(20) As Char                         'Datensatz für Uhr definieren

  '      Variablen für den Max7311 Solarzähler über I2C-Bus
  '      -----------------------------------------------------------------------------------
         Dim MAX7311_SlaveID As Byte                  'Standard I2C-Adresse für MAX7311
         Dim MAX7311_SlaveID_read As Byte             'Leseadresse für MAX 7311
         Dim Code As Byte                             'Code für I2C-Status
         Dim CountHex1 As Byte                        'Wert von Zähler 1 Hexadezimal
         Dim CountDez1 As Byte                        'Wert von Zähler 1 Dezimal
         Dim CountHex2 As Byte                        'Wert von Zähler 2 Hexadezimal
         Dim CountDez2 As Byte                        'Wert von Zähler 2 Dezimal
         Dim Counter As Word                          'Gesamtzähler

  '      Variablen für die Auswertung und Anzeige des Solarzählers
  '      -----------------------------------------------------------------------------------
         Dim Stand As Single                          'tatsächlicher Stand des Solarzählers
         Dim Start As Single                          'ausgelesener Startwert des Zählers
         Dim Zahl As Single                           'ausgelesener aktueller Zählerwert
         Dim Sol As Single                            'aktueller Wert des Solarzählers
         Dim Solges As Single                         'aktueller Gesamtwert des Solarzählers
         Dim Solar(12) As Char                        'Textstring für LCD-Ausgabe Solar
         Dim SolText2(12) As Char                     'Text für Solar, 2 Nachkommastellen
         Dim Solarges(12) As Char                     'Textstring für Solar-Gesamtzähler
         Dim SolSD(10) As Char                        'Textstring Solar gesamt für SD-Card
         Dim SolgesText2(12) As Char                  'Text für Solar-Gesamt, 2 Nachkommast.

  '      Variablen für die Auswertung der IR-Schnittstelle und Anzeige des Stromzählers
  '      -----------------------------------------------------------------------------------
         Dim ser As Byte                              'Nummer der Schnittstelle
         Dim i As Long                                'i als Long definieren
         Dim n As Integer                             'n als Integer definieren
         Dim Bez1 As Single                           'Variable für Bezug 1. Stelle
         Dim Bez2 As Single                           'Variable für Bezug 2. Stelle
         Dim Bez3 As Single                           'Variable für Bezug 3. Stelle
         Dim Bez4 As Single                           'Variable für Bezug 4. Stelle
         Dim Bez5 As Single                           'Variable für Bezug 5. Stelle
         Dim Bez As Single                            'Gesamtvariable für Bezug
         Dim BezText2(10) As Char                     'Text für Bezug, 2 Nachkommastellen
         Dim Bezug(12) As Char                        'Textstring Bezug für LCD-Ausgabeg
         Dim BezSD(10) As Char                        'Textstring Bezug für SD-Card
         Dim Ein1 As Single                           'Variable für Einspeisen 1. Stelle
         Dim Ein2 As Single                           'Variable für Einspeisen 2. Stelle
         Dim Ein3 As Single                           'Variable für Einspeisen 3. Stelle
         Dim Ein4 As Single                           'Variable für Einspeisen 4. Stelle
         Dim Ein5 As Single                           'Variable für Einspeisen 5. Stelle
         Dim Ein As Single                            'Gesamtvariable für Einspeisen
         Dim EinText2(10) As Char                     'Text für Einsp., 2 Nachkommastellen
         Dim Einspeisen(12) As Char                   'Textstring Einspeisen für LCD-Ausgabe
         Dim EinSD(10) As Char                        'Textstring Einspeisen für SD-Card
         Dim Leer(5) As Char                          'Text für Leerzeichen

  '      Variablen für den Betriebsstundenzähler
  '      -----------------------------------------------------------------------------------
         Dim Brennerzustand As Byte                   'Variable für Brennerzustand
         Dim BrDa As Single                           'Brenndauer für 1 Periode [min]
         Dim BrZt As Single                           'Brenner-Einschaltzeit/Tag [h]
         Dim BZ(20) As Char                           'String für Betriebszeit definieren
         Dim BrZtSD(10) As Char                       'Textstring Brennzeit für SD-Card

  '      Variablen für den SFR02 Entfernungsmesser
  '      -----------------------------------------------------------------------------------
         Dim SFR02_SlaveID As Byte                    'Standard I2C-Adresse für SFR02
         Dim SFR02_SlaveID_read As Byte               'Leseadresse für SFR02
         Dim HighByte As Byte                         'High Byte (Register 2)
         Dim LowByte As Byte                          'Low Byte (Register 3)
         Dim Entfernung As Word                       'Entfernung [cm]
         Dim Hoehe As Integer 'Single                          'Füllstand [cm]
         Dim x1,x2,x3,x4 As Single                    'Variablen für Volumenberechnung
         Dim Inhalt As Word                           'Tankinhalt [Liter]
         Dim DaLit(20) As Char                        'Datensatz für Tank definieren
         Dim LitSD(10) As Char                        'Textstring Tankinhalt für SD-Card

  '      Variablen für den Temperatursensor (DS1820 an Port F.5 = 45)
  '      -----------------------------------------------------------------------------------
         Dim WirePort As Byte                         'Eingangsport definieren
         Dim ret, x As Integer                        'Rückgabewert des Befehls
         Dim Temp1 As Single                          'Variable für Temperatur
         Dim TempText1(10) As Char                    'Text Tempratur1 mit 1 Nachkommastelle
         Dim Tp1(15) As Char                          'Textstring Temperatur LCD-Ausgabe
         Dim scratch_pad(9) As Byte                   'Scratch Pad definieren

  '      Variablen für das Beschreiben der SD-Card
  '      -----------------------------------------------------------------------------------
         Dim Daten(100) As Char                       'Datensatz für SD-Karte definieren
         Dim fat(562) As Byte                         'Puffer zur Initialisierung SD-Card
         Dim fil(32) As Byte                          'Variable für den File-Puffer
         Dim Info(4) As Long                          'Variable für Dateiinformationen
         Dim size As Long                             'Speichergröße der SD-Card
         Dim SD As Byte                               'Zählvariable für SD-Karte

  '      Hauptprogramm
  '      ===================================================================================

         Sub main()

  '          Startparameter für Uhr (DS3232 über I2C-Bus) festlegen
  '          -------------------------------------------------------------------------------
  '          Die Uhr kann über das Programm KJS_DS3232 gestellt werden. Da die Uhr batterie-
  '          gepuffert ist, ist hier keine neue Eingabe der Startparameter vorgesehen.

  '          Parameter für MAX7311 Zähler festlegen
  '          -------------------------------------------------------------------------------
             MAX7311_SlaveID = &H22                   'MAX7311 Standard-Adresse = &H22 = 22
             MAX7311_SlaveID_read = MAX7311_SlaveID+1 'Leseadresse für MAX 7311
             I2C_Init(I2C_100kHz)                     'I2C-Bus initialisieren
             MAX7311_lesen()                          'MAX7311 Zähler lesen
             Start = 0 - Zahl                         'Startwert des Zählers ermitteln
  '          ********************                     'Zählerstand des Solarzähler, muß
             Stand = 89179.400000  '23.01.2013 23:00  'beim Neustart des Programms hier von
  '          ********************                     'Hand neu eingetragen werden !!!

  '          Parameter für Energiezähler (IR-Koppler) festlegen
  '          -------------------------------------------------------------------------------
             ser = 1                                  'Nummer der Schnittstelle festlegen
             Serial_Init(ser,SR_8BIT,SR_BD9600)       'Initialisierung der Schnittstelle

  '          Startparameter für Betriebstundenzähler festlegen
  '          -------------------------------------------------------------------------------
             Brennerzustand = 0                       'Brennerzustand bei Start = AUS
             BrDa = 0

  '          Startparameter für SFR02 Entfernungsmesser festlegen
  '          -------------------------------------------------------------------------------
             I2C_Init(I2C_100kHz)                     'I2C-Bus initialisieren
             SFR02_SlaveID = &HE0                     'SFR02 Standard-Adresse = &HE0 = 224
             SFR02_SlaveID_read = SFR02_SlaveID + 1

  '          Startparameter für Temperatursensor (DS1820 an Port F.5 = 45) festlegen
  '          -------------------------------------------------------------------------------
             WirePort = 45                            'Eingangsport festlegen

  '          Startparameter für das Beschreiben der SD-Karte festlegen
  '          -------------------------------------------------------------------------------
             SD = 0                                   'SD Schreiben erlaubt wenn SD = 0

  '          Endlosschleife zur Datenerfassung
  '          -------------------------------------------------------------------------------
             SPI_Disable()                            'SPI-Schnittstelle ausschalten
             LCD_Init()                               'LCD initialisieren
             LCD_ClearLCD()                           'LCD-Display löschen
             LCD_CursorOff()                          'LCD Cursor ausschalten
             I2C_Init(I2C_100kHz)                     'I2C-Bus initialisieren

             Do While 1                               'Endlosschleife

                RS232()                               'Unterprogramm "Stromzähler" aufrufen
                If Sekunde = 40 Then
                   MAX7311_lesen()                    'Unterprogramm "Solarzähler" aufrufen
                End If
                DS3232_Zeit_lesen()                   'Externe DS3232 Uhr lesen
                Brennzeit_lesen()                     'Einschaltzeit des Brenners ermitteln

  '             Datenausgabe:
                Screen_Ausgabe()                      'Daten auf Bildschirm ausgeben
                LCD_Ausgabe()                         'Daten auf LCD-Display ausgeben

  '             Solar-Zähler zurücksetzen:
  '             Da ein sekundengenauer Rücksetzbefehl im Programmablauf nicht immer erkannt
  '             wird, wird während der Minute 0 der Rücksetzbefehl beliebig oft ausgeführt.

                If Stunde = 1 And Minute = 0 Then     'um 1 Uhr nachts bei Minute 0
                   Start = 0 - Zahl                   'Startwert Solarzähler zurücksetzen
                   Stand = Solges                     'Solarzählerstand aktualisieren
                End If

  '             Brenner-Betriebszeit zurücksetzen:
  '             Da ein sekundengenauer Rücksetzbefehl im Programmablauf nicht immer erkannt
  '             wird, wird während der Minute 0 der Rücksetzbefehl mehrfach ausgeführt.

                If Stunde = 23 And Minute = 0 Then    'um 23 Uhr nachts bei Minute = 0
                   If Sekunde < 5 Then                'innerhalb der ersten 5 Sekunden
                      BrZt = 0                        'Brenner-Betriebszeit zurücksetzen
                   End If
                End If

  '             Inhalt des Öltanks ermitteln:
                If Minute = 15 Or Minute = 45 Then    'jede halbe Stunde
                   If Sekunde < 5 Then                'max. 5 Sekunden lang
                      SFR02_lesen()                   'SFR02 Entfernung auslesen
                   End If
                End If

  '             Temperatursensor auslesen:            'im 10-Minuten Takt
                If Minute=7 Or Minute=17 Or Minute=27_
                Or Minute=37 Or Minute=47 Or Minute=57 Then
                   If Sekunde = 0 Then
                      DS1820_Temp_lesen()             'Temperatursensor DS18S20 lesen
                   End If
                End If

  '             Werte auf SD-Karte schreiben:
  '             Da ein sekundengenauer Schreibbefehl im Programmablauf nicht immer erkannt
  '             wird, wird über die Abfrage SD = 0 oder 1 geprüft, ob der Schreibbefehl aus-
  '             geführt wurde. Falls nicht wird innerhalb der gewählten Minute solange der
  '             Schreibbefehl wiederholt, bis die Aktion erfolgreich war.

                If Minute = 0 Or Minute = 30 Then     'jede halbe Stunde
                   If SD = 0 Then                     'wenn Schreiben erlaubt
                      SFR02_lesen()                   'SFR02 Entfernung auslesen
                      SD_schreiben()                  'Daten auf SD-Karte schreiben
                      SD = 1                          'Schreiben gesperrt
                   End If
                End If
                If Minute = 1 Or Minute = 31 Then     'nach Ablauf 1 Min
                   SD = 0                             'Schreiben wieder erlauben
                End If

             End While

         End Sub
  '      ===================================================================================

  '      ***********************************************************************************
  '      Ausgabe der Daten auf dem Bildschirm
  '      ***********************************************************************************

         Sub Screen_Ausgabe()
             Msg_WriteText(DaZt)                      'Datum ausgeben
             Msg_WriteText(" ")                       'Leerzeichen
             Msg_WriteText(Solarges)                  'Solar-Gesamtzähler ausgeben
             Msg_WriteText(" ")                       'Leerzeichen
             Msg_WriteText(Bezug)                     'Bezugszähler ausgeben
             Msg_WriteText(" ")                       'Leerzeichen
             Msg_WriteText(Einspeisen)                'Einspeisezähler ausgeben
             Msg_WriteText(" ")                       'Leerzeichen
             Msg_WriteText(BZ)                        'Betriebszeit ausgeben
             Msg_WriteText(" ")                       'Leerzeichen
             Msg_WriteWord(Inhalt)                    'Inhalt des Öltanks ausgeben
             Msg_WriteText(" Lit")                    '"Liter" hinzufügen
             Msg_WriteText(" ")                       'Leerzeichen
             Msg_WriteText(Tp1)                       'Temperatur 1 ausgeben
             Msg_WriteChar(13)                        'Zeilenvorsprung
         End Sub

  '      ***********************************************************************************
  '      Ausgabe der Daten auf dem LCD-Display
  '      ***********************************************************************************

         Sub LCD_Ausgabe()
             LCD_CursorPos(0x00)                      'Cursor Zeile 1, Position 0
             LCD_WriteText(DaZt)                      'Datum & Zeit ausgeben
             LCD_CursorPos(0x40)                      'Cursor Zeile 2, Position 0
             LCD_WriteText(Solarges)                  'Solar-Gesamtzähler ausgeben
             LCD_CursorPos(0x14)                      'Cursor Zeile 3, Position 0
             LCD_WriteText(Bezug)                     'Bezugszähler ausgeben
             LCD_CursorPos(0x54)                      'Cursor Zeile 4, Position 0
             LCD_WriteText(Einspeisen)                'Einspeisezähler ausgeben
             LCD_CursorOff()
         End Sub

  '      ***********************************************************************************
  '      Zeit des externen Uhrenchips DS3232 über I2C auslesen
  '      ***********************************************************************************

         Sub DS3232_Zeit_lesen()
             Dim TimeArr (7) As Byte
             Dim i As Integer
             I2C_Start()
             I2C_Write(&HD0)                          'DS3232 Schreibadresse = 11010000 = D0
             I2C_Write(&H00)                          'Startadresse für Sekunden
             I2C_Start()                              'Neustart
             I2C_Write(&HD1)                          'DS3232 Leseadresse = 11010001 = D1
             For i = 0 To 5
                 TimeArr (i) = I2C_Read_ACK()
             Next
             TimeArr (6) = I2C_Read_NACK()
             I2C_Stop()

            'Binärzahlen in Dezimalzahlen umwandeln
             Sekunde = (TimeArr (0) And &H0F) +(TimeArr (0) >> 4)*10
             Minute  = (TimeArr (1) And &H0F) +(TimeArr (1) >> 4)*10
             Stunde  = (TimeArr (2) And &H0F) +(TimeArr (2) >> 4)*10
             DoW     = (TimeArr (3) And &H0F) +(TimeArr (3) >> 4)*10
             Tag     = (TimeArr (4) And &H0F) +(TimeArr (4) >> 4)*10
             Monat   = (TimeArr (5) And &H0F) +(TimeArr (5) >> 4)*10
             Jahr    = (TimeArr (6) And &H0F) +(TimeArr (6) >> 4)*10

             DaZt = ""
             Str_WriteWord(Tag,10,DaZt,0,2)              'Tag
             Str_Copy(DaZt,".",STR_APPEND)               'Punkt hinzufügen
             Str_WriteWord(Monat,10,DaZt,STR_APPEND,2)   'Monat hinzufügen
             Str_Copy(DaZt,".20",STR_APPEND)             '".20" hinzufügen
             Str_WriteWord(Jahr,10,DaZt,STR_APPEND,2)    'Jahr hinzufügen
             Str_Copy(DaZt,"  ",STR_APPEND)              '"  " hinzufügen
             Str_WriteWord(Stunde,10,DaZt,STR_APPEND,2)  'Stunde hinzufügen
             Str_Copy(DaZt,":",STR_APPEND)               '"." hinzufügen
             Str_WriteWord(Minute,10,DaZt,STR_APPEND,2)  'Minute hinzufügen
             Str_Copy(DaZt,":",STR_APPEND)               '"." hinzufügen
             Str_WriteWord(Sekunde,10,DaZt,STR_APPEND,2) 'Sek. hinzufügen
         End Sub

  '      ***********************************************************************************
  '      Daten vom Solar-Stromzähler lesen und auswerten
  '      ***********************************************************************************
  '      Der Solarzähler arbeitet mit einer Ferraris-Scheibe. Diese wird mit einer Reflex-
  '      Lichtschranke abgetastet. Die silberne Scheibe reflektiert gut, d.h. am Ausgang
  '      liegen dann +5V an. Bei Durchlauf der roten Markierung ist die Reflektion schlecht,
  '      es entsteht eine negative Flanke, die LED leuchtet und diese Impulse werden gezählt
  '      und als Verbrauchswert aufsummiert. Dies geschieht in einem Zählerbaustein mit
  '      Speicher, der dann im Rahmen der Schleife ausgelesen wird.

  '      Zählerstand des MAX7311 über I2C auslesen
  '      -----------------------------------------------------------------------------------
         Sub MAX7311_lesen()
             I2C_Start()
             I2C_Write(MAX7311_SlaveID)               'MAX7311 Standard-Adresse ansprechen
             I2C_Write(0x00 - 1 + &H01)               'Port Input Register auswählen
             I2C_Start()                              'I2C erneut starten
             I2C_Write(MAX7311_SlaveID_read)          'MAX7311 in Lesebetrieb schalten
             CountHex1=I2C_Read_NACK()                'Byte lesen und von Hex
             CountDez1=(CountHex1 And &H0F) + (CountHex1 >> 4)*16     'in Dez umwandeln
             I2C_Write(0x01 - 1 + &H01)               'Port Input Register auswählen
             I2C_Start()                              'I2C erneut starten
             I2C_Write(MAX7311_SlaveID_read)          'MAX7311 in Lesebetrieb schalten
             CountHex2=I2C_Read_NACK()                'Byte lesen und von Hex
             CountDez2=(CountHex2 And &H0F) + (CountHex2 >> 4)*16     'in Dez umwandeln
             Counter = CountDez2 * 256
             Counter = Counter + CountDez1            'Gesamtzählerstand in Dez
             Zahl = Counter / 75.000000               '75 Impulse/kWh = Zählerstand in kWh
             I2C_Stop()                               'Stop
             If Zahl > 0 Then                         'nur bei realistischem Zählerwert
                Sol = Start + Zahl                    'Solarzähler = Startwert - akt. Wert
                Else
                Sol = Start
             End If
             Solges = Stand + Sol                     'Gesamtwert des Solarzählers

  '          LCD Textstring für Solar-Gesamtzähler erstellen
             Str_WriteFloat(Solges,2,SolgesText2,0)   'Text Solar-Gesamt mit 2 Nachkommast.
             Solarges = ""                            'alten String löschen !! wichtig !!
             If Solges < 100000 Then Leer = "   ":End If   'Leerstellen für rechtsbündige
             If Solges < 10000 Then Leer = "    ":End If   'Schreibweise ermitteln
             If Solges < 1000 Then Leer = "     ":End If
             If Solges < 100 Then Leer = "      ":End If
             If Solges < 10 Then Leer = "       ":End If
             Str_Copy(Solarges,"Solar",STR_APPEND)    'LCD Text-String für Solar erstellen
             Str_Copy(Solarges,Leer,STR_APPEND)       'Leerzeichen hinzufügen
             Str_Copy(Solarges,SolgesText2,STR_APPEND)'Solarwert hinzufügen
             Str_Copy(Solarges," kWh",STR_APPEND)     'Text "kWh" hinzufügen

  '          SD-Card Textstring Solar erstellen
             SolSD=""                                 'alten String löschen
             Str_WriteFloat(Solges,2,SolSD,0)         'Text Solar mit 2 Nachkommastellen
         End Sub

  '      ***********************************************************************************
  '      Lesen der Daten vom IR-Transmitter des Stromzählers über die serielle Schnittstelle
  '      ***********************************************************************************

  '      Programm zum Lesen und Auswerten der seriellen Schnittstelle
  '      -----------------------------------------------------------------------------------
         Sub RS232()

  '          Das HEX-Datentelegramm enthält 356 Zeichen, die nicht benötigten Zeichen werden
  '          zwar gelesen, aber dann übersprungen. Nur die Zählerstände werden ausgegeben.

             For n = 1 To 142                         'für die ersten 142 HEX-Zahlen
                 i = Serial_Read(ser)                 'Lesen der Schnittstelle
             Next

  '          Bei den nächsten 5 Byte folgt der Zählerstand für Bezug
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (10.000-er)
             Bez1 = i * 429496.7296                   'mit Stellen-Faktor multiplizieren
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (1.000-er)
             Bez2 = i * 1677.7216                     'mit Stellen-Faktor multiplizieren
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (100-er)
             Bez3 = i * 6.5536                        'mit Stellen-Faktor multiplizieren
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (10-er)
             Bez4 = i * 0.0256                        'mit Stellen-Faktor multiplizieren
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (1-er)
             Bez5 = i/10000
             For n = 1 To 18                          'für die nächsten 18 HEX-Zahlen
                 i = Serial_Read(ser)                 'Lesen der Schnittstelle
             Next

  '          Folgende 5 Byte enthalten den Einspeise-Zählerstand (5-stellig, 4 Kommastellen)
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (10.000-er)
             Ein1 = i * 42949.67296                   'mit Stellen-Faktor multipliziere
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (1.000-er)
             Ein2 = i * 1677.7216                     'mit Stellen-Faktor multipliziere
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (100-er)
             Ein3 = i * 6.5536                        'mit Stellen-Faktor multipliziere
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (10-er)
             Ein4 = i * 0.0256                        'mit Stellen-Faktor multipliziere
             i = Serial_Read(ser)                     'Lesen der Schnittstelle (1-er)
             Ein5 = i/10000
             For n = 1 To 186                         'für die restlichen 186 HEX-Zahlen
                 i = Serial_Read(ser)                 'Lesen der Schnittstelle
             Next

  '          Die Addition der einzelnen Werte ergibt die Zählerstände in kWh mit den
  '          Nachkommastellen. Durch Umwandlung in einen Text-String können die Nachkomma-
  '          stellen für die Anzeige auf eine definierte Stellenzahl begrenzt werden.

             Bez = (Bez1 + Bez2 + Bez3 + Bez4 + Bez5) 'Addition der Bezugswerte (Float-Zahl)
             Str_WriteFloat(Bez,2,BezText2,0)         'Text für Bezug mit 2 Nachkommastellen
             Bezug=""                                 'alten String löschen !! wichtig !!
             If Bez<100000 Then Leer="   ":End If     'Leerstellen für rechtsbündige
             If Bez<10000 Then Leer="    ":End If     'Schreibweise ermitteln
             If Bez<1000 Then Leer="     ":End If
             If Bez<100 Then Leer="      ":End If
             Str_Copy(Bezug,"Bezug",STR_APPEND)       'LCD Text-String für Bezug erstellen
             Str_Copy(Bezug,Leer,STR_APPEND)          'Leerzeichen hinzufügen
             Str_Copy(Bezug,BezText2,STR_APPEND)      'Bezugswert hinzufügen
             Str_Copy(Bezug," kWh",STR_APPEND)        'Text "kWh" hinzufügen

  '          SD-Card Textstring Bezug erstellen
             BezSD=""                                 'alten String löschen
             Str_WriteFloat(Bez,2,BezSD,0)            'Text Bezug mit 2 Nachkommastellen

             Ein = (Ein1 + Ein2 + Ein3 + Ein4 + Ein5) 'Addition der Einspeisewerte
             Str_WriteFloat(Ein,2,EinText2,0)         'Text Einspeis. mit 2 Nachkommastellen
             Einspeisen=""                            'alten String löschen !! wichtig !!
             If Ein<100000 Then Leer="   ":End If     'Leerstellen für rechtsbündige
             If Ein<10000 Then Leer="    ":End If     'Schreibweise ermitteln
             If Ein<1000 Then Leer="     ":End If
             If Ein<100 Then Leer="      ":End If
             Str_Copy(Einspeisen,"Einsp",STR_APPEND)  'LCD Text-String Einspeisen erstellen
             Str_Copy(Einspeisen,Leer,STR_APPEND)     'Leerzeichen hinzufügen
             Str_Copy(Einspeisen,EinText2,STR_APPEND) 'Einspeisewert hinzufügen
             Str_Copy(Einspeisen," kWh",STR_APPEND)   'Text "kWh" hinzufügen

  '          SD-Card Textstring Einspeisen erstellen
             EinSD=""                                 'alten String löschen
             Str_WriteFloat(Ein,2,EinSD,0)            'Text Einspeisen, 2 Nachkommastellen

         End Sub

  '      ***********************************************************************************
  '      Einschaltzeit des Ölbrenners über Port F.4 (44) ermitteln
  '      ***********************************************************************************

         Sub Brennzeit_lesen()
             Port_DataDirBit(44,0)                    'Port F.4 (44) auf Eingang schalten
             If Brennerzustand = 0 Then               'wenn Brennerzustand = AUS
                If Port_ReadBit(44) = 1 Then          'wenn Brenner eingeschaltet wird
                   Timer_TickCount()                  'Timer starten
                   Brennerzustand = 1                 'Brennerzustand auf EIN setzen
                End If
             End If
             If Brennerzustand = 1 Then               'wenn Brennerzustand = EIN
                If Port_ReadBit(44) = 0 Then          'wenn Brenner ausgeschaltet wird
                   BrDa = Timer_TickCount()/6000.00   'Timer stoppen >> Brenndauer [min]
                   BrZt = BrZt + BrDa/60              'Brennzeit/Tag [h]
                   Brennerzustand = 0                 'Brennerzustand auf AUS setzen
                End If
             End If
             BZ = "Brenner "                          'String Brennzeit erstellen
             Str_WriteFloat(BrZt,2,BZ,STR_APPEND)     'Zeit hinzufügen
             Str_Copy(BZ," h/Tag",STR_APPEND)         '" h/Tag" hinzufügen
  '          SD-Card Textstring Brennzeit erstellen
             BrZtSD=""                                'alten String löschen
             Str_WriteFloat(BrZt,2,BrZtSD,0)          'Text Brennzeit, 2 Nachkommastellen
         End Sub

  '      ***********************************************************************************
  '      SFR02 Ultraschall Entfernungsmesser über I2C auslesen
  '      ***********************************************************************************

         Sub SFR02_lesen()
             I2C_Start()
             I2C_Write(SFR02_SlaveID)                 'SFR02 Standard-Adresse ansprechen
             I2C_Write(0x00)                          'Befehlsregister ansprechen
             I2C_Write(0x51)                          'Startet Messvorgang [cm], Dez = 81
             I2C_Stop()
             AbsDelay(250)                            '250 ms warten auf Messung
             I2C_Start()                              'I2C erneut starten
             I2C_Write(SFR02_SlaveID)                 'SFR02 Standard-Adresse ansprechen
             I2C_Write(0x02)                          'Leseregister festlegen
             I2C_Stop()
             I2C_Start()                              'I2C erneut starten
             I2C_Write(SFR02_SlaveID_read)            'Schnittstelle in Lesebetrieb schalten
             HighByte = I2C_Read_ACK()                'High Byte lesen
             LowByte = I2C_Read_NACK()                'Low Byte lesen
             I2C_Stop()
             Entfernung = HighByte * 256 + LowByte    'Berechnung der Entfernung
             Hoehe = 146 - Entfernung                 'Sensorposition 146 cm über Tankboden

  '          In Abhängigkeit von Tankgeometrie und Füllhöhe wird der Inhalt berechnet
             Select Case Hoehe
                    Case > 140: Inhalt = 5792.0 + (Hoehe - 140) * 26.4
                    Case > 135: Inhalt = 5620.0 + (Hoehe - 135) * 34.4
                    Case > 130: Inhalt = 5432.0 + (Hoehe - 130) * 37.6
                    Case > 125: Inhalt = 5236.0 + (Hoehe - 125) * 39.2
                    Case > 120: Inhalt = 5028.0 + (Hoehe - 120) * 41.6
                    Case > 115: Inhalt = 4812.0 + (Hoehe - 115) * 43.2
                    Case >  35: Inhalt = 1188.0 + (Hoehe -  35) * 45.3
                    Case >  30: Inhalt =  972.0 + (Hoehe -  30) * 43.2
                    Case >  25: Inhalt =  746.0 + (Hoehe -  25) * 41.6
                    Case >  20: Inhalt =  568.0 + (Hoehe -  20) * 39.2
                    Case >  15: Inhalt =  380.0 + (Hoehe -  15) * 37.6
                    Case >  10: Inhalt =  208.0 + (Hoehe -  10) * 34.4
                    Case >   5: Inhalt =   76.0 + (Hoehe -   5) * 26.4
             End Case

             DaLit = ""                                  'alten String löschen
             Str_WriteWord(Hoehe,10,DaLit,0,2)           'Füllstand
             Str_Copy(DaLit," cm",STR_APPEND)            '"cm" hinzufügen
             If Hoehe > 99 Then
                Str_Copy(DaLit,"  ",STR_APPEND)          '2 Leerzeichen hinzufügen
                Else
                Str_Copy(DaLit,"   ",STR_APPEND)         '3 Leerzeichen hinzufügen
             End If
             Str_WriteWord(Inhalt,10,DaLit,STR_APPEND,2) 'Inhalt hinzufügen
             Str_Copy(DaLit," Liter",STR_APPEND)         '"Liter" hinzufügen
  '          SD-Card Textstring Tankinhalt erstellen
             LitSD=""                                    'alten String löschen
             Str_WriteFloat(Inhalt,2,LitSD,0)            'Text Tankinhalt, 2 Kommastellen
         End Sub

  '      ***********************************************************************************
  '      Temperatur des Sensors DS1820 über Port auslesen
  '      ***********************************************************************************

         Sub DS1820_Temp_lesen()
             ret = OneWire_Reset(WirePort)            'Rückgabewert ermitteln
             OneWire_Write(0xcc)   		            'ROM überspringen Kommando
             OneWire_Write(0x44)   		            'Start Temperaturmessung aller Fühler
             AbsDelay(800)                            'Warten bis Temperatur gelesen wurde
  '          Auslesen Außenfühler
             OneWire_Reset(WirePort)                  'Eingangsport OneWire zurücksetzen
             OneWire_Write(0x55)   		            'Fühler empfangsbereit schalten
             OneWire_Write(16)   		                'Adressbyte 0 Fühler 3
             OneWire_Write(115)    	                'Adressbyte 1 Fühler 3
             OneWire_Write(183)  		                'Adressbyte 2 Fühler 3
             OneWire_Write(141)  		                'Adressbyte 3 Fühler 3
             OneWire_Write(2)  		                'Adressbyte 4 Fühler 3
             OneWire_Write(8)   		                'Adressbyte 5 Fühler 3
             OneWire_Write(0)   		                'Adressbyte 6 Fühler 3
             OneWire_Write(89)  		                'Adressbyte 7 Fühler 3
             OneWire_Write(0xbe)  		            'Befehl für scratchpad lesen
             For x = 0 To 9        		            'Komplettes scratchpad lesen
                 scratch_pad(x)= OneWire_Read()
             Next
             Temp1 = (scratch_pad(1)*256+scratch_pad(0))*0.5     'Temperatur berechnen
             Str_WriteFloat(Temp1,1,TempText1,0)      'Text Temperatur mit 1 Nachkommastelle
  '          String für LCD-Ausgabe Temperatur 1
             If Temp1 > -20 And Temp1 < 50 Then       'Meßbereich Temperatur 1 eingrenzen
                Tp1="Temp "                           'String Tp1 erzeugen
                Str_Copy(Tp1,TempText1,STR_APPEND)    'Temperatur hinzufügen
                Str_Copy(Tp1," °C",STR_APPEND)        '" °C" hinzufügen
             End If
         End Sub

  '      ***********************************************************************************
  '      Daten auf SD-Karte schreiben
  '      ***********************************************************************************

  '      Zuerst wird der verfügbare Speicherplatz auf der SD-Karte ermittelt. Anschließend
  '      wird die Datei "Daten.txt" geöffnet bzw. falls nicht vorhanden neu angelegt. In
  '      einem vorgegebenen Zeitintervall werden dann Daten in die Datei geschrieben.

  '      Hauptprogramm zum Beschreiben der SD-Card
  '      ===================================================================================
         Sub SD_schreiben()
             Dim br(1) As Word
             SD_Reset()                               'Sprung zu "SD_Reset"
             SD_Power_On()                            'Sprung zu "SD_Power_On"
             SDC_Init(fat)                            'SD-Karte initialisieren
             SDC_FStat("0:/Daten.txt",Info)           'Dateiinformationen ermitteln
             size=Info(0)                             'Dateigröße = Info(0)

             SDC_FOpen(fil,"0:/Daten.txt",FA_OPEN_ALWAYS) 'Öffnet oder erzeugt Datei
             SDC_FOpen(fil,"0:/Daten.txt",FA_WRITE)       'Schreibfreigabe für Datei

             Daten=""                                  'alten Daten-String löschen
             Str_Copy(Daten,DaZt,STR_APPEND)           'String erstellen mit Datum & Zeit
             Str_Copy(Daten," , ",STR_APPEND)          'Trennzeichen hinzufügen
             Str_Copy(Daten,BezSD,STR_APPEND)          'Bezug hinzufügen
             Str_Copy(Daten," , ",STR_APPEND)          'Trennzeichen hinzufügen
             Str_Copy(Daten,EinSD,STR_APPEND)          'Einspeisung hinzufügen
             Str_Copy(Daten," , ",STR_APPEND)          'Trennzeichen hinzufügen
             Str_Copy(Daten,SolSD,STR_APPEND)          'Solar hinzufügen
             Str_Copy(Daten," , ",STR_APPEND)          'Trennzeichen hinzufügen
             Str_Copy(Daten,BrZtSD,STR_APPEND)         'Brennzeit hinzufügen
             Str_Copy(Daten," , ",STR_APPEND)          'Trennzeichen hinzufügen
             Str_Copy(Daten,LitSD,STR_APPEND)          'Tankinhalt hinzufügen
             Str_Copy(Daten," , ",STR_APPEND)          'Trennzeichen hinzufügen
             Str_Copy(Daten,TempText1,STR_APPEND)      'Temperatur 1 hinzufügen
             Str_Copy(Daten,"%d\r\n",STR_APPEND)       'Zeilenumbruch hinzufügen

             Str_Printf(Daten,Daten,0)                 'String formatieren ???
             SDC_FSeek(fil, size)                      'Schreibposition Festlegen
             SDC_FWrite(fil, Daten, Str_Len(Daten), br)'Schreibt Daten in die Datei
             Msg_WriteText(Daten)
             SDC_FSync(fil)                            'Puffer auf SD-Karte schreiben
             SDC_FClose(fil)                           'Datei schließen
             SD_Power_Off()                            'Sprung zu "SD_Power_Off"
         End Sub
  '      ===================================================================================

  '      Zurücksetzen der SD-Card (Reset Pin EN1 & EN2)
  '      -----------------------------------------------------------------------------------
         Sub SD_Reset()
             Port_DataDirBit(13,1)                    'Port B.5 (13) auf Ausgang schalten
             Port_DataDirBit(14,1)                    'Port B.6 (14) auf Ausgang schalten
             Port_WriteBit(13,1)                      'Port B.5 (13) auf High >> EN1 = +5V
             Port_WriteBit(14,0)                      'Port B.6 (14) auf Low  >> EN2 = GND
             AbsDelay(50)                             'Pause 50 ms
             Port_WriteBit(13,0)                      'Port B.5 (13) auf Low  >> EN1 = GND
             Port_WriteBit(14,1)                      'Port B.6 (14) auf High >> EN2 = +5V
         End Sub

  '      Einschalten des SD-Kartenadapters (Pin EN2 auf +5V setzen)
  '      -----------------------------------------------------------------------------------
         Sub SD_Power_On()
             Port_DataDirBit(14,1)                    'Port B.6 (14) auf Ausgang schalten
             Port_WriteBit(14,1)                      'Port B.6 (14) auf High >> EN2 = +5V
         End Sub

  '      Ausschalten des SD-Kartenadapters
  '      -----------------------------------------------------------------------------------
         Sub SD_Power_Off()
             Port_DataDirBit(14,1)                    'Port B.6 (14) auf Ausgang schalten
             Port_WriteBit(14,0)                      'Port B.6 (14) auf Low >> EN2 = GND
         End Sub

  '      ***********************************************************************************