Hur man mäter temperatur och hur jag använder det för att styra ett relä som slår av och på värmaren är beskrivet på flera andra ställen i bloggen men här kommer en sammanställning och uppdatering.
Jag aktiverade 1-wire i raspi-config under uppsättningen av min paj och har anslutit min DS18B20. Man behöver ett pull-up-motstånd på 4,7kΩ mellan plus- och data-kablarna för att kunna ansluta den direkt till GPIO på pajen. Data är anslutet till standardporten GPIO4 (pinne 7) och minus till valfri jord-pinne, jag använder nummer 25. Plus ansluter man normalt till en av 3,3V-pinnarna men jag har anslutit den till GPIO5 (pinne 29) för att kunna slå av och på strömmen via programmet. Det händer att DS18B20 slutar svarar (känt problem, tydligen vanligare på billiga kopior) och på det här sättet kan jag starta om den när det händer.
Reläet som styr värmaren är anslutet till GPIO22 (pinne 15).
För att kunna presentera data och statistik installerade jag webbservern Apache tillsammans med MRTG som kan presentera historiken grafiskt:
1 | sudo apt install apache2 mrtg |
För att MRTG ska få data från temperatur-sensorn krävs det en liten fil som jag kallar mrtg.py. Man får byta adressen till filen w1_slave till aktuell adress, varje sensor har sin egen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/usr/bin/python from time import strftime tfile = open ( "/sys/bus/w1/devices/28-011553897dff/w1_slave" , 'r' ) text = tfile.read() tfile.close() secondline = text.split( "\n" )[ 1 ] temperaturedata = secondline.split( " " )[ 9 ] temperature = float (temperaturedata[ 2 :]) temperature = int (temperature / 10 ) print ( str (temperature)) print ( "0" ) print (strftime( "%Y-%m-%d %H:%M:%S" )) print ( "Temperatur i kar 3" ) |
Man behöver även byta ut /etc/mrtg.cfg mot en som hämtar data på rätt ställe och visar det som man vill:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ###################################################################### # Multi Router Traffic Grapher -- Sample Configuration File ###################################################################### # This file is for use with mrtg-2.5.4c # Global configuration WorkDir: /var/www/html/mrtg WriteExpires: Yes RunAsDaemon: Yes Interval: 5 Title[^]: Temperaturstatistik for Title[kar3]: kar 3 PageTop[kar3]: <h1>Temperaturen i kar 3</h1> Target[kar3]: `/usr/local/bin/aquacontrol/mrtg.py` MaxBytes[kar3]: 4000 Options[kar3]: growright,gauge,expscale Factor[kar3]: 0.01 YLegend[kar3]: Temperatur YTicsFactor[kar3]: 0.01 ShortLegend[kar3]: C Legend1[kar3]: Temperatur i grader celcius Legend2[kar3]: Legend3[kar3]: Maximal 5 Minute Incoming Traffic Legend4[kar3]: Maximal 5 Minute Outgoing Traffic LegendI[kar3]: Temp: LegendO[kar3]: |
För att MRTG ska starta automatiskt behövs ett startscript, skapa filen mrtg i /etc/init.d med följande innehåll:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #! /bin/sh ### BEGIN INIT INFO # Provides: mrtg # Required-Start: # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: mrtg init script # Description: This file is used to start, stop, restart, # and determined status of the mrtg daemon. # Author: iceflatline <iceflatline@gmail.com> ### END INIT INFO ### START OF SCRIPT set -e # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="mrtg" NAME=mrtg DAEMON=/usr/bin/$NAME DAEMON_ARGS="/etc/mrtg.cfg" PIDFILE=/etc/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the mrtg package is not installed [ -x "$DAEMON" ] || exit 0 # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions # Function that starts the mrtg daemon start() { env LANG=C start-stop-daemon --start --quiet \ --exec $DAEMON -- $DAEMON_ARGS } # Function that stops the mrtg daemon stop() { start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \ --pidfile $PIDFILE } case "$1" in start) log_daemon_msg "Starting $DESC" start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; esac ;; stop) log_daemon_msg "Stopping $DESC" stop case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; esac ;; restart|force-reload) log_daemon_msg "Restarting $DESC" stop case "$?" in 0|1) start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; esac ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" ;; *) echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" ;; esac exit 0 ### END OF SCRIPT |
Kör sedan
1 2 3 4 5 | sudo chmod +x /usr/local/bin/aquacontrol/mrtg.py sudo chmod +x /etc/init.d/mrtg sudo systemctl daemon-reload sudo update-rc.d mrtg defaults sudo mkdir /var/www/html/mrtg |
så ska MRTG-biten vara klar. Dags för lite styrning! Följande Python-skript läser av temperaturen varje minut, kollar mot gränsvärden, slår av och på värmaren och larmar via epost om det avviker för mycket. Ange dina egna epostuppgifter och förstås rätt adress till w1_slave-filen som vanligt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | #!/usr/bin/python import RPi.GPIO as GPIO import time import datetime import syslog import smtplib from time import strftime import sys import os syslog.openlog( 'aqua-temp' ) syslog.syslog( 'Aqua-temp started' ) # Definiera temperaturgranser tempgoal = 25 alarmLow = tempgoal - 1 alarmHigh = tempgoal + 1 tempLow = tempgoal - 0.1 tempHigh = tempgoal + 0.1 lastKnownGoodTemp = tempgoal # Klasser class DS18B20Class: def __init__( self ): self .power = 5 def init( self ): syslog.syslog( 'DS18B20Class.init called.' ) GPIO.setup( self .power, GPIO.OUT) def off( self ): syslog.syslog( 'DS18B20Class.off called.' ) GPIO.output( self .power, GPIO.LOW) time.sleep( 30 ) def on( self ): syslog.syslog( 'DS18B20Class.on called.' ) GPIO.output( self .power, GPIO.HIGH) time.sleep( 30 ) def restart( self ): syslog.syslog( 'DS18B20Class.restart called.' ) GPIO.output( self .power, GPIO.LOW) time.sleep( 30 ) GPIO.output( self .power, GPIO.HIGH) time.sleep( 30 ) class heaterClass: def __init__( self ): self .heater = 22 def init( self ): syslog.syslog( 'heaterClass.init called.' ) GPIO.setup( self .heater, GPIO.OUT) def off( self ): syslog.syslog( 'heaterClass.off called.' ) GPIO.output( self .heater, heatOff) def on( self ): syslog.syslog( 'heaterClass.on called.' ) GPIO.output( self .heater, heatOn) # Funktioner def check_throttle(): MESSAGES = { 0 : 'Under-voltage!' , 1 : 'ARM frequency capped!' , 2 : 'Currently throttled!' , 3 : 'Soft temperature limit active' , 16 : 'Under-voltage has occurred since last reboot.' , 17 : 'Throttling has occurred since last reboot.' , 18 : 'ARM frequency capped has occurred since last reboot.' , 19 : 'Soft temperature limit has occurred since last rebot' } throttled_output = os.popen( "vcgencmd get_throttled" ).readline() throttled_output = throttled_output.replace( "throttled=" ,"") throttled_output = throttled_output.strip() throttled_binary = bin ( int (throttled_output, base = 16 )) result = 'Throttle notices: ' for position, message in MESSAGES.items(): if len (throttled_binary) > position and throttled_binary[ 0 - position - 1 ] = = '1' : result = result + ' ' + message return result def measure_cpu_temp(): temp = os.popen( "vcgencmd measure_temp" ).readline() temp = temp.replace( "temp=" ,"") return temp.strip() + '. ' def sendMail(subject, msg): fromaddr = 'Me Self <me@self.com>' toaddr = 'Me Self <me@self.com>' message = ( 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s' % (fromaddr, toaddr, subject, msg)) username = 'username' password = 'password' server = smtplib.SMTP( 'smtp.gmail.com:587' ) server.starttls() server.login(username,password) try : server.sendmail(fromaddr, toaddr, message) syslog.syslog( 'Mail sent' ) except : syslog.syslog( 'Error sending mail' ) server.quit() def writeTemp(temp): tfile = open ( '/var/www/html/temp.html' , 'w' ) tfile.write( '<html><head>' ) tfile.write( '<title>Info för kar 1</title>' ) tfile.write( '<META HTTP-EQUIV="refresh" CONTENT="60">' ) tfile.write( '</head><body>' ) tfile.write(strftime( "%Y-%m-%d %H:%M:%S" )) tfile.write( '<h1>Temperaturen i kar 1 är ' + str (temp) + '°C</h1>' ) tfile.write( '<h2>Värmaren är ' ) if heatStatus: tfile.write( 'på</h2>' ) else : tfile.write( 'av</h2>' ) tfile.write( '<p><a href="/mrtg/kar1.html">Historik</a>' ) tfile.write( '</body></html>' ) tfile.close() def checkTemp(): global tempLarm global heatStatus global lastKnownGoodTemp sensorError = True fName = "/sys/bus/w1/devices/28-0415a841a3ff/w1_slave" temperature = lastKnownGoodTemp try : tfile = open (fName, 'r' ) text = tfile.read() tfile.close() if text: secondline = text.split( "\n" )[ 1 ] temperaturedata = secondline.split( " " )[ 9 ] temperature = float (temperaturedata[ 2 :]) temperature = float ( int ((temperature / 100 ) + 0.5 )) / 10 sensorError = False except : syslog.syslog( 'Error reading file: ' + str (sys.exc_info()[ 0 ])) if (temperature < 10 or temperature > 40 ): syslog.syslog( 'Error when reading temp: ' + str (temperature)) temperature = lastKnownGoodTemp sensorError = True else : lastKnownGoodTemp = temperature if (sensorError): DS18B20.restart() if (temperature < alarmLow or temperature > alarmHigh): if not tempLarm: syslog.syslog( 'Temp is wrong' ) sendMail( "Temperaturen i kar 1 ar felaktig" , "Temperaturen i kar 1 ar nu " + str (temperature)) tempLarm = True else : if tempLarm: syslog.syslog( 'Temp is normal again' ) sendMail( "Temperaturen i kar 1 ar normal" , "Temperaturen i kar 1 ar nu " + str (temperature)) tempLarm = False if (temperature < = tempLow): if not heatStatus: syslog.syslog( 'Temp is ' + str (temperature) + '. Heater switched on' ) heater.on() heatStatus = True if (temperature > = tempHigh): if heatStatus: syslog.syslog( 'Temp is ' + str (temperature) + '. Heater switched off' ) heater.off() heatStatus = False writeTemp(temperature) # Variabler DS18B20 = DS18B20Class() heater = heaterClass() heatOn = 0 heatOff = 1 tempLarm = False heatStatus = False # Initiera GPIO GPIO.setmode(GPIO.BCM) GPIO.setwarnings( False ) heater.init() heater.off() DS18B20.init() DS18B20.on() syslog.syslog( 'GPIO initialized. Heater switched off, DS18B20 switched on' ) # Huvudloop while True : syslog.syslog( 'CPU temp: ' + measure_cpu_temp() + check_throttle()) checkTemp() time.sleep( 60 ) |
För att skriptet ska starta automatiskt behöver man skapa filen aqua-temp.service i /lib/systemd/system med följande innehåll:
1 2 3 4 5 6 7 8 9 10 | [Unit] Description=Aqua-Temp After=multi-user.target [Service] Type=idle ExecStart=/usr/local/bin/aquacontrol/aqua-temp.py [Install] WantedBy=multi-user.target |
Kör sedan
1 2 3 4 | sudo chmod +x /usr/local/bin/aquacontrol/aqua-temp.py sudo chmod 644 /lib/systemd/system/aqua-temp.service sudo systemctl daemon-reload sudo systemctl enable aqua-temp.service |
så ska det starta automatiskt vid omstart. Det är praktiskt att få loggen som skapas till en egen fil. Skapa filen /etc/rsyslog.d/aqua-temp.conf med innehållet
1 | if $programname == 'aqua-temp' then /var/log/aqua-temp.log |
Överkurs
Ibland kan det vara fiffigt att kunna hämta ut data i JSON-format. Jag har därför följande lilla fil, vid namn temp-xml.py, placerad i /var/www/cgi-bin. Ja, du behöver justera sökvägen till w1_slave…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/usr/bin/python tfile = open ( "/sys/bus/w1/devices/28-011553897dff/w1_slave" , 'r' ) text = tfile.read() tfile.close() secondline = text.split( "\n" )[ 1 ] temperaturedata = secondline.split( " " )[ 9 ] temperature = float (temperaturedata[ 2 :]) temperature = float ( int ((temperature / 100 ) + 0.5 )) / 10 print ( "Content-type: text/html\n" ) print ( '<?xml version="1.0" encoding="UTF-8"?>' ) print ( '<rss version="2.0">' ) print ( ' <item>' ) print ( ' <temp3>' + str (temperature) + '</temp3>' ) print ( ' </item>' ) print ( '</rss>' ) |
För att Apache ska gå med på att köra python-script, och begripa vad cgi-bin är, behöver dessa rader läggas till i /etc/apache2/sites-available/000-default.conf efter DocumentRoot-raden
1 2 3 4 5 6 7 | ScriptAlias /cgi-bin/ /var/www/cgi-bin/ <Directory "/var/www/cgi-bin"> AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch Order allow,deny Allow from all </Directory> |
Kör sedan
1 2 3 | sudo chmod +x /var/www/cgi-bin/temp-xml.py sudo a2enmod cgi sudo systemctl restart apache2 |
så ska det hela rulla.