Blog

MicroPython – Pythonic Edge Computing

24 Apr, 2019
Xebia Background Header Wave

As the Internet of Things becomes more and more popular, so too has the need for computing solutions that work across a network of smaller devices, rather than just centralized processing servers.

To this end, developers are always challenged by hardware requirements, yet more modern solutions, such as edge computing, are increasing our demand from such components.

However, it’s often mistakenly assumed that programming edge computing on these controllers is no mean feat, as they rely on older languages and have limited potential. Yet this couldn’t be further from the truth. As an avid Python programmer – a more recent yet very popular addition to the programming landscape – I wanted to demonstrate that we can, in fact, use modern technology to get the results we want.

The Hardware Problem

So, what’s the problem? The issue is that many developers come into the industry using newer languages that appeal to them. When thinking about hardware, it’s very easy to start thinking about older solutions and means that we’ve been trying to avoid so far – we don’t want to become grounded on older approaches.

Although Python is gaining popularity and, thanks to many different frameworks and libraries, is able to secure its position as an efficient tool for solving a wide spectrum of software problems, there are parts of the electronic world that are still occupied by the old guard of programming languages. I’m talking about electronics hardware in the form of microcontrollers (MCUs) which, for years now have been, with little to no exception, programmed using C or C++, typically with very hardware-specific libraries.

The software engineering community’s perception of MCUs had been changed once before, with the rise of projects such as Arduino, which enabled a whole generation of programmers to try and experiment with writing software for devices without having to master the hardware itself. This gave birth to an enormous community, which includes both software and electronics engineers, who exchange information and often work together to solve each other’s problems and advance the Internet of Things (IoT) field.

This is a problem that’s not unlike legacy systems – if new solutions continue to be unused, then a dependency on an older, less popular option will only continue to grow. Our IoT devices might be dynamic and forward thinking, but if the underlying microcontrollers are still shackled to a limited range of options, this limits the ultimate potential our solutions can draw from them.

Once again, we can see a new big leap is taking place, with languages like JavaScript (JS) and Python entering the stage of programing languages for hardware. With this post, I would like to encourage everyone hesitant to check out these new possibilities to just give it a go with MicroPython.

What is MicroPython?

MicroPython is an implementation of a small subset of Python 3.5 interpreters. It is partially platform-specific, which means that it has a part of itself that will differ, depending on the hardware it’s running on. It has been ported onto various hardware platforms, which contain both industrial and more hobbyist MCUs. At its core, it has a layer of Python API (Application Programming Interface) built on top of C libraries provided by the hardware producer. This core is then used by libraries written in pure Python.

Why use it?

MicroPython gives you the power of Python on a microcontroller. Instead of writing your code in C or C++ and then compiling and uploading it to a device, you can simply code in Python and run your program as you would normally do on your computer. Although I’ve used MicroPython in many projects, it’s still impressive to me, especially because of Python’s reputation of being resource hungry and slow.

Talking about resources – while writing code, you generally have to be more mindful about hardware limitations, such as the number of cores or memory size. 64KB is not enough for some cases, after all. MCUs are generally less forgiving when it comes to resource allocation, so MicroPython includes different mechanism to mitigate these related difficulties.

The ability to run Python and the tools related with it on MCU makes programming electronics available not only for any Python programmer, but for every beginner programmer as well. Why? Because Python is a very user-friendly language that’s growing in popularity, often as a beginner’s first choice over the likes of JS or C++.

How does it work?

The secret to MicroPython’s success lays in a clever set of transformations that, while not very pretty or elegant, allows platform-specific code which comes through to become Python API. MicroPython, similar to CPython, is written in C and must be compiled for a specific machine. Such a version of MicroPython is called a port.

As mentioned before, at the base of each port lays a machine-specific C API. It has to be wrapped in different ways about 5 times, becoming one data structure after another, to finally transform into Python API, which can be used to run machine-specific operations. On top of this base, virtually every library implemented purely in Python can be used.

Best features

MicroPython has some features that really are a gamechanger when it comes to programming microcontrollers. Here, I want to show you that, if you know Python, programming hardware is absolutely within your reach.

First of all, this is a Python interpreter, which means that it has a fully functioning REPL (read-eval–print-loop) so you can write your code in real time, import libraries and do everything else that you would normally do in a Python REPL.

Another amazing feature is the built-in filesystem, which enables you to load files onto a microcontroller and then read and write to them. I’ve used this feature several times to upload some configuration file onto a microcontroller and then run:


>>> import json
>>> with open(‘config.json’, ‘r’) as f:
…       config = json.loads(f)

I now have a ‘config’ variable, which is a dict with the JSON (JavaScript Object Notation) data loaded into it. By default, MicroPython searches for files in its root directory.

Most of the time, you would not want to use REPL, but rather write your own program, load it on a device and make it run upon starting. Fortunately, nothing could be simpler – immediately after powering up, MicroPython runs the ‘boot.py’ file, which usually contains the hardware setup, and then ‘main.py’, which should typically contain your application code. You can, of course, upload your code in several files and then import from those files, just like you would when using regular Python REPL.

The next big feature would be installing PyPI packages directly onto your device. If you have a device with internet access and you have configured your internet connection, you simply have to run:


>>> import upip
>>> upip.install(‘micropython-requests’)

Now you have the ‘urequests’ package installed. Usually, packages are installed inside the ‘/libs’ directory, from which they can be imported the same way as if they would reside within the root directory, by calling:


>>> import urequests

Programming Hardware

Machine specific operations are done as easily as those already described above, but can differentiate between platforms. Here is an example of setting up an output pin (for example, a pin connected to an LED) for an ESP8266 microchip:

>>> import machine
>>> pin = machine.Pin(15, machine.Pin.OUT)
>>> pin.value()
>>> pin.value(1)

The code above creates a new Pin object (which is linked with boards pin 15) from the machine library and then uses the property value to read and then change its state to high. If you want to create an input pin, you have to change machine.Pin.OUT to machine.Pin.IN. This is virtually all you need to program your digital inputs and outputs with MicroPython.

Some of the best tricks you can accomplish with MicroPython originate from the fact that functions are first-class objects in Python. This means that you can pass them as parameters to another function, which makes programming callbacks a piece of cake. While programming digital inputs, you often want to implement some action when the state of the pin changes. You can write your plain while loop, but it’s blocking, so we don’t really want to use that. When programming a microcontroller, you would usually use interrupts to implement this functionality. Simply put, when you set an interrupt, it’s listening for a state change on a given pin, and it runs a program when this happens. Combine this with the fact that functions can be passed as parameters and you get this:

>>> import machine
>>> def irq_func(interrupt_pin):
…       print(“Interrupt happened on pin: {pin}”.format(pin=interrupt_pin.id)
>>> input_pin = machine.Pin(15, machine.Pin.IN)
>>> machine.irq(trigger=machine.Pin.IRQ_FALLING, handler=irq_func)

And that’s all – ‘irq_func’ will be executed with a pin that the interrupt happened on as a parameter.

Edge Computing

Edge computing is one of two main types of IoT applications, the other being Cloud computing. In the latter, the whole computation phase of processing data acquired from IoT system devices is done on a central server, usually in the Cloud, hence the name. With Edge computing, however, some part of the data processing or filtering is done on devices that acquire data and are a part of the network – these are the so-called edge devices.

Discussing the benefits of edge computing is a separate topic in itself, but it’s sufficient to say that edge computing enjoys faster response times, cuts down data transportation requirements and provides other cost-efficient solutions, among other things. Edge computing focuses on processing data at the very source – or “edge” – of where it’s generated. It pairs well with the IoT, which is full of various input devices and data gathering sensors, for this very reason.

Consequently, the need for powerful and reliable MCUs is paramount – we want to process data on the very devices that collect it, so we are restricted only by the internal hardware’s limitations and how we subsequently program them.

Giving the well-regarded suitability of Python for data processing, it seems that MicroPython would be a perfect tool for programming Edge computing IoT systems. And indeed, it is! As an example, let’s look at Amazon Web Services (AWS) IoT platform, which provides a Software Development Kit (SDK) for Python. It can be installed on an MCU using upip, which we discussed earlier. Connecting to AWS IoT using MicroPython is effortless and, what’s more, large portions of the code can be platform-agnostic, which means that logic for data processing can be easily exported to or from other applications written in Python.

To create a system that is capable of performing Edge computing, one needs MCUs that provide substantial computing power (for a microcontroller) and have some networking capabilities. The most popular MCUs out there at the moment are ESP8266 and ESP32. These are probably the most popular MCUs used in the IoT. Both of them are equipped with a WiFi module and have quite a powerful microprocessor. They are also really cheap. Given their popularity, it should come as no surprise that MicroPython was ported to support these devices.

Alternatives

There aren’t many alternatives to MicroPython, and there seems to be only one that doesn’t directly descend from it. For the latter, there are two that are worth discussing as viable alternatives.

CircuitPython is a fork of MicroPython, designed to be used with Adafruit hardware. Its main purpose is to teach programming with microcontrollers. There are minor differences between it and MicroPython, but the code is largely the same.

Pycom is an IoT platform which provides clients with hardware, software and networking solutions. The firmware for its MCUs is open source and based on MicroPython. Pycom has nicely reimplemented AWS IoT Python SDK, which makes it easily installable on ESP32 microcontrollers running MicroPython.

The only alternative for MicroPython out there with its own codebase is Zerynth. This started off as Viper and it is a hybrid of Python and C language. It is typed and tries to combine the easy of Python development with the control of C. Zerynth is also an IoT platform and provides integration with many Cloud IoT services. Some of Zerynth’s language structure is available to use inside MicroPython to optimise code performance.

Summary

IoT systems and applications are appearing increasingly in the world around us. They are responsible for managing fleets of devices in a multitude of environments. The trend is proving stable and it’s widely accepted that the IoT is conquering the world. Up until now, to become a part of this phenomena, one had to learn languages like C or C++. MicroPython allows every Python developer to try his/her programming skills in a microcontroller world.

There is, for sure, an enormous amount of platform-specific knowledge that that has to be assimilated in order to design and program complicated hardware applications, but the first and hardest step can be done seamlessly with MicroPython.

Business Perspective

If you want to make the most of the IoT, edge computing can deliver fast and powerful results, all while saving money. However, while Cloud and Serverless technology can help, to fully achieve this, you need to optimise the microcontrollers (MCUs) underneath these devices. Utilising more modern coding solutions, such as Python, can help update your MCUs and provide a solution that’s more accessible.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts