What is it?

  • “micro-ified”
  • MicroPython-specific libraries
    • btree - simple BTree database
    • framebuf - Frame buffer manipulation
    • machine - functions related to the hardware
    • micropython - access and control MicroPython internals
    • network - network configuration
    • ucryptolib - cryptographic ciphers
    • uctypes - access binary data in a structured way
  • ESP8266-specific library
    • esp
  • Optimizations
  • Limitations

In [35]:
%serialconnect


serial exception on close write failed: [Errno 5] Input/output error
Connecting to --port=/dev/ttyUSB1 --baud=115200 
Ready.

Getting Connected

  • Obtain a terminal emulater to access REPL:
    • Linux: mpfshell or picocom /dev/ttyUSB0 -b115200
    • Windows: TeraTerm
    • Mac: the built-in screen program
  • Optionally, WebREPL:
    • import webrepl_setup

Find your SSID

  • $ nmcli -f SSID,BSSID,DEVICE dev wifi

In [36]:
import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)  # activate station interface
#sta_if.connect('<your ESSID>', '<your password>')
if sta_if.isconnected():
    ip_address, netmask, gateway, dns = sta_if.ifconfig()
    print('IP: {}, netmask: {}, gateway: {}, DNS: {}'.format(*sta_if.ifconfig()))
else:
    'Not Connected'


[leftinbuffer] ['scandone']
[leftinbuffer] ['no FiOS-8N3GB found, reconnect after 1s']
[leftinbuffer] ['reconnect']
[leftinbuffer] ['scandone']
[leftinbuffer] ['no FiOS-8N3GB found, reconnect after 1s']
[leftinbuffer] ['reconnect']

In [4]:
ap_if = network.WLAN(network.AP_IF)
print(ap_if.active())  # Activate access point.
ap_if.active(False)  # Disable if not using.


True
bcn 0
del if1
pm open,type:2 0
mode : sta(a0:20:a6:34:99:59)

In [5]:
import socket

def http_get(url):
    _, _, host, path = url.split('/', 3)
    addr = socket.getaddrinfo(host, 80)[0][-1]
    s = socket.socket()
    s.connect(addr)
    s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8'))
    while True:
        data = s.recv(100)
        if data:
            print(str(data, 'utf8'), end='')
        else:
            break
    s.close()

http_get('http://micropython.org/ks/test.html')


HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Wed, 06 Mar 2019 04:12:21 GMT
Content-Type: text/html
Content-Length: 180
Last-Modified: Tue, 03 Dec 2013 00:16:26 GMT
Connection: close
Vary: Accept-Encoding
ETag: "529d22da-b4"
Accept-Ranges: bytes

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Test</title>
    </head>
    <body>
        <h1>Test</h1>
        It's working if you can read this!
    </body>
</html>

In [6]:
import machine
pins = [machine.Pin(i, machine.Pin.IN) for i in (0, 2, 4, 5, 12, 13, 14, 15)]

html = """<!DOCTYPE html>
<html>
    <head> <title>ESP8266 Pins</title> </head>
    <body> <h1>ESP8266 Pins</h1>
        <table border="1"> <tr><th>Pin</th><th>Value</th></tr> %s </table>
    </body>
</html>
"""

import socket
addr = socket.getaddrinfo('0.0.0.0', 8081)[0][-1]

s = socket.socket()
s.bind(addr)
s.listen(1)

print('listening on', addr)

while True:
    cl, addr = s.accept()
    print('client connected from', addr)
    cl_file = cl.makefile('rwb', 0)
    while True:
        line = cl_file.readline()
        if not line or line == b'\r\n':
            break
    rows = ['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
    response = html % '\n'.join(rows)
    cl.send(response)
    cl.close()


Traceback (most recent call last):
  File "<stdin>", line 17, in <module>
OSError: [Errno 98] EADDRINUSE

Micropython for Jupyter Notebook (ESP8266-specific)

If you get this error:


In [60]:
%serialconnect
# May need to allow permission:
#  sudo chmod 777 /dev/ttyUSB0
# Could also add your user to the dialout group
#  sudo usermod -a -G dialout your_username


serial exception on close write failed: [Errno 5] Input/output error
Connecting to --port=/dev/ttyUSB0 --baud=115200 
Ready.

In [61]:
%lsmagic
# Common commands:
# %rebootdevice
# %sendtofile
# %disconnect
# %sendtofile yourfilename.py


%capture [--quiet] [--QUIET] outputfilename
    records output to a file

%comment
    print this into output

%disconnect [--raw]
    disconnects from web/serial connection

%esptool [--port PORT] {erase,esp32,esp8266} [binfile]
    commands for flashing your esp-device

%fetchfile [--binary] [--print] [--quiet] [--QUIET]
                  sourcefilename [destinationfilename]
    fetch and save a file from the device

%lsmagic
    list magic commands

%mpy-cross [--set-exe SET_EXE] [pyfile]
    cross-compile a .py file to a .mpy file

%readbytes
    does serial.read_all()

%readbytes [--binary]
    does serial.read_all()

%rebootdevice
    reboots device

%sendtofile [--append] [--mkdir] [--binary] [--execute]
                   [--source [SOURCE]] [--quiet] [--QUIET]
                   [destinationfilename]
    send cell contents or file/direcectory to the device

%serialconnect [--raw] [--port PORT] [--baud BAUD] [--verbose]
    connects to a device over USB wire

%socketconnect [--raw] ipnumber portnumber
    connects to a socket of a device over wifi

%suppressendcode
    doesn't send x04 or wait to read after sending the contents of the cell
  (assists for debugging using %writebytes and %readbytes)

%websocketconnect [--raw] [--password PASSWORD] [--verbose]
                         [websocketurl]
    connects to the webREPL websocket of an ESP8266 over wifi
    websocketurl defaults to ws://192.168.4.1:8266 but be sure to be connected

%writebytes [--binary] [--verbose] stringtosend
    does serial.write() of the python quoted string given

%%writefile [--append] [--execute] destinationfilename
    write contents of cell to a file


In [46]:
import network

wlan = network.WLAN(network.STA_IF) # create station interface
wlan.active(True)       # activate the interface
wlan.scan()             # scan for access points
wlan.isconnected()      # check if the station is connected to an AP
wlan.connect('Student', 'Improving') # connect to an AP
wlan.config('mac')      # get the interface's MAC adddress
wlan.ifconfig()         # get the interface's IP/netmask/gw/DNS addresses

ap = network.WLAN(network.AP_IF) # create access-point interface
ap.active(True)         # activate the interface
ap.config(essid='ESP-AP') # set the ESSID of the access point


scandone

In [63]:
# Simple demo.

import machine
from machine import Pin
import dht
from time import sleep

motion = Pin(14, Pin.IN, Pin.PULL_UP)
light = Pin(12, Pin.IN, Pin.PULL_UP)
hum = dht.DHT22(Pin(13))
blue = Pin(0, Pin.OUT)
green = Pin(4, Pin.OUT)
red = Pin(5, Pin.OUT)

In [64]:
def blink(led, loops=1, delay=0.25):
     for _ in range(loops):
         led.on()
         sleep(delay)
         led.off()
         sleep(delay)

def main():
     while True:
         if light.value():
             blue.on()
         else:
             blue.off()
         if motion.value():
             green.on()
         else:
             green.off()
         hum.measure()
         if hum.temperature() > 26 or hum.humidity() > 50:
             red.on()
         else:
             red.off()
         sleep(2)
main()  # To end script, interrupt kernel (or press 'Esc' twice and 'i' twice).


...........................................................

*** Sending Ctrl-C

Traceback (most recent call last):
  File "<stdin>", line 24, in <module>
  File "<stdin>", line 18, in main
  File "dht.py", line 16, in measure
KeyboardInterrupt: 

Demo time...

Simple loop with no efficiency in mind.


In [65]:
import time, machine

blue = machine.Pin(0, machine.Pin.OUT)
loops = 200_000
start = time.ticks_us()

for i in range(loops):
    blue.on()
    blue.off()

end = time.ticks_us()
diff = time.ticks_diff(end, start)
temp = '{:5.3f} sec, {:6.3f} usec/blink, {:8.2f} kblinks/sec'
print(temp.format(diff * 1e-6, diff / loops, loops / diff * 1e3))


.9.137 sec, 45.686 usec/blink,    21.89 kblinks/sec

Simple loop wrapped in a function.

  • Number of loops no longer looked up in global scope.

In [66]:
import time, machine

blue = machine.Pin(0, machine.Pin.OUT)
loops = 200_000

def blink_me(num):
    for i in range(num):
        blue.on()
        blue.off()

def time_me(func, num):
    start = time.ticks_us()
    func(num)
    end = time.ticks_us()
    diff = time.ticks_diff(end, start)
    temp = '{:5.3f} sec, {:6.3f} usec/blink, {:8.2f} kblinks/sec'
    print(temp.format(diff * 1e-6, diff / num, num / diff * 1e3))

time_me(blink_me, loops)


..8.196 sec, 40.980 usec/blink,    24.40 kblinks/sec

Preload methods into local scope of function.

  • Further reduces lookups.

In [67]:
import time, machine

blue = machine.Pin(0, machine.Pin.OUT)
loops = 200_000

def blink_me(num):
    for i in range(num):
        blue.on()
        blue.off()

def time_me(func, num):
    start = time.ticks_us()
    func(num)
    end = time.ticks_us()
    diff = time.ticks_diff(end, start)
    temp = '{:5.3f} sec, {:6.3f} usec/blink, {:8.2f} kblinks/sec'
    print(temp.format(diff * 1e-6, diff / num, num / diff * 1e3))

time_me(blink_me, loops)


..8.198 sec, 40.988 usec/blink,    24.40 kblinks/sec

Patial loop unrolling.

  • Reduces the overhead of looping.

In [69]:
import time, machine

blue = machine.Pin(0, machine.Pin.OUT)
loops = 200_000

def blink_me(num):
    num //= 8
    on = blue.on
    off = blue.off
    r = range(num)
    for i in r:
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()

In [70]:
# continued from above...

def time_me(func, num):
    start = time.ticks_us()
    func(num)
    end = time.ticks_us()
    diff = time.ticks_diff(end, start)
    temp = '{:5.3f} sec, {:6.3f} usec/blink, {:8.2f} kblinks/sec'
    print(temp.format(diff * 1e-6, diff / num, num / diff * 1e3))

time_me(blink_me, loops)


3.409 sec, 17.043 usec/blink,    58.68 kblinks/sec

Machine code instead of bytecode.


In [71]:
import time, machine

blue = machine.Pin(0, machine.Pin.OUT)
loops = 200_000

@micropython.native
def blink_me(num):
    num //= 8
    on = blue.on
    off = blue.off
    r = range(num)
    for i in r:
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()
        on()
        off()

In [72]:
# continued from above...

def time_me(func, num):
    start = time.ticks_us()
    func(num)
    end = time.ticks_us()
    diff = time.ticks_diff(end, start)
    temp = '{:5.3f} sec, {:6.3f} usec/blink, {:8.2f} kblinks/sec'
    print(temp.format(diff * 1e-6, diff / num, num / diff * 1e3))

time_me(blink_me, loops)


.2.248 sec, 11.238 usec/blink,    88.99 kblinks/sec

Viper mode!

  • Writes directly to GPIO registers.
  • Allows direct manipulation of registers.

In [ ]:
import time, machine

blue = machine.Pin(0, machine.Pin.OUT)
loops = 200_000

@micropython.viper
def blink_me(num:int):
    num //= 8
    p = ptr32(0x60000328)
    for i in range(num)
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low
        p[0] = 1 << 4 # High
        p[1] = 1 << 4 # Low

In [ ]:
# continued from above...

def time_me(func, num):
    start = time.ticks_us()
    func(num)
    end = time.ticks_us()
    diff = time.ticks_diff(end, start)
    temp = '{:5.3f} sec, {:6.3f} usec/blink, {:8.2f} kblinks/sec'
    print(temp.format(diff * 1e-6, diff / num, num / diff * 1e3))

time_me(blink_me, loops)

Assembler

  • Write directly to the GPIO registers.

In [ ]:
import time, machine

blue = machine.Pin(0, machine.Pin.OUT)
loops = 200_000

@micropython.asm_thumb
def blink_me(r0):
    lsr(r0, r0, 3)
    movwt(r1, 0x60000328)
    mov(r2, 1 << 4)
    lable(loop)
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low
    strh(r2, [r1, 0]) # High
    strh(r2, [r1, 2]) # Low

In [ ]:
# continued from above...

def time_me(func, num):
    start = time.ticks_us()
    func(num)
    end = time.ticks_us()
    diff = time.ticks_diff(end, start)
    temp = '{:5.3f} sec, {:6.3f} usec/blink, {:8.2f} kblinks/sec'
    print(temp.format(diff * 1e-6, diff / num, num / diff * 1e3))

time_me(blink_me, loops)

Fin



In [32]:
help("modules")


__main__          hashlib           socket            urandom
_boot             inisetup          ssl               ure
_onewire          io                struct            uselect
_webrepl          json              sys               usocket
apa102            lwip              time              ussl
array             machine           ubinascii         ustruct
binascii          math              ucollections      utime
btree             micropython       ucryptolib        utimeq
builtins          neopixel          uctypes           uzlib
collections       network           uerrno            webrepl
dht               ntptime           uhashlib          webrepl_setup
ds18x20           onewire           uheapq            websocket
errno             os                uio               websocket_helper
esp               port_diag         ujson             zlib
flashbdev         random            uos
framebuf          re                upip
gc                select            upip_utarfile
Plus any modules on the filesystem

In [31]:
import machine
help(machine)


object <module 'umachine'> is of type module
  __name__ -- umachine
  mem8 -- <8-bit memory>
  mem16 -- <16-bit memory>
  mem32 -- <32-bit memory>
  freq -- <function>
  reset -- <function>
  reset_cause -- <function>
  unique_id -- <function>
  idle -- <function>
  sleep -- <function>
  deepsleep -- <function>
  disable_irq -- <function>
  enable_irq -- <function>
  time_pulse_us -- <function>
  RTC -- <class 'RTC'>
  Timer -- <class 'Timer'>
  WDT -- <class 'WDT'>
  Pin -- <class 'Pin'>
  Signal -- <class 'Signal'>
  PWM -- <class 'PWM'>
  ADC -- <class 'ADC'>
  UART -- <class 'UART'>
  I2C -- <class 'I2C'>
  SPI -- <class 'HSPI'>
  DEEPSLEEP -- 4
  PWRON_RESET -- 0
  HARD_RESET -- 6
  DEEPSLEEP_RESET -- 5
  WDT_RESET -- 1
  SOFT_RESET -- 4

In [ ]:
def do_connect():
    import network
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect('<essid>', '<password>')
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())

In [89]:
help(sta_if)


object <WLAN> is of type WLAN
  active -- <function>
  connect -- <function>
  disconnect -- <function>
  status -- <function>
  scan -- <function>
  isconnected -- <function>
  config -- <function>
  ifconfig -- <function>

In [90]:
[print(i) for i in sta_if.scan()]


.scandone
(b'FiOS-8N3GB', b'H]60E\xf0', 1, -68, 3, 0)
(b'OO6E5', b'\x00\x1f\x90\xb3\xa3\xe0', 1, -88, 4, 0)
(b'MySpectrumWiFi50-2G', b'\xa8\x9a\x93\x9d\xa8V', 1, -86, 3, 0)
(b'TigerGuest', b'\xec\x08kY\xd7\xbd', 1, -86, 3, 0)
(b'ACTANONVERBA', b',03o\x16\x9b', 1, -90, 3, 0)
(b'MySpectrumWiFi5d-2G', b'\xac\x84\xc6\xc2O\xc1', 5, -89, 4, 0)
(b'MySpectrumWiFi5d-2G', b'x)\xed\x19Q[', 5, -87, 3, 0)
(b'MySpectrumWiFi5b-2G', b'x)\xed|\xcbY', 5, -90, 3, 0)
(b'SkyFi', b'\x10\xdaC\x11;\x86', 5, -90, 4, 0)
(b'2QNDG', b'\x18\x1b\xeb\x85w\xe7', 6, -92, 3, 0)
(b'WIFI8EA5BF', b'0\xf7r\x8e\xa5\xc3', 6, -89, 3, 0)
(b'NTGR_VMB_1406851322', b',03>\xf8N', 8, -90, 3, 0)
(b'KnT_2015', b'\xac\x84\xc6\x8aK\xae', 8, -88, 4, 0)
(b'DIRECT-D1-HP ENVY 5640 series', b'\x98\xe7\xf4\x185\xd2', 8, -93, 3, 0)
(b'FTRSecure_4920_VK9V', b'\x88A\xfc\x9b5Q', 10, -49, 3, 0)
(b'FTRSecure_4920_VK9V', b'\x88A\xfc\x9b5%', 10, -66, 3, 0)
(b'6975c74dc7a3e03c866430f764cfab6e', b'\xe8\xfc\xaf\xbfl\xcb', 11, -87, 3, 0)
(b'WIFIC63D0F', b'@I\x0f\xc6=\x13', 11, -93, 3, 0)
(b'tl62005e', b'\x00%\xf0b\x00^', 11, -74, 3, 0)

In [47]:
help(network)


object <module 'network'> is of type module
  __name__ -- network
  WLAN -- <function>
  phy_mode -- <function>
  STA_IF -- 0
  AP_IF -- 1
  STAT_IDLE -- 0
  STAT_CONNECTING -- 1
  STAT_WRONG_PASSWORD -- 2
  STAT_NO_AP_FOUND -- 3
  STAT_CONNECT_FAIL -- 4
  STAT_GOT_IP -- 5
  MODE_11B -- 1
  MODE_11G -- 2
  MODE_11N -- 3
  AUTH_OPEN -- 0
  AUTH_WEP -- 1
  AUTH_WPA_PSK -- 2
  AUTH_WPA2_PSK -- 3
  AUTH_WPA_WPA2_PSK -- 4

In [56]:
%rebootdevice


[leftinbuffer] [' 2 (b0)']
[leftinbuffer] ['state: 2 -> 3 (0)']
[leftinbuffer] ['state: 3 -> 5 (10)']
[leftinbuffer] ['add 0']
[leftinbuffer] ['aid 3']
[leftinbuffer] ['cnt ']
[leftinbuffer] ['connected with Student, channel 1']
[leftinbuffer] ['dhcp client start...']
[leftinbuffer] ['ip:10.1.227.225,mask:255.255.0.0,gw:10.1.1.2']
[leftinbuffer] ['10.676 sec, 53.381 usec/blink,    18.73 kblinks/sec']
[leftinbuffer] ['MicroPython v1.9.4-762-gfa50047bb on 2018-12-28; ESP module with ESP8266']
[leftinbuffer] ['Type "help()" for more information.']
[leftinbuffer] ['>>> ']
repl is in normal command mode
[\r\x03\x03] b'\r\n>>> \r\n>>> \r\nMicroPython v1.9.4-762-gfa50047bb on 2018-12-28; ESP module with ESP8266\r\nType "help()" for more information.\r\n>>> \r\n>>> \r\nPYB: softTraceback (most recent call last):\r\n  File "main.py", line 15, in <module>\r\nKeyboardInterrupt: \r\nMicroPython v1.9.4-762-gfa50047bb on 2018-12-28; ESP module with ESP8266\r\nType "help()" for more information.\r\n>>> \r\n>>> '
[\r\x01] b'\r\n>>> \r\nraw REPL; CTRL-B to exit\r\n>'

In [ ]: