close
close

Putting code on a Nerves device

Putting code on a Nerves device

Underjord is a small, healthy team doing Elixir consulting and contract work. If you like writing, you should really try your hand at coding. Check out our services to learn more.

The Nerves project is a way to build embedded Linux devices where the BEAM virtual machine does the running. It doesn’t limit what you can run in any significant way.

The easiest way to think about it is as a replacement for systemd. But it’s not just that. I’ll try to explain the different ways you can run things through the BEAM and thus also on Nerves.

1. BEAM code (Elixir, Erlang and friends)

If you write your damn application the way it was meant to be written. In an Elixir, or Erlang, or maybe Gleam, maybe even LfE. You’ll have the best time. Not because that gets preferential treatment, but simply because these languages ​​are able to utilize the entire runtime.

You get lightweight processes that communicate via messaging. Resilient preemptive scheduling. Consistently low latency. Hot code updates which we use a lot in Nerves because it’s really nice to just paste code into the device during development instead of reflashing.

And you get easy access to libraries, the Phoenix web framework and the parts just fit together. Furthermore, the Nerves runtime is built the same way and you can interact with things like your network configuration, firmware update and a lot of hardware via messages and function calls. This makes it very easy to write systems that respond live to changes.

In my view, this is where you want to spend most of your development time, because you get fast iteration speed, great capabilities, and a beautiful language that does a great job of not making a mess.

2. Natively implemented functions or C+ BEAM

This is number two in terms of how close it is to the BEAM, not how recommended it is. The risk of NIFs needs to be mentioned. You segfault into a NIF and the BEAM can’t protect you and you undermine its resilience. The Nerves variant of the Erlang heart module can still save you. But a lot of the safety measures of the higher abstraction level go out the window. You’re running code directly in the BEAM.

The different types of NIFs are a bit much to discuss here. If you can divide the work into thin time intervals, the old fashioned NIFs are great, because the scheduler can treat them like any other BEAM process. But there are also dirty NIFs that run on a different lifecycle with “worse” guarantees, but a much more manageable programming model.

C is the basic way to do this. Then you have Zig via the Zigler tool. And of course Rust via Rustler. Both Zig and Rust reduce the risk of segfaulting the BEAM to some extent, so they are potentially preferable.

3. Ports

There are several ways to implement Ports. This is an Erlang concept where a Port is an external process that you start from the BEAM and communicate with via stdin and stdout. In Nerves we typically use a Linux-specific and more featureful Muontrap library. It supports cgroup stuff to control the memory and CPU usage of the processes. This can be plugged directly into your BEAM supervisory tree and its lifecycle can be managed as a true gentleprocess.

So if you have a vendor-supplied binary, you need to run a Python script, a nice statically compiled Go binary. This is a good path. With tools like beam_notify you can even pass reasonable BEAM messages from shell scripts.

That’s all?

These three cover everything you need. Especially number 3, the ports. You can run processes on Linux. What else do you need?

But there are still some interesting things you can do.

4. C-junction

You can build an application that pretends to be an Erlang node and… I would say that is a challenge for most Nerves usage. Let me know if you have other ideas. But it is one of the Erlang mechanisms for interop.

5. Containers

Apparently containers are cool.

They’re actually cool. I don’t like most of the tools around them, but sometimes it’s very practical to just pack up the dev machine and ship it instead of figuring out what exact combination of deps will make Node happy. By default, Nerves doesn’t ship a container runtime because it’s a lot of extra stuff if you don’t need it, and in most embedded projects you can just ship a firmware update if you want to ship new code. But it can make sense because you’re getting containers from somewhere, or if you need very isolated and selective updates. Or you just really like it architecturally.

Several people have used Docker and variants of Nerves for this. Steffen Deusch has put together a few systems using Balena Engine, which I had never heard of. It’s a container runtime that keeps things pretty lean for embedded use. I tried it and it worked. It’s not an officially maintained set of systems. If enough people request containers, we might consider it.

6. Special language implementations

If you want to run a sandboxed language on the device, I would look at luerl, an implementation of Lua in Erlang with some really cool facets. Each run of Lua code returns a new state of the Lua program, meaning you can fork the execution or save, load, etc. Very neat.

The highly experimental pythonx library also offers an embedded language, but this time by putting Python in a NIF. Pretty wild, very early. But damn, if that isn’t a fast track to AI/ML work.

Conclusion

You can ship your machine learning as Python if you want. Include Python in your system and run it via a Port or run it in a container. Nerves doesn’t care. There are actually many nice libraries for improved interoperability between Python and Erlang programs.

You can use your cool Go binaries and your not-so-cool hardware vendor software.

For most of the work I recommend using the Elixir you have on hand.

Regardless of the fact that the Nerves project offers you what you need.


If you have any questions about Nerves or this topic, please do not hesitate to contact me, I am @lawik on the Fediverse and my email is [email protected]. I hope you can run your code in a satisfactory manner.

Underjord is a team of 4 doing Elixir consulting and contract work. If you like writing, you should really try the code. Check out our services to learn more.

Please note: Or watch the videos on the YouTube channel.