Button Press To Shutdown Raspberry Pi Python - python

I am writing a script for a surveillance camera. I have programmed it to take a picture when motion is detected (PIR sensor) then attach the photo to an email, to a chosen recipient. However because I am running this without a screen, mouse or keyboard attached I had no way to turn the device off without pulling the plug! so I created a script to turn off the computer on a button press(this works fine on its own) however, because the rest of the code is in a loop I don't know where to place it. Keeping in mind I need to be able to turn it off any where in the code.
If you have any ideas they would be appreciated
Thanks
James
import os, re
import sys
import smtplib
import RPi.GPIO as GPIO
import time
import picamera
inport os
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import time
import RPi.GPIO as gpio
GPIO.setmode(GPIO.BCM)
GPIO_PIR = 4
print ("PIR Module Test (CTRL-C to exit)")
GPIO.setup(GPIO_PIR,GPIO.IN)
Current_State = 0
Previous_State = 0
try:
print ("Waiting for PIR to settle ...")
while GPIO.input(GPIO_PIR)==1:
Current_State = 0
print (" Ready")
while True :
Current_State = GPIO.input(GPIO_PIR)
surv_pic = open('/home/pi/Eaglecam/surveillance.jpg', 'wb')
if Current_State==1 and Previous_State==0:
print(" Motion detected!")
with picamera.PiCamera() as cam:
cam.capture(surv_pic)
surv_pic.close()
print(' Picture Taken')
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
sender = '**************'
password = "**********"
recipient = '**************'
subject = 'INTRUDER DETECTER!!'
message = 'INTRUDER ALLERT!! INTRUDER ALERT!! CHECK OUT THIS PICTURE OF THE INTRUDER! SAVE THIS PICTURE AS EVIDENCE!'
directory = "/home/pi/Eaglecam/"
def main():
msg = MIMEMultipart()
msg['Subject'] = 'INTRUDER ALERT'
msg['To'] = recipient
msg['From'] = sender
files = os.listdir(directory)
jpgsearch = re.compile(".jpg", re.IGNORECASE)
files = filter(jpgsearch.search, files)
for filename in files:
path = os.path.join(directory, filename)
if not os.path.isfile(path):
continue
img = MIMEImage(open(path, 'rb').read(), _subtype="jpg")
img.add_header('Content-Disposition', 'attachment', filename=filename)
msg.attach(img)
part = MIMEText('text', "plain")
part.set_payload(message)
msg.attach(part)
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo
session.login(sender, password)
session.sendmail(sender, recipient, msg.as_string())
session.quit()
if __name__ == '__main__':
print(' Email Sent')
Previous_State=1
elif Current_State==0 and Previous_State==1:
print(" Ready")
Previous_State=0
time.sleep(0.01)
import time
import RPi.GPIO as gpio
except KeyboardInterrupt:
print('Quit')
GPIO.cleanup()
Here is the shutdown section of the script. Where would I place this in the loop?
gpio.setmode(gpio.BCM)
gpio.setup(7, gpio.IN, pull_up_down=gpio.PUD_UP)
buttonReleased = True
while buttonReleased:
gpio.wait_for_edge(7, gpio.FALLING)
buttonReleased = False
for i in range(1):
time.sleep(0.1)
if gpio.input(7):
buttonReleased = True
break

Two separate scripts would be unnecessary for this project.
You can do this a couple ways.
Create a global boolean variable called shutdownButton or whatever. Use a callback function for the GPIO pin, and set shutdownButton = True in the callback. And then change your main loop to be while not shutdownButton: instead of while True:.
You could just change your main loop to while gpio.input(7): instead of while True: (assuming you are pulling the pin up and then grounding it down with your switch).
And then finally just add a shutdown like os.system('sudo shutdown -h now') or call some other script you want to run to clean it up. The point is you just need a function to break out of your main loop when the button has been pressed and then shut down your pi at the end of the program.
There are other ways to do this also (I personally like the kernel patches from Adafruit that allow a power switch configuration to be added to /etc/modprobe.d...) but I only listed methods that applied directly to your question.

Related

How do i rerun a python script after it has finished

I'm trying to rerun my script after it has finished running
preferably after 30 seconds i have tried some methods but can't find out how to continue with my code
my code:
import os.path
import os
import sys
import smtplib
import time
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from PIL import ImageGrab
def enviar_email():
email = 'x.com'
password = 'x'
subject = 'This is the subject'
message = 'This is my message'
file_location = 'ss1.jpg'
msg = MIMEMultipart()
msg['x'] = email #sender
msg['x'] = email #receiver
msg['Subject'] = subject
msg.attach(MIMEText(message, 'plain'))
# Setup the attachment
filename = os.path.basename(file_location)
attachment = open(file_location, "rb").read()
image = MIMEImage(attachment, name=filename)
msg.attach(image)
# Attach the attachment to the MIMEMultipart object
# msg.attach(part)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(email, password)
text = msg.as_string()
server.sendmail(email, email, text)
server.quit()
def main():
imagem = ImageGrab.grab()
imagem.save('ss1.jpg', 'jpeg')
enviar_email()
print("Taking screenshot...")
if _name_ == '_main_':
main()
time.sleep(10)
os.execl(sys.executable, sys.executable, *sys.argv)
i tried adding:
time.sleep(10)
os.execl(sys.executable, sys.executable, *sys.argv)
but the script did not restart any advice how i can make it so that it restarts the script ?
You can call main() function again after time.sleep(10). Try this:
if _name_ == '_main_':
main()
time.sleep(10)
main()
Use the while loop :
if __name__=='__main__':
while True:
do_something()
You can even pair it with a try/except block like this :
if __name__=='__main__':
while True:
try:
do_something()
except:
print('Error Occured in running the script, Rerunning...')
This will prevent the script from abruptly stopping in case of an error.
For re running the script a specific number of times, you can define a count variable and break it when it matches a value, like this:
if __name__=='__main__':
count=0
no_of_runs=5
while True:
try:
if count==no_of_runs:
break
else:
do_something()
count+=1
except:
print('Error occured, Rerunning...')
pass
You can use schedule library for this purpose.
#!pip install schedule
import schedule
import time
while True:
# Checks whether a scheduled task
# is pending to run or not
schedule.your_function()
time.sleep(1)

Python Script To Continually Run (Better)

The below script is a working prototype of a script that runs on a rasberry pi and its job is to grab files from a specified folder on a flashdrive when plugged in. After that point it emails the files as attachments. Now, everything works perfect, but not logically in the way I want. I have a really janky while True: loop with janky sleep timers to kind delay some things. Ideally I need help developing a way for all of this to happen only ONCE per usb drive plug in. Then it would go into "Idle mode" waiting for the next stick. As of now, without my added sleep timers, It would just continually run over and over. I've been stuck trying to logically figure out a way to make it work how I would like.
Thanks in advance!
import smtplib, ssl
import shutil
import os
from time import sleep
from distutils import dir_util
from psutil import disk_partitions
from datetime import datetime
from datetime import date
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
today = date.today()
day = today.strftime("%B %d, %Y")
def CopyFilesFromSD():
try:
dir_util.copy_tree(driver+'/REDACTED', '/home/pi/Desktop/Transferred Files')
print("Files Succesfully Copied")
if 'rw' in opts:
with open(driver+'/Transfer_Log_REDACTED.txt', 'a', encoding = 'utf-8') as fp:
fp.write("\nFiles Succesfully Transferred to REDACTED on " + day + " at " + current_time)
except:
print("Device unmounted during transfer process!")
sleep(10)
def SendFiles():
dir_path = "/home/pi/Desktop/Transferred Files/"
files = os.listdir(dir_path)
msg = MIMEMultipart()
msg['To'] = "REDACTED"
msg['From'] = "REDACTED"
msg['Subject'] = "REDACTED"
password = "REDACTED"
body = MIMEText('Test.', 'html', 'utf-8')
msg.attach(body)
for f in files:
file_path = os.path.join(dir_path, f)
attachment = MIMEApplication(open(file_path, "rb").read(), _subtype="txt")
attachment.add_header('Content-Disposition','attachment', filename=f)
msg.attach(attachment)
context = ssl.create_default_context()
with smtplib.SMTP_SSL("REDACTED", 465, context=context) as server:
server.login(msg['From'], password)
server.sendmail(
msg['From'], msg['To'], msg.as_string()
)
shutil.rmtree(dir_path, ignore_errors = False)
sleep(5)
while True:
sleep(1)
for item in disk_partitions():
if 'nodev' in item.opts:
driver, opts = item.mountpoint, item.opts
CopyFilesFromSD()
SendFiles()
break
else:
continue
break
Answered by iScripters in the comment section:
https://betterprogramming.pub/how-to-run-a-python-script-on-insertion-of-a-usb-device-2e86d38dcdb
This will allow for the python script to only be ran when USB insertion is detected. No need to have a constant running program looking for drives.

Raspberry pi:send only last captured image using webcam

I have captured image using webcam and when i press button it sends mail.I use while loop as shown in below code,this code is running perfectly but problem is this program send so many image(also send earlier capured image)while we stop the program to run,so there are so many images attached in one mail only.Now i want program continuonsly running in raspberry pi but still it should be mail only last captured image so what i have to do changes for that?
Thanks in advance.
This is my code:
import smtplib
import time
import subprocess
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
import RPi.GPIO as GPIO
# Define these once; use them twice!
strFrom = 'example#gmail.com'
strTo = 'example#gmail.com'
#create email
# Create the root message and fill in the from, to, and subj$
msgRoot = MIMEMultipart()
msgRoot['Subject'] = 'subject'
msgRoot['From'] = strFrom
msgRoot['To'] = strTo
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN)
print "press button to send email"
while True:
input=GPIO.input(4)
if input == True:
print "button pressed"
subprocess.Popen(["fswebcam","-r 640x480", "capture.jpg"])
time.sleep(2)
# This example assumes the image is in the current
# directory
fp = open('capture.jpg', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
msgRoot.attach(msgImage)
# send mail
s = smtplib.SMTP('smtp.gmail.com',587)
s.starttls()
s.login('example#gmail.com' , 'password')
s.sendmail(strFrom, strTo,msgRoot.as_string())
s.close()
print "Email sent"
time.sleep(0.2)
msgRoot.attach(msgImage)
msgRoot.set_payload(msImage) #Such modified
Because you are using the same copy of the annex has been superimposed on the upswing

pySerial buffering gpsd coordinates on Raspberry Pi

I am having quite a frustrating issue, and I'm quite new to Python/Raspberry pi
I have a script that is to match gps coordinates with a SSID filtered iwlist scan and then email the resulting file out whenever there is a wifi connection.
My issue is, the results are continuously buffered, so the gps coordinates could be significantly far away from the scanned SSIDs.
I have the serial line being flushed at the beginning of the while loop, but it doesn't seem to work. I also have python running un-buffered (-u, but that may only be for the text files..) I added Output.flush(), but I have not been able to test it yet. Figured I'd ask first.
So, my question is, is there a way to turn off the serial line buffer so every iteration is getting the GPS coordinate at the time the while loop is executing? All of my text files have file.flush() after they have been written. Does that need to be before the files are written, or would that not affect the serial buffer? What am I missing?
Any help would be appreciated
#!/usr/bin/python -u
import os
import gps
import time
import serial
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
username = 'gmail account'
password = 'account password'
port = serial.Serial("/dev/ttyAMA0", baudrate=9600)
#Setting GPS session, listen on port 2947 (gpsd) of localhost
session = gps.gps("localhost", "2947")
session.stream(gps.WATCH_ENABLE | gps.WATCH_NEWSTYLE)
#Begin GPS and WiFi Scan loop..
while True:
try:
port.flushInput()
#port.flushOutput() commented out because I haven't tested with this yet.
time.sleep(.5)
report = session.next()
#Uncomment below for report data
#print report
if report['class'] == 'TPV':
if hasattr(report, 'lat' and 'lon'):
#setting GPS variables and strings
latitude = report.lat
latString = "%f" %(latitude)
longitude = report.lon
lonString = "%f" %(longitude)
#WiFi scan and parse script. I don't think it is necessary to post,
#but if it is, I will. All text files are flushed before being closed
#Email when connected
ping = os.system('sudo ping -c4 8.8.8.8')
try:
if ping == 0:
msg = MIMEMultipart()
msg['Subject'] = "GPS/WiFi data from GPS PILOT"
msg['From'] = username
msg['To'] = username
body = "GPS/WiFi data attached.."
msg.attach(MIMEText(body, 'plain'))
part = MIMEBase('application', "octet-stream")
part.set_payload(open("/home/pi/gpsMaster/dataLog.csv", "rb").read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="GPSWiFi_Scan.csv"')
msg.attach(part)
server = smtplib.SMTP('smtp.gmail.com:587')
server.ehlo()
server.starttls()
server.login(username, password)
server.sendmail(username, username, msg.as_string())
server.quit()
os.system("sudo rm /home/pi/gpsMaster/dataLog.csv")
else:
pass
except smtplib.SMTPException:
os.system('sudo reboot now')
#Adding loop delay
time.sleep(10)
#Loop ending exceptions
except KeyError:
pass
except IOError:
print ("ERROR")
#os.system('sudo reboot now')
except KeyboardInterrupt:
quit()
except StopIteration:
session = None
os.system("sudo reboot now")
I was having a similar issue. I fixed by flushing input and output.
port.flushInput()
port.flushOutput()
I appreciate that this is an old question, but it is still relevant as I hit the same problem. The issue is due to:
time.sleep(10)
in the main loop. The gpsd thread is adding received data to a queue which is accessed via:
report = session.next()
This mean that when you get to pull the next sample, over time it will drift further and further from reality (you are using GPS coordinates that lag behind your current location). We have a big queue, the consumer is pulling data much more slowly than it is being added due to the 10s sleep. I had much fun solving this and only by cutting the system to its bare essentials did I finally happen across the cause. The solution is to pull the data as soon as it is available, but discard as many data points as necessary until your delay time has been achieved. This stops the gpsd queue getting out of sync as the gpsd Python client is consuming them too slowly which causes coordinate drift over time.
Pseudo code (it can be polished, an exercise left for the reader):
import time
start = time.time()
while True:
# Consume the report from the gpsd
report = session.next()
# What time is it?
now = time.time()
# Magic number of 10s
if now > (start + 10):
# use the data contained in report
# Update with the latest time ready for the next iteration.
start = now

Motion Sensing Loop Error Python?

Hi Guys I have been working on this code for quite a while but once the code has sensed motion, taken a picture and sent it off as an email attachment it works once but once the state returns to ready it comes up with this error but I can't understand why. I apologise for including the whole code because although the error states line 43 I'm not to sure what's causing it to go wrong.
any suggestions would be greatly appreciated. I am a beginner in python programming so I may be missing something very obvious. By the way I'm running this on a raspberry pi for anyone who would like to test out the code.
Thanks
import os, re
import sys
import smtplib
import RPi.GPIO as GPIO
import time
import picamera
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
GPIO.setmode(GPIO.BCM)
GPIO_PIR = 4
print "PIR Module Test (CTRL-C to exit)"
GPIO.setup(GPIO_PIR,GPIO.IN)
Current_State = 0
Previous_State = 0
cam = picamera.PiCamera()
try:
print "Waiting for PIR to settle ..."
while GPIO.input(GPIO_PIR)==1:
Current_State = 0
print " Ready"
while True :
Current_State = GPIO.input(GPIO_PIR)
if Current_State==1 and Previous_State==0:
print " Motion detected!"
cam.capture('/home/pi/Eaglecam/surveillance.jpg')
print('picture taken')
cam.close()
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
sender = '*******************'
password = "secret!!"
recipient = '*********************'
subject = 'INTRUDER DETECTER!!'
message = 'INTRUDER ALLERT!! INTRUDER ALERT!! CHECK OUT THIS PICTURE OF THE INTRUDER! SAVE THIS PICTURE AS EVIDENCE!'
directory = "/home/pi/Eaglecam/"
def main():
msg = MIMEMultipart()
msg['Subject'] = 'INTRUDER ALERT'
msg['To'] = recipient
msg['From'] = sender
files = os.listdir(directory)
jpgsearch = re.compile(".jpg", re.IGNORECASE)
files = filter(jpgsearch.search, files)
for filename in files:
path = os.path.join(directory, filename)
if not os.path.isfile(path):
continue
img = MIMEImage(open(path, 'rb').read(), _subtype="jpg")
img.add_header('Content-Disposition', 'attachment', filename=filename)
msg.attach(img)
part = MIMEText('text', "plain")
part.set_payload(message)
msg.attach(part)
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo
session.login(sender, password)
session.sendmail(sender, recipient, msg.as_string())
session.quit()
if __name__ == '__main__':
print(' Email Sent')
Previous_State=1
elif Current_State==0 and Previous_State==1:
print " Ready"
Previous_State=0
time.sleep(0.01)
except KeyboardInterrupt:
print " Quit"
GPIO.cleanup()
Here is the Error:
Traceback (most recent call last):
File "/home/pi/Python/hi2.py", line 43, in <module>
cam.capture('/home/pi/Eaglecam/surveillance.jpg')
File "/usr/lib/python2.7/dist-packages/picamera/camera.py", line 1446, in capture
camera_port, output_port = self._get_ports(use_video_port, splitter_port)
File "/usr/lib/python2.7/dist-packages/picamera/camera.py", line 708, in _get_ports
self._camera[0].output[self.CAMERA_CAPTURE_PORT]
TypeError: 'NoneType' object has no attribute '__getitem__'
Try not persisting your cam object...picamera flushes and closes as it sees fit so you may be having problems calling the object repeatedly with the code you are using. Also, you are calling the .close() method on your cam object after if takes the first picture. I might accomplish this like so:
surv_pic = open('/home/pi/Eaglecam/surveillance.jpg', 'wb')
if Current_State==1 and Previous_State==0:
print " Motion detected!"
with picamera.PiCamera() as cam:
##if camera is not producing pictures because it is not warmed up yet uncomment the next two lines
##cam.start_preview()
##time.sleep(2)
cam.capture(surv_pic)
surv_pic.close()
SMTP_SERVER = 'smtp.gmail.com'
## continue as is from here

Categories