Arduino Tutorial

Arduino – Saunatimer mit Temperatur-/Feuchteanzeige und Audioausgabe

Moderne Saunen verfügen häufig über umfangreiche Steuerungs- und Informationssysteme. Die folgende Anleitung beschreibt ein Gerät zur Nachrüstung älterer bzw. traditionell ausgestatteter Saunakabinen, das den Saunagast über das Klima innerhalb der Sauna und optisch sowie akustisch über die Dauer des Saunagangs informiert.

Dieser Beitrag wurde – redaktionell überarbeitet – im Rahmen des Leserwettbewerbs im ELV-Journal 03/2014 veröffentlicht.


Features:
Temperatur im Vorraum (optisch)
Temperatur in der Sauna (optisch)
Luftfeuchtigkeit in der Sauna (optisch)
Sanuagangdauer (optisch und alle 5 Minuten akustisch)
Laufindikator des Timers (optisch)
Automatische Zurücksetzung des Timers
Abschaltbare Entspannungsmusik (akustisch)
Statusinformationen (optisch und akustisch)

Dem Projekt liegt die Idee zu Grunde, einen Saunatimer mit den oben genannten Fähigkeiten zu realisieren, der sich ohne große mechanische Eingriffe in die bestehende Sauna-Infrastruktur integrieren lässt und auf teure temperaturresistente Komponenten verzichtet. Daher besteht das Projekt aus einem außen angebrachten Steuergerät, einer abgesetzt von außen an eine Glasscheibe befestigten Anzeige und (nur) einer Lautsprecher- und Sensoreinheit innerhalb der Kabine.

Hardware:


Bauteileliste:
1 Arduino Uno R3
1 ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler, Komplettbausatz
1 ELV MiniWavePlayer MWP1, Komplettbausatz
1 microSD-Karte (eine älterere Karte mit wenig Speicherplatz genügt)
1 Visaton Kleinlautsprecher mit Kunststoffmembran, 5 cm, K 50 FL / 50 Ohm
1 12864 Graphic LCD Display mit seriellem Anschluss (3Wire RSPI)
1 Temperatur-/Luftfeuchtesensor DHT22
2 1Wire Temperatursensoren DS18x20
1 Widerstand 4k7 5%
12 Dioden 1N4148
1 Streifenrasterplatine (einseitig)
1 Kunststoffgehäuse (B 160 H 95 T 55 mm) für das Steuergerät
1 transparentes Kunststoffgehäuse (B 100 H 50 T 25 mm) für die LED-Anzeige
1 Holzgehäuse (Zigarrenkiste) für die Lautsprecher- und Sensoreinheit
1 Steckernetzteil für den Arduino Uno (12V, 1000 mA, innen +)
diverses Stecker-, Leitungs- und Befestigungsmaterial

Schaltplan:

SaunaTimer Schaltplan

Testaufbau:
Der Prototyp wurde mit einer Steckplatine realisiert und der Arduino Uno zu Testzwecken mit einem Sensor Shield versehen.

Sauna003

Dieser Prototyp bot bereits die nahezu volle Funktionalität, die Lautstärke des nicht in ein Gehäuse eingebauten Lautsprechers war jedoch noch viel zu gering für die praktische Anwendung:

Arduino:
Für die Umsetzung der oben genannten Funktionen werden alle 20 Pins des Arduino Uno benötigt. Für weitere Funktionen kann auf einen Arduino mit mehr verfügbaren Pins ausgewichen werden (z.B. Arduino Mega). Dabei ist jedoch zu beachten, dass das verwendete Programm möglicherweise nicht unmittelbar auf anderen Ardunino-Plattformen läuft und angepasst werden muss.

Übersicht der Pinbelegung des Arduino Uno:
ArduninoUNO

PINBelegung
0 (Serial RX)intern belegt
1 (Serial TX)intern belegt
2 (External Interrupt)1Wire-BUS
3 (External Interrupt, PWM)ELV MiniWavePlayer MWP1
Audiokanal 07
(Es sind fünfzehn Miunten vergangen)
4ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler
Taster 1 (Start)
5 (PWM)ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler
Taster 2 (Stopp)
6 (PWM)ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler
Taster 3 (Reset)
7ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler
Taster 4 (Musik an/aus)
8ELV MiniWavePlayer MWP1
Audiokanal 01
(Entspannungsmusik)
9 (PWM)ELV MiniWavePlayer MWP1
Audiokanal 02
(Der Saunagang wurde gestartet, viel Spaß)
10 (PWM)ELV MiniWavePlayer MWP1
Audiokanal 03
(Der Saunagang wurde gestoppt)
11 (PWM)ELV MiniWavePlayer MWP1
Audiokanal 04
(Der Saunatimer wurde zurück gesetzt)
12ELV MiniWavePlayer MWP1
Audiokanal 05
(Es sind fünf Miunten vergangen)
13 (LED)ELV MiniWavePlayer MWP1
Audiokanal 06
(Es sind zehn Miunten vergangen)
14 (A0)Temperatur-/Luftfeuchtesensor DHT22
(Pin als Digitalpin definiert)
15 (A1)12864 Graphic LCD Display mit seriellem Anschluss (RSPI)
clockPin --> SCK(EN)
(Pin als Digitalpin definiert)
16 (A2)12864 Graphic LCD Display mit seriellem Anschluss (RSPI)
latchPin --> CS(RS)
(Pin als Digitalpin definiert)
17 (A3)12864 Graphic LCD Display mit seriellem Anschluss (RSPI)
dataPin --> SID(RW)
(Pin als Digitalpin definiert)
18 (A4)ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler
I2C-BUS - SDA
19 (A5)ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler
I2C-BUS - SCL

Sensoren:
Für Arduninos stehen mehrere Sensortypen zur Verfügung. Neben den hier verglichenen Typen beinhaltet auch der ELV I2C-4-Digit LED Bausatz einen Sensor zur Temperaturmessung. Da dessen Messbereich jedoch nur bis 85 °C reicht, was für die meisten Saunafreunde etwas zu kühl sein dürfte, wurde er am LED-Modul belassen und nicht weiter verwendet. Bei Bedarf können diese Daten für eine Überwachung der Temperatur im Anzeigemodul herangezogen werden.

Der Temperaturmessbereich des Temperatur-/Luftfeuchtesensors DHT22 endet ebenfalls bei nur 80 °C. Dieser wird daher einzig für die Messung der Luftfeuchtigkeit an der Lautsprecher- und Sensoreinheit in der Saunakabine verwendet, die an einer vergleichsweise kühlen Stelle nicht sichtbar unterhalb einer Liege montiert ist.

Sauna015

Zur Temperaturmessung in der Saunakabine und im Vorraum dient jeweils ein günstiger 1Wire-Sensor vom Typ DS18x20, dessen Messbereich bis 125 °C für eine Sauna ausreicht. Die Möglichkeit, diese Sensoren an einem einfachen 1Draht-Bus zu betreiben, ist für den vorliegenden Anwendungsfall ideal.

Einer der beiden DS18x20 (Saunavorraum) ist an der Seite des Steuermodulgehäuses in einer LED-Halterung aus Gummi verbaut.

Sauna017

Der zweite Sensor (Kabine) wurde, ausgehend von der Lautsprecher- und Sensoreinheit, kaum sichtbar in einer Ecke der Saunakabine, etwas unterhalb der Decke angebracht.

Sauna018

Diese Sensoren lassen sich grundsätzlich auch rein parasitär speisen (nur Masse und der Bus werden angeschlossen, der Bus aber über einen 4k7 Pullup-Widerstand mit +5V verbunden). Da zwischen Steuergerät und Kabinensensor mehrere Meter Distanz und vier Schraubverbindungen liegen, wurde die Versorgungsspannung für die Sensoren zur Sicherheit dennoch mitgeführt und mit deren Anschluss VDD verbunden.

Optische Anzeigen:
Für Statusmeldungen am Steuergerät wird ein 12864 Graphic LCD Display mit Hintergrundbeleuchtung und einer Auflösung von 128×64 verwendet, das in vier Zeilen hinreichend große Zeichen darstellt, um diese möglichst auch ohne Lesebrille noch einigermaßen erkennen zu können. Um Pins zu sparen, wird das Display im seriellem „3Wire-Mode“ (Methode 2) betrieben. Details hierzu können im zugehörigen Wiki nachgelesen werden.

Sauna016

Zur Anzeige der Saunagangdauer, der Temperatur und der Luftfeuchtigkeit in der Kabine wird die LED-Anzeige des ELV I2C-4-Digit LED genutzt, die groß und hell genug ist, um sie durch das Gehäuse und eine getönte Scheibe einwandfrei zu erkennen.

Sauna014

Audioausgabe:
Die Ausgabe der Audioansagen ist mit einem ELV Mini Wave Player MWP1, einem einfachen und kostengünstigen Audio-Player, realisiert, der wav-Dateien von einer microSD-Speicherkarte abspielt. Der Player besitzt hierzu vier Auswahleingänge für jeweils einen Kanal. Die jeweilige wav-Datei wird abgespielt, wenn der zugeordnete Eingang mit Masse verbunden wird. Da die Eingänge binär kodiert sind, können bis zu 15 Logikzustände abgefragt und somit über eine Diodenmatrix max. 15 verschiedene Audioausgaben realisiert werden.

Aufgrund des Limits der zur Verfügung stehenden Arduino-Pins werden im Saunatimer lediglich sieben Kanäle wie folgt verwendet:

PinKanalAudioausgabe
81Entspannungsmusik
92Der Saunagang wurde gestartet, viel Spaß
103Der Saunagang wurde gestoppt
114Der Saunatimer wurde zurück gesetzt
125Es sind fünf Miunten vergangen
136Es sind zehn Miunten vergangen
37Es sind fünfzehn Miunten vergangen

Die wav-Dateien können z.B. aufgesprochen oder auch über einen der bekannten Text-to-Speech-Dienste erstellt werden. Die Entspannungsmusik sollte ca. 4:55 min lang sein und am Ende ausgeblendet werden, da nach jeweils 5:00 min eine Zeitansage erfolgt. Nach der Zeitansage wird die Musik erneut gestartet, sofern die Musikausgabe eingeschaltet ist. Als Musiktitel empfiehlt sich ein „Klangteppich“, dessen man auch nach mehrmaligem Hören nicht überdrüssig wird.

Der Player ist über die Datei 000.txt so zu konfigurieren, dass er bereits bei einem kurzen Tastendruck getriggert wird und eine „Retriggerung“ möglich ist (Mode 3):

3 # Trigger mode (0..3)
  #  0:Level triggered
  #  1:Level triggered (sustained)
  #  2:Edge triggered
  #  3:Edge triggered (retriggerable)

Details zur Konfiguration und der „Tasten“-Erweiterung können der dem MWP1-Bausatz beiliegenden Bauanleitung entnommen werden.

Entgegen meiner ursprünglichen Befürchtung, ist die Lautstärke des MWP1 mit dem 5 cm / 50 Ohm Lautsprecher in der „Zigarrenbox“ so hoch, dass ich überlege, noch einen Lautstärkeregler nachzurüsten. Als Lautsprecher wurde ein Variante mit Kunststoffmembran gewählt, da diese den klimatischen Bedingungen in der Sauna auf lange Sicht besser Stand halten dürfte als eine Papiermembran.

Baugruppen:
Steuergerät:
Im Steuergerät sind der MWP1, der Arduino UNO, ein selbstgebauter Arduino Shield mit den Pin-Anschlüssen und dem Pullup-Widerstand (4k7, 5%) für den 1Wire Bus sowie ein weiterer Schield mit der Diodenmatrix verbaut. Aufgrund der Lochrastermaße des Arduino Uno müssen bei Realisierung mit einer einfachen Lochrasterplatine zwei getrennte Shields verwendet werden. Außerdem sollte die masseführende USB-Buchse des Arduino Uno mit einem Stück Isolierband versehen werden, damit sie nicht mit einer der darüber liegenden Dioden in Kontakt kommt. Mit einem kleinen Anschusspanel und dem Vorraum-Temperatursensor DS18x20 an der Seite ist das Steuergerätegehäuse komplett.

Sauna007

Im Deckel des Steuergerätes befinden sich die Platinenhälfte des ELV I2C-4-Digit LED mit den Tasten und das 12864 Graphic LCD Display. Alternativ zu der Darstellung im Schaltplan wurde die Masse für die Tasterplatine nicht mit dem Schield sondern der LCD Anzeige verbunden.

Sauna008

Die Bedienung des Gerätes ist sehr einfach gehalten. Nach der Spannungszufuhr bootet der Arduino und testet die Anzeige. Danach wird das Gerät zurück gesetzt, was auch angesagt wird, und es erscheint in der LCD-Anzeige „Das System ist betriebsbereit“.

