This post is about using Raspi to send email using secure SMTP server like Gmail. Especially useful when the user need to be alerted about some sensor or actuator event. As you will see below that you need to turn ON the less secure app access in gmail settings.
This can be annoying as Gmail will prompt and advice you to OFF the feature due to security reason, hence many people set up another gmail account just for Raspi to send alert emails.
And currently I have Raspbian Version 10 (Buster) installed and after 2 nights of trying, I found out that ssmtp is deprecated for Buster version OS. So the below ssmtp method is not successful even after doing the following steps:
- ON the less secure app access in gmail settings
- root setting change to postmaster
- UseTLS=YES and UseSTARTTLS=YES
- change hostname=raspberrypi
From this website (https://techrapiduk.blogspot.com/2017/04/send-email-on-raspberry-pi-with-msmtp.html), I found out that you can use MSMTP to send email from Raspi. And after trying, it WORKS. 🙂
==============The below SSMTP steps, up to Raspi logo, is the SSMTP method==========
1st you need to install ssmtp using the following command: sudo apt install ssmtp mailutils
If some components are not downloaded, you may need to do some updating. Key sudo apt-get update
After which sudo apt install ssmtp mailutils again.
Then navigate to the ssmtp folder(cd /etc/ssmtp), key more ssmtp.conf and set the email settings(your email address and password).
Then key in your email address(from), pw and the rest is the same as per picture. Remember that any user on the Raspi will be able to see your password. So you should adjust the file attributes as follows(after below pic):
Lastly you need to adjust your gmail security setting by choosing ‘ON’ for ‘Allow less secure apps’.
Remarks: The above SSMTP method does not work for Raspbian Buster
The following MSMTP sent email method works for Buster OS.↓
I learn about this method from this website ( https://techrapiduk.blogspot.com/2017/04/send-email-on-raspberry-pi-with-msmtp.html )
Adjust your gmail security setting by choosing ‘ON’ for ‘Allow less secure apps.
Install MSMTP and ca-certificates, key following in terminal:
sudo apt-get install msmtp ca-certificates
After installation completion, screen shot:
Key the following in terminal:
sudo touch msmtprc sudo nano msmtprc
You will see a blank screen with some DOS command at the bottom:
Key the following setting into the screen, then press Ctrl X , follow by ‘Y’ when ask whether to save settings. ‘ email@example.com ‘ is your ‘From’ email and ‘ thepassword ‘ is your email pw.
account default host smtp.gmail.com port 587 logfile /tmp/msmtp.log tls on tls_starttls on tls_trust_file /etc/ssl/certs/ca-certificates.crt auth login user firstname.lastname@example.org password thepassword from First Last Name account account2
Testing: Send an email
Key the following in the terminal:
echo -e "Subject: Test Mail\r\n\r\nThis is a test mail" |msmtp --debug --from=default -t email@example.com
‘ firstname.lastname@example.org ‘ is the destination email address
Finally, email received. 🙂
Combining Motion Detection and Sending Email alerts
Ok, now we shall use the ‘Raspi Camera with motion detection’ project together with sending email via Raspi. Previously we sent the email via terminal, now we wrote a python function inside the ‘motion detection and recording’ script. So when motion is detected, the Raspi cam will record a 5 sec video and also send an email alert to my email. See below:
from picamera.array import PiRGBArray # Generates a 3D RGB array from picamera import PiCamera # Provides a Python interface for the RPi Camera Module import time # Provides time-related functions from time import sleep import datetime import cv2 # OpenCV library import numpy as np # Import NumPy library import datetime import os import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email import encoders def sendEmail(): email_user = "email@example.com" email_password = "xxxxxxxxxxxx" email_send = "firstname.lastname@example.org" subject = "Test email from pi" msg = MIMEMultipart() msg["From"] = email_user msg["To"] = email_send msg["Subject"] = subject body = "Hi there, motion detected. Sending this email from Raspy!" msg.attach(MIMEText(body,"plain")) text = msg.as_string() server = smtplib.SMTP("smtp.gmail.com",587) server.starttls() server.login(email_user,email_password) server.sendmail(email_user,email_send,text) server.quit() # Initialize the camera camera = PiCamera() camera.rotation = 180 # Set the camera resolution camera.resolution = (640, 480) #camera.resolution = (1280, 720) #camera.resolution = (1640, 922) #camera.resolution = (3280, 2464) # Set the number of frames per second #camera.framerate = 30 #camera.framerate = 60 camera.framerate = 30 # Generates a 3D RGB array and stores it in rawCapture raw_capture = PiRGBArray(camera, size=(640, 480)) #raw_capture = PiRGBArray(camera, size=(1280, 720)) #raw_capture = PiRGBArray(camera, size=(1640, 922)) #raw_capture = PiRGBArray(camera, size=(3280, 2464)) # Create the background subtractor object # Feel free to modify the history as you see fit. #back_sub = cv2.createBackgroundSubtractorMOG2(history=150, #varThreshold=25, detectShadows=True) back_sub = cv2.createBackgroundSubtractorMOG2(history=25, varThreshold=20, detectShadows=True) # Wait a certain number of seconds to allow the camera time to warmup time.sleep(0.1) # Create kernel for morphological operation. You can tweak # the dimensions of the kernel. # e.g. instead of 20, 20, you can try 30, 30 kernel = np.ones((20,20),np.uint8) # Capture frames continuously from the camera for frame in camera.capture_continuous(raw_capture, format="bgr", use_video_port=True): # Grab the raw NumPy array representing the image image = frame.array # Convert to foreground mask fg_mask = back_sub.apply(image) # Close gaps using closing fg_mask = cv2.morphologyEx(fg_mask,cv2.MORPH_CLOSE,kernel) # Remove salt and pepper noise with a median filter fg_mask = cv2.medianBlur(fg_mask,5) # If a pixel is less than ##, it is considered black (background). # Otherwise, it is white (foreground). 255 is upper limit. # Modify the number after fg_mask as you see fit. _, fg_mask = cv2.threshold(fg_mask, 127, 255, cv2.THRESH_BINARY) # Find the contours of the object inside the binary image contours, hierarchy = cv2.findContours(fg_mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)[-2:] areas = [cv2.contourArea(c) for c in contours] # If there are no countours if len(areas) < 1: # Display the resulting frame cv2.imshow('Frame',image) # Wait for keyPress for 1 millisecond key = cv2.waitKey(1) & 0xFF # Clear the stream in preparation for the next frame raw_capture.truncate(0) # If "q" is pressed on the keyboard, # exit this loop if key == ord("q"): break # Go to the top of the for loop continue else: # Find the largest moving object in the image max_index = np.argmax(areas) # Draw the bounding box cnt = contours[max_index] x,y,w,h = cv2.boundingRect(cnt) cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2) # Draw circle in the center of the bounding box x2 = x + int(w/2) y2 = y + int(h/2) cv2.circle(image,(x2,y2),4,(0,255,0),-1) # Print the centroid coordinates (we'll use the center of the # bounding box) on the image text = "x: " + str(x2) + ", y: " + str(y2) cv2.putText(image, text, (x2 - 10, y2 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) # Display the resulting frameq cv2.imshow("Frame",image) #Record..... #while w > 500 and h > 500: #doesn't work while h > 350: date = datetime.datetime.now().strftime("%d-%m-%Y_%H-%M-%S") camera.start_recording('/mnt/mydisk/'+date+'.h264') sleep(5) camera.stop_recording() sendEmail() break # Wait for keyPress for 1 millisecond key = cv2.waitKey(1) & 0xFF # Clear the stream in preparation for the next frame raw_capture.truncate(0) # If "q" is pressed on the keyboard, # exit this loop if key == ord("q"): break ##Delete video files which are more than 7 days old path = '/mnt/mydisk/' today = datetime.datetime.today()#gets current time os.chdir(path) #changing path to current path(same as cd command) #Iterate thru files for root, directories, files in os.walk(path,topdown=False): for name in files: #last modified time timeStamp = os.stat(os.path.join(root, name)) filetime = datetime.datetime.fromtimestamp(timeStamp) - today #check if recirdings file is more than 7 days old, if yes then delete them if filetime.days <= -7: print(os.path.join(root, name), filetime.days) os.remove(os.path.join(root, name)) # Close down windows cv2.destroyAllWindows()
The sendEmail function is at line 16 and the function call is at line 151. Below is the email alert that I received in my yahoo mailbox.
That’s it folks, hope everyone enjoyed this geeky post. 😉
I personally feel that sending email alerts is too troublesome and not so practical. You have to set the gmail ‘allow less secure apps’ thingy, and upon setting, you will receive a few alerts and security emails from Google. And security wise is not advisable as your pw is in the script.
Therefore I will have another post where Raspi or Arduino will sent SMS alert instead of email. I think SMS alert is more practical. Right?