{"id":506,"date":"2015-12-05T09:24:07","date_gmt":"2015-12-05T08:24:07","guid":{"rendered":"http:\/\/chriskrz.selfhost.bz\/?p=506"},"modified":"2020-03-22T15:10:17","modified_gmt":"2020-03-22T14:10:17","slug":"iot-heizungssteuerung-1-lcd-panel","status":"publish","type":"post","link":"https:\/\/www.rustimation.eu\/index.php\/iot-heizungssteuerung-1-lcd-panel\/","title":{"rendered":"IoT Heizungssteuerung 1 (LCD Panel)"},"content":{"rendered":"<p>F\u00fcr eine IoT Steuerung gibt es fertige Angebote zu Hauf. Viel mehr Spa\u00df macht es, wenn man das alles selbst entwickelt und so auch volle Kontrolle \u00fcber seine Daten beh\u00e4lt.<\/p>\n<p>Vor einiger Zeit ist meine Heizungssteuerung wegen \u00dcberspannung im Stromnetz \u00fcber den Jordan gegangen &#8211; ein alter Analogrechner, einfach durchgeschmort. Eberhard, der genialische Heizungsmonteur, konnte die Heizung zwar wieder in Gang bringen aber nur mit einer Regelung \u00fcber den Kesselthermostaten. Die Heizung l\u00e4uft vor sich hin und produziert W\u00e4rme, egal, ob diese gebraucht wird oder nicht.<\/p>\n<p>Da die Heizungsanlage sowieso jenseits des \"End of Life\" ist, habe ich zur \u00dcberbr\u00fcckung, bis die neue Anlage kommt, eine Raspberry Pi Steuerung entwickelt, welche die Umlaufpumpe \u00fcber einen Funkschalter nach Bedarf ein- und ausschaltet. Features sind Einstellung und Statusanzeige \u00fcber ein LCD Panel, Nachtabsenkung und <a href=\"http:\/\/www.rustimation.eu\/index.php\/iot-heizungssteuerung-2-web-interface\/\">Fernsteuerung \u00fcber Internet<\/a> &#8211; neudeutsch IoT oder<em> Internet of Things<\/em>.<\/p>\n<p>Diese Anleitung k\u00f6nnt ihr modifiziert f\u00fcr alle m\u00f6glichen anderen Einsatzzwecke verwenden z.B. Klimaanlage, L\u00fcftung, Rollosteuerung etc., das Prinzip ist meist dasselbe.<\/p>\n<p><!--more-->Voraussetzungen: Neben den \u00fcblichen Voraussetzungen in Bezug auf Programmier- und Linux Kenntnisse auf leicht fortgeschrittenem Niveau, etwas handwerklichem Geschick mit dem L\u00f6tkolben und Englischkenntnissen f\u00fcr die Adafruit Anleitungen braucht ihr:<\/p>\n<ul>\n<li>einen Raspberry Pi &#8211; egal welches Modell, ein uralter Raspberry 1 Modell A reicht auch.<\/li>\n<li>Ein aktuelles Raspbian Betriebssystem &#8211; ob Wheezy oder Jessie ist egal.<\/li>\n<li>einen BMP 180 oder BMP 085 Temperatur- und Luftdrucksensor (z.B. bei <a href=\"http:\/\/www.watterott.com\/de\/BMP085-Barometric-Pressure\/Temperature\/Altitude-Sensor-5V\">Watterott.com<\/a>), es geht aber auch ein beliebiger anderer Temperatursensor, wenn ihr wisst, wie der angesteuert wird. Die Luftdruckmessung wird hier nicht ben\u00f6tigt, man k\u00f6nnte die Funktion aber f\u00fcr eine kleine Wetterstation verwenden. Der BMP180 hat den Vorteil Temperaturen auf 0,1 \u00b0C genau messen zu k\u00f6nnen. Einfachere Sensoren, z.B. der DHT11 k\u00f6nnen nur auf ein Grad genau messen.<\/li>\n<li>einen 433MHz Sender (gibt es bei <a href=\"http:\/\/www.watterott.com\/de\/RF-Link-Sender-434MHz\" target=\"_blank\" rel=\"noopener noreferrer\">Watterott<\/a>) sowie eine Schaltsteckdose (m\u00f6glichst von Elro) aus dem Baumarkt , von Amazon oder einem Elektronik Versender.<\/li>\n<li>Ein Adafruit RGB 16&#215;2 LCD+Keypad Kit for Raspberry Pi. RGB muss nicht unbedingt sein, ob die Darstellung positiv oder negativ ist, ist auch egal. Ich empfehle euch, das Teil bei <a href=\"http:\/\/www.watterott.com\/de\/Adafruit-RGB-Positive-16x2-LCDKeypad-Kit-for-Raspberry-Pi\" target=\"_blank\" rel=\"noopener noreferrer\">Watterott <\/a>zu kaufen. Da kommt die Ware schnell und g\u00fcnstig aus Deutschland und nicht aus Fernost. Achtung: Etwas L\u00f6tarbeit ist erforderlich.<\/li>\n<li>optional ein Geh\u00e4use<\/li>\n<li>Und&#8230; nein, ich habe keinerlei Verbindung zu der Firma Watterott. <a href=\"http:\/\/reichelt.de\">Reichelt.de<\/a> oder <a href=\"http:\/\/conrad.de\">conrad.de<\/a> sind z.B. sehr gute Alternativen.<\/li>\n<\/ul>\n<p>Dieses Instructable baut auf den oben erw\u00e4hnten Komponenten auf; es spricht aber nichts dagegen, es mit anderen Bauteilen zu versuchen. Dann m\u00fcsst ihr halt etwas herumprobieren. Der Pi arbeitet \"headless\" im Terminal Modus via Putty oder einem anderen Terminalprogramm und muss nicht an einen Bildschirm angeschlossen sein.<\/p>\n<figure id=\"attachment_424\" aria-describedby=\"caption-attachment-424\" style=\"width: 1012px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2015\/11\/Rustimation_B1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-424\" src=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2015\/11\/Rustimation_B1.jpg\" alt=\"Adafruit 16x4 LCD Panel\" width=\"1012\" height=\"623\" \/><\/a><figcaption id=\"caption-attachment-424\" class=\"wp-caption-text\">Adafruit 16&#215;4 LCD Panel<\/figcaption><\/figure>\n<h2>Messung, Steuerung und Bedienung<\/h2>\n<p>Damit das alles nicht zu schwierig wird gehen wir Schritt f\u00fcr Schritt vor. Als erstes k\u00fcmmern wir uns um die.<\/p>\n<h3>Anzeige im LCD Panel<\/h3>\n<p>F\u00fcr das Panel gibt es eine sehr gut gemachte englische Anleitung bei <a href=\"https:\/\/learn.adafruit.com\/adafruit-16x2-character-lcd-plus-keypad-for-raspberry-pi\/overview\" target=\"_blank\" rel=\"noopener noreferrer\">Adafruit<\/a>. Nachdem ihr das Panel gem\u00e4\u00df Anleitung zusammengel\u00f6tet habt, solltet ihr zuerst einmal das I2C Interface aktivieren. I2C braucht ihr nachher auch, um das BMP180 Modul anzusprechen.<\/p>\n<p>I2C ist eine M\u00f6glichkeit, angeschlossene Peripherie mit nur drei Leitungen anzusteuern, und wird gebraucht um das Adafruit Panel sowie den BMP180 Sensor anzusteuern. Das geschieht \u00fcber <\/p>\n<pre class=\"lang:sh decode:1 inline:1 \" >sudo raspi-config<\/pre>\n<p> . Dort dann die \"<em>Advanced Options<\/em>\" ausw\u00e4hlen und im n\u00e4chsten Screen <em>I2C<\/em> ausw\u00e4hlen und im folgenden Bildschirm die Frage \"<em>Would you like the ARM I2C interface to be enabled?<\/em>\" mit \"JA\" beantworten.<\/p>\n<p>Im n\u00e4chsten Screen mit OK best\u00e4tigen und dann auf die Frage \"<em>Would you like the I2C kernel module to be loaded by default?<\/em>\" ebenfalls mit \"JA\" beantworten. Nochmal best\u00e4tigen und dann mit \"<em>Finish<\/em>\" alles speichern und anschlie\u00dfend neu booten.<\/p>\n<p>Anschlie\u00dfend haltet ihr euch an folgende englischsprachige Anleitung bei Adafruit: <a href=\"https:\/\/learn.adafruit.com\/adafruit-16x2-character-lcd-plus-keypad-for-raspberry-pi\/usage\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/learn.adafruit.com\/adafruit-16&#215;2-character-lcd-plus-keypad-for-raspberry-pi\/usage <\/a><\/p>\n<p>Zum Testen verwendet ihr am besten das im Zuge der Installation heruntergeladene Testprogramm <\/p>\n<pre class=\"lang:default decode:1 inline:1 \" >char_lcd_plate.py<\/pre>\n<p> das ihr wie \u00fcblich mit <\/p>\n<pre class=\"lang:default decode:1 inline:1 \" >chmod +x char_lcd_plate.py<\/pre>\n<p>lauff\u00e4hig machen m\u00fcsst.<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #333333;\">Nochwas: Ich habe das Teil mit Raspbian Wheezy entwickelt. Das hei\u00dft, alle Programme, die den I2C Anschluss oder sonstige GPIO Funktionen des Pi benutzen, m\u00fcssen mit einem <\/p>\n<pre class=\"lang:default decode:1 inline:1 \" >sudo<\/pre>\n<p> vornedran aufgerufen werden. Mit dem neueren Raspbian Jessie ist das nicht mehr erforderlich.<br \/>\n<\/span><\/p>\n<p>Anschlie\u00dfend wird der<\/p>\n<h3>BMP180 Temperatur- und Lufdrucksensor<\/h3>\n<p class=\"headline\">angeschlossen. F\u00fcr den Anfang empfehle ich, das erst einmal auf einem Steckbrett (Breadboad) auszuprobieren. Auch hierf\u00fcr gibt es eine sehr gut gemachte Anleitung bei <a href=\"https:\/\/learn.adafruit.com\/using-the-bmp085-with-raspberry-pi\/overview\">Adafruit<\/a>. Achtet beim Installieren der Software und Treiber darauf, die neueste Library aus dem Abschnitt <em><span id=\"using-the-adafruit-bmp-python-library\"><a href=\"https:\/\/learn.adafruit.com\/using-the-bmp085-with-raspberry-pi\/using-the-adafruit-bmp-python-library\" target=\"_blank\" rel=\"noopener noreferrer\">Using the Adafruit BMP Python Library (Updated)<\/a> <\/span><\/em><span id=\"using-the-adafruit-bmp-python-library\">zu verwenden.<\/span><em><span id=\"using-the-adafruit-bmp-python-library\"><br \/>\n<\/span><\/em><\/p>\n<p class=\"headline\" style=\"padding-left: 30px;\"><span style=\"color: #808080;\">Die Pinbelegung ist gerade f\u00fcr Einsteiger etwas verwirrend, weil sie keinem erkennbaren Schema folgt. Eine gute \u00dcbersicht findet ihr bei<a href=\"http:\/\/elinux.org\/RPi_Low-level_peripherals#Introduction\" target=\"_blank\" rel=\"noopener noreferrer\"> Elinux.org<\/a>. Hier sind die Pins bei allen verschiedenen Varianten A\/B oder A+\/B+ und 2B beschrieben.<\/span><\/p>\n<p class=\"headline\">Die i2C Pinbelegung ist vorgegeben und ist wie folgt:<\/p>\n<figure id=\"attachment_432\" aria-describedby=\"caption-attachment-432\" style=\"width: 198px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2015\/11\/IMG_20151122_155353594.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-432 size-medium\" src=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2015\/11\/IMG_20151122_155353594-e1448725017546-198x300.jpg\" alt=\"BMP180 Luftdruck und Temperatursensor\" width=\"198\" height=\"300\" \/><\/a><figcaption id=\"caption-attachment-432\" class=\"wp-caption-text\">BMP180 Luftdruck und Temperatursensor<\/figcaption><\/figure>\n<ul>\n<li>SDA wird mit Pin 3 verbunden,<\/li>\n<li>SCL mit Pin 5,<\/li>\n<li>GND mit Pin 6 f\u00fcr Masse,<\/li>\n<li>VIN mit Pin 1 &#8211; 3,3 V. Auch wenn der Sensor 5 V vertr\u00e4gt solltet ihr bei 3,3 V Betriebsspannnung bleiben<\/li>\n<\/ul>\n<p>Wir brauchen f\u00fcr sp\u00e4ter ein kleines Pyhton Skript namens<em> get_temperature.py<\/em>, das den Temperatursensor anspricht und die Temperatur ausgibt. Den Luftdruck brauchen wir hier nicht.<\/p>\n<pre class=\"lang:python decode:true\" title=\"get_temperature.py\">#!\/usr\/bin\/python\r\n\r\nimport Adafruit_BMP.BMP085 as BMP085\r\nsensor = BMP085.BMP085()\r\nprint sensor.read_temperature()<\/pre>\n<p>Wenn alles wie gew\u00fcnscht funktioniert, k\u00f6nnt ihr den Sensor direkt mit dem LCD Panel verl\u00f6ten oder per Drahtschlinge drekt auf die GPIO Steckkontakte aufschieben und anschlie\u00dfend das Panel aufstecken.<\/p>\n<h3>433 MHz Sender<\/h3>\n<p>Die Heizungspumpe wird mit einer handels\u00fcblichen 433 MHz Schaltsteckdose ein- und ausgeschaltet.<\/p>\n<p><a href=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2014\/06\/433MHz_Sender.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-96 size-thumbnail\" src=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2014\/06\/433MHz_Sender-150x150.jpg\" alt=\"433MHz_Sender\" width=\"150\" height=\"150\" \/><\/a> Der daf\u00fcr erforderliche Sender h\u00e4ngt am Pi und wird entweder mit den passenden Kontakten auf dem LCD Panel verl\u00f6tet. Bei mir ist das<\/p>\n<ul>\n<li>Pin 17 f\u00fcr 3,3V,<\/li>\n<li>Pin 25 f\u00fcr Masse und<\/li>\n<li>Pin 26 f\u00fcr GPO 7 &#8211; hier k\u00f6nnt ihr auch einen anderen freien Kontakt verwenden und die Anleitung entsprechend ver\u00e4ndern<\/li>\n<\/ul>\n<figure id=\"attachment_430\" aria-describedby=\"caption-attachment-430\" style=\"width: 604px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2015\/11\/IMG_20151122_155329098_HDR.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-430\" src=\"http:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2015\/11\/IMG_20151122_155329098_HDR-1024x575.jpg\" alt=\"GPIO Kontakte f\u00fcr BMP180 und 433MHz Sender\" width=\"604\" height=\"339\" \/><\/a><figcaption id=\"caption-attachment-430\" class=\"wp-caption-text\">GPIO Kontakte f\u00fcr BMP180 und 433MHz Sender<\/figcaption><\/figure>\n<p>Die Ansteuerung des Senders habe ich bereits im <a href=\"http:\/\/www.rustimation.eu\/index.php\/433mhz-schalt-steckdosen-fernsteuern\/\" target=\"_blank\" rel=\"noopener noreferrer\">Beitrag 433 MHZ Schaltsteckdosen fernsteuern<\/a> beschrieben. Wenn ihr diese Anleitung befolgt, kann eigentlich nichts schiefgehen.<\/p>\n<p>Nachdem wir die Hardware entsprechend vorbereit haben, geht es jetzt an die Software:<\/p>\n<h3>Python Steuer- und Bedienlogik<\/h3>\n<p>Das Programm besteht im Wesentlichen aus einer Endlosschleife, in der laufend gecheckt wird, ob eine der Tasten des LCD Panels gedr\u00fcckt wurde, innerhalb dieser Schleife wird in regelm\u00e4\u00dfigen Abst\u00e4nden (timeout) die Temperatur gemessen und bei Bedarf die Schaltsteckdose (de-)aktiviert. Nebenbei wird nat\u00fcrlich die Anzeige im LCD Panel aktualisiert. Obwohl das eigentlich keine besonderen Anforderungen an den Prozessor stellt, liegt die Systemauslastung aufgrund der Endlosschleife bei 40%. Interruptgesteuert w\u00e4re das wesentlich eleganter, aber ich w\u00fcsste nicht, wie man das mit Python und dem Adafruit Panel hinbekommen sollte.<\/p>\n<p>Das Men\u00fc ist 6-stufig und f\u00e4ngt nach Erreichen des letzten Punkts wieder von vorne an:<\/p>\n<ul>\n<li>Standardanzeige (Datum\/Zeit\/Isttemperatur\/Solltemperatur)<\/li>\n<li>Einstellen Solltemperatur Tag<\/li>\n<li>Einstellen Solltemperatur Nacht<\/li>\n<li>Einstellen Nachtabsenkung von<\/li>\n<li>Einstellen Nachtabsenkung bis<\/li>\n<li>Shutdown (zum sauberen Herunterfahren des Pi)<\/li>\n<\/ul>\n<p>Der Programmablauf ist wie folgt strukturiert:<\/p>\n<pre class=\"width:750 nums:false nums-toggle:false wrap-toggle:false lang:default highlight:0 decode:true\">Laden der Bibliotheken\r\nInitialisieren der Variablen\r\nFunktionsblock f\u00fcr Cursortasten\r\nEndlosschleife\r\n    Abfrage ob Taste gedr\u00fcckt wurde \r\n        rechts\/links schaltet durch die Men\u00fcs\r\n        Tastendruck oben\/unten bedeutet Wert\u00e4nderung. \r\n            abh\u00e4ngig von Men\u00fcposition Temperatur, Zeit \u00e4ndern bzw. Shutdown einstellen\r\n        Tastendruck oben\/unten wird zwischengespeichert\r\n        Select Taste speichert Werte in settings.dat\r\n        Anzeige entsprechend Men\u00fcposition einstellen\r\n    Wenn Timeout erreicht (alle 60 Sekunden)\r\n        Einstellungen aus Datei settings.dat lesen\r\n            (diese Datei wird auch von der Web Applikation beschrieben)\r\n        Temperatur messen\r\n        Check ob Nachtabsenkungszeitraum - Temperaturbereich Tag\/Nacht verwenden\r\n        Falls Temperatur zu niedrig: Pumpe an\r\n        Falls Temperatur zu hoch: Pumpe aus<\/pre>\n<p>Hier das ganze Programm namens<em> lcd_menu_integrated.py<\/em>. Nicht vergessen, das Ganze mit<\/p>\n<pre class=\"lang:default decode:1 inline:1 \" >chmod +x lcd_menu_integrated.py<\/pre>\n<p> ausf\u00fchrbar zu machen:<\/p>\n<pre class=\"height-set:true width-set:true width:750 lang:python decode:true\" title=\"lcd_menu_integrated.py\">#!\/usr\/bin\/python\r\n#coding=UTF-8\r\nimport math\r\nimport time\r\nimport string\r\nimport Adafruit_CharLCD as LCD\r\nimport Adafruit_BMP.BMP085 as BMP085\r\nfrom elropi import RemoteSwitch\r\nimport os\r\nimport logging\r\n\r\n#logging\r\nlogging.basicConfig(filename='\/var\/log\/heizung.log',level='INFO',format='%(asctime)s %(message)s',filemode='a')\r\n\r\n##############INITS##################################\r\n# ELRO constants\r\n# change device number according to your requirements\r\ndeviceno=1\r\n# Change the key[] variable below according to the dipswitches on your Elro receivers.\r\ndefault_key = [1,0,0,0,1]\r\n# change the GPIO pin according to your wiring\r\ndefault_pin =7\r\n# set parameters for class elropi.py\r\ndevice = RemoteSwitch(device=deviceno,key=default_key,pin=default_pin)\r\n\r\n# constants\r\nhystoff = 0.1  # Lower hysteresis: temperature must be desiredTemp+hysoff to turn pump off \r\nhyston = 0.1   # Upper hysteresis: temperature must be desiredTemp+hysoff to turn pump n\r\nmenu = 0       # Default display: Date-Time\/actual temperature - set temperature\r\ntimeout_start = time.time()  # initialize timeout for main loop\r\nbacklite_start = time.time() # initialize timeout for backlite\r\ntimeout = 60   # Display refresh and temperature measurement every xxx seconds \r\nbacklite_timeout = 30 # timeout for backlight\r\nmenumax = 5    # maximum number of menue-items\r\nstatsign = \"---\" # status sign will be &gt;&gt;&gt; when pump is on\r\nudaction = \"\"    # init up down keys\r\nlcd_ebene0 = \"xx\" # some value to force new display on startup\r\ndisplay = \"yy\"     # some value to force new display on startup\r\nprev_display = \"\" # init display comparison - forces new display\r\nshutdown = False  # init shutdown value\r\npump_status = False # init pump status\r\nsantosubito = True # run timer logic at once without waiting for timeout\r\n\r\n#write pump status to scratch to ensure correct status display in web app\r\nstatusfile=open(\"\/tmp\/status.dat\",\"w\")\r\nstatusfile.write(\"AUS\")\r\nstatusfile.close()\r\n\r\n# Initialize the LCD using the pins \r\nlcd = LCD.Adafruit_CharLCDPlate()\r\nlcd.set_color(1,1,1) # white backlite\r\n\r\n#initialize BMP085\r\nsensor = BMP085.BMP085()\r\n\r\n# create custom characters\r\nlcd.create_char(1, [7, 5, 7, 0, 0, 0, 0, 0])    # Degree sign\r\n\r\nlcd.clear()\r\nlcd.message(\"Start...\")\r\n\r\n####LCD menu structure\r\n# rechts\/links Schleife \u00fcber Basis\/Tageinstellung\/Temp\/Nacht Beginn\/Nacht Ende\/Aus-Ein\r\n# 0. Basis Anzeige:\r\n   # 22:15 23.05.2015\r\n   # 20,2\u00b0C&gt;&gt;&gt;22,5\u00b0C\r\n# 1. Temp oben\/unten Solltemp +\/- 0,5\u00b0\r\n   # Anzeige: Z1: \"Tagtemperatur:\" Z2: \"22,5\u00b0C\"\r\n   # Enter speichert\r\n# 2. Temp oben\/unten Solltemp +\/- 0,5\u00b0\r\n   # Anzeige: Z1: \"Nachttemperatur:\" Z2: \"18,5\u00b0C\"\r\n   # Enter speichert\r\n# 3. oben\/unten Zeit +\/- 10Min Schritten Start NA\r\n   # Anzeige: Z1: \"Nacht Beginn:\" \/ Z2: \"22:15\"\r\n   # enter speichert\r\n# 4. oben\/unten Zeit +\/- 10Min Schritten Ende NA\r\n   # Anzeige: Z1: \"Nacht Ende:\" \/ Z2: \"06:15\"\r\n   # Enter speichert\r\n# 5. pi runterfahren\r\n   # Anzeige \"Stop Pi\"\r\n   # Enter f\u00fchrt aus\r\n#############FUNCTIONS#############################\r\n\r\ndef pretty_time(): # used to display date and time in human readable format\r\n    act_time = time.localtime()\r\n    year, month, day, hour, minute, second = act_time[0:6]\r\n    pretty_time=\"%02i.%02i.%04i-%02i:%02i\" %(day, month, year, hour, minute)\r\n    return pretty_time\r\n\r\ndef change_temp(dir, temp): # increase\/decrease temperature setting\r\n    if dir == \"u\":\r\n        temp = temp + 0.5\r\n    elif dir == \"d\":\r\n        temp = temp - 0.5\r\n    return temp\r\n    \r\ndef change_time(dir, ctime): # increase\/decrease set time\r\n    ct = string.split(ctime,\":\")\r\n    ctimehour = int(ct[0])\r\n    ctimemin = int(ct[1])\r\n    if dir == \"u\":\r\n        ctimemin = ctimemin + 10\r\n        if ctimemin &gt; 50:  #carry forward full 60 minutes = + 1 hour, 0 mins\r\n            ctimemin = 0\r\n            ctimehour += 1\r\n            if ctimehour &gt; 23: # carry forward 24 hours = 0 hours\r\n                ctimehour = 0\r\n    elif dir == \"d\":\r\n        ctimemin = ctimemin - 10 \r\n        if ctimemin &lt; 0: #carry forward -10 minutes = - 1 hour, 0 mins\r\n            ctimemin = 50\r\n            ctimehour -= 1\r\n            if ctimehour &lt; 0:\r\n                ctimehour = 23\r\n    if ctimemin == 0: # ensure double digit minutes (as string)\r\n        ctimeminstr=\"00\"\r\n    else:\r\n        ctimeminstr=str(ctimemin) # convert integer minute to string\r\n    ctime=str(ctimehour) + \":\" + ctimeminstr\r\n    return ctime  \r\n    \r\ndef change_shutdown(tf): # shutdown selection\r\n    if tf == True:\r\n        tf = False\r\n    elif tf == False:\r\n        tf = True\r\n    return tf       \r\n\r\n\r\n #\r\n\r\n##################################################################################################\r\n############ Endless loop                                                                 ########\r\n############ Cycle through each button and check if it was pressed                        ########\r\n############ check timer event, read settings, compare, turn on\/off, create default output########\r\n############ then perform action depending on button and menu position                    ########\r\n##################################################################################################\r\n\r\nwhile True:\r\n    ### Menu handling\r\n    if lcd.is_pressed(LCD.RIGHT): #Move to next menu\r\n        time.sleep(0.2)\r\n        backlite_start=time.time()\r\n        lcd.set_color(1,1,1)\r\n        menu += 1\r\n        if menu&gt;menumax:\r\n            menu=0\r\n            #time.sleep(1)\r\n           \r\n    elif lcd.is_pressed(LCD.UP): # store action \"up\"\r\n        time.sleep(0.2)\r\n        lcd.set_color(1,1,1)\r\n        backlite_start=time.time()\r\n        udaction = \"u\"\r\n\r\n    elif lcd.is_pressed(LCD.DOWN): # store action \"down\"\r\n        time.sleep(0.2)\r\n        backlite_start=time.time()\r\n        lcd.set_color(1,1,1)\r\n        udaction = \"d\"\r\n        \r\n    elif lcd.is_pressed(LCD.LEFT): # move to previous menu\r\n        time.sleep(0.2)\r\n        backlite_start=time.time()\r\n        lcd.set_color(1,1,1)\r\n        menu -= 1\r\n        if menu &lt; 0: \r\n            menu=menumax\r\n      \r\n    elif lcd.is_pressed(LCD.SELECT): # execute. Save settings or shutdown\r\n        time.sleep(0.2)\r\n        backlite_start=time.time()\r\n        lcd.set_color(1,1,1)\r\n        if 0 &lt; menu &lt; 5: # save settings and return to main display (menu0)\r\n            settings = open(\"\/var\/www\/settings.dat\",\"w\") \r\n            settings.writelines(nstart)\r\n            settings.writelines(nend)\r\n            settings.write(str(dtemp) + \"\\n\")\r\n            settings.write(str(ntemp) + \"\\n\")\r\n            settings.write(onoff)\r\n            settings.close()            \r\n            lcd.clear()\r\n            lcd.message(\"Werte\\ngespeichert\")\r\n            time.sleep(1)\r\n            menu = 0\r\n            santosubito = True\r\n        if menu == 5:\r\n            if shutdown: # shutdown\r\n                lcd.clear()\r\n                lcd.message(\"BYE! Wait 30sec\\nthen disconnect\")\r\n                for x in range(0,3):\r\n                    device.switchOff()\r\n                    time.sleep(2)\r\n                os.system(\"sudo halt\")\r\n            else:\r\n                menu = 0\r\n                santosubito = True\r\n\r\n    ### Main display menu 0    \r\n    if menu == 0:\r\n        if lcd_ebene0 &lt;&gt; prev_display: # update display only if changed; this \r\n            lcd.clear()                # avoids display flicker caused by \r\n            lcd.message(lcd_ebene0)    # unnecessary updates \r\n            prev_display = lcd_ebene0  # repeated for every menu item \r\n        \r\n    ###set day temperature\r\n    if menu == 1:    \r\n        display = 'Solltemperatur\\nTag:' + str(dtemp) +'\\x01C'    \r\n        if display &lt;&gt; prev_display: # update display only if changed\r\n            lcd.clear()\r\n            lcd.message(display)\r\n            prev_display = display\r\n        if udaction &lt;&gt; \"\":\r\n            dtemp = change_temp(udaction,dtemp)\r\n            udaction = \"\"\r\n            \r\n    ###set night temperature  \r\n    if menu == 2:\r\n        display = 'Solltemperatur\\nNacht:' + str(ntemp) +'\\x01C'\r\n        if display &lt;&gt; prev_display: # update display only if changed\r\n            lcd.clear()\r\n            lcd.message(display)\r\n            prev_display=display\r\n        if udaction &lt;&gt; \"\":\r\n            ntemp = change_temp(udaction,ntemp)\r\n            udaction = \"\"\r\n    \r\n    ### set night start\r\n    if menu == 3:\r\n        display = 'Nachtabsenkung\\nvon ' + nstart.rstrip()+' Uhr'\r\n        if display &lt;&gt; prev_display: # update display only if changed\r\n            lcd.clear()\r\n            lcd.message(display)\r\n            prev_display = display\r\n        if udaction &lt;&gt; \"\":\r\n            nstart=change_time(udaction,nstart)\r\n            udaction = \"\"\r\n\r\n     ### set night end\r\n    if menu == 4:\r\n        display = 'Nachtabsenkung\\nbis ' + nend.rstrip()+' Uhr'\r\n        if display &lt;&gt; prev_display: # update display only if changed\r\n            lcd.clear()\r\n            lcd.message(display)\r\n            prev_display = display\r\n        if udaction &lt;&gt; \"\":\r\n            nend=change_time(udaction,nend)\r\n            udaction = \"\"     \r\n      \r\n    ### set shutdown\r\n    if menu == 5:\r\n        if shutdown:\r\n            display = 'SHUTDOWN?\\nJA'\r\n        else:\r\n            display = 'SHUTDOWN?\\nNEIN'\r\n        if display &lt;&gt; prev_display: # update display only if changed\r\n            lcd.clear()\r\n            lcd.message(display)\r\n            prev_display = display\r\n        if udaction &lt;&gt; \"\":\r\n            shutdown = change_shutdown(shutdown)\r\n            udaction = \"\"        \r\n            \r\n    #display timeout - reset to default menu after given time w\/o input\r\n    if time.time() - backlite_start &gt; backlite_timeout:\r\n        lcd.set_color(0,0,0) # backlite off\r\n        menu = 0\r\n        shutdown = False             # just to go sure...\r\n        santosubito = True           # calculate default display right away\r\n        backlite_start = time.time() # reset backlite timer\r\n        \r\n    #####################################################\r\n    ################Timerloop############################       \r\n    #####################################################\r\n    \r\n    # reads settings (temperature and time profile)\r\n    # reads temperature sensor\r\n    # turns on or off pump depending on thresholds\r\n    # constructs LCD output for default screen\r\n\r\n    if time.time() - timeout_start &gt; timeout or santosubito: \r\n    # read settings file\r\n        try:\r\n            settings = open(\"\/var\/www\/settings.dat\",\"r\")\r\n            nstart = settings.readline()\r\n            nend = settings.readline()\r\n            dtemp = float(settings.readline())\r\n            ntemp = float(settings.readline())\r\n            onoff = settings.readline()\r\n            onoff = onoff.rstrip()\r\n            settings.close()\r\n        except IOError: # if settings don't exist, take default values\r\n            nstart = \"22:00\"\r\n            nend = \"06:00\"\r\n            dtemp = 21.00\r\n            ntemp = 18.00\r\n            onoff = \"AN\"\r\n\r\n        # Time handling\r\n        # time constants\r\n        act_time = time.localtime()\r\n        year, month, day, hour, minute, second = act_time[0:6]\r\n        now = time.mktime(act_time) # \"now\" has float format\r\n        zerotime = year, month, day, 0, 0, 0, 0, 0, 0  \r\n        zerotime=time.mktime(zerotime)\r\n\r\n        # convert night time settings resulting in seconds since midnight of the active day\r\n        starttime = string.split(nstart,\":\")\r\n        nstarthour = int(starttime[0])\r\n        nstartmin = int(starttime[1])\r\n        nstarttime = int(nstarthour) * 3600 + int(nstartmin) * 60\r\n        endtime = string.split(nend,\":\")\r\n        nendhour = int(endtime[0])\r\n        nendmin = int(endtime[1])\r\n        nendtime = int(nendhour) * 3600 + int(nendmin) * 60\r\n\r\n        nstarttime=nstarttime+zerotime # epoch of night setting start\r\n        nendtime=nendtime+zerotime     # epoch of night setting end  \r\n\r\n        if nstarttime &gt; now &gt; nendtime:  \r\n            print \"Day\"\r\n            set_temp = dtemp\r\n        else:\r\n            print \"Night\"\r\n            set_temp = ntemp         \r\n        \r\n        # read temperature from BMP180 \r\n        act_temp = float(sensor.read_temperature())\r\n\r\n        # compare actual temperature with set temperature and change status if necessary\r\n        if (act_temp &gt; set_temp + hystoff) and pump_status:\r\n            print \"turning off pump\"\r\n            lcd.set_color(0,0,1)  # show blue backlite\r\n            for x in range(0,3):  # to go sure the ELRO switch is triggered three times\r\n                device.switchOff()\r\n                time.sleep(5)\r\n            pump_status = False\r\n            statusfile = open(\"\/tmp\/status.dat\",\"w\") # needed for web interface status display\r\n            statusfile.write(\"AUS\")\r\n            statusfile.close()\r\n            logging.info('Pump OFF ' + str(act_temp))\r\n        elif act_temp &lt; set_temp - hyston and not pump_status:\r\n            print \"turning on pump\"\r\n            lcd.set_color(1,0,0) # show red backlite\r\n            for x in range(0,3):   #to go sure the ELRO switch is triggered three times\r\n                device.switchOn()\r\n                time.sleep(5)\r\n            pump_status = True\r\n            statusfile=open(\"\/tmp\/status.dat\",\"w\")\r\n            statusfile.write(\"AN\")\r\n            statusfile.close()\r\n            logging.info('Pump ON ' + str(act_temp))\r\n\r\n        #construct lcd output\r\n        if not pump_status:\r\n            statsign = \"---\"\r\n        else:\r\n            statsign = \"&gt;&gt;&gt;\"\r\n        \r\n        lcd_ebene0 = pretty_time() + '\\n' + str(act_temp) + '\\x01C' + statsign + str(set_temp) +'\\x01C'\r\n        #print lcd_ebene0\r\n        santosubito = False         # reset forced timeloop\r\n        timeout_start = time.time() # reset timeout\r\n        \r\n\t########################################################################\r\n\t####################### End timer Loop #################################\r\n\t########################################################################\r\n\r\n\r\n<\/pre>\n<p>Die Datei settings.dat wird in \/var\/www gespeichert, da dort das <a href=\"http:\/\/www.rustimation.eu\/index.php\/iot-heizungssteuerung-2-web-interface\/\">Webinterface<\/a> liegt, mit dem wir unsere Heizung auch von unterwegs steuern k\u00f6nnen. Aus Sicherheitsgr\u00fcnden darf der vom Webserver verwendete User &#8211; bei mir ist das www-data &#8211; nicht in Dateien au\u00dferhalb seines Verzeichnisses hinein schreiben.<\/p>\n<p>Die Datei status.dat wird in das Verzeichnis \/tmp geschrieben, da sie nur zur Laufzeit ben\u00f6tigt wird. im Beitrag <a href=\"https:\/\/www.rustimation.eu\/index.php\/sd-karten-verschleiss-vermeiden\/\" target=\"_blank\" rel=\"noopener noreferrer\">SD-Karten Verschlei\u00df vermeiden<\/a> erkl\u00e4re ich noch, wie man sich h\u00e4ufig \u00e4ndernde Dateien und Logs ins RAM anstatt auf unsere \"Festplatte\", die SD-Karte schreibt.<\/p>\n<p>Bei Fragen zum Programm einfach die Kommentarfunktion verwenden.<\/p>\n<p>Das <a href=\"http:\/\/www.rustimation.eu\/index.php\/iot-heizungssteuerung-2-web-interface\/\">Webinterface<\/a> steht aus Performancegr\u00fcnden und der \u00dcbersichtlichkeit wegen in einem <a href=\"http:\/\/www.rustimation.eu\/index.php\/iot-heizungssteuerung-2-web-interface\/\">eigenen Beitrag<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>F\u00fcr eine IoT Steuerung gibt es fertige Angebote zu Hauf. Viel mehr Spa\u00df macht es, wenn man das alles selbst entwickelt und so auch volle Kontrolle \u00fcber seine Daten beh\u00e4lt. Vor einiger Zeit ist meine Heizungssteuerung wegen \u00dcberspannung im Stromnetz \u00fcber den Jordan gegangen &#8211; ein alter Analogrechner, einfach durchgeschmort. Eberhard, der genialische Heizungsmonteur, konnte &hellip; <a href=\"https:\/\/www.rustimation.eu\/index.php\/iot-heizungssteuerung-1-lcd-panel\/\" class=\"more-link\"><span class=\"screen-reader-text\">IoT Heizungssteuerung 1 (LCD Panel)<\/span> weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[54,3,53,9,10],"tags":[13,14,57,61,55,34],"class_list":["post-506","post","type-post","status-publish","format-standard","hentry","category-433mhz","category-fernsteuern","category-iot","category-python","category-raspberry-pi","tag-433mhz","tag-bmp085","tag-bmp180","tag-lcd-panel","tag-programmieren","tag-python-2"],"_links":{"self":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/506","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/comments?post=506"}],"version-history":[{"count":1,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/506\/revisions"}],"predecessor-version":[{"id":1248,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/506\/revisions\/1248"}],"wp:attachment":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/media?parent=506"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/categories?post=506"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/tags?post=506"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}