Die obere Taste startet den Saunagang. Nach dem Start läuft die Stoppuhr und per Voreinstellung wird eine Entspannungsmusik abgespielt. Mit der unteren Taste kann die Entspannungsmusik ein- und ausgeschaltet werden (toggle). Die Einstellung bleibt auch für den nächsten Start eines Saunagangs erhalten. Falls gewünscht ist, dass die Entspannungsmusik per Voreinstellung nicht abgespielt wird, muss in der Zeile 101 des Sketch als Wert der Variablen „musikflag“ statt „1“ eine „0“ eingetragen werden. Beim Abschalten der Musik wird zur Bestätigung der Kanal 4 „Der Saunatimer wurde zurück gesetzt“ abgespielt. Aufgrund der begrenzten Pin-Verfügbarkeit am Arduino Uno wurde hier auf eine spezielle Ansage verzichtet. Beim Einschalten wird unmittelbar der Kanal 1 „Entspannungsmusik“ getriggert.

Sauna011

Mit der zweiten Taste von oben wird der Saunagang angehalten. Er wird bei erneutem Drücken der oberen Taste dann mit dem gleichen Zählerstand fortgesetzt. Die dritte Taste von oben setzt den Timer zurück. Nach Ablauf von 20 Minuten wird er automatisch zurück gesetzt und steht für den nächsten Saunagang wieder zur Verfügung.

Anzeigemodul:
Im transparenten Anzeigemodul ist die zweite Platinenhälfte (LED-Anzeige) des ELV I2C-4-Digit LED eingebaut.

Sauna010

Lautsprecher- und Sensoreinheit:
Diese Einheit enthält ein Anschlusspanel, den Lautsprecher für die Audioausgabe, den Temperatur-/ Luftfeuchtesensor DHT22 und etwas abgesetzt einen 1Wire Temperatursensor DS18x20 zur Messung der Temperatur in der Kabine.

Sauna012

Spannungsversorgung:
Die Spannung zum Betrieb des ELV I2C-4-Digit LED, des ELV MWP1 und der Sensoren wird am +5V-Pin des Arduino abgegriffen. Daher erfolgt die Spannungsversorgung der gesamten Schaltung über das an den Arduino angeschlossene Steckernetzteil, das nicht zu leistungsschwach gewählt werden sollte. Die maximale Leistungsaufnahme meiner Schaltung liegt bei 3,7 W. Mit einem 12V, 1000mA Netzteil hat man noch gute Reserven. Da der Arduino zum Aussenden von Störungen neigt, sollte man die Zuleitung des Steckernetzteils mit einem Ferritkern versehen.

Software:

Bei der Programmierung des Saunatimers wurde auf einige verbreitete Bibliotheken und Programmschnipsel zurück gegriffen, so wurde z.B. die Stoppuhr Funktionalität aus der Demo Software des ELV I2C-4-Digit LED entnommen.



In der Arduino Software bereits enthaltene Libraries:
Wire.h
stdlib.h




Arduino-Sketch:

Die wesentlichen Funktionen der Software sind in den Kommentaren erläutert.

/******************************************************************************
Sauna - Monitor Version 5.0.2

Software für den Arduino Uno in Verbindung mit folgender Zusatz-Hardware:
- ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler
- 12864 Graphic LCD Display mit seriellem Anschluss (RSPI)
- Temperatur-/Luftfeuchtesensor DHT22
- 1 Wire Temperatursensoren DS18x20
- ELV MiniWavePlayer MWP1

(c) 2015 by Jens-P. Stern, https://smart-wohnen.org
Lizenziert unter einer Creative Commons Namensnennung – Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Ggf. bestehende Lizenzrechte Dritter bleiben hiervon unberührt.
Die Software wird angeboten wie sie ist und ohne Garantien jedweder Art. Der Nutzer übernimmt somit das vollständige Risiko deren Gebrauchs.
Unter keinen Umständen ist der Verfasser für irgendwelche Schäden haftbar, die aus dem Gebrauch, dem Missbrauch oder dem Unvermögen, die Software anzuwenden, entstehen.
Schadenersatz hierfür ist ausgeschlossen.
******************************************************************************/

/******************************************************************************
Übersicht der Pinbelegung des Arduino Uno:

PIN  0 (Serial RX)               - intern belegt
PIN  1 (Serial TX)               - intern belegt
PIN  2 (External Interrupt)      - 1Wire-BUS
PIN  3 (External Interrupt, PWM) - ELV MiniWavePlayer MWP1 - Audiokanal 07 (Es sind fünfzehn Miunten vergangen)
PIN  4                           - ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler - Taster 1 (Start)
PIN  5 (PWM)                     - ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler - Taster 2 (Stopp)
PIN  6 (PWM)                     - ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler - Taster 3 (Reset)
PIN  7                           - ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler - Taster 4 (Musik an/aus)
PIN  8                           - ELV MiniWavePlayer MWP1 - Audiokanal 01 (Entspannungsmusik)
PIN  9 (PWM)                     - ELV MiniWavePlayer MWP1 - Audiokanal 02 (Der Saunagang wurde gestartet, viel Spaß)
PIN 10 (PWM)                     - ELV MiniWavePlayer MWP1 - Audiokanal 03 (Der Saunagang wurde gestoppt)
PIN 11 (PWM)                     - ELV MiniWavePlayer MWP1 - Audiokanal 04 (Der Saunatimer wurde zurück gesetzt)
PIN 12                           - ELV MiniWavePlayer MWP1 - Audiokanal 05 (Es sind fünf Miunten vergangen)
PIN 13 (LED)                     - ELV MiniWavePlayer MWP1 - Audiokanal 06 (Es sind zehn Miunten vergangen)
PIN 14 (A0)                      - Temperatur-/Luftfeuchtesensor DHT22 (als Digitalpin definiert)
PIN 15 (A1)                      - 12864 Graphic LCD Display mit seriellem Anschluss (RSPI) - clockPin --> SCK(EN) (als Digitalpin definiert)
PIN 16 (A2)                      - 12864 Graphic LCD Display mit seriellem Anschluss (RSPI) - latchPin --> CS(RS) (als Digitalpin definiert)
PIN 17 (A3)                      - 12864 Graphic LCD Display mit seriellem Anschluss (RSPI) - dataPin --> SID(RW) (als Digitalpin definiert)
PIN 18 (A4)                      - ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler - I2C-BUS - SDA
PIN 19 (A5)                      - ELV I2C-4-Digit LED Display I2C-4DLED mit Temperaturfühler - I2C-BUS - SCL
******************************************************************************/

/******************************************************************************
Bibliotheken / globale Konstanten / Hardware Initialisierung
******************************************************************************/
#include "Wire.h" // I2C Bibliothek einbeziehen
#include "I2C_4DLED.h" // I2C_4DLED Bibliothek einbeziehen
#include "MCP9801.h" // I2C_4DLED Temperatursensor Bibliothek einbeziehen
#include "LCD12864RSPI.h" // LCD Bibliothek einbeziehen
#include "DFrobot_bmp.h" // LCD Bibliothek einbeziehen (DFrobot Logo)
#include "DFrobot_char.h" // LCD Bibliothek einbeziehen (Zeichensatz)
#include "stdlib.h" // LCD Bibliothek einbeziehen
#include "DHT.h" // DHT22 Bibliothek einbeziehen
#include <OneWire.h> // 1Wire Bibliothek einbeziehen
#include <DallasTemperature.h> // DS18x20 Bibliothek einbeziehen

#define AR_SIZE( a ) sizeof( a ) / sizeof( a[0] ) // 12864 LCD Größe definieren

#define DHTPIN 14     // PIN für den DHT22 definieren
#define DHTTYPE DHT22   // Typ des DHT-Moduls festlegen
DHT dht(DHTPIN, DHTTYPE); // DTH Modul initialisieren

#define ONE_WIRE_BUS 2 // PIN für den 1Wire-Bus (18B20) definieren
#define TEMPERATURE_PRECISION 9 // Messgenauigkeit für den 18B20 definieren
OneWire oneWire(ONE_WIRE_BUS); // Setup einer 1Wire Instanz zur Kommunikation mit allen 1Wire Geräten (nicht nur dieses Herstellers)
DallasTemperature sensors(&oneWire); // Die 1Wire Referenz auf "Dallas Temperature" festlegen

byte saa1064 = 0x70 >> 1; // I2C Bus Adresse für SAA1064 definieren -> SAA1064
const uint8_t i2cAddressSAA1064 = 0x70; // I2C Bus Adresse für SAA1064 definieren -> ELV I2C_4DLED
const uint8_t i2cAddressMCP9801 = 0x90; // I2C Bus Adresse für MCP9801 definieren -> ELV I2C_4DLED

const uint32_t crystalFrequency     = 16000000UL; // Frequenz des Oszillators in Hz -> 16 MHz
const uint8_t timerFrequency        = 1UL; // Frequenz des Timer 1 in Hz -> 10 Hz

const uint8_t pinStartButton        = 4; // PIN für die Taste START definieren
const uint8_t pinStopButton         = 5; // PIN für die Taste STOP definieren
const uint8_t pinResetButton        = 6; // PIN für die Taste RESET definieren
const uint8_t pinMusikButton        = 7; // PIN für die Taste MUSIK definieren

const int audiokanal01    = 8;  // Pinbelegung des Audio-Players (Entspannungsmusik)
const int audiokanal02    = 9;  // Pinbelegung des Audio-Players (Der Saunagang wurde gestartet, viel Spaß)
const int audiokanal03    = 10; // Pinbelegung des Audio-Players (Der Saunagang wurde gestoppt)
const int audiokanal04    = 11; // Pinbelegung des Audio-Players (Der Saunatimer wurde zurück gesetzt)
const int audiokanal05    = 12; // Pinbelegung des Audio-Players (Es sind fünf Miunten vergangen)
const int audiokanal06    = 13; // Pinbelegung des Audio-Players (Es sind zehn Miunten vergangen)
const int audiokanal07    = 3;  // Pinbelegung des Audio-Players (Es sind fünfzehn Miunten vergangen)

/******************************************************************************
globale Variablen
******************************************************************************/
uint8_t running = 0; // Indikator, ob Stoppuhr steht (0) oder läuft (1)
uint16_t time = 0; // vergangene Zeit in 0,1 s Schritten
volatile uint8_t timerflag = 0; // Flag zur Anzeige, dass der Timerinterrupt aktiviert wurde
int32_t I2C_temperatur = 0; // Temperaturwert des ELV I2C_4DLED MCP9801
I2C_4DLED::OutputCurrent outputCurrent = I2C_4DLED::OUTPUT_CURRENT_0_MA; // Ausgangsstrom des SAA1064 (Helligkeit)
int schwitzminuten = 0; // Dauer des Saunagangs
int anzeigezaehler = 0; // Zeitliche Festlegung der Anzeigedauer
int anzeigeflag = 0; // Schritte der Anzeigedauer für die unterschiedlichen Darstellungen
int runningindicator = 0; // Saunauhr an/aus
int musikflag = 1; // Musik an (1)/aus (0) (toggeln)

/******************************************************************************
Funktionen
******************************************************************************/
void timerInit() // Initialisierung Timer 1
{
  TCCR1A = (0 << WGM11) | (0 << WGM10); // Modus 4: CTC
  TCCR1B = (0 << WGM13) | (1 << WGM12); // Modus 4: CTC
  TCNT1  = 0; // Counter zurücksetzen
  TCCR1B |= (1 << CS12) | (0 << CS11) | (0 << CS10); // Prescaler: 256
  OCR1A = (uint16_t)(crystalFrequency / (timerFrequency * 256UL)) - 1; // Output Compare Register für 10 Hz bei einem Prescaler von 256 einstellen
  TIFR1 = (1 << OCF1A); // Compare Match Flag zurücksetzen
  TIMSK1 |= (1 << OCIE1A); // Compare Match Interrupt aktivieren
}

ISR(TIMER1_COMPA_vect) // Interrupt-Routine des Timer 1, welcher im 0,1s-Takt auslöst
{
  timerflag = 1; // Timerflag setzen, so dass die loop-Routine erkennt, dass 0,1 s vergangen sind
}

void clearDisplay() // Alle Digits der LED-Anzeige löschen
{
  Wire.beginTransmission(saa1064);
  Wire.write(1); // instruction byte - erstes Digit ist Digit 1
  Wire.write(0); // digit 1 (rechts)
  Wire.write(0); // digit 2
  Wire.write(0); // digit 3
  Wire.write(0); // digit 4 (links)
  Wire.endTransmission();
}

/******************************************************************************
Setup
******************************************************************************/
void setup()
{
  // Timer initialisieren
  noInterrupts();
  timerInit();
  interrupts();

  Wire.begin(); // I2C-Modul initialisieren
  FourDigitLedDisplay.begin(i2cAddressSAA1064); // 4-Digit-LED-Anzeige mit I2C-Adresse des SAA1064 initialisieren
  FourDigitLedDisplay.setDisplayOutputCurrent(I2C_4DLED::OUTPUT_CURRENT_12_MA); // Helligkeit der 4-Digit-LED-Anzeige einstellen

  TemperatureSensor.begin(i2cAddressMCP9801); // Temperatursensor mit I2C-Adresse des MCP9801 initialisieren
  TemperatureSensor.setADCResolution(TemperatureSensor.RESOLUTION_12BIT); // Temperatursensor auf eine Auflösung von 12 Bit umschalten

  sensors.begin(); // Start der 1Wire Library

  // LCD Display initialisieren und ansteuern
  unsigned char zeile_1_1[]="Sauna - Monitor "; // Text für Zeile 1 (0)
  unsigned char zeile_2_1[]="(c) Jens-P.Stern"; // Text für Zeile 2 (1)
  unsigned char zeile_3_1[]="Initialisierung "; // Text für Zeile 3 (2)
  unsigned char zeile_4_1[]="bitte warten    "; // Text für Zeile 4 (3)
  LCDA.initDriverPin(15,16,17); // Das SPI Interface des LCD Displays initialisieren und die PINs 15, 16 und 17 definieren
  LCDA.Initialise(); // LCD Display initialisieren
  delay(100);
  LCDA.DrawFullScreen(logo); // DFrobot Logo anzeigen
  delay(2000);
  randomSeed(0);
  LCDA.CLEAR(); // LCD Display löschen
  delay(100);
  LCDA.DisplayString(0,0,zeile_1_1,16); // Zeile 1 (0) schreiben
  LCDA.DisplayString(1,0,zeile_2_1,16); // Zeile 2 (1) schreiben
  LCDA.DisplayString(2,0,zeile_3_1,16); // Zeile 3 (2) schreiben
  LCDA.DisplayString(3,0,zeile_4_1,16); // Zeile 4 (3) schreiben

  // Pins für den Audio-Player als Ausgänge setzen
  pinMode(audiokanal01, OUTPUT);
  pinMode(audiokanal02, OUTPUT);
  pinMode(audiokanal03, OUTPUT);
  pinMode(audiokanal04, OUTPUT);
  pinMode(audiokanal05, OUTPUT);
  pinMode(audiokanal06, OUTPUT);
  pinMode(audiokanal07, OUTPUT);

  // Schaltausgänge auf High setzen (+5V)
  digitalWrite(audiokanal01, HIGH);
  digitalWrite(audiokanal02, HIGH);
  digitalWrite(audiokanal03, HIGH);
  digitalWrite(audiokanal04, HIGH);
  digitalWrite(audiokanal05, HIGH);
  digitalWrite(audiokanal06, HIGH);
  digitalWrite(audiokanal07, HIGH);

  // Pins für die Taster als Eingänge setzen
  pinMode(pinStartButton, INPUT);
  pinMode(pinStopButton, INPUT);
  pinMode(pinResetButton, INPUT);
  pinMode(pinMusikButton, INPUT);

  // interne Pullup-Widerstände für die Tastereingänge aktivieren
  digitalWrite(pinStartButton, HIGH);
  digitalWrite(pinStopButton, HIGH);
  digitalWrite(pinResetButton, HIGH);
  digitalWrite(pinMusikButton, HIGH);

  // Segmenttest der LED Anzeige durchführen
  FourDigitLedDisplay.testDisplaySegments(1);
  delay(2000);
  FourDigitLedDisplay.testDisplaySegments(0);

  // LED Anzeige ansteuern
  Wire.beginTransmission(saa1064); // JPS
  Wire.write(1); // instruction byte - erstes Digit ist Digit 1
  Wire.write(B00011111); // J
  Wire.write(B01110011); // P
  Wire.write(B01101101); // S
  Wire.write(B00000000); //
  Wire.endTransmission();
  delay(2000);
  FourDigitLedDisplay.clearDisplay(); // LED Display löschen

  digitalWrite(audiokanal04, LOW); // Audio Mitteilung RESET

  // Variablen zurücksetzen
  running = 0;
  time = 0;
  schwitzminuten = 0;
  runningindicator = 0;

  // LED Anzeige ansteuern
  FourDigitLedDisplay.clearDisplay(); // LED Display löschen
  Wire.beginTransmission(saa1064); // rESt
  Wire.write(1); // instruction byte - erstes Digit ist Digit 1
  Wire.write(B01010000); // digit r
  Wire.write(B01111001); // digit E
  Wire.write(B01101101); // digit S
  Wire.write(B01111000); // digit t
  Wire.endTransmission();
  delay(50);
  digitalWrite(audiokanal04, HIGH);

  // LCD Display ansteuern
  unsigned char zeile_1_2[]="Sauna - Monitor "; // Text für Zeile 1 (0)
  unsigned char zeile_2_2[]="(c) Jens-P.Stern"; // Text für Zeile 2 (1)
  unsigned char zeile_3_2[]="Das System ist  "; // Text für Zeile 3 (2)
  unsigned char zeile_4_2[]="betriebsbereit  "; // Text für Zeile 4 (3)
  LCDA.CLEAR(); // LCD Display löschen
  LCDA.DisplayString(0,0,zeile_1_2,16); // Zeile 1 (0) schreiben
  LCDA.DisplayString(1,0,zeile_2_2,16); // Zeile 2 (1) schreiben
  LCDA.DisplayString(2,0,zeile_3_2,16); // Zeile 3 (2) schreiben
  LCDA.DisplayString(3,0,zeile_4_2,16); // Zeile 4 (3) schreiben
  delay(2000);
}

/******************************************************************************
Loop
******************************************************************************/
void loop()
{
  // Auslesen des DHT22. Das Auslesen der Temperatur und Feuchte kann bis zu 250 ms dauern. Die Werte können bis zu 2 Sek alt sein (sehr langsamer Sensor)
  float DHT22_temperatur = dht.readTemperature(); // Temperatur des DHT22 (diese Daten werden nicht weiter genutzt, da Temperaturmeßbereich nur bis 80° C)
  float DHT22_feuchte = dht.readHumidity(); // Luftfeuchtigkeit des DHT22 (in der Sauna)
  int DHT22_feuchte_innen_int=(int)(DHT22_feuchte+.5); // Float in Integer wandeln mit kaufmännischer Rundung
  char DHT22_feuchte_innen_char[16]; // String Variable anlegen
  sprintf(DHT22_feuchte_innen_char,"%d", DHT22_feuchte_innen_int); // Integer in String wandeln, Text für Zeile 3 (2)

  sensors.requestTemperatures(); // Temperaturen der 1Wire-Sensoren abfragen

  float onewire_sauna_aussen_float = sensors.getTempCByIndex(0); // Erster 1Wire-Sensor (außerhalb der Sauna)
  int onewire_sauna_aussen_int=(int)(onewire_sauna_aussen_float+.5); // Float in Integer wandeln mit kaufmännischer Rundung
  char onewire_sauna_aussen_char[16]; // String Variable anlegen
  sprintf(onewire_sauna_aussen_char,"%d", onewire_sauna_aussen_int); // Integer in String wandeln, Text für Zeile 3 (2)

  float onewire_sauna_innen_float = sensors.getTempCByIndex(1); // Zweiter 1Wire-Sensor (in der Sauna)
  int onewire_sauna_innen_int=(int)(onewire_sauna_innen_float+.5); // Float in Integer wandeln mit kaufmännischer Rundung
  char onewire_sauna_innen_char[16]; // String Variable anlegen
  sprintf(onewire_sauna_innen_char,"%d", onewire_sauna_innen_int); // Integer in String wandeln, Text für Zeile 3 (2)

  // Auslesen des Temperatursensors vom I2C_4DLED in Grad Celsius (der Temperaturwert ist in Feskommadarstellung mit 4 Nachkommastellen gespeichert)
  float I2C_tempanzeige = 1.0; // Temperaturwert des ELV I2C_4DLED MCP9801 (an der LED-Anzeige, diese Daten werden nicht weiter genutzt, können aber später zur Überwachung der Anzeigetemperatur herangezogen werden)
  float I2C_temperatur = TemperatureSensor.readTemperature();
  I2C_tempanzeige = (((I2C_temperatur + 500) / 1000)/10);
  int I2C_sauna_anzeige_int=(int)(I2C_tempanzeige+.5); // Float in Integer wandeln mit kaufmännischer Rundung
  char I2C_sauna_anzeige_char[16]; // String Variable anlegen
  sprintf(I2C_sauna_anzeige_char,"%d", I2C_sauna_anzeige_int); // Integer in String wandeln, Text für Zeile 3 (2)

  // Alle 0,1 s die Zeit inkrementieren, die neue Zeit ausgeben und die Taster auswerten
  if(timerflag == 1)
  {
    timerflag = 0;
    anzeigezaehler++;
    if(anzeigezaehler == 1) // Saunagangdauer (schwitzzeit)
    {
      anzeigeflag = 1;
    }
    if(anzeigezaehler == 15) // "t °C"
    {
      anzeigeflag = 2;
    }
    if(anzeigezaehler == 16)  // Temp Sauna
    {
      anzeigeflag = 3;
    }
    if(anzeigezaehler == 20) // F %
    {
      anzeigeflag = 4;
    }
    if(anzeigezaehler == 21) // Feuchte Sauna
    {
      anzeigeflag = 5;
    }
    if(anzeigezaehler == 25) // ZEIT
    {
      anzeigeflag = 0;
      anzeigezaehler = 0;
    }

    if(running == 1) // die Zeit nur erhöhen, wenn die Stoppuhr aktiviert ist
    {
      time++;
      runningindicator++;
        if(runningindicator > 2)
        {
          runningindicator = 0;
        }
      schwitzminuten = (time/60);
    }

    if (time == 300) // nach 300 Sekunden Audio Ausgabe 5 MINUTEN
       {
       digitalWrite(audiokanal05, LOW);
       delay(50);
       digitalWrite(audiokanal05, HIGH);
       if (musikflag == 1) // Audio Ausgabe ENTPANNUNGSMUSIK, wenn die Musik eingeschaltet ist
       {
       delay(3000);
       digitalWrite(audiokanal01, LOW);
       delay(50);
       digitalWrite(audiokanal01, HIGH);
       }
       }

    if (time == 600) // nach 600 Sekunden Audio Ausgabe 10 MINUTEN
       {
       digitalWrite(audiokanal06, LOW);
       delay(50);
       digitalWrite(audiokanal06, HIGH);
       if (musikflag == 1) // Audio Ausgabe ENTPANNUNGSMUSIK, wenn die Musik eingeschaltet ist
       {
       delay(3000);
       digitalWrite(audiokanal01, LOW);
       delay(50);
       digitalWrite(audiokanal01, HIGH);
       }
       }

    if (time == 900) // nach 900 Sekunden Audio Ausgabe 15 MINUTEN
       {
       digitalWrite(audiokanal07, LOW);
       delay(50);
       digitalWrite(audiokanal07, HIGH);
       if (musikflag == 1) // Audio Ausgabe ENTPANNUNGSMUSIK, wenn die Musik eingeschaltet ist
       {
       delay(3000);
       digitalWrite(audiokanal01, LOW);
       delay(50);
       digitalWrite(audiokanal01, HIGH);
       }
       }

    if(anzeigeflag == 0) // Anzeigeperiode 1
    {
    // LED Anzeige ansteuern
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "ZEIt" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01011011); // digit Z
    Wire.write(B01111001); // digit E
    Wire.write(B00000110); // digit I
    Wire.write(B01111000); // digit t
    Wire.endTransmission();

    if(running == 0) // Sanagang läuft nicht
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Raumtemperatur: "; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Grad Celsius    "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)onewire_sauna_aussen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }

    if(running == 1) // Saunagang läuft
    {
    // LCD Display ansteuern
    char schwitzminuten_char[16]; // String Variable anlegen
    sprintf(schwitzminuten_char,"%d", schwitzminuten); // Integer in String wandeln, Text für Zeile 3 (2)
    unsigned char zeile_1[]="Saunatimer: EIN "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunagangdauer: "; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Minuten         "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)schwitzminuten_char,2); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }
    }

    if(anzeigeflag == 1) // Anzeigeperiode 2
    {
    FourDigitLedDisplay.writeDecimal(schwitzminuten, 0); // Saunagangdauer auf der LED Anzeige ausgeben

    if(running == 0) // Saunagang läuft nicht
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Raumtemperatur: "; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Grad Celsius    "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)onewire_sauna_aussen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben

    Wire.beginTransmission(saa1064); // Laufanzeige (Striche von unten nach oben) auf der LED Anzeige zurücksetzen
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B00000000); // digit _
    Wire.endTransmission();
    }

    if(running == 1) // Saunagang läuft
    {
    // LCD Display ansteuern
    char schwitzminuten_char[16]; // String Variable anlegen
    sprintf(schwitzminuten_char,"%d", schwitzminuten); // Integer in String wandeln, Text für Zeile 3 (2)
    unsigned char zeile_1[]="Saunatimer: EIN "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunagangdauer: "; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Minuten         "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)schwitzminuten_char,2); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben

    // Laufanzeige (Striche von unten nach oben) auf der LED Anzeige ausgeben
    if (runningindicator == 0)
       {
       Wire.beginTransmission(saa1064);
       Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
       Wire.write(B00001000); // digit _
       Wire.endTransmission();
       }
    if (runningindicator == 1)
       {
       Wire.beginTransmission(saa1064);
       Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
       Wire.write(B01000000); // digit _
       Wire.endTransmission();
       }
    if (runningindicator == 2)
       {
       Wire.beginTransmission(saa1064);
       Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
       Wire.write(B00000001); // digit _
       Wire.endTransmission();
       }
    }
    }

    if(anzeigeflag == 2) // Anzeigeperiode 3
    {
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "t °C" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01111000); // digit t
    Wire.write(B00000000); // digit .
    Wire.write(B01100011); // digit °
    Wire.write(B00111001); // digit C
    Wire.endTransmission();

    if(running == 0) // Saunagang läuft nicht
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunatemperatur:"; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Grad Celsius    "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)onewire_sauna_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }

    if(running == 1) // Saunagang läuft
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: EIN"; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunatemperatur:"; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Grad Celsius    "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)onewire_sauna_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }
    }

    if(anzeigeflag == 3) // Anzeigeperiode 4
    {
    FourDigitLedDisplay.writeDecimal(onewire_sauna_innen_int, 0); // Sauna Innentemperatur auf der LED Anzeige ausgeben

    if(running == 0) // Saunagang läuft nicht
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunatemperatur: "; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Grad Celsius    "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)onewire_sauna_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }

    if(running == 1) // Saunagang läuft
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: EIN "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunatemperatur: "; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Grad Celsius    "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)onewire_sauna_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }
    }

    if(anzeigeflag == 4) // Anzeigeperiode 5
    {
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "f°/o" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01110001); // digit f
    Wire.write(B01100011); // digit °
    Wire.write(B01010010); // digit /
    Wire.write(B01011100); // digit o
    Wire.endTransmission();

    if(running == 0) // Saunagang läuft nicht
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunaluftfeuchte"; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Prozent         "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)DHT22_feuchte_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }

    if(running == 1) // Saunagang läuft
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: EIN "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunaluftfeuchte"; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Prozent         "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)DHT22_feuchte_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }
    }

    if(anzeigeflag == 5) // Anzeigeperiode 6
    {
    FourDigitLedDisplay.writeDecimal(DHT22_feuchte_innen_int, 0); // Sauna Innenfeuchte auf der LED Anzeige ausgeben

    if(running == 0) // Saunagang läuft nicht
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunaluftfeuchte"; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Prozent         "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)DHT22_feuchte_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }

    if(running == 1) // Saunagang läuft
    {
    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: EIN "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="Saunaluftfeuchte"; // Text für Zeile 2 (1)
    unsigned char zeile_4[]="Prozent         "; // Text für Zeile 4 (3)
    LCDA.CLEAR(); // LCD Display löschen
    LCDA.DisplayString(0,0,zeile_1,16); //Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(2,0,(unsigned char *)DHT22_feuchte_innen_char,3); // Zeile 2 (1) schreiben (die LCDA Funktion erwartet "unsigned char arrays")
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben
    }
    }

    // Stoppuhr laufen lassen, wenn der Start-Taster gedrückt wurde
    if(digitalRead(pinStartButton) == 0)
    {
    digitalWrite(audiokanal02, LOW); // Audio Ausgabe START
    delay(50);
    digitalWrite(audiokanal02, HIGH);
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "Strt" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01101101); // digit S
    Wire.write(B01111000); // digit t
    Wire.write(B01010000); // digit r
    Wire.write(B01111000); // digit t
    Wire.endTransmission();

    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: EIN "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="                "; // Text für Zeile 2 (1)
    unsigned char zeile_3[]="Saunagang       "; // Text für Zeile 3 (2)
    unsigned char zeile_4[]="gestartet       "; // Text für Zeile 4 (3)
    LCDA.CLEAR();
    LCDA.DisplayString(0,0,zeile_1,16); // Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 2 (1) schreiben
    LCDA.DisplayString(2,0,zeile_3,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben

    if (musikflag == 1) // Audio Ausgabe ENTPANNUNGSMUSIK, wenn die Musik eingeschaltet ist
    {
    delay(4000);
    digitalWrite(audiokanal01, LOW);
    }
    delay(50);
    running = 1;
    digitalWrite(audiokanal01, HIGH);
    }

    // Stoppuhr anhalten, wenn der Stopp-Taster gedrückt wurde
    if(digitalRead(pinStopButton) == 0)
    {
    digitalWrite(audiokanal03, LOW); // Audio Ausgabe STOPP
    running = 0;
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "StoP" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01101101); // digit S
    Wire.write(B01111000); // digit t
    Wire.write(B01011100); // digit o
    Wire.write(B01110011); // digit P
    Wire.endTransmission();

   // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="                "; // Text für Zeile 2 (1)
    unsigned char zeile_3[]="Saunagang       "; // Text für Zeile 3 (2)
    unsigned char zeile_4[]="gestoppt        "; // Text für Zeile 4 (3)
    LCDA.CLEAR();
    LCDA.DisplayString(0,0,zeile_1,16); // Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 2 (1) schreiben
    LCDA.DisplayString(2,0,zeile_3,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben

    delay(4000);
    digitalWrite(audiokanal03, HIGH);
    }

    // Stoppuhr anhalten und zurücksetzen, wenn der Reset-Taster gedrückt wurde oder der Timer bei 1200 (20 Minuten) steht
    if ((digitalRead(pinResetButton) == 0) || (time == 1200))
    {
    digitalWrite(audiokanal04, LOW); // Audio Ausgabe RESET
    running = 0;
    time = 0;
    schwitzminuten = 0;
    runningindicator = 0;
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "rESt" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01010000); // digit r
    Wire.write(B01111001); // digit E
    Wire.write(B01101101); // digit S
    Wire.write(B01111000); // digit t
    Wire.endTransmission();

    // LCD Display ansteuern
    unsigned char zeile_1[]="Saunatimer: AUS "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="                "; // Text für Zeile 2 (1)
    unsigned char zeile_3[]="Saunagang       "; // Text für Zeile 3 (2)
    unsigned char zeile_4[]="zurueck gesetzt "; // Text für Zeile 4 (3)
    LCDA.CLEAR();
    LCDA.DisplayString(0,0,zeile_1,16); // Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 2 (1) schreiben
    LCDA.DisplayString(2,0,zeile_3,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben

    delay(4000);
    digitalWrite(audiokanal04, HIGH);
    }

    // Musik an-/ausschalten (toggeln), wenn der Musik-Taster gedrückt wurde
    if ((digitalRead(pinMusikButton) == 0) && (musikflag == 0))
    {
    musikflag = 1;
    digitalWrite(audiokanal01, LOW);
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "AUon" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01110111); // digit A
    Wire.write(B00111110); // digit U
    Wire.write(B01011100); // digit o
    Wire.write(B01010100); // digit n
    Wire.endTransmission();

    // LCD Display ansteuern
    unsigned char zeile_1[]="Sauna - Monitor "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="                "; // Text für Zeile 2 (1)
    unsigned char zeile_3[]="Musik EIN       "; // Text für Zeile 3 (2)
    unsigned char zeile_4[]="                "; // Text für Zeile 4 (3)
    LCDA.CLEAR();
    LCDA.DisplayString(0,0,zeile_1,16); // Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 2 (1) schreiben
    LCDA.DisplayString(2,0,zeile_3,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben

    delay(2000);
    digitalWrite(audiokanal01, HIGH);
    }

    if ((digitalRead(pinMusikButton) == 0) && (musikflag == 1))
    {
    musikflag = 0;
    digitalWrite(audiokanal04, LOW);
    FourDigitLedDisplay.clearDisplay(); // LED Display löschen
    Wire.beginTransmission(saa1064); // "AUof" auf der LED Anzeige ausgeben
    Wire.write(1); // instruction byte - first digit to control is 1 (right hand side)
    Wire.write(B01110111); // digit A
    Wire.write(B00111110); // digit U
    Wire.write(B01011100); // digit o
    Wire.write(B01110001); // digit f
    Wire.endTransmission();

    // LCD Display ansteuern
    unsigned char zeile_1[]="Sauna - Monitor "; // Text für Zeile 1 (0)
    unsigned char zeile_2[]="                "; // Text für Zeile 2 (1)
    unsigned char zeile_3[]="Musik AUS       "; // Text für Zeile 3 (2)
    unsigned char zeile_4[]="                "; // Text für Zeile 4 (3)
    LCDA.CLEAR();
    LCDA.DisplayString(0,0,zeile_1,16); // Zeile 1 (0) schreiben
    LCDA.DisplayString(1,0,zeile_2,16); // Zeile 2 (1) schreiben
    LCDA.DisplayString(2,0,zeile_3,16); // Zeile 3 (2) schreiben
    LCDA.DisplayString(3,0,zeile_4,16); // Zeile 4 (3) schreiben

    delay(2000);
    digitalWrite(audiokanal04, HIGH);
    }
  }
}

Known Bugs:

Bei Einschalten des Gerätes kommt zunächst die Ansage des Kanals 6 „Es sind zehn Minuten vergangen“, da dieser am PIN13 angeschlossen ist, mit dem auch die interne LED verbunden ist. Anscheinend wird der Pin 13 ganz am Anfang des Initialisierungsvorganges auf LOW gesetzt und triggert damit den angeschlossenen Kanal des MWP1.

Die Temperaturanzeige zeigt manchmal ganz kurz einen negativen Temperaturwert (-12n °C) an. Dies kommt vor, wenn der Arduino vom jeweiligen 1Wire Sensor – warum auch immer – keine Daten empfangen kann und ist ein nicht ganz unbekanntes Phänomen bei diesen Sensoren.

Bei Betätigen der Reset Taste während noch laufender Stoppuhr geht der Timer zuweilen zunächst in den Stopp- respektive Start-Modus. Zur Abhilfe ist die Reset-Taste ist solange gedrückt zu halten oder mehrmals zu drücken, bis die gewünschte Funktion (Stop oder Reset) eingenommen wurde. Manchmal kann das ähnlich auch bei anderen Tasten vorkomen, je nachdem, welches „Zeitfenster man erwischt“. Am besten lässt man den Timer nach dem Saunagang einfach weiterlaufen, dann setzt er sich nach 20 Minuten automatisch zurück und steht für den nächsten Gang zur Verfügung.

Nicht wirklich ein Bug aber möglicherweise dennoch hilfreich: Falls der Sauatimer wiederholt in einer Endlosschleife bootet, kann dies in einem fehlenden oder fehlerhaften Anschluss des Temperatur-/Luftfeuchtesensors DHT22 begründet sein.

Abschließend noch eine Anmerkung zur Genauigkeit des Timers. Nach der Zeitansage wurde eine Verzögerung von jeweils drei Sekunden eingebaut, damit die Entspannungsmusik nicht wieder zu schnell beginnt. Dadurch verzögert sich die Zeit bei eingeschalteter Musik um ca. drei Sekunden je fünf Minuten. Auf die Maximallaufzeit von 20 Minuten hochgerechnet, sind das ca. 12 Sekunden Verzögerung. Wem das zu ungenau ist, kann den Wert der „delay(3000)“-Befehle in den Zeilen 330, 344 und 358 reduzieren oder einfach die Musik abschalten.

Änhnlich verhält sich dies bei den anderen „delay()“-Befehlen, deren Auswirkung z.T. vom Bedienerverhalten abhängen. So verzögert sich z.B. die Laufzeit mit jedem Ein- respektive Abschalten der Musik um zwei Sekunden („delay(2000)“-Befehle in den Zeilen 727 und 755).

Weiterentwicklungspotenzial

Anzeige der Uhrzeit

Aus mehreren Gründen wurde dies hier nicht realisert. Der Timer wird zusammen mit der Saunabeleuchtung ein- und ausgeschaltet und ist daher meist vom Netz getrennt. Hier müsste entweder die Uhrzeit immer manuell eingestellt, gepuffert oder über entsprechend aufwändige technische Lösungen (DCF77-Modul, Netzwerk-Shield etc.) in das System eingebracht werden. Darüber hinaus widerspricht eine Uhr innerhalb der Saunakabine auch ein wenig dem Entspannungsgedanken und ist für mich durchaus entbehrlich.

Ansage, wenn eine bestimmte Sauna-Innentemperatur erreicht ist.

Dies ließe sich bei Bedarf recht einfach realisieren. Mit dem Arduino Uno müsste dann allerdings auf eine andere Ansage verzichtet werden.

Integration in HomeMatic

Mit Hilfe des Projektes „Interface für 1wire, Analog- und Digitalsignale mit Arduino“ sollte es über die USB-Schnittstelle und den CUxD problemlos möglich sein, die Daten des Timers in die HomeMatic zu bekommen und dort weiter zu verwenden. Leider ist bei mir der Weg von der Sauna zur CCU2 etwas zu weit.

Regelung des Saunaofens

Unter Beachtung der einschlägigen Vorschriften kann das Projekt grundsätzlich für eine Regelung des Saunaofens erweitert werden.

Die Schaltpläne und Steckbrettbilder wurden mit Fritzing erstellt.

 

Bitte beachten...

SMART WOHNEN by sTeRn AV ist ein rein privates Projekt. Support kann ich daher nur im Rahmen meiner begrenzten Freizeit leisten, hierfür bitte ich um Verständnis. Meine Hinweise, Anleitungen, Schaltungen und Software werden so angeboten, „wie sie sind“. Die Verwendung erfolgt auf eigenes Risiko, ich übernehme hierfür keinerlei Gewährleistung bzw. Haftung.
Creative Commons Lizenzvertrag
Copyright © Jens-Peter Stern | SMART WOHNEN by sTeRn AV | smart-wohnen.org

Schreibe einen Kommentar

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

* Die Checkbox für die Zustimmung zur Speicherung ist nach DSGVO zwingend.

Ich akzeptiere