Raspberry Pi Pico W SMTP Client Send Sensor Readings via Email

In this tutorial, you will learn to send sensor readings via email with an SMTP client and MicroPython using Raspberry Pi Pico W. For demonstration purposes, we will send BME280 sensor readings such as temperature, pressure, and humidity to the Gmail SMTP server but you can modify this example for any sensor and SMTP server.

Raspberry Pi Pico W send sensor readings via email using SMTP client

We have a similar guide for ESP32 and ESP8266:

Prerequisites

Before we start this tutorial, make sure you are familiar with and have the latest version of Python 3 in your system, have set up MicoPython in Raspberry Pi Pico, and have a running Integrated Development Environment(IDE) in which we will be doing the programming. We will be using the same Thonny IDE as we have done previously in the getting started guide. If you have not followed our previous tutorial, you check it out here:

SMTP Servers Introduction

SMTP (Simple Mail Transfer Protocol) is a protocol used to send and receive emails. An SMTP server is a server that implements the SMTP protocol and is responsible for sending and delivering email messages.

Here are some popular SMTP servers:

  • Gmail: smtp.gmail.com (port 587)
  • Outlook: smtp-mail.outlook.com (port 587)
  • Yahoo Mail: smtp.mail.yahoo.com (port 465)
  • Hotmail: smtp.live.com (port 587)

Please note that different email providers may have different policies and requirements for using their SMTP servers. You should consult the provider’s documentation for more information on how to use their SMTP servers.

Gmail SMTP Server Settings

You can view the Gmail server settings as shown below. Knowing this is important because we have to include them in our program code to configure our account properly.

  • Server: smtp.gmail.com
  • Sender Username: the complete email address of the sender account.
  • Sender Password: the password of the sender account
  • Server port (TLS): 587
  • Server port (SSL): 465
  • SMTP TLS/SSL required: yes

Outlook SMTP Server Settings

These are the common SMTP server settings for Outlook:

  • Server: smtp-mail.outlook.com
  • Port: 587
  • Encryption method: TLS
  • User name: Your full Outlook email address
  • Password: Your Outlook email password

Hotmail SMTP Server Settings

These are the common SMTP server settings for Outlook.com:

  • Server: smtp-mail.outlook.com
  • Port: 587
  • Encryption method: TLS
  • User name: Your full Outlook.com email address
  • Password: Your Outlook.com email password

Setting up Gmail Account

To send emails using the Raspberry Pi Pico W, we would require two email accounts. One for the sender and one for the recipient. You can use your email account as the sender account but it is recommended to create a new one just in case something goes wrong in the program code. For this article, we will use Gmail as the primary email provider. You can use any other provider according to your preference.

Creating a Gmail Account

For this tutorial, we have created a new Gmail account and we also recommend you create a new Gmail account. Because if something wrong happens with your Raspberry Pi Pico W code and you may incidentally make too many email requests from your personal account, Google may ban your account or block it temporarily. The receiver’s email address can be your personal email address.

To create a Google account:

  • Go to the Google Sign Up page: https://www.google.com/gmail/about/ with the required information, such as first and last name, desired email address, password, phone number, and date of birth.
  • Click “Next” to proceed with the verification process.
  • Read and accept the terms of service and privacy policy.
  • Click “Create Account.”
Setting new gmail account 1

Now type all the relevant information and click ‘Next’. You can give in details according to your preference.

Setting new gmail account 2

Proceed with all the steps accordingly to create your account successfully.

Create an App Password

To send an email with Raspberry Pi Pico W, first, you need to create an App password using your Gmail account. An app password in Gmail is a unique password generated for an app or device that does not support 2-step verification. When 2-step verification is enabled for a Gmail account, users are required to enter a verification code in addition to their password when signing in. However, some apps or devices may not be able to prompt for the verification code. In these cases, users can generate an app password to use instead of their regular password for accessing Gmail on the app or device. The app password is only valid for a specific app or device and can be revoked at any time from the Google Account security settings.

First, enable 2-step verification on your account by following these steps:

  • Log in to your Google Account.
  • Go to the “Security” section in your Google Account settings.
  • Scroll down to “Signing in to Google.”
  • Under “2-Step Verification,” click “Get Started.”
  • Follow the prompts to set up 2-step authentication, which may include adding a phone number to receive verification codes via text or voice call.
  • After setting up 2-step authentication, you will be prompted to enter a verification code whenever you sign in to your Google Account on a new device or browse

To get an app password:

Log in to your Google Account and Go to the “Security” section in your Google Account settings.

get app password gmail

Scroll down to “Signing in to Google.” and Click “App Passwords.”

get app password gmail 1

Follow the prompts to generate a unique password for the app you want to use with your Google Account. From the select app field, chose mail and device as other. Give any name to this device. After that click on generate.

It will generate an app password for this specific device. Save this for later use. Because we will use it inside our code instead of the Gmail password.

get app password gmail 2

Interfacing BME280 with Raspberry Pi Pico W

This section shows how to connect Raspberry Pi W Pico with BME280 sensor.

Raspberry Pi Pico W with BME280

We will require the following components:

  • Raspberry Pi Pico W
  • BME280 Sensor
  • Connecting Wires
  • Breadboard

The connection of BME280 with the Raspberry Pi Pico W is very simple. We have to connect the VCC terminal with 3.3V, ground with the ground (common ground), SCL of the sensor with SCL of the board, and SDA of the sensor with the SDA pin of the board.

Raspberry Pi Pico W I2C Pins

Raspberry Pi Pico W has two I2C controllers. Both I2C controllers are accessible through GPIO pins of Raspberry Pi Pico. The following table shows the connection of GPIO pins with both I2C controllers. Each connection of the controller can be configured through multiple GPIO pins as shown in the figure. But before using an I2C controller, you should configure in software which GPIO pins you want to use with a specific I2C controller. 

I2C ControllerGPIO Pins
I2C0 – SDAGP0/GP4/GP8/GP12/GP16/GP20
I2C0 – SCLGP1/GP5/GP9/GP13/GP17/GP21
I2C1 – SDAGP2/GP6/GP10/GP14/GP18/GP26
I2C1 – SCLGP3/GP7/GP11/GP15/GP19/GP27

The connections between the two devices which we are using can be seen below.

BME280Raspberry Pi Pico W
VCC3.3V
SDAGP20 (I2C0 SDA)
SCLGP21 (I2C0 SCL)
GNDGND

We have used the same connections as specified in the table above. However, you can use other combinations of SDA/SCL pins as well but remember to change them in the MicroPython script.

Connection Diagram Raspberry Pi Pico W with BME280

We have used the same connections as given in the tables above. All devices will be commonly grounded and will be powered with the same 3.3V pin of Raspberry Pi Pico W.

The diagram below shows the connection diagram of Raspberry Pi Pico W with BME280 sensor.

Raspberry Pi Pico W with BME280 sensor schematic diagram

Install BME280 Libraries in Thonny IDE

We will have to install the BME280 library for MicroPython to continue with our project.

To successfully do that, open your Thonny IDE with your Raspberry Pi Pico W plugged in with your laptop. Go to Tools > Manage Packages. This will open up the Thonny Package Manager.

Raspberry Pi Pico Installing ssd1306 OLED library MicoPython 1

Search for “bme280” in the search bar by typing its name and clicking the button ‘Search on PyPI.’ From the following search results click on the one highlighted below: micropython-bme280. Install this library.

Installing BME280 Micropython library Thonny

After few moments, this library will get successfully installed.

Upload umail MicroPython SMTP Client Library to Raspberry Pi Pico W

As mentioned earlier, we are using the umail SMTP client library to send emails. This library is not available in the Thonny IDE MicroPython package manager. Therefore, we need to upload it to Raspberry Pi Pico W manually through Thonny IDE.

Create a new file in Thonny IDE. Copy the following code into that file and save it with the name of umail.py.

# uMail (MicroMail) for MicroPython
# Copyright (c) 2018 Shawwwn <shawwwn1@gmai.com> https://github.com/shawwwn/uMail/blob/master/umail.py
# License: MIT
import usocket

DEFAULT_TIMEOUT = 10 # sec
LOCAL_DOMAIN = '127.0.0.1'
CMD_EHLO = 'EHLO'
CMD_STARTTLS = 'STARTTLS'
CMD_AUTH = 'AUTH'
CMD_MAIL = 'MAIL'
AUTH_PLAIN = 'PLAIN'
AUTH_LOGIN = 'LOGIN'

class SMTP:
    def cmd(self, cmd_str):
        sock = self._sock;
        sock.write('%s\r\n' % cmd_str)
        resp = []
        next = True
        while next:
            code = sock.read(3)
            next = sock.read(1) == b'-'
            resp.append(sock.readline().strip().decode())
        return int(code), resp

    def __init__(self, host, port, ssl=False, username=None, password=None):
        import ussl
        self.username = username
        addr = usocket.getaddrinfo(host, port)[0][-1]
        sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM)
        sock.settimeout(DEFAULT_TIMEOUT)
        sock.connect(addr)
        if ssl:
            sock = ussl.wrap_socket(sock)
        code = int(sock.read(3))
        sock.readline()
        assert code==220, 'cant connect to server %d, %s' % (code, resp)
        self._sock = sock

        code, resp = self.cmd(CMD_EHLO + ' ' + LOCAL_DOMAIN)
        assert code==250, '%d' % code
        if not ssl and CMD_STARTTLS in resp:
            code, resp = self.cmd(CMD_STARTTLS)
            assert code==220, 'start tls failed %d, %s' % (code, resp)
            self._sock = ussl.wrap_socket(sock)

        if username and password:
            self.login(username, password)

    def login(self, username, password):
        self.username = username
        code, resp = self.cmd(CMD_EHLO + ' ' + LOCAL_DOMAIN)
        assert code==250, '%d, %s' % (code, resp)

        auths = None
        for feature in resp:
            if feature[:4].upper() == CMD_AUTH:
                auths = feature[4:].strip('=').upper().split()
        assert auths!=None, "no auth method"

        from ubinascii import b2a_base64 as b64
        if AUTH_PLAIN in auths:
            cren = b64("\0%s\0%s" % (username, password))[:-1].decode()
            code, resp = self.cmd('%s %s %s' % (CMD_AUTH, AUTH_PLAIN, cren))
        elif AUTH_LOGIN in auths:
            code, resp = self.cmd("%s %s %s" % (CMD_AUTH, AUTH_LOGIN, b64(username)[:-1].decode()))
            assert code==334, 'wrong username %d, %s' % (code, resp)
            code, resp = self.cmd(b64(password)[:-1].decode())
        else:
            raise Exception("auth(%s) not supported " % ', '.join(auths))

        assert code==235 or code==503, 'auth error %d, %s' % (code, resp)
        return code, resp

    def to(self, addrs, mail_from=None):
        mail_from = self.username if mail_from==None else mail_from
        code, resp = self.cmd(CMD_EHLO + ' ' + LOCAL_DOMAIN)
        assert code==250, '%d' % code
        code, resp = self.cmd('MAIL FROM: <%s>' % mail_from)
        assert code==250, 'sender refused %d, %s' % (code, resp)

        if isinstance(addrs, str):
            addrs = [addrs]
        count = 0
        for addr in addrs:
            code, resp = self.cmd('RCPT TO: <%s>' % addr)
            if code!=250 and code!=251:
                print('%s refused, %s' % (addr, resp))
                count += 1
        assert count!=len(addrs), 'recipient refused, %d, %s' % (code, resp)

        code, resp = self.cmd('DATA')
        assert code==354, 'data refused, %d, %s' % (code, resp)
        return code, resp

    def write(self, content):
        self._sock.write(content)

    def send(self, content=''):
        if content:
            self.write(content)
        self._sock.write('\r\n.\r\n') # the five letter sequence marked for ending
        line = self._sock.readline()
        return (int(line[:3]), line[4:].strip().decode())

    def quit(self):
        self.cmd("QUIT")
        self._sock.close()
  • Connect the Raspberry Pi Pico W to your computer using a USB cable.
  • Open the Thonny IDE and go to “Tools” > “Options” > “Interpreters” > “Raspberry Pi Pico”.
  • Click on the “Autodetect” button to detect the Pico board connected to your computer.
  • Once the board is detected, you can upload umail.py file to Raspberry Pi Pico W in the Thonny IDE.
  • Save your code.
  • To upload the code to the Raspberry Pi Pico W, go to “Run” > “Run current script” or press the “F5” key.
  • The code will be uploaded to the Pico and automatically run.

After you have done all the above steps successfully, you will be able to use umail.py code inside your MicroPython sketch with import umail.

MicroPython Code to Send Sensor Readings via Email

This Micropython code reads sensor data from a BME280 sensor and sends an email with the sensor readings. It imports the necessary libraries, sets up network credentials and email details, initializes the I2C method, and reads the BME280 sensor for temperature, humidity, and pressure values. It then connects to a network and sends an email containing the sensor readings using the umail library.

# Import necessary libraries
import umail # Micropython lib to send emails: https://github.com/shawwwn/uMail
import network
import bme280        
import network
from machine import Pin, I2C    

# Your network credentials
ssid = 'replace_with_your_ssid' # Replace with the name of your network
password = 'replace_with_your_password' # Replace with your network password

# Email details
sender_email = 'write_senders_email' # Replace with the email address of the sender
sender_name = 'BME280 Mail Client' # Replace with the name of the sender
sender_app_password = 'write_senders_app_password' # Replace with the app password of the sender's email account
recipient_email ='write_receivers_email' # Replace with the email address of the recipient
email_subject = 'BME280 Sensor Readings' # Subject of the email

# Assign pins for BME280 sensor on Raspberry Pi Pico W
i2c = I2C(0, sda=Pin(20), scl=Pin(21), freq=400000)  # initializing the I2C method
bme = bme280.BME280(i2c=i2c)         

# Define function to read sensor data
def read_bme_sensor():
  try:
    # Get sensor readings
    temp = bme.values[0] # Temperature in Celsius
    hum = bme.values[2] # Humidity in %
    pres = bme.values[1] # Pressure in hPa

    return temp, hum, pres
    # else:
    #   return('Invalid sensor readings.')
  except OSError as e:
    return('Failed to read sensor.')
  
# Define function to connect to WiFi network
def connect_wifi(ssid, password):
  # Connect to your network
  station = network.WLAN(network.STA_IF)
  station.active(True)
  station.connect(ssid, password)
  while station.isconnected() == False:
    pass
  print('Connection successful')
  print(station.ifconfig())
    
# Connect to your WiFi network
connect_wifi(ssid, password)

# Get sensor readings
temp, hum, pres = read_bme_sensor()
print(temp)
print(hum)
print(pres)

# Send email with sensor readings
smtp = umail.SMTP('smtp.gmail.com', 465, ssl=True) # Gmail's SSL port
smtp.login(sender_email, sender_app_password)
smtp.to(recipient_email)
smtp.write("From:" + sender_name + "<"+ sender_email+">\n")
smtp.write("Subject:" + email_subject + "\n")
smtp.write("Temperature " + temp + "\n")
smtp.write("Humidity " + hum + "\n")
smtp.write("Pressure " + pres + "\n")
smtp.send()
smtp.quit()

Before uploading the above MicroPython script to Raspberry Pi Pico W replace ssid, password, sender_email, sender_app_password, and recipient email with your own details.

How MicroPython Script Works?

First imports the umail and network libraries. The umail library is an SMTP client library which provides methods to send emails through SMTP servers and the network library provides methods to connect Raspberry Pi Pico W with your network.

# Import necessary libraries
import umail # Micropython lib to send emails: https://github.com/shawwwn/uMail
import network
import bme280        
import network
from machine import Pin, I2C   

Defines the network credentials and email details such as the sender’s email address, name, and app password, the recipient’s email address, and the email subject.

ssid = 'replace_with_your_ssid' # Replace with the name of your network
password = 'replace_with_your_password' # Replace with your network password

# Email details
sender_email = 'write_senders_email' # Replace with the email address of the sender
sender_name = 'Mail Client' # Replace with the name of the sender
sender_app_password = 'write_senders_app_password' # Replace with the app password of the sender's email account
recipient_email ='write_receivers_email' # Replace with the email address of the recipient
email_subject ='Test Email' # Subject of the email

Next, we will connect the Raspberry Pi Pico W board to the Wi-Fi network. The network.WLAN() is used to create a WLAN network interface object. 

Supported interfaces are:

  • network.STA_IF (station mode)
  • network.AP_IF (Soft access point mode)

After that, activate the station by passing the “True” argument to the sta_if.active() method. The connect() method is used to connect to the specified wireless network using the specified Wi-Fi name (SSID) and password. 

In station mode, isconnected() method returns “True” if Raspberry Pi Pico W successfully connects to a Wi-Fi network and a device also assigned a valid IP address. Otherwise, it returns “False”. This statement checks if the Raspberry Pi Pico W device connects to the Wi-Fi or not. The code does not move to the next step till the board is not connected to the Wi-Fi network.

After a Wi-Fi connection is established on the Raspberry Pi Pico W board, an IP address gets assigned. The ifconfig() method provides an IP address assigned to the Raspberry Pi Pico W. In this statement, we print the IP address using the ifconfig() method on the station object which we created previously.

def connect_wifi(ssid, password):
  # Connect to your network using the provided credentials
  station = network.WLAN(network.STA_IF)
  station.active(True)
  station.connect(ssid, password)
  # Wait for the connection to be established
  while station.isconnected() == False:
    pass
  print('Connection successful') # Print a message if the connection is successful
  print(station.ifconfig()) # Print the network configuration

Call the “connect_wifi” function to connect to the network.

# Connect to your network
connect_wifi(ssid, password)

Initialize BME280 Sensor Pins

Now, we initialize the I2C method by giving it three arguments. The first argument specifies the GPIO pin for SCL. This is given as GPIO20 for Raspberry Pi Pico W. The second parameter specifies the GPIO pin for the SDA. This is given as GPIO21 for Raspberry Pi Pico W. Keep in mind, these are the default I2C pins for SCL and SDA which we have used for the Raspberry Pi Pico W. The third parameter specifies the maximum frequency for SCL to be used.

i2c = I2C(0, sda=Pin(20), scl=Pin(21), freq=400000)  # initializing the I2C method
bme = bme280.BME280(i2c=i2c)         

The read_bme_sensor() method reads the data from a BME280 sensor. It first tries to read the temperature, humidity, and pressure values from the sensor using the bme.values method on bme object. If the read is successful, it returns these values as a tuple of (temp, hum, pres). If the read is unsuccessful, it catches the OSError exception and returns the string ‘Failed to read sensor.’.

def read_bme_sensor():
  try:
    # Get sensor readings
    temp = bme.values[0] # Temperature in Celsius
    hum = bme.values[2] # Humidity in %
    pres = bme.values[1] # Pressure in hPa

    return temp, hum, pres
    # else:
    #   return('Invalid sensor readings.')
  except OSError as e:
    return('Failed to read sensor.')

Send Sensor Reading via Email with Gmail SMTP Server

First read the temperature, pressure and humidity values into a tuple as defined in above method:

# Get sensor readings
temp, hum, pres = read_bme_sensor()
print(temp)
print(hum)
print(pres)

Connect to Gmail’s SMTP server through the SSL port. Use the “SMTP” class from the “umail” library to create an object for connecting to Gmail’s secure SMTP server, which runs on port 465. If you are using another SMTP server such as Outlook or Hotmail, use the server address and port number accordingly.

smtp = umail.SMTP('smtp.gmail.com', 465, ssl=True) # Gmail's SSL port

Login to the email account using the smtp.login(). This function logs in to the Gmail account using the email address and app password provided.

smtp.login(sender_email, sender_app_password)

The smtp.to() function specifies the recipient’s email address.

smtp.to(recipient_email)

writes the email header which includes the “From” field with the sender’s name and email address.
and also writes the “Subject” field of the email header with the specified subject.

smtp.write("From:" + sender_name + "<"+ sender_email+">\n")
smtp.write("Subject:" + email_subject + "\n")

In the end, writes the body of the email. In the email body, we want to send temperature, pressure, and humidity readings that we get from BME280 sensor. Hence, these lines will construct the email body.

smtp.write("Temperature " + temp + "\n")
smtp.write("Humidity " + hum + "\n")
smtp.write("Pressure " + pres + "\n")

As soon as send() method will execute, it will send the email to the server.

smtp.send()

Finally, close the SMTP client connection with the Gmail server.

smtp.quit()

Demonstration

After uploading umail.py and main.py files to Raspberry Pi Pico W, click on the run button on Thonny IDE.

As soon as you click on the run button and if everything goes well, you will see a success message on the Thonny ID console as shown below:

Raspberry Pi Pico W send BME280 readings via email MicroPython

After a few seconds, you will receive an email in your recipient account:

Raspberry Pi Pico W send BME280 readings via email using SMTP client
Raspberry Pi Pico W send BME280 readings via email

In summary:

In this tutorial, we learned to send emails with an SMTP client and MicroPython using Raspberry Pi Pico W. We learned to send emails to SMTP servers such as Gmail, Hotmail, and Outlook

You may also like to read:

Leave a Comment