Sending emails from NodeMCU Esp8266

Gmail Oct 12, 2019

We'll show you how to send an email via the Gmail SMTP Server from your NodeMCU Esp8266.

Required Hardware:

  1. NodeMcu Esp8266
  2. USB Cable
  3. Internet Connection

Steps:

After connecting your NodeMCU to your laptop follow these steps,
Step 1:
Create file umail.py by writing following command in terminal

nano umail.py 

or

gedit umail.py

Put the following code into the file

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 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()
       

We use uMail as it's a lightweight SMTP client for MicroPython.

Step 2:
Go to REPL mode by using following command:

picocom /dev/tty.SLAB_USBtoUART -b115200

If You are using Linux or Windows check for the sudo permissions if the above command doesn't work

Step 3:

While in REPL,
First, import the umail.py file

import umail.py

Then, go to Paste mode in REPL by pressing Ctrl-E and paste the following code.

import umail
smtp = umail.SMTP('smtp.gmail.com', 465, ssl=True) # Gmail's SSL port
smtp.login('your_email@gmail.com', 'your_password')
smtp.to('receiver_email@gmail.com')
smtp.write("From: Your_Name <your_email@gmail.com>\n")
smtp.write("To: Receiver_Name <receiver_email@gmail.com>\n")
smtp.write("Subject: This is Test\n")
smtp.write("Yay!\n")
smtp.write("It's Working\n")
smtp.write("...\n")
smtp.send()
smtp.quit()
  • Edit the smtp.login('your_email@gmail.com', 'your_password') with your email and password to login into your Gmail Account.
  • Change smtp.to('receiver_email@gmail.com') with receiver's email address.
  • smtp.write() is used to change content of your email.
  • After writing your content smtp.send() is used o send the email.

Step 4: After completing step 3 exit the paste mode with Ctrl-D to execute the pasted code.

If you complete all the steps without any errors your email would be delivered to the receiver.

Note:

If you get AssertionError: auth error 535
Check whether "Allow less secure apps" Option is ON or not on your Gmail account, if it is OFF turn it ON.