E-Mail senden bei GPIO Input

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
malarkey
User
Beiträge: 3
Registriert: Mittwoch 10. Februar 2016, 12:59

E-Mail senden bei GPIO Input

Beitragvon malarkey » Mittwoch 10. Februar 2016, 13:23

Hallo liebe Forenmitglieder,

mein Raspi B+ soll am GPIO4 (das müsste Pin17 sein) auf Spannung horchen und bei einer 1 eine Email senden. Das ganze teste ich, indem ich das Programm starte und dann GPIO4 mit dem 3,3V per Jumperkabel kurzschließe. Das klappt soweit wie gewünscht.
Allerdings tritt ein unerwünschte Effekt auf beim Neustart des Pi auf: Das Python Programm startet automatisch nach dem Boot und sendet sofort genau 1x eine Mail, obwohl kein Jumperkabel angeschlossen ist, und horcht weiter. Brücke ich jetzt wieder GPIO4 und 3,3V sendet er wie gewohnt eine Mail.
Es ist ja genau das, was ich haben wollte, wäre da nicht die 1 Mail, die nach dem Boot versendet wird.
Als Autostart-Skript für die Anwendung habe ich übrigens .bashrc genommen.


Code: Alles auswählen

import smtplib
from email.mime.text import MIMEText
import RPi.GPIO as GPIO
import time
import socket
import datetime
import sys

# Aktuelles Datum holen
Datum = datetime.date.today()

#setup gpio pins
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN)

#Print message on screen
print "Warte auf Signal am Pin 4"

#start loop
loop = "1"
while loop == "1":
        #test GPIO for input
        if GPIO.input(4):
                #create email
                message = """Diese E-Mail wurde vom Raspberry Pi versendet."""
                msg = MIMEText(message)
                msg['subject'] = 'Ereignis auf Raspberry Pi - %s' % time.strftime('%a %d %b %Y, $
                msg['from'] = 'stevenkrieg@hotmail.de>'
                msg['to'] = 'stevenkrieg@hotmail.de'
                msg['Date'] = time.strftime('%a, %d %b %Y %H:%M:%S %z')
                # send mail
                s = smtplib.SMTP('smtp-mail.outlook.com')
                s.starttls()
                s.login('stevenkrieg@hotmail.de' , '********')
                s.sendmail(msg['From'], msg['To'], msg.as_string())
                s.quit
                print "Email sent"
                time.sleep(2)
        while GPIO.input(4):
                pass
BlackJack

Re: E-Mail senden bei GPIO Input

Beitragvon BlackJack » Mittwoch 10. Februar 2016, 14:40

@malarkey: Dann liegt wohl aus irgendwelchen Gründen nach dem Neustart Strom an dem Pin an‽ Was hängt denn da dran? Wird das auch neugestartet?

Der Quelltext hat übrigens einen Syntaxfehler der sehr wahrscheinlich beim kopieren entstanden ist und weil mindestens eine Zeile zu lang ist. Wäre vielleicht kein Problem gewesen wenn mit vier Leerzeichen pro Ebene eingerückt wäre, wie es für Python-Quelltext üblich ist, aber auch dann sollte man darauf achten das die Zeilen nicht zu lang werden. Der Style Guide for Python Code sagt auch was zur Zeilenlänge.

Sonstige Anmerkungen: `socket` und `sys` werden importiert, aber nicht verwendet.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Kommentare sollten dem Leser einen Mehrwert bieten und nicht einfach noch mal das (Offensichtliche) beschreiben was gleich darauf als Code noch mal dort steht. Faustregel: Kommentare beschreiben nicht *was* gemacht wird, das tut bereits der Code, sondern *warum* das so gemacht wird. Sofern das nicht schon aus dem Code ersichtlich ist.

`Datum` wird nirgends verwendet.

Anstelle von ”magischen” Zahlen sollte man Konstanten verwenden. Beispielsweise für die Pin-Nummer. Die sollte einen beschreibenden Namen bekommen. Dann kann man den Pin an *einer* Stelle im Programm ändern, und weiss an allen Stellen wo die Pin-Nummer verwendet wird, was dieser Pin für eine Bedeutung hat.

Auch andere Werte die mehrfach verwendet werden, oder auch nur einmal verwendet werden, aber potentiell mal geändert werden müssen, wie E-Mail-Adressen, sollte man als Konstanten am Anfang des Programms definieren.

`loop` ist überflüssig weil sich der Wert nie ändert. Und eine Zeichenkette mit der Ziffer 1 darin sieht auch ziemlich willkürlich gewählt aus.

Abkürzungen die nicht geläufig sind, sollte man bei Namen vermeiden. Es ist lesbarer wenn man nicht erst rätseln oder nach der Definition suchen muss.

Die aktuelle Zeit würde ich nur einmal ermitteln wenn sie zwei mal gebraucht wird. Und dort würde ich das `datetime`-Modul vorziehen, statt dem „low level“ `time`-Modul.

Die `SMTP.quit()`-Methode sollte man auch *aufrufen*!

Man sollte ein wenig Wartezeit beim „busy waiting“ auf den Pin einfügen sonst wird da 100% Prozessorzeit nur für's warten verbraten. Noch besser wäre es gar kein „busy waiting“ zu machen. Das `GPIO`-Modul bietet das soweit ich weis auch etwas mit Rückruffunktion an das effizienter und CPU-schonender ist.

Ich bin dann ungefähr bei so etwas (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import smtplib
from time import sleep
from datetime import datetime as DateTime
from email.mime.text import MIMEText

import RPi.GPIO as GPIO

THE_PIN = 4  # TODO Find a more descriptive name.
FROM_EMAIL = 'stevenkrieg@hotmail.de'
TO_EMAIL = FROM_EMAIL
SMTP_SERVER = 'smtp-mail.outlook.com'
SMTP_USERNAME = FROM_EMAIL
SMTP_PASSWORD = '********'


def main():
    GPIO.cleanup()
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(THE_PIN, GPIO.IN)

    print('Warte auf Signal am Pin', THE_PIN)
    #
    # TODO Remove busy waiting!! Urgent!!!
    #
    while True:
        if GPIO.input(THE_PIN):
            now = DateTime.now()
            text = 'Diese E-Mail wurde vom Raspberry Pi versendet.'
            message = MIMEText(text)
            message['Subject'] = (
                'Ereignis auf Raspberry Pi - {0:%a %d %b %Y, %H:%M:%S}'.format(
                    now
                )
            )
            message['From'] = FROM_EMAIL
            message['To'] = TO_EMAIL
            message['Date'] = format(now, '%a, %d %b %Y %H:%M:%S %z')
            smtp = smtplib.SMTP(SMTP_SERVER)
            smtp.starttls()
            smtp.login(SMTP_USERNAME, SMTP_PASSWORD)
            smtp.sendmail(message['From'], message['To'], message.as_string())
            smtp.quit()
            print('Email sent')
            sleep(2)

        while GPIO.input(THE_PIN):
            sleep(0.1)

        sleep(0.1)


if __name__ == '__main__':
    main()
malarkey
User
Beiträge: 3
Registriert: Mittwoch 10. Februar 2016, 12:59

Re: E-Mail senden bei GPIO Input

Beitragvon malarkey » Donnerstag 11. Februar 2016, 08:46

Vielen Dank für die Hilfe.
Ich muss sagen, dass ich ein Python-Neuling bin. Ich kenn Programmierungen nur mit C++ und das auch nur grundlegend. Zugegeben, mein Quelltext ist sehr zusammengewürfelt aus dem, was ich so in diversen Foren zu Emailversand und GPIO Ansteuerung gefunden habe. Das muss ja auffallen :roll: .

Deinen Code habe ich getestet. Beim Boot (es liegt immer noch keine Spannung an den Pins) erfolgt nach Programmstart kein Input. Also verschickt er nicht mehr fälschlicherweise eine Mail.

Die Installation sieht später so aus: eine Meldeanlage überwacht Kontakte an Türen und Fenster. Beim Auslösen wird diese ein Signal an den GPIO 4 senden. Python sendet daraufhin die Mail.

Ich warte erstmal einen Test mit der Anlage ab, und reboote nach der Installation je dein Pi bzw. die Anlage. Es soll ja ausgeschlossen werden, dass bei Stromausfall eine Email gesendet wird.
malarkey
User
Beiträge: 3
Registriert: Mittwoch 10. Februar 2016, 12:59

Re: E-Mail senden bei GPIO Input

Beitragvon malarkey » Donnerstag 11. Februar 2016, 15:29

Ich habe noch ein weiteres Problem. Es sollen jetzt 2 oder mehr Pins überwacht werden. Spontan fällt mir dazu nur ein, dasselbe Programm noch einmal nehmen, die Pins anpassen und diese parallel laufen lassen. Oder geht das auch sauberer?
BlackJack

Re: E-Mail senden bei GPIO Input

Beitragvon BlackJack » Donnerstag 11. Februar 2016, 17:10

@malarkey: Du könntest sie nacheinander prüfen, oder die entsprechenden Funktionen aus dem GPIO-Modul verwenden die Flanken erkennen und daraufhin Rückruffunktionen aufrufen. Was ich ja sowieso schon nahegelegt habe — kein „busy waiting“ betreiben.

Vorsicht: Die Aufrufe sind dann asynchron, also mit den üblichen Problemen mit denen man bei nebenläufiger Programmierung rechnen muss, falls die Aktionen sich Ressourcen teilen (sollen).
Benutzeravatar
kaytec
User
Beiträge: 513
Registriert: Dienstag 13. Februar 2007, 21:57

Re: E-Mail senden bei GPIO Input

Beitragvon kaytec » Donnerstag 11. Februar 2016, 17:32

Hallo marlaky,
Suche nach pulldown bzw. pulldup widerständen für die Beschaltung der Ports - Das Elektronikkompendium hat einen guten Beitrag dazu oder ziehe beim Start den Port auf 0.
Gruß Frank
MaierA
User
Beiträge: 10
Registriert: Sonntag 3. Dezember 2017, 10:32

Re: E-Mail senden bei GPIO Input

Beitragvon MaierA » Sonntag 3. Dezember 2017, 10:54

Guten Tag,
Ich hab den verbesserten Version genommen und sie erweitert um mehrere GPIO Pins abzufragen was auch funktioniert.
Mein Problem ist das ich in dem Text der Gesendet wir entweder den GPIO Wert mit angezeigt werden möchte bzw. Einem Variablem Text haben möchte z.b.
Ist PIN 6 auf Fals / 0 soll 'Betrieb Heizung' im Text gesendet werden
Ist PIN 6 auf True / 1 soll 'Störung Heizung' im Text gesendet werden, dies soll mit den Weiteren 4 Pins genau so laufen.

  1. #!/usr/bin/env python
  2. # coding: utf8
  3. from __future__ import absolute_import, division, print_function
  4. import smtplib
  5. from time import sleep
  6. from datetime import datetime as DateTime
  7. from email.mime.text import MIMEText
  8.  
  9. import RPi.GPIO as GPIO
  10.  
  11. AL1 = 5  # TODO Find a more descriptive name.
  12. AL2 = 6
  13. AL3 = 13
  14. AL4 = 19
  15. AL5 = 26
  16. A = 'Test'
  17. FROM_EMAIL = '********@gmail.com'
  18. TO_EMAIL = '*********@hotmail.com'
  19. SMTP_SERVER = 'smtp.gmail.com'
  20. SMTP_USERNAME = FROM_EMAIL
  21. SMTP_PASSWORD = '*******'
  22.  
  23.  
  24. def main():
  25.     GPIO.cleanup()
  26.     GPIO.setmode(GPIO.BCM)
  27.     GPIO.setup(AL1, GPIO.IN)
  28.     GPIO.setup(AL2, GPIO.IN)
  29.     GPIO.setup(AL3, GPIO.IN)
  30.     GPIO.setup(AL4, GPIO.IN)
  31.     GPIO.setup(AL5, GPIO.IN)
  32.  
  33.     print('Überwachung der Störungen')
  34.     #
  35.     # TODO Remove busy waiting!! Urgent!!!
  36.     #
  37.     while True:
  38.         if GPIO.input (AL1):
  39.             A = 'Stoerung Gastherme'
  40.         else:
  41.             A = 'Gastherme in Ordnung'
  42.         if GPIO.input(AL1) or GPIO.input(AL2) or GPIO.input(AL3) or GPIO.input(AL4) or GPIO.input(AL5):
  43.             now = DateTime.now()
  44.             text  = 'Störung an der Heizungsanlage \n', A
  45.             message = MIMEText(text)
  46.             message['Subject'] = (
  47.                 'Störung Heizungsanlage - {0:%a %d %b %Y, %H:%M:%S}'.format(
  48.                     now
  49.                 )
  50.             )
  51.             message['From'] = FROM_EMAIL
  52.             message['To'] = TO_EMAIL
  53.             message['Date'] = format(now, '%a, %d %b %Y %H:%M:%S %z')
  54.             smtp = smtplib.SMTP(SMTP_SERVER)
  55.             smtp.starttls()
  56.             smtp.login(SMTP_USERNAME, SMTP_PASSWORD)
  57.             smtp.sendmail(message['From'], message['To'], message.as_string())
  58.             smtp.quit()
  59.             print('Email sent')
  60.             sleep(2)
  61.  
  62.         while GPIO.input(AL1):
  63.             sleep(0.1)
  64.  
  65.         sleep(0.1)
  66.  
  67.  
  68. if __name__ == '__main__':
  69.     main()
  70.  
MaierA
User
Beiträge: 10
Registriert: Sonntag 3. Dezember 2017, 10:32

Re: E-Mail senden bei GPIO Input

Beitragvon MaierA » Sonntag 3. Dezember 2017, 11:32

MaierA hat geschrieben:Guten Tag,
Ich hab den verbesserten Version genommen und sie erweitert um mehrere GPIO Pins abzufragen was auch funktioniert.
Mein Problem ist das ich in dem Text der Gesendet wir entweder den GPIO Wert mit angezeigt werden möchte bzw. Einem Variablem Text haben möchte z.b.
Ist PIN 6 auf Fals / 0 soll 'Betrieb Heizung' im Text gesendet werden
Ist PIN 6 auf True / 1 soll 'Störung Heizung' im Text gesendet werden, dies soll mit den Weiteren 4 Pins genau so laufen.

Bei der jetzigen Version kommt eine Fehlermeldung.

  1. #!/usr/bin/env python
  2. # coding: utf8
  3. from __future__ import absolute_import, division, print_function
  4. import smtplib
  5. from time import sleep
  6. from datetime import datetime as DateTime
  7. from email.mime.text import MIMEText
  8.  
  9. import RPi.GPIO as GPIO
  10.  
  11. AL1 = 5  # TODO Find a more descriptive name.
  12. AL2 = 6
  13. AL3 = 13
  14. AL4 = 19
  15. AL5 = 26
  16. A = 'Test'
  17. FROM_EMAIL = '********@gmail.com'
  18. TO_EMAIL = '*********@hotmail.com'
  19. SMTP_SERVER = 'smtp.gmail.com'
  20. SMTP_USERNAME = FROM_EMAIL
  21. SMTP_PASSWORD = '*******'
  22.  
  23.  
  24. def main():
  25.     GPIO.cleanup()
  26.     GPIO.setmode(GPIO.BCM)
  27.     GPIO.setup(AL1, GPIO.IN)
  28.     GPIO.setup(AL2, GPIO.IN)
  29.     GPIO.setup(AL3, GPIO.IN)
  30.     GPIO.setup(AL4, GPIO.IN)
  31.     GPIO.setup(AL5, GPIO.IN)
  32.  
  33.     print('Überwachung der Störungen')
  34.     #
  35.     # TODO Remove busy waiting!! Urgent!!!
  36.     #
  37.     while True:
  38.         if GPIO.input (AL1):
  39.             A = 'Stoerung Gastherme'
  40.         else:
  41.             A = 'Gastherme in Ordnung'
  42.         if GPIO.input(AL1) or GPIO.input(AL2) or GPIO.input(AL3) or GPIO.input(AL4) or GPIO.input(AL5):
  43.             now = DateTime.now()
  44.             text  = 'Störung an der Heizungsanlage \n', A
  45.             message = MIMEText(text)
  46.             message['Subject'] = (
  47.                 'Störung Heizungsanlage - {0:%a %d %b %Y, %H:%M:%S}'.format(
  48.                     now
  49.                 )
  50.             )
  51.             message['From'] = FROM_EMAIL
  52.             message['To'] = TO_EMAIL
  53.             message['Date'] = format(now, '%a, %d %b %Y %H:%M:%S %z')
  54.             smtp = smtplib.SMTP(SMTP_SERVER)
  55.             smtp.starttls()
  56.             smtp.login(SMTP_USERNAME, SMTP_PASSWORD)
  57.             smtp.sendmail(message['From'], message['To'], message.as_string())
  58.             smtp.quit()
  59.             print('Email sent')
  60.             sleep(2)
  61.  
  62.         while GPIO.input(AL1):
  63.             sleep(0.1)
  64.  
  65.         sleep(0.1)
  66.  
  67.  
  68. if __name__ == '__main__':
  69.     main()
  70.  
Benutzeravatar
__deets__
User
Beiträge: 2147
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: E-Mail senden bei GPIO Input

Beitragvon __deets__ » Sonntag 3. Dezember 2017, 12:28

Das Skript hat einen dicken Fehler. Im Fehlerfall wird es eine Maschinengewehrsalve an E-Mails feuern. Pro 0.1 Sekunde eine... das willst du doch nicht, oder?
MaierA
User
Beiträge: 10
Registriert: Sonntag 3. Dezember 2017, 10:32

Re: E-Mail senden bei GPIO Input

Beitragvon MaierA » Sonntag 3. Dezember 2017, 13:06

__deets__:
Laut Test kommt nur eine Mail was auch passen würde, werde es aber gleich nochmal probieren und antworten.

Hab's getestet, bekomme nicht jede 0,1 sek eine Email aber ca im 1 sek Takt. da muss ich mir noch was überlegen.

Am besten wäre wenn ich bei jeder Änderung eine Mail bekomme mit den Variablen.

Meine Fehlermeldung ist folgende:

  1. Python 3.5.3 (default, Jan 19 2017, 14:11:04)
  2. [GCC 6.3.0 20170124] on linux
  3. Type "copyright", "credits" or "license()" for more information.
  4. >>>
  5. =============== RESTART: /home/pi/Documents/send_email_v1.0.py ===============
  6.  
  7. Warning (from warnings module):
  8.   File "/home/pi/Documents/send_email_v1.0.py", line 25
  9.     GPIO.cleanup()
  10. RuntimeWarning: No channels have been set up yet - nothing to clean up!  Try cleaning up at the end of your program instead!
  11. Überwachung der Störungen
  12. Traceback (most recent call last):
  13.   File "/home/pi/Documents/send_email_v1.0.py", line 69, in <module>
  14.     main()
  15.   File "/home/pi/Documents/send_email_v1.0.py", line 45, in main
  16.     message = MIMEText(text)
  17.   File "/usr/lib/python3.5/email/mime/text.py", line 34, in __init__
  18.     _text.encode('us-ascii')
  19. AttributeError: 'tuple' object has no attribute 'encode'
  20. >>>
Benutzeravatar
__deets__
User
Beiträge: 2147
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: E-Mail senden bei GPIO Input

Beitragvon __deets__ » Sonntag 3. Dezember 2017, 13:15

Ja, die 0.1 wäre falsch weil du noch einen sleep mehr drin hast. Aber jede Sekunde ist auch zu viel. Du musst die Frage beantworten können, warum die kleine while Schleife unten da ist. Dann kommst du drauf.

Und der Fehler kommt von Zeile 44 - da baust du ein Tupel (mit dem Komma), und nicht einen zusammengesetzten Text.
MaierA
User
Beiträge: 10
Registriert: Sonntag 3. Dezember 2017, 10:32

Re: E-Mail senden bei GPIO Input

Beitragvon MaierA » Sonntag 3. Dezember 2017, 13:31

__deets__:
Mit SPS hab ich schon viel Erfahrung, mit Python bzw. mit Skript schreiben leider kaum. Da ich den Skript nicht selber geschrieben habe, komme ich leider nicht so richtig auf den Trichter für was die kleine Schleife sein soll.

Zu Zeile 44:
Wenn ich das Komma weg lasse kommt Invalid Syntax mit Makierung des Bustabens A
Wenn ich die Variable in den Text mit rein nehme : 'Störung der Heizungsanlage /n A' schreibt er mir nur den Buchstaben :(

Bin leider etwas Überfragt und wäre sehr froh wenn du mir helfen könntest.
Benutzeravatar
__deets__
User
Beiträge: 2147
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: E-Mail senden bei GPIO Input

Beitragvon __deets__ » Sonntag 3. Dezember 2017, 13:36

Du musst den gesamten Text erzeugen. Komma ist halt nicht die Operation dafür. Sondern +. Oder besser noch der Aufruf von Format, wie weiter unten geschehen.

Und denk nochmal genau über die Bedingung nach, im Original Skript. Warum wird die while-Schleife verlassen? Und warum ist das wichtig?
MaierA
User
Beiträge: 10
Registriert: Sonntag 3. Dezember 2017, 10:32

Re: E-Mail senden bei GPIO Input

Beitragvon MaierA » Sonntag 3. Dezember 2017, 13:41

__deets__
Danke schon mal für den Lösung von dem Problem in Zeile 44
Auch Danke für die Frage damit ich in die richtige Richtung hinterfrage.
Ich melde mich wenn ich meine zu Wissen wie ich das Problem löse bzw. warum es so ist.
MaierA
User
Beiträge: 10
Registriert: Sonntag 3. Dezember 2017, 10:32

Re: E-Mail senden bei GPIO Input

Beitragvon MaierA » Sonntag 3. Dezember 2017, 14:10

__deets__
Also wenn ich richtig denke ist die while schleife am ende um die Zeit zu verzögern bis erneut eine Email gesendet wird. also wenn ein Wert wahr ist dann verzögert die Schleife das erneute Abfragen den while true schleife.
Wichtig ist es das nicht jede 0.1 sek eine Email abgesendet werden darf.
Liege ich damit richtig?

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder