.title-slide # What Python Can Do with Microcontrollers .right[Andrey Vlasovskikh] .right[JetBrains] .right[#PyConRU 2017] --- .center[
] * I'm [@vlasovskikh](http://twitter.com/vlasovskikh) on Twitter * From St.Petersburg, Russia * I'm the PyCharm Community Lead at JetBrains * IDE for Python and Web development * I've been contributing to PEP 484: Type Hints --- # Microcontrollers .center[
] * Microcontroller = CPU + memory + I/O in a single chip --- # Why are they used? * They control physical devices * Proximity sensors, accelerometers, LEDs, servomotors * They are everywhere * Tens of billions * Much more than computers and phones * Electronic watches, air conditioners, cars, robots * An integral part of the Internet of Things * They are cheap * Around $1 --- # Why should you care? * You had to program microcontrollers in _assembly_ or _C_ * Since 2014 you can use _Python_ * Well, sort of Python * And only for some microcontrollers --- # Why Python and why now? * CPUs get faster, languages can do more for you * Rapid prototyping, readability, portability, cost savings * Higher-level languages replace lower-level ones over time * 2000s: C started to replace assembly on microcontrollers * 2010s: it's time Python and others to replace C there --- # Regular Python still doesn't fit * CPUs in microcontrollers are fast enough (100 MHz) * But memory is too small (100 KB) * Tiny amount of RAM to keep the price low * CPython requires _100-1000 times more_ --- .title-slide # MicroPython Optimized for **low memory** consumption --- # MicroPython in 16 KB RAM .center[
] $ screen /dev/cu.usbmodemFD122 115200 MicroPython v1.7-9-gbe020eb on 2016-04-18; micro:bit with nRF51822 Type "help()" for more information. >>> * Micro:bit: 16 MHz CPU, **16 KB RAM**, 256 KB flash --- # Only GC for memory management * No reference counting * Unlike CPython * Drop refcount memory overheadfrom each object * Garbage collection is quite fast * For 100 KB RAM it takes a few milliseconds --- # Integers in pointers * Memory is 32-bit word addressable * Two lower bits can be used for other purposes * Regular pointers * `........ ........ ........ ......00` * Integers * `........ ........ ........ .......1` --- # Objects in ROM * Can put objects into read-only flash memory * Builtin modules, functions, strings, etc. * Importing a module is just adding an entry to `locals()` --- # Performance is not a priority .center[
] * Anti-lock braking system in cars * If a car wheel blocks during braking, temporarily reduce the braking force * Real-time, but not CPU-bound * It's enough to run the control loop every 50 ms * You must finish the current iteration in that time slot --- # Roughly the same language * The language is mostly compatible with CPython 3.4 * Some semantic differences * Different MRO, no `__del__` user for classes, etc. * Much fewer stdlib modules * Either missing or replaced by a micro-version * `json` with only two functions --- .title-slide # **7 features** of coding for microcontrollers in Python Not 5, not 6, and definitely not 8 --- .title-slide # 0. The development process --- # Microcontrollers have no OS * You're the only program * No kernel, no processes, no threads * Write binary code and data to flash * Usually over a USB serial port --- # Flashing MicroPython * Flash MicroPython to ESP8266 * Get a MicroPython binary from [micropython.org](https://micropython.org) * Run `pip install esptool` * Run esptool with appropriate options .bash $ python -m esptool --port /dev/cu.wchusbserialfd120 --baud 115200 write_flash --flash_size detect --flash_mode dio 0 /path/to/micropython.bin --- # You got REPL .python $ screen /dev/cu.wchusbserialfd120 115200 MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266 Type "help()" for more information. >>> 2 + 2 4 >>> >>> exec("import math; print(math.sqrt(2))") 1.41421 >>> >>> import os >>> os.listdir() ['boot.py', 'esptest.py', 'main.py', 'subdir'] >>> >>> with open('boot.py', 'rb') as fd: ... print(len(fd.read())) ... 162 --- # Flashing code * Compile modules into your binary image * Or create modules in REPL * REPL raw mode, `pip install adafruit-ampy` * REPL paste mode .python
>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish === with open('tmp.py', 'w') as fd: === fd.write('''\\ === print("Hello, MicroPython!") === ''') === ===
31 >>> import tmp Hello, MicroPython! --- # MicroPython plugin for PyCharm .center[
] * [https://github.com/vlasovskikh/intellij-micropython](https://github.com/vlasovskikh/intellij-micropython) --- .title-slide # 1. Types of devices Sensors, controllers, robots --- # Sensors * Devices * Temerature sensors, distance sensors, accelerometers * Program structure .python while True: data = sensor.read() store(data) deep_sleep(MEASUREMENT_INTERVAL) --- # Controllers * Devices * LEDs, displays, motors, switches * Program strucure .python while True: idle_while_no_commands() command = sensor.read() if should_act(command): device.act() --- # Robots * Negative feedback loop controllers * Cyber physical systems, homeostats, ... * Devices * Thermostats, drones, ABS and ACC in cars * Program strucure .python desired_state = ... while True: actual_state = sensor.read() difference = compare(desired_state, actual_state) if too_large(difference): device.act(difference) sleep(FEEDBACK_LOOP_INTERVAL) --- .title-slide # 2. Basic electronics --- # Only a few concepts required * School-level physics * Voltage, electric current, resistance, Ohm's law * Learn components as you encounter them * LEDs have polarity, sensitive to voltage and current, and so on .center[
] --- # Learn more * Discover new components and concepts * Pull-up resistors, batteries, capacitors, and so on * Read tutorials at [learn.sparkfun.com](https://learn.sparkfun.com/tutorials/tags/concepts) --- .title-slide # 3. Low memory usage --- # Procedural programming * Limit your memory allocations * MicroPython saved some memory for you, use it wisely * Use only basic OOP and FP * Prefer procedures that modify mutable data * I know it sounds like C * `socket.readinto(buf)`, `bytearray`, `memoryview` --- .title-slide # 4. Device drivers --- # Drivers in Python * Drivers are libraries for working with devices * DHT12 temperature and humidity sensor * It has I2C protocol pins, but we don't care * A driver should provide an API to measure and return T or H .center[
] --- # DHT12 driver .python class DHT12: def __init__(self, i2c, addr=0x5c): self.i2c = i2c self.addr = addr self.buf = bytearray(5) def measure(self): buf = self.buf self.i2c.readfrom_mem_into(self.addr, 0, buf) sum = buf[0] + buf[1] + buf[2] + buf[3] if sum & 0xff != buf[4]: raise Exception("checksum error") def humidity(self): return self\.buf[0] + self\.buf[1] * 0.1 def temperature(self): t = self\.buf[2] + (self\.buf[3] & 0x7f) * 0.1 if self\.buf[3] & 0x80: t = -t return t --- .title-slide # 5. Hardware interrupts --- # What are IRQs? * Requests to immediately react to a hardware event * High / low signal level on your pins * CPU pauses your program and jumps to the interrupt handler * Similar to callback functions * You may know callbacks from JavaScript: DOM, AJAX, Node.js --- # Only simple callbacks * Cannot allocate memory * An interrupt could occur in the middle of GC from machine import Pin from time import sleep_ms irq_count = 0 def on_irq(p): global irq_count irq_count += 1 d3 = Pin(0, Pin.IN, Pin.PULL_UP) d3.irq(on_irq, Pin.IRQ_FALLING) while True: print(irq_count) sleep_ms(50) --- .title-slide # 6. Power management --- # Sensors work on batteries * Running ESP8266 on a 2500 mAh battery * 1 day * Idle CPU mode, interrupts to react on time * 2 days with `machine.idle()` * Deep sleep turns CPU off * 6 months with `machine.deepsleep()` * Connect GPIO16 to RST to reset on IRQ from RTC .python import machine rtc = machine.RTC() rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP) rtc.alarm(rtc.ALARM0, 5000) machine.deepsleep() --- .title-slide # 7. Debugging is hard --- # {dd��|�l�|�l�c|����s�c�c��nn�dno���cp�lslsdx�g� �lbo�|�c��gg�l��l`�ol`o{���g���`x�g�s�����co�|l l�c��gg�d`�ol`gr���g� p�n�{������r�cg�|�c��'n�d `�ood`o{���orl`s��gsd`�l��|��rgc��l�b�db䄜����c �l{dll��|��ssgc��ll��b�b䄜��c��dsl�d��|��{oc��l `�ood`o{���orl`s��gsd`�l��|��rgc��l�b�db䄜����c �lbo�|�c��gg�l��l`�ol`o{���g���`x�g�s�����co�|l l�c��gg�d`�ol`gr���g� p�n�{������r�cg�|�c��'n�d �l{dll��|��ssgc��ll��b�b䄜��c��dsl�d��|��{oc��l ��c�c쌜���c�lc --- # I promised only 0..6 features * And debugging was 7th >>> features = bytearray(7) >>> features[7] Traceback (most recent call last): File "
", line 1, in
IndexError: bytearray index out of range * In C you get undefined behavior for `features[7]` * There is no MMU to give you SIGSEGV * In MicroPython REPL helps a lot * There is no debugger though... --- # Wrap-up * MicroPython is optimized for memory use * Coding for microcontrollers is different but fun :) * Follow me on Twitter * I'm [@vlasovskikh](http://twitter.com/vlasovskikh)