commit 06531ce833d324c6f3e1d9070a1851c0d3aacf6f Author: Valentin Ochs Date: Sun Nov 24 20:57:07 2019 +0100 Initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..58a5670 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +README.html: README.md template.html5 + pandoc --toc -t html5 --template=template README.md > README.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..5438ab5 --- /dev/null +++ b/README.md @@ -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("Hello, world") + +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` + diff --git a/template.html5 b/template.html5 new file mode 100644 index 0000000..e956651 --- /dev/null +++ b/template.html5 @@ -0,0 +1,72 @@ + + + + + + +$for(author-meta)$ + +$endfor$ +$if(date-meta)$ + +$endif$ +$if(keywords)$ + +$endif$ + $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ + +$if(quotes)$ + +$endif$ +$if(highlighting-css)$ + +$endif$ +$for(css)$ + +$endfor$ +$if(math)$ + $math$ +$endif$ + +$for(header-includes)$ + $header-includes$ +$endfor$ + + +
+$for(include-before)$ +$include-before$ +$endfor$ +$if(title)$ +
+

$title$

+$if(subtitle)$ +

$subtitle$

+$endif$ +$for(author)$ +

$author$

+$endfor$ +$if(date)$ +

$date$

+$endif$ +
+$endif$ +$if(toc)$ + +$endif$ +$body$ +$for(include-after)$ +$include-after$ +$endfor$ +
+ +