Sending email from Raspberry Pi

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.

Installation done.

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:

After installing MSMTP

Configure MSMTP:

Key the following in terminal:

cd /etc
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. ‘ username@gmail.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 username@gmail.com
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 username@gmail.com

‘ username@gmail.com ‘ is the destination email address

the masked text 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 = "xxxxxxxxxxxxxx@gmail.com"
    email_password = "xxxxxxxxxxxx"
    email_send = "xxxxxxxxxxx@yahoo.com"
    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))[8] 
            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?