Jump to content

[Python] Wetterstation


Recommended Posts

Hallo Leute,

 

bin nun endlich dazu gekommen, etwas mit der Wetterstation zu machen.

Mein Ziel ist es erstmal das Skript, welches verschieden Wetterdaten anzeigen kann ( http://www.tinkerforge.com/de/doc/Kits/WeatherStation/CSharpToButtonControl.html#starter-kit-weather-station-button-control-csharp ) von C# in Python zu übertragen.

Bisher klappt folgendes:

-Alle Buttons funktionieren  ;D

-Button 0: Anzeige des Datums

-Button 1: Aktuelle Wetterdaten

-Button 2: Min-Max-Avg Daten

-Button 3: Display ausschalten

 

Was noch zu tun ist:

-Fortlaufende Aktualisierung des Datums

-Bisher nur Min-Max Werte für die Temperatur, auch die anderen Daten per Button anzeigen lassen

-Einen Timer, der nach einer bestimmten Zeit das Display ausschaltet

-Die Graphanzeige wäre auch cool

 

Anbei ist das bisherige Python-Skript.

Das Problem ist im Moment, wie ich eine fortlaufende Anzeige des Datums hinbekomme (while-Schleife), aber auch noch die anderen Buttons funktionieren.

Weil mit einer "while True"-Schleife komme ich nicht mehr aus der Anzeige raus.

Ebenso brauche ich eine Schleife/Thread, der die Queues befüllt, welche die Daten für die Min-Max-Avg und die Graphenanzeige bereitstellt. Mein Ansatz ist, dass im Hauptprogramm eine Schleife läuft, die jede Sekunde eine Update-Funktion aufruft, die die Daten in die Queues legt. Jedoch besteht auch dabei das Problem, dass ich das Programm nicht mehr mit Enter beenden kann, nur noch mit ctrl+c.

 

Dies ist mein erstes Python-Skript und hoffe, dass evtl. einer schon mal etwas mit Threads und co gemacht hat.

weatherstation_tf.py

Link zu diesem Kommentar
Share on other sites

Hallo,

 

ich hatte mal das gleiche Problem mit dem Raspi und mir deshalb zusammen mit diversen Leuten aus dem python Forum(Danke) ein kleines Script geschrieben welches mit Events arbeitet.

D.h. das Script ist in keiner Schleife "gefangen", sondert wartet auf vorher von mir bestimmte Events und führt dann die ensprechenden Befehle um.

 

Ich werde das Script heute abend mal hier posten, im Moment sitze ich noch auf der Arbeit.

Link zu diesem Kommentar
Share on other sites

Anbei ist das bisherige Python-Skript.

Das Problem ist im Moment, wie ich eine fortlaufende Anzeige des Datums hinbekomme (while-Schleife), aber auch noch die anderen Buttons funktionieren.

Weil mit einer "while True"-Schleife komme ich nicht mehr aus der Anzeige raus.

Ebenso brauche ich eine Schleife/Thread, der die Queues befüllt, welche die Daten für die Min-Max-Avg und die Graphenanzeige bereitstellt. Mein Ansatz ist, dass im Hauptprogramm eine Schleife läuft, die jede Sekunde eine Update-Funktion aufruft, die die Daten in die Queues legt. Jedoch besteht auch dabei das Problem, dass ich das Programm nicht mehr mit Enter beenden kann, nur noch mit ctrl+c.

 

Dies ist mein erstes Python-Skript und hoffe, dass evtl. einer schon mal etwas mit Threads und co gemacht hat.

 

Wow, für einen ersten Gehversuch in Python sieht das Script sehr sauber aus!

 

Threads dürften nicht nötig sein. Was der Kern Deines Problems sein dürfte: Die "while True"-Schleife in update_queues(). Wenn Du genau diese Zeile rausnimmst, und auch das sleep() am Ende dieser Methode, sollte das Script schon um einiges besser laufen.

 

Dann kann die "while True"-Scheife im "if __name__ = '__main__'"-Block des Scripts update_queues() einmal pro Sekunde aufrufen, und Du hast aktualisierte Werte in Deinen queues. Dann fehlt nur noch der Aufruf einer Methode, die die aktualiserten Daten auf das Display bringt.

 

Das ginge in update_queues() selbst, kann aber natrlich auch in der Hauptschleife mit einem Aufruf einer geeigneten Methode nach dem call von update_queues() erfolgen.

Link zu diesem Kommentar
Share on other sites

Anbei mein "altes" Python Script

import asyncore
import socket
import select
import subprocess
import os

class Client(asyncore.dispatcher_with_send):
    def __init__(self, socket=None, pollster=None):
        asyncore.dispatcher_with_send.__init__(self, socket)
        self.data = ''
        if pollster:
            self.pollster = pollster
            pollster.register(self, select.EPOLLIN)

    def handle_close(self):
        if self.pollster:
            self.pollster.unregister(self)

    def handle_read(self):
        receivedData = self.recv(8192)
        if not receivedData:
            self.close()
            return
        receivedData = self.data + receivedData
        while '\n' in receivedData:
            line, receivedData = receivedData.split('\n',1)
            self.handle_command(line)
        self.data = receivedData

    def handle_command(self, line):
        if line == 'switch_he_1 on':
            self.send('on')
            os.system("/root/switch 50 1")
        elif line == 'switch_he_1 off':
            self.send('off')
            os.system("/root/switch 50 0")
        elif line == 'switch_he_2 on':
            self.send('on')
            os.system("/root/switch 51 1")
        elif line == 'switch_he_2 off':
            self.send('off')
            os.system("/root/switch 51 0")
        elif line == 'xbmc_restart':
            self.send('restart')
            os.system("xbmc-send --host=localhost --port=9777 --action=RestartApp")
        elif line == 'xbmc_off':
            self.send('off')
            ausgabe='xbmc-send --host=localhost --port=9777 --action="PlayerControl(Stop)"'
            os.system(ausgabe)
        elif line == 'switch_sis_1 on':
            self.send('on')
            os.system("sispmctl -o 1")
        elif line == 'switch_sis_1 off':
            self.send('off')
            os.system("sispmctl -f 1")
        elif line == 'get status_switch_sis_1':
            output = subprocess.check_output(["sispmctl", "-qg", "1"]);
            if output.rstrip() == "on":
                self.send('on')
            else:
                self.send('off')
        elif line == 'switch_sis_2 on':
            self.send('on')
            os.system("sispmctl -o 2")
        elif line == 'switch_sis_2 off':
            self.send('off')
            os.system("sispmctl -f 2")
        elif line == 'get status_switch_sis_2':
            output = subprocess.check_output(["sispmctl", "-qg", "2"]);
            if output.rstrip() == "on":
                self.send('on')
            else:
                self.send('off')
        elif line == 'switch_sis_3 on':
            self.send('on')
            os.system("sispmctl -o 3")
        elif line == 'switch_sis_3 off':
            self.send('off')
            os.system("sispmctl -f 3")
        elif line == 'get status_switch_sis_3':
            output = subprocess.check_output(["sispmctl", "-qg", "3"]);
            if output.rstrip() == "on":
                self.send('on')
            else:
                self.send('off')
        elif line == 'switch_sis_4 on':
            self.send('on')
            os.system("sispmctl -o 4")
        elif line == 'switch_sis_4 off':
            self.send('off')
            os.system("sispmctl -f 4")
        elif line == 'get status_switch_sis_4':
            output = subprocess.check_output(["sispmctl", "-qg", "4"]);
            if output.rstrip() == "on":
                self.send('on')
            else:
                self.send('off')
        else:
            self.send('unknown command')

class Server(asyncore.dispatcher):
    def __init__(self, listen_to, pollster):
        asyncore.dispatcher.__init__(self)
        self.pollster = pollster
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(listen_to)
        self.listen(5)

    def handle_accept(self):
        newSocket, address = self.accept()
        print "Connected from", address
        Client(newSocket,self.pollster)


def readwrite(obj, flags):
    try:
        if flags & select.EPOLLIN:
            obj.handle_read_event()
        if flags & select.EPOLLOUT:
            obj.handle_write_event()
        if flags & select.EPOLLPRI:
            obj.handle_expt_event()
        if flags & (select.EPOLLHUP | select.EPOLLERR | select.POLLNVAL):
            obj.handle_close()
    except socket.error, e:
        if e.args[0] not in asyncore._DISCONNECTED:
            obj.handle_error()
        else:
            obj.handle_close()
    except asyncore._reraised_exceptions:
        raise
    except:
        obj.handle_error()

class EPoll(object):
    def __init__(self):
        self.epoll = select.epoll()
        self.fdmap = {}

    def register(self, obj, flags):
        fd = obj.fileno()
        self.epoll.register(fd, flags)
        self.fdmap[fd] = obj

    def unregister(self, obj):
        fd = obj.fileno()
        del self.fdmap[fd]
        self.epoll.unregister(fd)

    def poll(self):
        evt = self.epoll.poll()
        for fd, flags in evt:
            yield self.fdmap[fd], flags

if __name__ == "__main__":
    pollster = EPoll()
    pollster.register(Server(("",54321),pollster), select.EPOLLIN)
    while True:
        evt = pollster.poll()
        for obj, flags in evt:
            readwrite(obj, flags)

 

Ich habe das genutzt um auf Eingaben aus dem Internet zu reagieren.

Das Script macht einen Server auf Port 54321 auf und reagiert dann auf verschiedene Befehle die aus dem Netz kommen.

Das Ganze läuft ohne Schleife damit es auch wirklich schnell läuft.

 

In einer anderen Version habe ich auch noch einen Schalter und eine LED an den Pi angeschlossen und konnte dann sowohl per Taste als auch per Internet dieLED steuern, das klappte nur über einen Socket Server.

 

Ich denke mit etwas Arbeit kann man das auch für Tinkerforge umbasteln.

 

Als Anregung hier mal mein aktuelles Tinkerforge Skript, allerdings noch ohne Socket Server

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


import datetime
from datetime import timedelta
from tinkerforge.ip_connection import IPConnection
from tinkerforge.bricklet_led_strip import LEDStrip
from tinkerforge.bricklet_distance_ir import DistanceIR
from tinkerforge.bricklet_motion_detector import MotionDetector
from tinkerforge.bricklet_ambient_light import AmbientLight
from tinkerforge.bricklet_humidity import Humidity
from tinkerforge.bricklet_temperature import Temperature

class cls_LED_bricklet:
    led_RED = 0                                         
    led_GREEN = 0                                       
    led_BLUE = 0                                       
    helligkeit = 0.0                                   
    led_verlauf = [0]*101
    UID = "jGU"

    def __init__(self):
        self.bricklet = LEDStrip(self.UID, ipcon) 
        zahl = 12
        multiplikator = 0.0
        zaehler = 0
        while zahl < 100:
            multiplikator = zahl/46.2
            zahl = round(zahl + multiplikator,3)
            self.led_verlauf[zaehler] = zahl
            zaehler = zaehler + 1
        self.led_verlauf[zaehler-1] = 100.0    

    def setColor(self,vr,vg,vb,he):
            self.led_RED = vr
            self.led_GREEN = vg
            self.led_BLUE = vb
            self.helligkeit = he
            vRED=(self.led_RED/100)*self.helligkeit
            vGREEN=(self.led_GREEN/100)*self.helligkeit
            vBLUE=(self.led_BLUE/100)*self.helligkeit
            var_led_RED = [0]*16                                    
            var_led_GREEN = [0]*16                                  
            var_led_BLUE = [0]*16      
            for i in range(16):
                var_led_RED[i] = int(vRED)
                var_led_GREEN[i] = int(vGREEN)
                var_led_BLUE[i] = int(vBLUE)
            # LED's setzen
            self.bricklet.set_rgb_values(0, 15, var_led_RED, var_led_BLUE, var_led_GREEN)
            self.bricklet.set_rgb_values(15, 15, var_led_RED, var_led_BLUE, var_led_GREEN)
            self.bricklet.set_rgb_values(30, 15, var_led_RED, var_led_BLUE, var_led_GREEN)
            self.bricklet.set_rgb_values(45, 15, var_led_RED, var_led_BLUE, var_led_GREEN)

class cls_Motion_bricklet:
    motion_detected = False                             
    UID = "jX1"    

    def __init__(self):
        self.bricklet = MotionDetector(self.UID, ipcon)

    def callbackSet(self):
        self.bricklet.register_callback(self.bricklet.CALLBACK_MOTION_DETECTED, self.__start)
        self.bricklet.register_callback(self.bricklet.CALLBACK_DETECTION_CYCLE_ENDED, self.__stop)
    
    def __start(self):
        self.motion_detected = True
        print(str(get_systemzeit()) + ' - Bewegung erkannt')

    def __stop(self):
        self.motion_detected = False
        print(str(get_systemzeit()) + ' - Bewegung hört auf')

class cls_Temperature_bricklet:
    temperature = 0.0
    UID = "dXW" 

    def __init__(self):
        self.bricklet = Temperature(self.UID, ipcon)
        
    def callbackSet(self,timeframe=5000):
        self.bricklet.set_temperature_callback_period(timeframe)
        self.bricklet.register_callback(self.bricklet.CALLBACK_TEMPERATURE, self.__write)

    def read(self):
        te = self.bricklet.get_temperature()
        self.temperature = (te/100.0)
        print(str(get_systemzeit()) + ' - Temperatur: ' + str(te/100.0) + ' °C')

    def __write(self, te):
        self.temperature = (te/100.0)
        print(str(get_systemzeit()) + ' - Temperatur: ' + str(te/100.0) + ' °C')

class cls_Humidity_bricklet:
    humidity = 0.0                                   
    UID = "kdt" 

    def __init__(self):
        self.bricklet = Humidity(self.UID, ipcon)

    def callbackSet(self,timeframe=5000):
        self.bricklet.set_humidity_callback_period(timeframe)
        self.bricklet.register_callback(self.bricklet.CALLBACK_HUMIDITY, self.__write)

    def read(self):
        rh = self.bricklet.get_humidity()
        self.humidity = (rh/10.0)
        print(str(get_systemzeit()) + ' - Luftfeuchtigkeit: ' + str(rh/10.0) + ' %')

    def __write(self, rh):
        self.humidity = (rh/10.0)
        print(str(get_systemzeit()) + ' - Relative Luftfeuchtigkeit: ' + str(rh/10.0) + ' %')

class cls_Ambient_bricklet:
    ambient = 0.0                                   
    UID = "jy1"                                       

    def __init__(self):
        self.bricklet = AmbientLight(self.UID, ipcon)

    def callbackSet(self,timeframe=5000):
        self.bricklet.set_illuminance_callback_period(timeframe)
        self.bricklet.register_callback(self.bricklet.CALLBACK_ILLUMINANCE, self.__write)

    def read(self):
        il = self.bricklet.get_illuminance()
        self.ambient = (il/10.0)
        print(str(get_systemzeit()) + ' - Lichtstärke: ' + str(il/10.0) + ' lx')

    def __write(self, il):
        self.ambient = (il/10.0)
        print(str(get_systemzeit()) + ' - Lichtstärke: ' + str(il/10.0) + ' lx')

class cls_IRDistance_bricklet:
    distance = 0.0
    distanceOld1 = 0.0                                  
    distanceOld2 = 0.0                                  
    distanceOld3 = 0.0
    firstRun = True
    UID = "ktd"  

    def __init__(self,ctbrick,chbrick):
        self.ctrl_bricklet = ctbrick
        self.chck_bricklet = chbrick
        self.bricklet = DistanceIR(self.UID, ipcon)

    def callbackSet(self,timeframe=200):
        self.bricklet.set_distance_callback_period(timeframe)
        self.bricklet.register_callback(self.bricklet.CALLBACK_DISTANCE, self.__changeDistance)
         
    def __changeDistance(self,distNew):         
        if self.firstRun:
            self.fristRun = False
            self.distanceOld1 = distNew                                  
            self.distanceOld2 = distNew                                 
            self.distanceOld3 = distNew 
            
        mittelwert=round(((self.distanceOld1 + self.distanceOld2 + self.distanceOld3 + distNew)/40),1)    
    
        y = mittelwert
        
        if mittelwert > 60 or mittelwert == 0:
            mittelwert = 60
        elif mittelwert < 10:
            mittelwert = 10
            
        x = round(99/50*(50 - (mittelwert - 10)),0)    
        led_leistung = self.ctrl_bricklet.led_verlauf[int(x)]

        if self.chck_bricklet.motion_detected:
            self.ctrl_bricklet.setColor(0,150,0,led_leistung)

        print (str(get_systemzeit()) + ' - Entfernungsänderung: ' + str(distNew/10.0) + ' cm => gemittelter ' + str(y) + ' => Wert ' + str(x) + ' => ' + str(led_leistung) + ' %')
        
        self.distanceOld1 = self.distanceOld2                                             
        self.distanceOld2 = self.distanceOld3
                                                          
        self.distanceOld3 = distNew                                                  


def get_systemzeit():
    now = datetime.datetime.utcnow()+timedelta(hours=1)
    return(now)


if __name__ == "__main__":
   
    ipcon = IPConnection()                              

    # Bricklets
    Temperature_bricklet = cls_Temperature_bricklet()
    Humidity_bricklet = cls_Humidity_bricklet()
    Ambient_bricklet = cls_Ambient_bricklet()
    LED_bricklet = cls_LED_bricklet()
    Motion_bricklet = cls_Motion_bricklet()
    IRDistance_bricklet = cls_IRDistance_bricklet(LED_bricklet,Motion_bricklet)
    
    ipcon.connect("192.168.127.81", 4223)             

    # Callbacks 
    Temperature_bricklet.callbackSet(10000)
    Humidity_bricklet.callbackSet(10000)
    Ambient_bricklet.callbackSet(10000)
    Motion_bricklet.callbackSet()
    IRDistance_bricklet.callbackSet(50)
    
    choice = input("Press key to exit\n")
    ipcon.disconnect()

Link zu diesem Kommentar
Share on other sites

  • 1 month later...

Hey Leute,

danke für eure Antworten und sorry für meine späte Antwort.

Habe zum Teil einige Punkte aus meinem ersten Post geschafft.

- Bei der fortlaufenden Anzeige des Datums habe ich ein bisschen geschummelt. Es hat leider nicht geklappt eine Funktion zu schreiben, die das Datum aktualisiert und auch auf die Tasten reagiert und so eine andere Seite anzeigt. Das "Schummeln" besteht jetzt darin, dass wenn die Callbacks aufgerufen werden, diese das Datum aktualisieren, wenn die Taste für das Datum gedrückt worden ist (weil ein Sensor ändert sich mindestens pro Sekunde).

- Habe es geschafft, dasss alle Sensoren sowohl in einer Queue, als auch in einer sqlite3 Datenbank gespeichert werden.  :D

- Leider klappt es bisher nicht, dass ein Timer nach einer gewissen Zeit das Display ausschaltet. Dafür ist Button 3 damit belegt.

 

Der Aufbau der Station ist wie folgt:

-RaspberryPi

-2 Master Bricks

-2 Temperatur Bricklets (einer für außen, einer für innen)

-1 Ambient Light Bricklet

-1 Humidity Bricklet

-1 Barometer Bricklet

-1 LCD Display Bricklet

 

Da ich den Luftdruck in hPa umrechne stellt sich die Frage, welche Temperatur zur Umrechnung benutzt werden sollte. Die von dem Außensensor oder von dem Innensensor?

 

Funktionsweise der Station:

-Button 0: Datumsanzeige

-Button 1: Anzeige 1: Wetterdaten von Temperatursensor(innen), Luftfeuchte, Luftdruck, Helligkeit

Anzeige 2: Temperatur von innen, aussen, Barometerchip

-Button 2: 6 Queues: Temperatur innen, aussen, Barometer, Luftfeuchtigkeit, Luftdruck, Helligkeit

-Button 3: Display aus

 

Jetzt noch die Skripte:

Ein wirklich ganz einfaches Skript zur Erstellung der sqlite Datenbank:

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

import sqlite3

if __name__ == "__main__":
    connection = sqlite3.connect("/var/local/tinkerweather/tinkerweather.sqlite")
    cursor = connection.cursor()
    cursor.execute("""CREATE TABLE temperature_aussen (datum REAL, temperature_aussen REAL)""")
    cursor.execute("""CREATE TABLE temperature_innen (datum REAL, temperature_innen REAL)""")
    cursor.execute("""CREATE TABLE illuminance (datum REAL, illuminance REAL)""")
    cursor.execute("""CREATE TABLE humidity (datum REAL, humidity REAL)""")
    cursor.execute("""CREATE TABLE air_pressure (datum REAL, air_pressure REAL)""")
    connection.commit()
    connection.close()
    print("Database created.")

 

Link zu diesem Kommentar
Share on other sites

Sorry für Doppelpost, aber 20.000 Zeichen sind zu wenig.  :P

2. Teil

 

Das Steuerungsskript:

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

import sqlite3
import socket
import sys
import time
import math
import logging as log
log.basicConfig(level=log.INFO)

import Queue
import heapq
from threading import Thread, Event, Timer
from datetime import datetime

from tinkerforge.ip_connection import IPConnection
from tinkerforge.ip_connection import Error
from tinkerforge.brick_master import Master
from tinkerforge.bricklet_lcd_20x4 import LCD20x4
from tinkerforge.bricklet_ambient_light import AmbientLight
from tinkerforge.bricklet_humidity import Humidity
from tinkerforge.bricklet_barometer import Barometer
from tinkerforge.bricklet_temperature import Temperature

class weatherstation:
    HOST = "localhost"
    PORT = 4223

    line_length = 20
    button_pressed = None
    button_counter = [0, 0, 0, 0]

    ipcon = None
    lcd = None
    al = None
    hum = None
    baro = None
    temp_innen = None
    temp_aussen = None

    latest_illuminance = None
    latest_humidity = None
    latest_air_pressure = None
    latest_temperature_baro = None
    latest_temperature_innen = 0
    latest_temperature_aussen = 0

    illuminance_queue = Queue.Queue(86400)
    humidity_queue = Queue.Queue(86400)
    air_pressure_queue = Queue.Queue(86400)
    temperature_baro_queue = Queue.Queue(86400)
    temperature_innen_queue = Queue.Queue(86400)
    temperature_aussen_queue = Queue.Queue(86400)

    def __init__(self):
        self.ipcon = IPConnection()
        while True:
            try:
                self.ipcon.connect(weatherstation.HOST, weatherstation.PORT)
                break
            except Error as e:
                log.error('Connection Error: ' + str(e.description))
                time.sleep(1)
            except socket.error as e:
                log.error('Socket error: ' + str(e))
                time.sleep(1)

        self.ipcon.register_callback(IPConnection.CALLBACK_ENUMERATE, self.cb_enumerate)
        self.ipcon.register_callback(IPConnection.CALLBACK_CONNECTED, self.cb_connected)

        while True:
            try:
                self.ipcon.enumerate()
                break
            except Error as e:
                log.error('Enumerate Error: ' + str(e.description))
                time.sleep(1)

    # Callbacks of the sensors
    # "if statements" for ongoing data on the lcd
    def cb_temperature_innen(self, temperature):
        self.latest_temperature_innen = temperature/100.0
        self.temperature_innen_queue.put(self.latest_temperature_innen)
        if self.temperature_innen_queue.full():
            self.temperature_innen_queue.get()
        if self.button_counter[1] % 4 == 1:
            text = 'Innentemp %7.2f \xDFC' % (self.latest_temperature_innen)
            self.lcd.write_line(0, 0, text)
        elif self.button_counter[1] % 4 == 2 or self.button_counter[1] % 4 == 3:
            text = 'Innen: %9.2f \xDFC' % (self.latest_temperature_innen)
            self.lcd.write_line(2, 0, text)            
        if self.button_pressed == 0:
            self.update_time()
        # Database
        connection = sqlite3.connect("/var/local/tinkerweather/tinkerweather.sqlite")
        cursor = connection.cursor()
        now = datetime.now()
        werte = (now, self.latest_temperature_innen)
        sql = "INSERT INTO temperature_innen VALUES (?, ?)"
        cursor.execute(sql, werte)
        connection.commit()
        #

    def cb_temperature_aussen(self, temperature):
        self.latest_temperature_aussen = temperature/100.0
        self.temperature_aussen_queue.put(self.latest_temperature_aussen)
        if self.temperature_aussen_queue.full():
            self.temperature_aussen_queue.get()
        if self.button_counter[1] % 4 == 2 or self.button_counter[1] % 4 == 3:
            text = 'Aussen: %8.2f \xDFC' % (self.latest_temperature_aussen)
            self.lcd.write_line(1, 0, text)
        if self.button_pressed == 0:
            self.update_time()
        # Database
        connection = sqlite3.connect("/var/local/tinkerweather/tinkerweather.sqlite")
        cursor = connection.cursor()
        now = datetime.now()
        werte = (now, self.latest_temperature_aussen)
        sql = "INSERT INTO temperature_aussen VALUES (?, ?)"
        cursor.execute(sql, werte)
        connection.commit()
        #

    def cb_illuminance(self, illuminance):
        self.latest_illuminance = illuminance/10.0
        self.illuminance_queue.put(self.latest_illuminance)
        if self.illuminance_queue.full():
            self.illuminance_queue.get()
        if self.button_counter[1] % 4 == 1:
            text = 'Helligkeit %6.2f lx' % (self.latest_illuminance)
            self.lcd.write_line(3, 0, text)
        if self.button_pressed == 0:
            self.update_time()
        # Database
        connection = sqlite3.connect("/var/local/tinkerweather/tinkerweather.sqlite")
        cursor = connection.cursor()
        now = datetime.now()
        werte = (now, self.latest_illuminance)
        sql = "INSERT INTO illuminance VALUES (?, ?)"
        cursor.execute(sql, werte)
        connection.commit()
        #

    def cb_humidity(self, humidity):
        self.latest_humidity = humidity/10.0
        self.humidity_queue.put(self.latest_humidity)
        if self.humidity_queue.full():
            self.humidity_queue.get()
        if self.button_counter[1] % 4 == 1:
            text = 'Luftfeuchte %5.2f %%' % (self.latest_humidity)
            self.lcd.write_line(1, 0, text)
        if self.button_pressed == 0:
            self.update_time()
        # Database
        connection = sqlite3.connect("/var/local/tinkerweather/tinkerweather.sqlite")
        cursor = connection.cursor()
        now = datetime.now()
        werte = (now, self.latest_humidity)
        sql = "INSERT INTO humidity VALUES (?, ?)"
        cursor.execute(sql, werte)
        connection.commit()
        #

    def cb_air_pressure(self, air_pressure):
        temperature = self.baro.get_chip_temperature()
        self.latest_temperature_baro = temperature/100.0
        self.latest_air_pressure = (air_pressure/1000.0)*((self.latest_temperature_aussen+273.15)/((self.latest_temperature_aussen+273.15)+(0.0065*520)))**-5.255
        self.air_pressure_queue.put(self.latest_air_pressure)
        if self.air_pressure_queue.full():
            self.air_pressure_queue.get()
        self.temperature_baro_queue.put(self.latest_temperature_baro)
        if self.temperature_baro_queue.full():
            self.temperature_baro_queue.get()
        if self.button_counter[1] % 4 == 2 or self.button_counter[1] % 4 == 3:
            text = 'Barometer: %4.2f \xDFC' % (self.latest_temperature_baro)
            self.lcd.write_line(3, 0, text)
        if self.button_counter[1] % 4 == 1:
            text = 'Luftdruck %7.2f hP' % (self.latest_air_pressure)
            self.lcd.write_line(2, 0, text)
        if self.button_pressed == 0:
            self.update_time()
        # Database
        connection = sqlite3.connect("/var/local/tinkerweather/tinkerweather.sqlite")
        cursor = connection.cursor()
        now = datetime.now()
        werte = (now, self.latest_air_pressure)
        sql = "INSERT INTO air_pressure VALUES (?, ?)"
        cursor.execute(sql, werte)
        connection.commit()
        #

    # Callback functions for buttons
    def cb_pressed(self, button):
        if button == self.button_pressed:
            #log.info('Button Counter: ' + str(self.button_counter))
            self.button_counter[button] = self.button_counter[button] + 1
        else:
            self.button_pressed = button
            self.button_counter = [0, 0, 0, 0]
        self.update_switch(button)

    #def cb_released(self, button):
        #log.info('Released: ' + str(button))

    # Turn the lcd on
    def lcd_on(self):
        self.lcd.clear_display()
        self.lcd.backlight_on()

    # Turn the lcd off
    def lcd_off(self):
        self.lcd.clear_display()
        self.lcd.backlight_off()

    # Functions for the lcd display
    # Button 0:
    def update_time(self):
        now = datetime.now()
        text = now.strftime('%d.%m.%Y')
        self.lcd.write_line(0, ((self.line_length-len(text))/2), text)
        text = now.strftime('%A') #%A, Woche %W
        self.lcd.write_line(1, ((self.line_length-len(text))/2), text)
        text = now.strftime('')
        self.lcd.write_line(2, ((self.line_length-len(text))/2), text)
        text = now.strftime('%H:%M:%S')
        self.lcd.write_line(3, ((self.line_length-len(text))/2), text)

    # Button 1:
    def update_standard(self):
        switch_lcd = self.button_counter[1] % 4
        if switch_lcd == 0 or switch_lcd == 1:
            text = 'Innentemp %7.2f \xDFC' % (self.latest_temperature_innen)
            self.lcd.write_line(0, 0, text)
            text = 'Luftfeuchte %5.2f %%' % (self.latest_humidity)
            self.lcd.write_line(1, 0, text)
            text = 'Luftdruck %7.2f hP' % (self.latest_air_pressure)
            self.lcd.write_line(2, 0, text)
            text = 'Helligkeit %6.2f lx' % (self.latest_illuminance)
            self.lcd.write_line(3, 0, text)
        elif switch_lcd == 2 or switch_lcd == 3:
            text = 'Temperatur'
            self.lcd.write_line(0, 0, text)
            text = 'Aussen: %8.2f \xDFC' % (self.latest_temperature_aussen)
            self.lcd.write_line(1, 0, text)
            text = 'Innen: %9.2f \xDFC' % (self.latest_temperature_innen)
            self.lcd.write_line(2, 0, text)
            text = 'Barometer: %4.2f \xDFC' % (self.latest_temperature_baro)
            self.lcd.write_line(3, 0, text)

    # Button 2:
    def update_min_max_avg(self):
        switch_lcd = self.button_counter[2] % 6
        if switch_lcd == 0:
            self.update_min_max_avg_write('Aussentemp ' + str(self.time_from_seconds(self.temperature_aussen_queue.qsize())), '\xDFC', self.get_min_max_avg(self.temperature_aussen_queue))
        elif switch_lcd == 1:
            self.update_min_max_avg_write('Innentemp ' + str(self.time_from_seconds(self.temperature_innen_queue.qsize())), '\xDFC', self.get_min_max_avg(self.temperature_innen_queue))
        elif switch_lcd == 2:
            self.update_min_max_avg_write('Barotemperatur ' + str(self.time_from_seconds(self.temperature_baro_queue.qsize())), '\xDFC', self.get_min_max_avg(self.temperature_baro_queue))
        elif switch_lcd == 3:
            self.update_min_max_avg_write('Luftfeuchte ' + str(self.time_from_seconds(self.humidity_queue.qsize())), '%', self.get_min_max_avg(self.humidity_queue))
        elif switch_lcd == 4:
            self.update_min_max_avg_write('Luftdruck ' + str(self.time_from_seconds(self.air_pressure_queue.qsize())), 'hPa', self.get_min_max_avg(self.air_pressure_queue))
        elif switch_lcd == 5:
            self.update_min_max_avg_write('Helligkeit ' + str(self.time_from_seconds(self.illuminance_queue.qsize())), 'lux', self.get_min_max_avg(self.illuminance_queue))

    def update_min_max_avg_write(self, title, unit, values):
        minimum = 'Minimum: ' + str(values[0]) + str(unit)
        average = 'Mittel.: ' + str(values[1]) + str(unit)
        maximum = 'Maximum: ' + str(values[2]) + str(unit)
        self.lcd.write_line(0, 0, title)
        self.lcd.write_line(1, 0, minimum)
        self.lcd.write_line(2, 0, average)
        self.lcd.write_line(3, 0, maximum)

    def time_from_seconds(self, s):
        string = None
        m = s/60
        h = m/60
        if h > 0:
            string = '(' + str(h) + 'h)'
        elif m >= 0:
            string = '(' + str(m) + 'm)'
        #log.info('Sekunden: ' + str(s))
        #log.info('Minuten: ' + string)
        return string

    def get_min_max_avg(self, q):
        minimum = None
        average = None
        maximum = None
        #log.info('Queue Size: ' + str(q.qsize()))
        #log.info('Queue full?:' + str(q.full()))
        heap = list(q.queue)
        heapq.heapify(heap)
        heap_length = len(heap)
        heap_sum = math.fsum(heap)
        average = round((heap_sum / heap_length), 2)
        minimum = round(min(heap), 2) #heapq.nsmallest(1, heap) #heap[0]
        maximum = round(max(heap), 2) #heapq.nlargest(1, heap)
        return minimum, average, maximum

    # Check which button is pressed, then update the lcd display
    def update_switch(self, button):
        if button == 0:
            #log.info('Update Time: Button ' + str(button))
            self.lcd_on()
            self.update_time()
        elif button == 1:
            #log.info('Update Standard: Button ' + str(button))
            self.lcd_on()
            self.update_standard()
        elif button == 2:
            #log.info('Update MinMaxAvg: Button ' + str(button))
            self.lcd_on()
            self.update_min_max_avg()
        elif button == 3:
            #log.info('Turn LCD Off: Button ' + str(button))
            self.lcd_off()

    # Initialize the bricklets
    def cb_enumerate(self, uid, connected_uid, position, hardware_version, firmware_version, device_identifier, enumeration_type):
        if enumeration_type == IPConnection.ENUMERATION_TYPE_CONNECTED or \
           enumeration_type == IPConnection.ENUMERATION_TYPE_AVAILABLE:
            if device_identifier == LCD20x4.DEVICE_IDENTIFIER:
                try:
                    self.lcd = LCD20x4(uid, self.ipcon)
                    self.lcd.clear_display()
                    self.lcd.register_callback(self.lcd.CALLBACK_BUTTON_PRESSED, self.cb_pressed) # Button support
                    #self.lcd.register_callback(self.lcd.CALLBACK_BUTTON_RELEASED, self.cb_released)
                    log.info('LCD 20x4 initialized')
                except Error as e:
                    log.error('LCD 20x4 init failed: ' + str(e.description))
                    self.lcd = None
            elif device_identifier == AmbientLight.DEVICE_IDENTIFIER:
                try:
                    self.al = AmbientLight(uid, self.ipcon)
                    self.al.set_illuminance_callback_period(1000)
                    self.al.register_callback(self.al.CALLBACK_ILLUMINANCE, self.cb_illuminance)
                    log.info('Ambient Light initialized')
                except Error as e:
                    log.error('Ambient Light init failed: ' + str(e.description))
                    self.al = None
            elif device_identifier == Humidity.DEVICE_IDENTIFIER:
                try:
                    self.hum = Humidity(uid, self.ipcon)
                    self.hum.set_humidity_callback_period(1000)
                    self.hum.register_callback(self.hum.CALLBACK_HUMIDITY, self.cb_humidity)
                    log.info('Humidity initialized')
                except Error as e:
                    log.error('Humidity init failed: ' + str(e.description))
                    self.hum = None
            elif device_identifier == Barometer.DEVICE_IDENTIFIER:
                try:
                    self.baro = Barometer(uid, self.ipcon)
                    self.baro.set_air_pressure_callback_period(1000)
                    self.baro.register_callback(self.baro.CALLBACK_AIR_PRESSURE, self.cb_air_pressure)
                    log.info('Barometer initialized')
                except Error as e:
                    log.error('Barometer init failed: ' + str(e.description))
                    self.baro = None
            elif device_identifier == Temperature.DEVICE_IDENTIFIER and uid == 'neE': # Innen
                try:
                    self.temp_innen = Temperature(uid, self.ipcon) 
                    self.temp_innen.set_temperature_callback_period(1000)
                    self.temp_innen.register_callback(self.temp_innen.CALLBACK_TEMPERATURE, self.cb_temperature_innen)
                    log.info('Temperatur innen initialized')
                except Error as e:
                    log.error('Temperatur innen init failed: ' + str(e.description))
                    self.temp_innen = None
                #print(self.temp_innen.get_identity())
            elif device_identifier == Temperature.DEVICE_IDENTIFIER and uid == 'ns4': # Außen
                try:
                    self.temp_aussen = Temperature(uid, self.ipcon) 
                    self.temp_aussen.set_temperature_callback_period(1000)
                    self.temp_aussen.register_callback(self.temp_aussen.CALLBACK_TEMPERATURE, self.cb_temperature_aussen)
                    log.info('Temperatur aussen initialized')
                except Error as e:
                    log.error('Temperatur aussen init failed: ' + str(e.description))
                    self.temp_aussen = None
                #print(self.temp_aussen.get_identity())

    def cb_connected(self, connected_reason):
        if connected_reason == IPConnection.CONNECT_REASON_AUTO_RECONNECT:
            log.info('Auto Reconnect')

            while True:
                try:
                    self.ipcon.enumerate()
                    break
                except Error as e:
                    log.error('Enumerate Error: ' + str(e.description))
                    time.sleep(1)

# Main Function
if __name__ == "__main__":
    log.info('Weatherstaion: Start\n')

    Weatherstation = weatherstation()

    if sys.version_info < (3, 0):
        input = raw_input # Compatibility for Python 2.x
    user_input = input('Press enter to exit.\n')

    log.info('Main program has exited!')

    if Weatherstation.lcd != None:
        Weatherstation.lcd_off()
    if Weatherstation.ipcon != None:
        Weatherstation.ipcon.disconnect()

    log.info('Weatherstation: End')

 

 

Damit das nun alles auch bei einem Systemstart automatisch gestart wird, habe ich dafür eine systemd-unit-file geschrieben:

[unit]
Description=Weatherstation
Requires=brickd.service
After=syslog.target

[service]
Type=simple
StandardInput=tty
TTYPath=/dev/tty24
Restart=always
RestartSec=30
ExecStart=/usr/bin/python /usr/local/bin/weatherstation.py

[install]
WantedBy=multi-user.target

 

Damit das Skipt unendlich läuft, habe ich die Eingabe auf tty24 gelegt, damit es nicht unterbrochen wird (gefällt mir eigentlich nicht so, wusste aber nicht, wie es einmal gestartet immer weiter läuft, vielleicht hat jemand eine Idee?).

 

So das war es erstmal. Wenn jemand noch Änderungen oder Verbesserungen hat bitte posten.

Link zu diesem Kommentar
Share on other sites

  • 2 weeks later...

Hey Leute,

danke für eure Antworten und sorry für meine späte Antwort.

 

So ist das Leben -- manchmal kommt einem was dazwischen, das einen darn hindert, mit hübscher Hardware zu basteln ;) Diese Antwort kommt reiseverursacht auch recht spät...

 

Habe zum Teil einige Punkte aus meinem ersten Post geschafft.

- Bei der fortlaufenden Anzeige des Datums habe ich ein bisschen geschummelt. Es hat leider nicht geklappt eine Funktion zu schreiben, die das Datum aktualisiert und auch auf die Tasten reagiert und so eine andere Seite anzeigt. Das "Schummeln" besteht jetzt darin, dass wenn die Callbacks aufgerufen werden, diese das Datum aktualisieren, wenn die Taste für das Datum gedrückt worden ist (weil ein Sensor ändert sich mindestens pro Sekunde).

 

Ich weiß ja nicht, was genau Du versucht hast, aber ich mutmaße mal, dass Dein Hauptproblem hier liegt:

 

if __name__ == "__main__":
    log.info('Weatherstaion: Start\n')

    Weatherstation = weatherstation()

    if sys.version_info < (3, 0):
        input = raw_input # Compatibility for Python 2.x
    user_input = input('Press enter to exit.\n')

 

"Weatherstation = weatherstation()" initialisiert die "gesamte Maschinerie", inclusive Callbacks. Danach kommt der [raw_]input() call, und hier bleibt das Script stehen, bis eben die Enter-Taste gedrückt wird. Damit muss alles in den Callbacks stattfinden, die von der IPConnection-Instanz in einem eigenen Thread ausgeführt werden, sobald die brick[lets] "etwas zu melden" haben.

 

Da Du das Script letztlich als daemon ausführen willst, kannst Du diesen input()-call aus einfach rausnehmen und durch ungefähr folgendes ersetzen [nicht getestet...]:

 

next_call_time = time.time()
while True:
    Weatherstation.update_display()
    next_call_time += 1
    time.sleep(next_call_time - time.time())

 

update_display() könnte dann die aktuelle Zeit anzeigen, und natürlich auch die ganzen Sensordaten, je nachdem, welcher Button las letztes gedrückt wurde.

 

Das Script kannst Du bei Testaufrufen vom Terminal aus immer noch mit ctrl-C stoppen.

 

Bleibt das Problem, das LCD beim Beenden abzuschalten. Das ginge so:

 

next_call_time = time.time()
try:
    while True:
        Weatherstation.update_display()
        next_call_time += 1
        time.sleep(next_call_time - time.time())
finally:
    Weatherstation.lcd_off()

 

- Leider klappt es bisher nicht, dass ein Timer nach einer gewissen Zeit das Display ausschaltet. Dafür ist Button 3 damit belegt.

 

...das ginge in der oben gezeigten while-Schleife problemlos.

 

Der Aufbau der Station ist wie folgt:

-RaspberryPi

-2 Master Bricks

-2 Temperatur Bricklets (einer für außen, einer für innen)

-1 Ambient Light Bricklet

-1 Humidity Bricklet

-1 Barometer Bricklet

-1 LCD Display Bricklet

 

Da ich den Luftdruck in hPa umrechne stellt sich die Frage, welche Temperatur zur Umrechnung benutzt werden sollte. Die von dem Außensensor oder von dem Innensensor?

 

Das würd ich den Temperatursensor nehmen, der am nächsten am Drucksensor liegt,

 

Sorry für Doppelpost, aber 20.000 Zeichen sind zu wenig.  :P

2. Teil

 

 

Damit das nun alles auch bei einem Systemstart automatisch gestart wird, habe ich dafür eine systemd-unit-file geschrieben:

[unit]
Description=Weatherstation
Requires=brickd.service
After=syslog.target

[service]
Type=simple
StandardInput=tty
TTYPath=/dev/tty24
Restart=always
RestartSec=30
ExecStart=/usr/bin/python /usr/local/bin/weatherstation.py

[install]
WantedBy=multi-user.target

 

Damit das Skipt unendlich läuft, habe ich die Eingabe auf tty24 gelegt, damit es nicht unterbrochen wird (gefällt mir eigentlich nicht so, wusste aber nicht, wie es einmal gestartet immer weiter läuft, vielleicht hat jemand eine Idee?).

 

Das Problem sind die logging-Aufrufe und der input()-Call, wenn kein Terminal vorhanden ist: Der input()-Call dürfte bei geschlosenem stdin einfach sofort einen Leerstring zurückliefern; die logging-Aufrufe gehen nach, was ist es normalerweise, stdout oder stderr? Wenn diese geschlossen sind, bricht das Script recht bald mit einem Fehler ab.

 

Ein recht schlichter Trick, eine Terminal-Nutzung zu vermeiden, wäre der Aufruf per

nohup

:

 

nohup /usr/bin/python /usr/local/bin/weatherstation.py

 

(

info coreutils 'nohup invocation'

liefert weitere Details). Ohne weitere Parameter leitet nohup stdout und stderr in einen Datei namens nohup.out um -- da müsstest Du in dem systemd file noch das passende work directory angeben (sorry, keine Ahnung, wie das geht...); vielleicht funktioniert auch ein Aufruf in der Art

nohup /usr/bin/python /usr/local/bin/weatherstation.py >> /home/pi/wstation.log

-- aber ob das Sinn hat, hängt davon ab, wie genau systemd diesen Aufruf macht, d.h.: Wird diese Zeile wie bei einer normalen shell ausgewertet, oder hat systemd da seine eigene Logik?

Link zu diesem Kommentar
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Reply to this topic...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

×
×
  • Neu erstellen...