Erlang for IoT

    The wave of interest in microelectronic devices and their interaction with each other for industrial and domestic needs led to the development of a large number of designers to develop on the basis of sufficiently powerful SoC (systems on a chip), fairly miniature with respect to microcontroller solutions, but already containing a full-fledged operating system. Developing applications for such designers is practically no different from the usual server development, except for the fact that the resource limit still needs to be kept in mind.



    As productivity and capabilities grow, the practice of using interpreted high-level languages ​​such as Lua, Python, JS for application development is gaining momentum. Some languages ​​gradually penetrate into the "younger brothers", microcontrollers, however, in a very limited version.

    There are several reasons for this:

    • Rapid prototyping - with all due respect to the C language, which mainly develops for microcontrollers, it is very difficult to call it concise. High-level languages ​​allow you to write less code and make it easier to make changes to what is already written, which is very important at the stage of creating a prototype;
    • automatic memory management and abstraction of the mechanisms of complex calculations - I think, does not need comments, both processes in manual execution with a sufficient volume of the project turn into a source of a large number of headaches;
    • Simplify debugging and testing - it is easier to check the interpreted code at the workstation until the moment of field testing;
    • the struggle with complexity - often, high productivity gives rise to a natural pragmatic desire to cram more preprocessing and analysis onto the device, which does not add to the simplicity of development.

    Alas, for all the convenience you have to pay. In this case, the price for convenience is the most valuable resources, performance, and code size (in many cases, you have to carry with you a rather voluminous execution environment). Therefore, the field application of high-level languages ​​in SoC and SoM is ambiguous and, at times, a compromise.

    We use the Erlang language for development, applying it both for its intended purpose (creating server applications and the control plane), and very unusual - web applications. Therefore, the idea to use the infrastructure of this language to create IoT solutions arose long before the appearance of boards on which the Erlang runtime environment could work without problems.

    There were many reasons to use Erlang:

    • Erlang is very convenient for parsing and creating binary sequences. Pattern matching (paired matching) paired with bit data processing allows implementing binary protocols very quickly and concisely;
    • the absence of global variables and immobility for most data - allows you to write and, last but not least, support reliable applications in which it is difficult to accidentally change something that is not right;
    • lightweight messaging processes, very much like what embedded developers have to deal with. In essence, they are an initialization procedure and a procedure that, in an infinite loop, processes incoming messages, changing the internal state. Very similar to Arduino, only processes can be many and they work in parallel, moreover, in the interval between messages, the processing procedure can be changed on the fly (hot code reload), which is very convenient in the case when you need to correct minor errors or correct behavior;
    • isolated environment and automatic memory allocation, I think, do not need an explanation;
    • cross-platform - the byte code for the ERTS runtime can be assembled on the developer's machine, and then transferred to the target device without any problems;
    • Excellent introspection tools - the ability to connect to a running application over the network and see that it slows down so often is very useful.

    First working board, which we tried Erlang, was Carambola 2 Lithuanian developers 8devices , gathered on the popular chip AR9331. The first version of this board, alas, did not have enough flash memory to put the runtime environment. But the second version already allowed itself to accommodate both ERTS and a small application.



    The installation was carried out using the classic method for such devices - building an OpenWRT image containing Erlang, followed by flashing it into the device's flash-memory. The first launch of Wednesday, alas, led to disappointment - everything hung. The reasons for this I have already told at the conference InoThings 2018 , but, alas, as it turned out, I misled my colleagues, mistakenly naming the source of such behavior.

    Briefly retell. When working with files, the ERTS virtual machine uses the off_t type , the size of which in the distribution kit is calculated during the autoconfiguration of the assembly (if it runs on the target platform) or is substituted from the cross-compilation environment, as it happened in the case of OpenWRT. It is not clear why, but in the settings for MIPS processors and derivatives in the configuration file of the assembly is twice the size than it is in fact. There would be no problem if the virtual machine code used non-preprocessor directives like

    #if SIZEOF_OFF_T == 4

    and a banal check in the code (I suspect that the final result of the compilation would be the same, but there was not enough fuse to check):

    if (sizeof(off_t) == 4) { 

    As a result, the file collected at the first ERTS iteration, when trying to read the ~ / .erlang.cookie file at startup (a kind of password for identification during network interaction), successfully received garbage in the upper digits and crashed.

    The patching patch and package with the penultimate version of ERTS for OpenWRT can be downloaded from GitHub . In addition, there were no problems yet, everything worked as expected.

    The second hardware platform on which we tried Erlang was LinkIt Smart 7688 from Mediatek and SeeedStudio.specially designed for rapid prototyping and learning the basics. This board is just the apotheosis of debauchery in terms of resources - the frequency of the MIPS core increased one and a half times, more RAM (for ERTS it matters, GC does not sleep) and more flash memory, as well as the presence of a MicroSD card and the possibility of using the Atmel co-processor Atmega 32U4 in the Duo version for working with peripherals.

    In general, the platform was very suitable for the continuation of the banquet, and the presence of additional connected devices, connecting without soldering, allows you to quickly and conditionally assemble a stand on the knee for testing.

    Included with the platform is software with its own web-interface, as well as with Python and NodeJS-libraries for development. The assembly ecosystem has not changed - this is still OpenWRT. If for some reason you find all this diversity superfluous, then there are packages on the aforementioned repository containing a minimum set of mandatory components . After assembly, the flash memory image is written to the device and after a reboot, you can safely use the REPL.

    To build applications on Erlang for IoT, it is necessary to solve one architectural issue.

    The language was designed and developed with an eye to what the control plane will serve, i.e. control layer, while working with iron was assumed by FFI. For this there are three types of interaction:

    1. ports (ports) - separately working processes written in any language, interaction with which occurs through input / output streams. They can be restarted in case of a fall, but due to the interaction method, their performance in terms of communication is small (however, we will be enough);
    2. NIF functions look like standard language functions, but their call causes the execution of compiled code in the runtime environment. In case of an error, they can drag out the entire virtual machine.
    3. C-node - when the work is performed entirely in a separate process, and the interaction is carried out as with a separately running runtime over the network. We will not consider this option due to sufficiently large overhead within one weak device.

    The dilemma is as follows - we can bring only transport items to FFI (that is, support for GPIO, I2C, SPI, PWM, UART, etc.), and interact directly with sensors and other devices on Erlang, or on the contrary, it is possible to take out device drivers entirely in the code of external modules, leaving the application to receive raw data and process them; in this case, it may make sense to use the code already written.

    We decided to use the first option. There are several reasons for this:

    • because we can;
    • as I already mentioned, Erlang has powerful enough tools for assembling and disassembling binary sequences, and quite simple math, which does not care about overflow protection and other magic used for processing results. Not that this magic was scary, but it acts shockingly on neophytes;
    • intuitively, it seemed that drivers in a high-level language would be easier and more reliable (the crashed process will restart the supervisor, which will lead to the reinitialization of the monitored device).

    Therefore , the ErlangALE library was quickly found , which contained already implemented support for GPIO, I2C and SPI through the kernel interfaces, and the developers of the hardware platform, in turn, took care of them. We already had a library for working with UART , plus we added erlexec , a supplement that allows us to create and manage OS processes.

    All of these applications used ports (separately launched binary processes) for working with hardware and operating systems, which required cross-compilation support for C and C ++ languages, for which a rather elaborate shell script was written that configures the build environment to use the necessary compilers.

    To test the decisions we made, we assembled a simple device from the LinkIt Smart 7866, two I2C devices (a temperature and pressure sensor BMP280 and an OLED display 128 at 64 points) and a USB GPS module that provides UART data. The GPIO was tested on the LED on the board, it works, and connecting the SPI display seemed unnecessary at this stage.



    It turned out quite compact and simple application, the source code can be viewed on Github.

    I will not delve into the code snippets, but will try to briefly describe how the application works.

    All device drivers are implemented as gen_server processes, which is convenient because it allows you to add additional parameters to the state and, sometimes, the state of the device. The latter can be seen in the example of uart_gps- data from the UART arrives asynchronously, is parsed by the NMEA0183 parser, and the results are written to the state of the process, from which they are retrieved by request.

    The main application cycle is described in the gps_temp_display module - every second the process reads GPS data and requests the temperature and pressure status from the BMP280 and outputs them to the OLED display. For the sake of interest, you can look at the display drivers and the BMP280 sensor - everything turned out quite succinctly, 150-170 lines per module.

    In general, the above task (writing driver code, combining everything into one application, building and testing) took about four evenings of two hours on average, i.e. strictly speaking - a couple of working days. As I personally think, this is a good indicator to try to use Erlang in more complex and serious embedded applications that do not require strict real-time restrictions.

    Of course, our attempts to use Erlang for embedded systems are not the only ones. There are several interesting projects on this subject:


    Also popular now: