micropython-workshop/README.md

389 lines
8.2 KiB
Markdown
Raw Normal View History

2019-11-24 19:57:07 +00:00
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](firmware.bin).
Afterwards, get `esptool` and use it to flash your board:
``` sh
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`:
``` sh
screen /dev/ttyUSB0 115200
```
You'll get a command prompt and be able to execute the first commands
``` python
help()
print("Hello, world")
```
A reference of all the default libraries can be found on [readthedocs.io](https://pycopy.readthedocs.io/en/latest/)
### File upload using `ampy`
You can use `ampy` to manage files from the CLI:
``` sh
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:
``` python
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
``` python
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
``` python
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.
``` python
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
``` python
2019-11-25 17:25:01 +00:00
from machine import Timer, Pin
2019-11-24 19:57:07 +00:00
import utime
# Create a virtual timer with ID -1.
# IDs >= 0 are hardware timers.
timer = Timer(-1)
data = [0] * 32
index = 0
2019-11-25 17:25:01 +00:00
led = Pin(5, Pin.OUT)
2019-11-24 19:57:07 +00:00
def callback(timer):
global data, index
2019-11-25 22:56:22 +00:00
data[index] = utime.ticks_ms()
2019-11-24 19:57:07 +00:00
index = (index + 1) % 32
2019-11-25 22:56:22 +00:00
led(not led())
2019-11-24 19:57:07 +00:00
# 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:
``` python
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.
``` python
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.
``` python
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.
``` python
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:
``` python
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
``` python
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:
``` python
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
``` python
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
``` python
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
``` python
import urequests
urequests.get('https://hotspot.vodafone.de/api/v4/login?loginProfile=6')
```
### Server
First, we need to install `picoweb`
``` python
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.
``` python
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`