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