Wetterstation [Update 2]

Updates zum Post sind ganz unten.

Auch dieses Projekt begann wieder mit einer Idee oder einem Wunsch meiner Frau. Sie wünscht sich eine automatische Belüftung des Gewächshauses in unserem Garten.
Der erste Schritt dabei ist die Erfassung der Umgebungstemperatur und -luftfeuchtigkeit. Also bastelte ich eine kleine Wetterstation - oder vielmehr nur ein Raspberry Pi-Thermometer.

Hardware

Steckplatine

Software

Ich habe eine ganz normale Raspbian-Installation durchgeführt. Danach natürlich noch die obligatorischen Befehle

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install rpi-update
sudo rpi-update

ausführen.
Damit auch der Sensor korrekt arbeitet und das Display angesprochen werden kann, muss mittels

sudo raspi-config

nochmals die Konfiguration aufgerufen werden. Hier auf der zweiten Seite ("Advanced Settings") müssen SPI und I2C aktiviert werden. Nach einem Neustart des Raspberry Pi sind die Schnittstellen aktiv.

Nun installierst du noch ein paar zusätzliche Pakete:

sudo apt-get install git build-essential python-dev python-pip python-imaging python-smbus
sudo pip install RPi.GPIO

Zum Ansprechen des Sensors und des Displays gibt es von Adafruit bereit gestellte Bibliotheken auf Github, die du nun installierst

cd ~ 
git clone https://github.com/adafruit/Adafruit_Python_DHT.git
cd Adafruit_Python_DHT/
sudo python setup.py install
cd ~
git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
cd Adafruit_Python_SSD1306
sudo python setup.py install

Damit die Daten später auch grafisch im Browser angezeigt werden können, musst du noch einen Web- und einen Datenbankserver installieren

sudo apt-get install apache2 php5 mysql-server python-mysqldb

Nach der Installation verbindest du dich mit dem Datenbankserver. Hier musst du das Passwort verwenden, das du bei der Installation angegeben hast. Bitte ersetze in der Zeile CREATE USER weather@localhost IDENTIFIED by 'PASSWORD'; PASSWORD durch ein von dir gewähltes Passwort.

pi@raspberrypi ~ $ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 43
Server version: 5.5.41-0+wheezy1 (Debian)

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE DATABASE weather;
Query OK, 1 row affected (0.01 sec)

mysql> CREATE USER weather@localhost IDENTIFIED by 'PASSWORD';
Query OK, 0 rows affected (0.01 sec)

mysql> USE weather;
Database changed
mysql> GRANT ALL ON weather.* TO weather@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE temperatures (id INT PRIMARY KEY AUTO_INCREMENT, sensor INT, date TIMESTAMP NOT NULL, temperature FLOAT, humidity FLOAT);
Query OK, 0 rows affected (0.03 sec)

mysql> quit
Bye

Nun legst du im Homeverzeichnis ein neues Verzeichnis "wetter" an

cd ~
mkdir wetter
cd wetter

und in dem Verzeichnis wiederum legst du einen Datei wetter.py an, die folgenden Inhalt hat:

#!/usr/bin/python
#  -*- coding: utf-8 -*-

import time
import MySQLdb as mdb
import sys

import Adafruit_DHT
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306

import Image
import ImageDraw
import ImageFont


# Raspberry Pi pin configuration:
RST = 24
# Note the following are only used with SPI:
DC = 23
SPI_PORT = 0
SPI_DEVICE = 0

sensor = Adafruit_DHT.DHT22
pin = 18

disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))

# Initialize library.
disp.begin()

# Clear display.
disp.clear()
disp.display()

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# initialize temperatures
humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

minTemp = temperature
maxTemp = temperature

lastInsert = 0

padding = 2
top = padding
x = padding

font = ImageFont.load_default()

con = mdb.connect('localhost', 'weather', 'PASSWORD', 'weather')

while 1:
    # Draw a black filled box to clear the image.
    draw.rectangle((0, 0, width, height), outline=0, fill=0)

    humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

    if temperature is not None and humidity is not None:

        if temperature > maxTemp:
            maxTemp = temperature

        if temperature < minTemp:
            minTemp = temperature

        if lastInsert < time.time() - 900:
            with con:
                cur = con.cursor()
                cur.execute("INSERT INTO temperatures (sensor, date, temperature, humidity) VALUES (1, %s, %s, %s)",
                            (time.strftime('%Y-%m-%d %H:%M:%S'), "{: f}".format(temperature),
                             "{: f}".format(humidity)))
            lastInsert = time.time()

        # Write two lines of text.
        draw.text((x, top), time.strftime("%d.%m.%Y %H:%M"), font=font, fill=255)
        draw.text((x, top + 20), '{0:.1f} *C  {1:.1f} % LF'.format(temperature, humidity), font=font, fill=255)
        draw.text((x, top + 40), 'min: {:.1f} *C'.format(minTemp), font=font, fill=255)
        draw.text((x, top + 50), 'max: {:.1f} *C'.format(maxTemp), font=font, fill=255)

        # Display image.
        disp.image(image)
        disp.display()

    time.sleep(15)

Ersetze hier auch wieder in der Zeile con = mdb.connect('localhost', 'weather', 'PASSWORD', 'weather') PASSWORD durch das von dir vergebene Passwort.

Das Skript liest alle 15 Sekunden die aktuelle Luftfeuchtigkeit und Temperatur aus und zeigt die Werte auf dem Display an. Zusätzlich werden die Werte alle 15 Minuten in die Datenbank geschrieben.

Gestartet wird das Skript mittels

sudo python /home/pi/wetter/wetter.py &

So sieht es zur Zeit bei mir aus:
fertige Wetterstation

Den Quelltext für die Anzeige habe ich in meiner ownCloud meinem GitHub-Repository abgelegt. Entpacke diesen, ändere in der Datei index.php in Zeile 3 ebenfalls das Passwort und lade dann die Dateien in den Ordner /var/www hoch. Nun kannst du unter http://<IP_DEINES_RASPBERRY_PI>/ die gespeicherten Daten deiner Wetterstation ansehen.

Updates

4. Mai 2015

Heute habe ich meinen Code auf einem GitHub-Repository hochgeladen, wo die weiteren Modifikationen zu finden sind.
Es steht jedem frei, den Code zu verwenden und auch Änderungen vorzunehmen.
Fragen und Anmerkungen dazu natürlich gern auch hier.

Ich habe das Verzeichnis bower_components nicht mit hochgeladen, das bedeutet also, dass du, nachdem du das Repository heruntergeladen hast, ein

bower install

im Verzeichnis web machen musst, damit Bootstrap heruntergeladen wird.

Zusätzlich habe ich noch kleine Veränderungen vorgenommen und Code-Teile optimiert.

15. Mai 2015

Heute habe ich einige Updates in das Repository gespielt, die sich um kleine Optimierungen kümmern. So ist die Python-Datei aufgeräumter und beide Skripte (Python und PHP) nutzen nun Konfigurationsdateien, in denen die Verbindungsparameter zur Datenbank hinterlegt sind.

Läuft das Display über mehrere Stunden oder Tage, ändern sich auch die minimalen und maximalen Temperaturen. Bisher war es so, wenn man die Werte löschen wollte, musste man das Skript stoppen und neu starten. Also dachte ich, dass es gut wäre, wenn es einen Button gäbe, den man drücken kann und dann werden die Werte zurück gesetzt. Die Umsetzung sieht dann so aus:

Reset-Button hinzugefügt

Im Code habe ich das auch schon eingefügt (Neuerungen sind hier abgebildet).

...
import RPi.GPIO as GPIO
...

RESET_PIN = 17

GPIO.setmode(GPIO.BCM)
GPIO.setup(RESET_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

...
            if GPIO.input(RESET_PIN):
                maxTemp = -9999.99
                minTemp = 9999.99
...

Es muss eine neue Bibliothek (RPi.GPIO) importiert werden. Danach setze ich für die Pin-Nummerierung die BCM-Nummerierung (siehe hier) ein. Der Pin wird als Eingang (GPIO.IN) deklariert. In der Schleife wird dann über GPIO.input der Wert abgefragt, der im gedrückten Zustand 1 und im nicht gedrückten Zustand 0 ist. Wird der Taster gedrückt (GPIO.input liefert 1), werden die minimalen und maximalen Temperaturen zurück (in meinem Fall auf sehr hohe bzw. sehr niedrige Werte) gesetzt.

Nur in der Realität funktioniert das nicht zuverlässig, da das Auslesen des Sensors ein paar Sekunden benötigt. Deshalb wartet das System an dieser Stelle und kann keine Eingaben von dem Taster annehmen.