Initial commit
This commit is contained in:
commit
06531ce833
2
Makefile
Normal file
2
Makefile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
README.html: README.md template.html5
|
||||||
|
pandoc --toc -t html5 --template=template README.md > README.html
|
385
README.md
Normal file
385
README.md
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
|
||||||
|
``` 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`
|
||||||
|
|
72
template.html5
Normal file
72
template.html5
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html$if(lang)$ lang="$lang$"$endif$$if(dir)$ dir="$dir$"$endif$>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="generator" content="pandoc">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||||
|
$for(author-meta)$
|
||||||
|
<meta name="author" content="$author-meta$">
|
||||||
|
$endfor$
|
||||||
|
$if(date-meta)$
|
||||||
|
<meta name="dcterms.date" content="$date-meta$">
|
||||||
|
$endif$
|
||||||
|
$if(keywords)$
|
||||||
|
<meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$">
|
||||||
|
$endif$
|
||||||
|
<title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
|
||||||
|
<style type="text/css">code{white-space: pre;}</style>
|
||||||
|
$if(quotes)$
|
||||||
|
<style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style>
|
||||||
|
$endif$
|
||||||
|
$if(highlighting-css)$
|
||||||
|
<style type="text/css">
|
||||||
|
body { background-color: white; }
|
||||||
|
div.sourceCode { background-color: #dddddd; padding: 0 0.5em; }
|
||||||
|
#content { background-color: #eeeeee; max-width: 40em; margin: auto; padding: 1em; }
|
||||||
|
$highlighting-css$
|
||||||
|
</style>
|
||||||
|
$endif$
|
||||||
|
$for(css)$
|
||||||
|
<link rel="stylesheet" href="$css$">
|
||||||
|
$endfor$
|
||||||
|
$if(math)$
|
||||||
|
$math$
|
||||||
|
$endif$
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
$for(header-includes)$
|
||||||
|
$header-includes$
|
||||||
|
$endfor$
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
$for(include-before)$
|
||||||
|
$include-before$
|
||||||
|
$endfor$
|
||||||
|
$if(title)$
|
||||||
|
<header>
|
||||||
|
<h1 class="title">$title$</h1>
|
||||||
|
$if(subtitle)$
|
||||||
|
<p class="subtitle">$subtitle$</p>
|
||||||
|
$endif$
|
||||||
|
$for(author)$
|
||||||
|
<p class="author">$author$</p>
|
||||||
|
$endfor$
|
||||||
|
$if(date)$
|
||||||
|
<p class="date">$date$</p>
|
||||||
|
$endif$
|
||||||
|
</header>
|
||||||
|
$endif$
|
||||||
|
$if(toc)$
|
||||||
|
<nav id="$idprefix$TOC">
|
||||||
|
$toc$
|
||||||
|
</nav>
|
||||||
|
$endif$
|
||||||
|
$body$
|
||||||
|
$for(include-after)$
|
||||||
|
$include-after$
|
||||||
|
$endfor$
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user