# MicroPython Acceleration

MicroPython is an implementation of the Python programming language for microcontrollers that enables the audience of this language using the familiar syntax and programming principles to work with small computing devices.

In my work, I use MicroPython to prototype, quickly test ideas and create small stands. Thanks to REPL and simple syntax, MicroPython is also great for DIY projects and for teaching programming.

When it comes to the interaction of computers with the real world, I am always interested in the speed of their interaction. In some cases, the use of microprocessor technology, for example in the field of the Internet of things, the reaction speed of the device is not so important. There isn’t much difference when the alarm siren turns on: 10 microseconds after motion detection or 10 milliseconds.

But in some aspects, the speed and reaction time is important and the question arises as to the appropriateness of using MicroPython. So I did a little research, which was inspired by the video from the presentation of the creator of MicroPython Damien George. I wondered how quickly a program written in Micropython would respond to input impact.

The experimental device will be the ESP8266 microcontroller, on the NodeMcu board with MicroPython version esp8266-2018511-v1.9.4 on board. I will press the button and register on the oscilloscope the time difference between pressing and the appearance of 3.3 V on the other leg of the microprocessor. Each measurement is done 15 times, the average is taken (illustrated in the graphs) and the standard deviation is calculated (black bar in the graphs).

# Test number 1.

If you solve this problem "head on", then the program looks pretty trivial:

``````import machine
import time
o = machine.Pin(5, machine.Pin.OUT)  #D1 out
i = machine.Pin(4, machine.Pin.IN)        #D2 in
while 1:
if i.value():
o.value(1)
time.sleep(0.1)
o.value(0)``````

A typical waveform with such a program looks like this: Here and on other waveforms, the “blue” signal is the pin with the button, the “green” response pin. With 15 repetitions, the following picture is obtained: On average, the reaction time is about 310 microseconds, the maximum is 356 μs, not very fast, but for some applications it is quite acceptable.

# Test number 2

You can speed up the standard code “out of the box” through interrupt handling.

``````import machine
import time
o = machine.Pin(5, machine.Pin.OUT)  #D1 out
i = machine.Pin(4, machine.Pin.IN)       #D2 in
def f(_):
o.value(1)
time.sleep(0.1)
o.value(0)
i.irq(trigger=machine.Pin.IRQ_RISING, handler=f)
``````

And the picture is as follows:  and the maximum response time is 306 μs.

Using interrupts gives an increase in speed of about 20%, but at the same time gives a rather large spread in response time.

# Test number 3

If the obtained speeds are not enough, then the next step is to use the @ micropython.native construction, which makes it possible to convert the Python code into native machine code. But there are some limitations .

Code Option:

``````import machine
import time
o = machine.Pin(5, machine.Pin.OUT)  #D1 out
i = machine.Pin(4, machine.Pin.IN)       #D2 in
@micropython.native
def f():
while 1:
if i.value():
o.value(1)
time.sleep(0.1)
o.value(0)
f()``````

Typical response pattern on the waveform: Compared to the previous method, the acceleration is almost doubled: The longest response time is 128 μs.

# Test number 4

The next step in the search for a “fast” MicroPython is to use the @ micropython.viper construct and access the microprocessor registers directly (register addresses can be found here .

``````import time
@micropython.viper
def f():
O = ptr32(0x60000300)  # регистр GPIO ESP8266
while 1:
s = ((O & 0x10) >> 4)  # считывание информации с 4 пина
if s:
O = 0x20			#активизация 5 пина
time.sleep(0.1)
O = 0x20			#деактивизация 5 пина
f()``````

And as a result, the response noticeably accelerated: The response time is very  short and not comparable to other methods (maximum 820 ns): If this is not enough, then you can use assembler inserts through the @ micropython.asm_thumb decorator. With this method, python doesn’t really remain (and Python’s high-level advantages are lost), and if higher speeds are needed, it is better to use other hardware, for example FPGA (where Python can also be useful, see here and here ).

# UART

If there is a need to transmit a lot of information after some event, you can use the serial UART interface.

Take for example two implementation options.

The first is through interrupt handling:

``````import machine
i = machine.Pin(4, machine.Pin.IN)  #D2 in
ua = machine.UART(1)
ua.init(1000000)
def f(_):
ua.write(b'\x01')
i.irq(trigger=machine.Pin.IRQ_RISING, handler=f)``````

And the response waveform: The maximum response time is 248 μs.
And the second test through viper:

``````import machine
import time
i = machine.Pin(4, machine.Pin.IN)  #D2 in
ua = machine.UART(1)
ua.init(1000000)
@micropython.viper
def f():
O = ptr32(0x60000300)
while 1:
if ((O & 0x10) >> 4):
ua.write(b'\x01')
time.sleep(0.1)
f()``````

And the waveform in the second test: The maximum response time with this code is 71 μs.
The average reaction time in two tests: Acceleration of the reaction is achieved due to more rapid detection of input effects in the second test.

# Conclusion

MicroPython allows you to use things that are characteristic of high-level languages ​​(OOP, exception handling, list and dict comprahansions, etc.) when programming microcontrollers, and, if necessary, significantly accelerate the "classic" Python code.