diff --git a/README.md b/README.md index 331c911..8921482 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,22 @@ MicroPython Workshop Getting started --------------- +### Operating systems, drivers... + +You might need drivers for the CP210x USB-to-serial converter, which you can +get from [the Silicon Labs site][silabs]. + +On Windows, the device should show up as some COM port (e.g. COM3), on Linux as +e.g. /dev/ttyUSB0, and on OSX as /dev/tty.SLAB_USBtoUART (not as +/dev/tty.usbserial!) + +[silabs]: https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers + ### 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: +Afterwards, get `esptool` and use it to flash your board, using the correct port: ``` sh pip install esptool @@ -23,6 +34,9 @@ You can now connect to your board's REPL with e.g. `screen` or `putty`: screen /dev/ttyUSB0 115200 ``` +In Putty, select "Serial" as connection type and use your COM port as port and +115200 as speed. + You'll get a command prompt and be able to execute the first commands ``` python @@ -30,7 +44,8 @@ help() print("Hello, world") ``` -A reference of all the default libraries can be found on [readthedocs.io](https://pycopy.readthedocs.io/en/latest/) +A reference of all the default libraries can be found on +[readthedocs.io](https://pycopy.readthedocs.io/en/latest/) ### File upload using `ampy` @@ -53,7 +68,7 @@ ampy get boot.py boot.py # Create some example file echo "print('hello, world')" > hello.py -# Run a local file +# Run a local file, without uploading or saving it ampy run hello.py # Save it to the board @@ -63,7 +78,9 @@ ampy put hello.py 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. +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. Make sure not to put an infinite loop in your `boot.py`. Hello, LED ---------- @@ -74,27 +91,42 @@ 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: +# The LED is connected between GPIO 5 and 3.3 V, so +# to allow current to flow and turn the LED on, you +# need a lower voltage on your pin: +led.value(0) + +# This is (in this case somewhat confusingly) equivalent to led.off() -# and this turns it off: +# or +led(0) + +# These will all turn it off: +led.value(1) +led(1) led.on() -# You can also use numbers or booleans: +# You can also use booleans: led.value(False) -led.value(1) +led.value(True) ``` ### Brightness control with PWM +To control the brightness, we will be using [Pulse-Width Modulation][pwm], +which quickly toggles the LED between on and off at some frequency, while +varying the time it is on: + ``` python from machine import PWM pwm = PWM(led) + # Read the frequency pwm.freq() # Set it -pwm.freq(20000) +pwm.freq(1000) + # Read and set the duty cycle pwm.duty() pwm.duty(1023) @@ -102,15 +134,31 @@ pwm.duty(512) pwm.duty(0) ``` +Contrary to what some people with previous embedded programming experience +might expect, the frequency here is not the frequency of the counter, but +how often the LED toggles per second. + +The maximum duty cycle is normally 1023 (which applies 3.3 volts for 1023 out +of 1024 cycles). For high frequencies this might change. + +Frequencies as low as 1 Hz are possible - you can not use floating point +numbers as your PWM frequency. + +[pwm]: https://en.wikipedia.org/wiki/Pulse-width_modulation ### Reserved pins -- UART: Pins 1 and 3 are TX/RX +Some of the GPIOs are reserved for certain functions. Most of their numbers are +not visible on the TTGO-T8 board: + +- UART: Pins 1 and 3 are TX/RX - visible as TXD and RXD - 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. +- 34-39 are input only and do not have pullups. Only 34 and 35 are available + on the board at all. -All other pins can be used for PWM. +All other pins can be freely used for any kind of digital input or output. -Pins 2, 4, 12, 13, 14, 15 are connected the the micro-SD slot. +Pins 2, 4, 12, 13, 14, 15 are connected the the micro-SD slot and can not be +used while the SD card is being used. Digital and analog input ------------------------ @@ -127,7 +175,16 @@ 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. +When connecting a button between a pin and the supply voltage (3V3 pins), use a +pull-down - then the normal state will read as 0, and switch to 1 when the +button is pressed. + +You can also connect the button between ground (GND) and a pin and use a pull-up, +now the pin will normally read as 1 and switch to 0 when pressed. + +For analog input, use `machine.ADC`. By default, the ADC has an input range of +1.0 V, but this can be changed to up to 3.6 V using `ADC.atten`. Available pins +are 32 through 35. ``` python from machine import Pin, ADC @@ -145,7 +202,10 @@ while True: 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. +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 @@ -157,15 +217,21 @@ import utime # IDs >= 0 are hardware timers. timer = Timer(-1) +# Create variables used by the interrupt data = [0] * 32 index = 0 +# Use the normal LED as output led = Pin(5, Pin.OUT) def callback(timer): global data, index + # Store the time since startup (in ms) data[index] = utime.ticks_ms() + # Next write will be at the next index + # Restarts at index 0 when the array is full! index = (index + 1) % 32 + # Toggle the LED led(not led()) # Run the callback once, after 100 ms @@ -174,8 +240,13 @@ timer.init(mode = Timer.ONE_SHOT, period = 100, callback = callback) # Run the callback every second timer.init(period = 1000, callback = callback) -# After some time +# After a few seconds +# Most of the values will have the same last three digits +# You might see something like +# [ 456, 5662, 6662, 7662, 8662, ... ] print(data) + +# Stop the timer timer.deinit() ``` @@ -205,7 +276,9 @@ 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. +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 @@ -342,8 +415,10 @@ HTTP ### Requests ``` python +import upip +upip.install('urequests') import urequests -urequests.get('https://hotspot.vodafone.de/api/v4/login?loginProfile=6') +urequests.get('https://imaginaerraum.de') ``` ### Server