Raspberry mit Python ansteuern (Bewegungsmelder)

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Hermann_12
User
Beiträge: 10
Registriert: Dienstag 17. Februar 2015, 10:34

Hallo liebe Community.
Ich würde gerne ein Programm entwickeln, dass ich für eine meine Arbeit benötigte. Da meine Kenntnisse in Python mehr als gering sind, bitte ich um eure Hilfe.

Folgende Aufgabenstellung: Mit Hilfe des Raspberry PI und der Programmiersprache Python will ich einen Monitor über einen Bewegungsmelder (welcher an raspberry pi angeschlossen ist) ansteuern. Der Bewegungsmelder liefert als Eingang 0 oder 1. Ist der Bewegungsmelder nicht aktiv, also logisch 0 soll der Raspberry den Monitor ausschalten. Dies ist mit einem Shell Befehl möglich, der an den raspberry über python geschickt wird. Wenn jetzt eine Person vor dem Bewegungsmelder ist, ist dieser auf logisch 1 und soll eine zuvor geöffnete Webseite anzeigen (in meinen Programm Test.html).

Mein derzeitiger Code, der daweil nicht funktioniert (identitation error) und noch Fehler aufweißt:

Code: Alles auswählen


import RPi.GPIO as GPIO
import subprocess
import webbrowser


GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)




PIR_01 = 7 #Pin Bewegungsmelder  

cs_01 = 0 # PIR 01 current state
ps_01 = 0 # previous state vom Bewegungsmelder

GPIO.setup(PIR_01, GPIO.IN)

while True:
   # PIR 01 Start     
   cs_01 = GPIO.input(PIR_01)
   # Bewegung 
   if cs_01 == 0 and ps_01 == 1:
	   webbrowser.open('www/Test.html') #öffnet die Html
   if cs_01 == 1 and ps_01 == 0:
       from subprocess import call #dreht den Monitor ab
	   call(["xset", "dpms", "force", "off"]) 
	cs_01 == ps_01
Probleme die jetzt auftreten:
1) Falls das Programm so funktioniert wie ich es programmiert habe, wird die Html Seite ja dauernd neu geöffnet so wie ich das verstehe. Im idealfall sollte die html aber einfach dauernd offen sein und das Programm (Python) steuert halt nur über den Bewegungsmelder (Keine Person=Monitor Schwarz; Person da= Monitor an) den Raspberry und der steuert den Monitor an.
2) Ich habe das Programm schon mal anderes getest und bin drauf gekommen, dass meiner Meinung nach (bin kein Experte) der Befehl zum Bildschirm ausschalten von der nächsten Programmzeile aufgehoben wird. Meine Theorie dazu ist, dass der Befehl ja eigentlich wie der Ruhemodus ist und sobald der nächste Befehl kommt wacht der Raspberry aus dem Ruhemodus auf. Das ist ein Problem weil er eben nur aufwachen soll, wenn eine Person davor steht.
3) Der Bewegungsmelder ist kein Präsenzmelder, dass heißt er erkennt nur Bewegungen. Das Problem hierbei ist, dass der Monitor auch ein bleiben soll wenn eine Person sich kürze Zeit davor befindet und sich nicht bewegt.

Falls ihr Interesse an meinem Problem habt und mir helfen wollt, bedenkt bitte das ich ein blutiger Anfänger in python Programmierung bin.
Ich wäre für jeder Art der Hilfe wirklich sehr dankbar.
MfG Hermann
BlackJack

@Hermann_12: Der Fehler ist ziemlich deutlich wenn man denn Python in den Grundzügen kennt. Einrückung ist wichtiger Bestandteil der Syntax weil man dem Compiler damit die Programmstruktur mitteilt. Und wenn die Einrückung so fehlerhaft ist das der Compiler das erkennen kann, dann bekommt man einen `IndentationError`. Ab Zeile 27 ist die Einrückung ganz offensichtlich sehr chaotisch.

An der Stelle gleich einmal einen Hinweis auf den Style Guide for Python Code. Eingerückt wird per Konvention vier Leerzeichen pro Ebene.

Ad 1) Ja, die Webseite wird immer wieder erneut geöffnet wenn man das in der Schleife macht. Die Lösung hast Du ja selbst schon beschrieben.

Ad 2) Nein, Aktivität bezieht sich beim Ausschalten des Monitors wegen Inaktivtät natürlich nicht auf den Prozessor, denn der arbeitet schliesslich *ständig*, sondern ob der Benutzer Eingaben tätigt. Dein PC schaltet den Monitor doch sicher auch erst wieder ein wenn Du eine Taste drückst oder die Maus bewegst, und nicht weil das E-Mail-Programm eine neue Nachricht vom Server abgerufen hat oder so.

Ad 3) Da darf man dann also nicht gleich nach der Zustandsänderung des Sensors den Monitor ausschalten sondern muss eine zeitlang warten ob währenddessen wieder eine Bewegung registriert wurde. Die Übergänge von 0 nach 1 und von 1 nach 0 sind da dann aber auch gar nicht interessant. Man könnte sich auch merken ob der Monitor an oder aus ist und dann immer bei einer wenn der Monitor aus ist diesen anschalten. Zusätzlich merkt man sich bei jeder 1 die aktuelle Zeit (`datetime`-Modul). Und bei einer 0 schaut man jedes mal ob seit der letzten 1 genug Zeit vergangen ist um sicher zu sein das der Benutzer nicht mehr da ist und gegebenfalls den Monitor ausschalten.

Schön wäre dann auch noch das man damit die CPU nicht 100% der Zeit beschäftigt sondern die Möglichkeit des `GPIO`-Moduls benutzt auf einen Interrupt von der Hardware mit einem Funtkionsaufruf zu reagieren.

Sonstige Anmerkungen zum Quelltext: Nicht alles auf Modulebene machen. Teile das sinnvoll auf Funktionen auf. Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden.

Namen weder abkürzen noch durchnummerieren. Statt `ps_01` und `cs_01` wäre zum Beispiel `current_state` und `previous_state` besser. Dann muss man nicht erklären was die Namen bedeuten. Die angehängte Nummer macht hier überhaupt keinen Sinn. Wenn man tatsächlich Namen fortlaufend nummeriert ist das fast immer ein Zeichen dass man stattdessen eine passende Datenstruktur verwenden sollte, meistens eine Liste.

Importe gehören an den Modulanfang. Dann sieht man am Anfang des Quelltextes schon wovon das Modul abhängt und muss nicht den gesamten Quelltext absuchen.
Hermann_12
User
Beiträge: 10
Registriert: Dienstag 17. Februar 2015, 10:34

Zu 1) das heißt ich gebe den Teil mit dem Webbrowser aus der schleife raus und setzte ihn davor. Dazu hab ich noch eine Frage, nämlich müsste sich die seite im Vollbildmodus öffnen, das tut sie aber daweil nicht. Sie öffnet sich nur in einem minimierten Fenster.
2) Der Bewegungsmelder ist ja ein Eingabegerät und liefert dauernd ein Eingangssignal, egal ob 0 oder 1. Wie gesagt, ich habe das schon mal probiert und der Monitor war nur für eine Sekunde im Ruhemodus, obwohl der Bewegungsmelder nicht aktiv war.
3) Das ist mir schon zu hoch, ich bin wie gesagt ein blutiger Anfänger in Python und programmiere seit 4 Tagen, an dem selben Problem.

Danke für deine Antwort. Mfg Hermann
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Mit Monitor ein / aus hat mich vor kurzem auch interessiert und da habe ich dahier gefunden: http://the-martins.org/?p=81
BlackJack

@Hermann_12: Ad 1) Das geht über die Möglichkeiten des `webbrowser`-Moduls hinaus. Da müsstest Du selbst einen bestimmten Webbrowser starten und dabei irgendwie dafür sorgen das der im Vollbild dargestellt wird. Wobei da auch die Frage ist ob Du maximiert oder tatsächlich Vollbild meinst. Und was der Zweck davon ist. Wenn das beispielsweise ein Kiosk-System werden soll, dann würde man das mit entsprechender Software oder Einstellungen auf Desktopebene lösen und nicht mit einem Programm das einen Browser startet.

Ad 2) Der Bewegungssensor ist natürlich in gewissem Sinn auch ein Eingabegerät, aber nicht aus Sicht der Energiesparverwaltung des Fenstersystems oder der Desktopumgebung. Die kennen nur die Eingabegeräte die der X-Server kennt und für die er Treiber besitzt, also eben typischerweise Maus und Tastatur. Wenn der Monitor also nicht länger aus bleibt, dann hat ihn irgendein Programm(teil) wieder eingeschaltet der nichts mit dem Sensor zu tun hat. Von dem ”weiss” ja niemand etwas. Die GPIO-Pins werden vom System nicht verwendet solange nicht ein Treiber geladen wurde der das tut. Also zum Beispiel wenn man über ein Kernelmodul irgendeine Form von serieller Übertragung (RS232, SPI, I²C, …) über bestimmte Pins abwickelt.

Ad 3) Wenn das noch zu hoch ist, dann musst Du Dich halt solange damit auseinandersetzen bis es das nicht mehr ist. Eine „busy loop” die das wie beschrieben löst ist nicht sooo schwer und vier Tage ist ja auch noch nicht wirklich viel Zeit um programmieren zu lernen.

Arbeite mal ein Grundlagentutorial durch, mindestens bis einschliesslich dem Thema Funktionen. In der Python-Dokumentation gibt es eines. Absoluten Anfängern wird auch oft Learn Python The Hard Way empfohlen. (Nicht vom Namen irritieren lassen.)

Dann beschäftige Dich mit dem `datetime`-Modul, mit den Datentypen darin und wie man mit denen Rechnen und solche Objekte vergleichen kann. Am besten interaktiv in einer Python-Shell. Da sieht man sofort die Ergebnisse und kein einfach mit den Werten ”herumspielen”.

Und dann kannst Du anfangen die Idee in Code umzusetzen. Wichtige Vorgehensweise bei Problemen die zu gross erscheinen: In kleinere Teilprobleme zerlegen und Funktionen schreiben die diese Teilprobleme lösen. Diese Teillösungen dann Testen und wenn die funktionieren zu einer grösseren (Teil)Lösung zusammensetzen. Und wieder testen. Wiederholen bis das Gesamtproblem gelöst ist.

Zum Beispiel ist das schalten des Monitors ganz sicher ein Kandidat für ein Teilprobleme das in einer Funktion gelöst werden kann die einen Wahrheitswert als Argument bekommt und anhand dessen den Monitor ein- oder ausschaltet.
Hermann_12
User
Beiträge: 10
Registriert: Dienstag 17. Februar 2015, 10:34

@BlackJack @Sr4l Danke für eure Hilfe, bei weiteren Problemene, werde ich mich wieder melden. MfG Hermann
Hermann_12
User
Beiträge: 10
Registriert: Dienstag 17. Februar 2015, 10:34

@ BlackJack
ich habe jetzt ein Programm geschrieben, das meiner Meinung nach funktionieren müsste. Es gibt aber ein Problem:
Wenn ich das Programm starte ist der Monitor ganz normal abgedreht und der Bewegungsmelder ist inaktiv (zugedeckt mit einem handtuch ^^). Das Problem ist aber das nach ein paar Sekunden der Monitor wieder angeht, ohne dass der Bewegungsmelder eine Bewegung erkennen kann (weil er ja zugedeckt ist). Hast du vielleicht eine Ahnung wo der Fehler sein könnte ?

Code: Alles auswählen

import RPi.GPIO as GPIO
import subprocess
import webbrowser
import time


GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)


webbrowser.open('www/Test.html') #oeffnet die Html

PIR_01 = 7 #Pin Bewegungsmelder  

cs_01 = 0 # PIR 01 current state
#ps_01 = 0 # previous state vom Bewegungsmelder

GPIO.setup(PIR_01, GPIO.IN)

while True:
   # PIR 01 Start     
   cs_01 = GPIO.input(PIR_01)
   # Bewegung 
   if cs_01 == 1:
	 from subprocess import call #dreht den Monitor auf
	 call(["xset", "dpms", "force", "on"])
	 #print "1"
	 time.sleep(2)
   if cs_01 == 0:
      while cs_01 ==0:
       from subprocess import call #dreht den Monitor ab
       call(["xset", "dpms", "force", "off"]) 
       #print "0"
       time.sleep(2) 

BlackJack

@Hermann_12: Funktioniert denn ein ``xset dpms force off`` wenn Du das direkt in der Konsole eingibst dauerhaft? S4rl hat da ja etwas verlinkt was das Monitor aus- und einschalten angeht.

Wenn das ausschalten funktionieren würde, dann machst Du das in dem Programm alle zwei Sekunden, und zwar ewig, denn die ``while``-Schleife dort wird niemals verlassen denn `cs_01` ändert seinen Wert nicht auf magische Weise irgendwann wieder auf 1.
Hermann_12
User
Beiträge: 10
Registriert: Dienstag 17. Februar 2015, 10:34

@BlackJack ja der Befehl wirkt dauerhaft, bis man die Maus bewegt oder in die Tastatur was eingibt. Dadurch wird der Befehl aufgehoben und der Monitor geht wieder an.
Ich verstehe das nicht ganz (sorry wenn ich mich jetzt ein wenig dumm anstelle), wenn das Programm nur dauernd 0 als Eingang bekommt müsste der Bildschirm ja aus bleiben. und ich versteht auch nicht wieso er beim aktivieren aus geht und dann ohne "1" sich wieder aufdreht. Was müsste ich jetzt ändern deiner Meinung nach, dass mein Programm endlich so funktioniert wie ich will. :-)
Hermann_12
User
Beiträge: 10
Registriert: Dienstag 17. Februar 2015, 10:34

@BlackJack noch etwas was mit aufgefallen ist: sobald ich die Maus bewege oder die Tastatur verwende und das Programm aktiv ist flackert der Bildschirm. Also er dreht sich ganz schnell auf und ab.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Hermann_12: Lies dir mal diesen Thread durch: http://www.python-forum.de/viewtopic.php?f=1&t=35397

Vielleicht kannst du ja daraus Inspiration ziehen.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@Hermann_12: Teste das abschalten per Python-Programm doch mal einzeln. Also einfach nur den Monitor abschalten. Wenn das von der Konsole funktioniert, dann müsste das ja auch aus einem Programm heraus funktionieren.

Was bedeutet ganz schnell denn? Wenn Dein Programm in die Schleife zum Ausschalten gerät dann schaltet es alle zwei Sekunden den Monitor aus. Immer und immer wieder. Bis das Programm beendet wird. Ich würde mir wie gesagt merken ob ich den Monitor ausgeschaltet habe und den jeweils nur *einmal* an- oder ausschalten und nicht ständig immer wieder.
Antworten