Go to file
Valentin Ochs 06531ce833 Initial commit 2019-11-24 20:57:07 +01:00
Makefile Initial commit 2019-11-24 20:57:07 +01:00
README.md Initial commit 2019-11-24 20:57:07 +01:00
template.html5 Initial commit 2019-11-24 20:57:07 +01:00

README.md

MicroPython Workshop

Getting started

Installing MicroPython

Get a recent build of Pycopy, a fork of MicroPython that we will be using for this workshop. A build of the current state as of 2019-11-24 is here.

Afterwards, get esptool and use it to flash your board:

pip install esptool
esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py --port /dev/ttyUSB0 --chip esp32 \
    write_flash -z 0x1000 firmware.bin

You can now connect to your board's REPL with e.g. screen or putty:

screen /dev/ttyUSB0 115200

You'll get a command prompt and be able to execute the first commands

help()
print("Hello, world")

A reference of all the default libraries can be found on readthedocs.io

File upload using ampy

You can use ampy to manage files from the CLI:

pip install adafruit-ampy

export AMPY_PORT=/dev/ttyUSB0

# List files
ampy ls

# Print file contents
ampy get boot.py

# Save a file
ampy get boot.py boot.py

# Create some example file
echo "print('hello, world')" > hello.py

# Run a local file
ampy run hello.py

# Save it to the board
ampy put hello.py

# Delete it again
ampy rm hello.py

The contents of boot.py are run each time the controller resets. It may contain instructions for setting up your network connection, local time, peripherals, etc.

Hello, LED

You can control the builtin LED, which is on pin 5:

from machine import Pin
led = Pin(5, Pin.OUT)

# The values are inverted, so this turns
# the LED on:
led.off()

# and this turns it off:
led.on()

# You can also use numbers or booleans:
led.value(False)
led.value(1)

Brightness control with PWM

from machine import PWM
pwm = PWM(led)
# Read the frequency
pwm.freq()
# Set it
pwm.freq(20000)
# Read and set the duty cycle
pwm.duty()
pwm.duty(1023)
pwm.duty(512)
pwm.duty(0)

Reserved pins

  • UART: Pins 1 and 3 are TX/RX
  • Flash: 6, 7, 8, 11, 16, 17. Messing with these will probably crash your program.
  • 34-39 are input only and do not have pullups.

All other pins can be used for PWM.

Pins 2, 4, 12, 13, 14, 15 are connected the the micro-SD slot.

Digital and analog input

Use machine.Pin for digital input

from machine import Pin
pin = Pin(4, Pin.IN)
pin.value()

# You can use pullups:
pin = Pin(4, Pin.IN, Pin.PULL_UP)
pin = Pin(4, Pin.IN, Pin.PULL_DOWN)

For analog input, use machine.ADC. By default, the ADC has an input range of 1.00 V, but this can be changed to up to 3.6 V. Available pins are 32 through 39.

from machine import Pin, ADC
pin = ADC(Pin(32, Pin.IN, None))
pin.read()

# You can set the resolution, it defaults to 12 bits
pin.width(ADC.WIDTH_10BIT)

pwm = machine.PWM(Pin(5, Pin.OUT))
while True:
    pwm.duty(1023 - pin.read())

Timers

MicroPython on the ESP32 has virtual timers and 4 hardware timers. You can use a lot more virtual timers, but they are more prone to jitter. In either case, you can not allocate memory in a timer, as they are executed from an interrupt context.

Timers execute a callback, which takes the timer as an argument

from machine import Timer
import utime

# Create a virtual timer with ID -1.
# IDs >= 0 are hardware timers.
timer = Timer(-1)

data = [0] * 32
index = 0

def callback(timer):
    global data, index
    data[index] = utime.ticks_ms
    index = (index + 1) % 32

# Run the callback once, after 100 ms
timer.init(mode = Timer.ONE_SHOT, period = 100, callback = callback)

# Run the callback every second
timer.init(period = 1000, callback = callback)

# After some time
print(data)
timer.deinit()

Files

MicroPython has an internal filesystem that is stored after the firmware. You can access it in the usual way:

import os
os.listdir("/")
print( open("/boot.py").read() )

# Write to a file. Deletes existing content.
f = open("data.txt", "a")
f.write("Hello ")
f.close()

# Append
f = open("data.txt", "a")
f.write("world.\n")
f.close()

print(open("data.txt").read())

Sleep modes and power consumption

When transmitting data to a WLAN, power consumption will be 160 - 260 mA (so use WLAN.active(false) when you don't need it). Even with a deactivated modem, the power consumption can still be up to 20 mA when the CPU is running.

Idle

In idle mode, the CPU is paused but peripherals keep working.

import machine

# Enter idle mode indefinitely
machine.idle()

Execution continues when an interrupt happens, usually after a few milliseconds

Light sleep

In light sleep, most of the microprocessor will be inactive. Power consumption will be at about 0.8 mA.

import machine

# Enter light sleep until the next interrupt
machine.lightsleep()

# Enter lightsleep until the next interrupt, but
# at most for one second
machine.lightsleep(1000)

Deep sleep

In deep sleep, most of the RAM and all digital peripherals are turned off. Only the RTC and ULP co-processor stay active. Power consumption is about 10 uA. After deep sleep is exited, the ESP32 will reset.

import machine

# Same behaviour as with lightsleep
machine.deepsleep()
machine.deepsleep(10000)

# To further reduce power consumption, disable pullups:
p1 = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_HOLD)

After the reset, use machine.reset_cause() to check if we were in a deep sleep:

import machine

if machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print("Woke up from deep sleep")

RTC

The RTC of the ESP32 is not only used to keep track of time, but can also save up to 2 kiB of data during deep sleep. This memory is still volatile, however, and will be deleted if you use the reset button or remove power

import machine
rtc = machine.RTC()
rtc.memory('some data')
rtc.memory()

Network

To create or connect to a WLAN, we will use the network module.

Creating a network

You can easily create a network with a DHCP server for any clients:

import network
wlan = network.WLAN(network.AP_IF)
wlan.config(essid = "micropython",
authmode = network.AUTH_WPA2_PSK,
password = "YELLOWSUBMARINE")
# You can look at some interface information:
# Address, subnet mask, gateway, DNS server.
# Pass in a tuple containing these values to
# set the information instead.
wlan.ifconfig()

Connecting to an existing network

import network
wlan = network.WLAN(network.STA_IF)
# Activate the interface
wlan.active(True)
# Scan for visible networks
wlan.scan()
# Check connection status
wlan.isconnected()
# We are not connected. Change that
wlan.connect("micropython", "YELLOWSUBMARINE")

TCP/UDP sockets

import usocket

# Create a TCP socket
tcp = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM)

# or UDP
udp = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM)

# Listen on a port and accept a connection from a client
address = usocket.getaddrinfo('0.0.0.0', 42)[0][-1]
tcp.bind(address)
tcp.listen()
conn, remote_address = tcp.accept()

# Connect to a server
address = usocket.getaddrinfo('192.168.4.1', 42)
tcp.connect(address)

# Write data
tcp.write(b"Hello\n")

# Receive data
conn.read(16)
conn.readline()

HTTP

Requests

import urequests
urequests.get('https://hotspot.vodafone.de/api/v4/login?loginProfile=6')

Server

First, we need to install picoweb

import upip
upip.install("picoweb")
upip.install("pycopy-ulogging")

This will automatically install picoweb and its dependencies into /lib/ if you have an internet connection.

Now you can create simple websites.

import picoweb

app = picoweb.WebApp(__name__)

@app.route("/")
def index(request, response):
    yield from picoweb.start_response(response)
    yield from response.awrite("<html><body>Hello, world</body></html>")

app.run("0.0.0.0", 80, debug=True)

Next steps

In no particular order:

  • WebREPL
  • Talking to peripherals with SPI, I2C, ...
  • LED libraries for WS2812, APA102, ...
  • Interrupt handlers
  • Using the SD card
  • Using uselect to handle multiple sockets
  • Handling sensor data with umqtt