The MicroPython Technological Evolution
Computer programming languages are a fascinating study of technological evolution. From machine code and assembly language to the first high-level languages such as COBOL and FORTRAN, the march toward more powerful yet easier-to-comprehend languages has enabled faster development cycles and brought programming to an increasingly broader spectrum of creators, from professional to amateur.
Before the advent of Arduino and Raspberry Pis, many proto-makers got our start with PBASIC and microcontrollers such as the BASIC Stamp. In the professional embedded world, the C programming language has long reigned supreme. Indeed, the Arduino “programming language” consist of a set of C/C++ functions. This allows the complexity of programming for various embedded devices to be abstracted away to be more friendly to those who are just beginning their embedded electronics education. So while the C-language dominance is still reasonably secure, the winds of change are afoot. Enter the Python programming language.
According to a July 2020 IEEE study, Python is the most popular programming language of 2020. Its creator, Guido van Rossum, explained that Python was created for the following reasons:
“I had extensive experience with implementing an interpreted language in the ABC group at CWI, and from working with this group, I had learned a lot about language design. This is the origin of many Python features, including the use of indentation for statement grouping and the inclusion of very-high-level data types.”
Guido goes on to explain the impact of other languages on Python design choices:
“Modula-3 is the origin of the syntax and semantics used for exceptions and some other Python features.”
Also:
“My experience with error handling in Amoeba made me acutely aware of the importance of exceptions as a programming language feature.”
The Python programming language was first released over 30 years ago. Python code is first compiled to a byte-code and then fed to an interpreter, which is a large C program (see, you just can’t avoid C). A popular aspect of Python is the interactive interpreter mode or read-evaluate-print-loop (REPL), a way to interact with Python via a command-line interactive prompt to test out concepts before committing the code to a .py
file. MicroPython firmware can also be built and ran without the compiler because the virtual machine can run pre-compiled (.mpy
) programs.
Fast forward to 2014, and MicroPython is released. For those with experience with programming on the desktop, you are likely to use a particular implementation of Python known as CPython, which is the language’s reference implementation. MicroPython is simply another implementation, one that is optimized for memory and processing constrained hardware such as a microcontroller. MicroPython contains a full Python 3 compiler, runtime, and subset of the Python standard library. As an aside, MicroPython has since been forked itself into CircuitPython, which is more focused on the education and maker communities. Here are examples of MicroPython’s versatility and influence in embedded development.
The Quick and Dirty of Programming a Microcontroller with MicroPython
The MicroPython interpreter is available on quite a few development boards with various hardware features. When acquiring a MicroPython-based development board, the first thing to do is update the interpreter firmware to the latest version. This is accomplished by visiting the MicroPython firmware website and downloading the latest device firmware update (.dfu
) file for your particular board. Each board has a different physical methodology for entering an update mode. It will typically involve shorting a certain pin to a voltage or ground when applying power to the board. With the development board plugged into a computer and set to update mode, launch a utility called dfu-util
(available at https://sourceforge.net/projects/dfu-util) to flash the new firmware to the development board.
The exact mechanics of using dfu-util
will vary depending on your host system operating system (Windows, Mac OS, Linux). Some additional steps might be required if using a Windows-based host computer.
Once complete, unplug the USB cable from the development board and plug it back in. The development board should enumerate as a removable medium such as a USB flash drive or disc. The drive should be listed as /flash
, and it should contain the following four files:
boot.py
: Contains configuration information for the particular development board.main.py
: Contains the source code for your particular application.pybcdc.inf
: This is a Windows driver file that lets the host computer interface with the development board as a serial USB device.readme.txt
: A file where the developer can leave any notes to the end-users or developers who might wish to fork the main.py file to meet their own needs.
It is possible to interact with the development board using a REPL via serial terminal on the host computer. Alternatively, we can use a code editor to edit the main.py
file. A neat feature about MicroPython and associated development boards is that that source code remains as a user-accessible file. By simply plugging the development board into a computer, it enumerates as a removable storage device. The source code file can then be viewed and edited directly in the end-users code editor of choice. Then by ejecting the development board, unplugging and powering the board up, the new or modified source code is executed.
Several development tools are capable of handling MicroPython. Some of the more popular choices include:
- Mu: A free, barebones editor with quite a few bells and whistles, including code completion, REPL, file manager, and the ability to tidy the code with a simple click of the mouse. It is a great place to get started, most similar to the Arduino IDE in its simplicity.
- PyCharm: Available as both a free community version and a paid professional version, PyCharm is the workhorse of Python development environments. The free version contains features that would be reserved only for paid versions of other similar development tools. Features such as a debugger, refactoring, code inspections, on-the-fly error highlighting, and quick-fixes are all available gratis.
- Visual Studio Code: The jack-of-all-languages code editor that is the default editor for many developers already. This free offering from Microsoft is easily extensible and can be integrated with other third-party tools such as GitHub. This can make Visual Studio Code an all-in-one development environment for MicroPython projects.
Practical Differences between Writing CPython and MicroPython Code
MicroPython has been ported to many Arm® Cortex®-M4-based embedded platforms (including STM32, TI CC3200/WiPy, Teensy boards, Nordic nRF series, SAMD21, and SAMD51), Also, it has been ported to other architectures such as ESP8266, ESP32, 16bit PICs, RISC-V (RV32 and RV64), and even Lego Mindstorms EV3. MicroPython must contend with a significant variety of architectures and embedded system specifications versus the relatively more homogenous environment of CPython (mostly x86/x64 and ARM architecture). As such, there are a few not insignificant differences between CPython and MicroPython. A few key differences include:
- How to Add Custom Libraries: Unlike CPython, MicroPython does not understand the concept of current directory. So, while it also uses
sys.path
to look for modules, it is relative to either0:/
(referring to the development board’s internal flash) or relative to1:/
(refers to the SD card, if the development board is so equipped). There are two methods for creating modules.The first option is to create a file in the root directory of the flash memory or SD card, for example,
mymodule.py
. Then inmain.py
, simply add a line withimport mymodule
, and all functions located withinmodule.py
will be available tomain.py
.The second option is to create a directory within the root directory, for example,
mymodule
. Within themymodule
folder, create a file named__init__.py
and add any needed custom functions inside the file. Then from withinmain.py
, add the lineimport mymodule
.In either case, to access any modules stored on the SD card, the line
sys.path.append('1:/')
must also be added tomain.py
. - Memory Management: CPython uses automatic reference-counting as its means for memory management, whereas MicroPython uses garbage collection.
- Limited and Micro-specific Modules: One of the principal conveniences of Python is the ability to easily import code that provides a lot of useful functionality—such as parsing JSON, string handling, or web socket programming. However, MicroPython does not have access to the full complement of CPython modules. Furthermore, many MicroPython implementations of standard CPython modules account for the memory and processing speed limitations of embedded hardware. Some modules use the standard Python name. Others start with a “u”; for example,
import array
(CPython) andimport uarray
(MicroPython).Also, some modules are specific for embedded hardware. The more generic import machine module allows code to maximize portability across various MicroPython-friendly development boards. There are also board-specific modules such as import
pyb
(for the pyBoard) or importesp
(for ESP8266-based boards). Board specific modules take advantage of any unique hardware components or maximize efficiency. For example,pyb.delay(ms)
orpyb.disable_irq()
are mechanisms to request a delay or disable interrupts, respectively, tailored specifically for the pyBoard.A complete list of modules that are available for MicroPython is available here.
- Syntax Differences:
- MicroPython requires spaces between literal numbers and keywords, CPython, in contrast, doesn’t have this requirement.
- MicroPython allows using
:=
to assign to the variable of a comprehension, CPython raises a SyntaxError.
- Language and Built-in Type Differences:
- Exception chaining is not implemented in MicroPython.
- Built-in types are handled differently; for example, MicroPython does not support deleting arrays.
- User-defined attributes for functions are not supported.
- Overriding
sys.stdin
,sys.stdout
, andsys.stderr
is not possible.
The entirety of the differences between CPython and MicroPython are too many to enumerate here. For the most part, the differences should be fairly minimal to all but the most fastidious Python coders. If you encounter an error trying to implement a feature that you know works with CPython, check this site for a robust list of differences between CPython and MicroPython.
Developers, Assemble!
If speed is a priority, it is possible to write inline assembly code within MicroPython. The inline assembler supports a subset of the ARM Thumb-2 instruction set. The inline assembly is subsequently converted into Python function calls. Per the Architecture Reference Manual, “… [the assembly] instructions operate on 32-bit signed integer data except where stated otherwise. Most supported instructions operate on registers R0-R7 only: where R8-R15 is supported, this is stated. Registers R8-R12 must be restored to their initial value before return from a function. Registers R13-R15 constitute the Link Register, Stack Pointer, and Program Counter, respectively.”
Conclusion
MicroPython is an exciting new entry into the embedded development world. It offers tantalizing new possibilities for professionals and makers alike. Although it might not be ideal for certain edge use cases with extremely tight timing and performance requirements, it can be a worthy companion to the traditional C programming language. Coupled with Python language’s rise, MicroPython can serve as a bridge to desktop application developers looking to get started in embedded hardware development.