One of the (admittedly less active these days) contributors here. Happy to answer questions to the best of my ability :)
As far as I'm aware, the focus of RIOT is to make embedded development as similar as possible to the way you'd write an application on Linux, including most of the relevant POSIX APIs. This is what, as far as I know, sets it apart from other systems such as Contiki or FreeRTOS, which have non-POSIX APIs for interaction with the OS.
Someone on this thread asked why RIOT vs FreeRTOS is not something being compared. FreeRTOS doesn't really come with all the drivers, network stack etc that you need in order to write an embedded IoT application. That is not to say that it's not possible to do so, but it requires more work of picking the right drivers and libraries, whereas RIOT tries to work out of the box for supported devices.
Finally, RIOT was born as a university project, and many of the people working on it are either university students or former students. In that sense, there are many, often conflicting interests and no single governing body. Depending on your point of view, this might either be good or bad.
POSIX APIs are typically not appropriate for embedded systems.
For example filesystems.
Filesystems are not generally applicable for single-purpose embedded systems. You're never going to need textual filenames, opening and closing file handles, etc. All that is just a waste, typically you just want to store your data to a log. This is much more appropriately stored in a simple circular buffer on FLASH without the overhead and non-determinacy of a filesytem.
Also, processes. These should be static. You typically never ever exit a task/thread on an embedded system.
Resets are a way of life. You have to acknowledge them in your design and error handling takes advantage of this.
I would not take POSIX compliance for a system like this as a plus point.
> POSIX APIs are typically not appropriate for embedded systems.
This is a very broad brush. My Embedded Linux system makes amazingly good use of Linux and its Posix APIs. It’s also way to big to be the target of RIOT, so let’s talk about it’s 3 other embedded MCUs.
> Filesystems are not generally applicatble...You’re never going to need textual filenames
Really? Because I always enjoy talking to my cell radio doing AT+CMOGS=257,FFFFFF^1. That’s open file 257 and write FF.
Maintenance, debugging and robuestness of embedded systems is frequently overlooked. It allows not only a single dev, but a team to work, debug and release a system is a big deal. Embedded systems, esepcially those logging data, holding SSL certs, config data can very much use a filesystem. None of this has to be complex JFFS2 FS.
For example, if it’s an IoT, how do you plan to OTA it without good storage management?
> I would not take POSIX compliance for a system like this as a plus point
As a hirring manager anything that opens my pool of candidates is useful. Teaching someone a whole new environment is a big deal. If there’s similar concept and tools this ia big plus.^2
——-
1) ok, I can’t remember the right AT command or the right file number, but serisouly how did this make it into a public API in 2019?
2) Mostly true. If it’s too close that it’s almost the same, but not due a laundry list of weird exceptions I’ll skip it. Micropython is my example here, it’s like really close to python, may too close but too different. There’s lots of other embeddedable languages that aren’t too close but weird.
- RIOT is obviously targetted at microcontroller-based systems (not Linux level).
- AT commands... you can't use modems in general as a good example of anything. Thes software running on those things tend to be terrible.
- Good storage management != filesystem. you can simply allocate a rotating circular buffer in FLASH with CRCd blocks. This is a simple mechanism that gives you both bad-block-management and wear-levelling in one trivial mechanism. No filenames in sight! ;o)
- Hiring... There is a lot more to embedded systems than "Can they use the API". All RTOSs use similar APIs, thats never been an issue.
Agree with TickleSteve; there are file system essential options which may work out of the box for you, or at least provide a nice framework for what you need.
The microcontroller landscape is shifting rapidly. I think many of the things you've mentioned - no dynamic tasks, no filesystem, etc - are appropriate for some devices and some software but not all.
MCUs today are pushing the boundary on performance. We'll see GHz Cortex-M7 chips (already 600MHz+ today) running an RTOS in the near future, with external DRAM and flash. Users rightly want to push a LOT of functionality on them and that comes with advanced OS usage, including dynamic memory and threading. The developers using these systems are increasingly coming from an embedded Linux world as the performance line blurs, and POSIX is familiar.
Don't know why you got downvoted - this is a highly informed comment. The embedded systems landscape has been shifting rapidly since the introduction of the Cortex M0 about 10 years ago, and continues to do so with the hardware you mentioned, and will continue to into the foreseeable future. Perhaps what we need here is a redefining of the term "embedded system," or if nomenclature purists are against that, a new term altogether to encompass the systems you describe. Regardless of nomenclature flame wars, it's an exciting time to be involved with embedd... erm... the microcontroller world as it evolves at an unprecedented pace.
The higher end of microcontrollers are getting affordable, true.
This does not change the fact that a smaller, less capable uC will always be cheaper.
Embedded system designs are typically cost-driven, hence cheaper (and therefore resource-constrained) devices will always be a fact of embedded-life, no matter how "powerful" uC become.
Replied to your comment below as well, but will reiterate my primary point here,
You seem to be conflating embedded systems with safety-critical systems. There are plenty of embedded systems that are neither safety critical nor single-purpose. Also, plenty of non safety-critical embedded systems use microcontrollers that don't have the resources (RAM) to run Linux effectively (even MMU-less Linux) but could benefit from dynamic kernel capabilities like dynamic loading and unloading of drivers for hot-plugging events to support external hardware/accessories.
For our current product, (and I would think a lot of IoT apps), power is king. We run on batteries, and getting back to idle/low power states as often as possible is crucial. I've found that a lot of the embedded RTOS often have tickless modes as an "add on." We had problems getting RIOT to a happy point with that for our chip (samd21).
Like some of the other commenters, I've grown a real distaste for embedded RTOS frameworks that try to provide all kinds of layers. In order to be generic/cross platform, they always come up short in capability. The functionality that I'm looking for, is simply getting the basic threading/sync primitives to work well and right. Past that, I'm fine doing the chip IO parts myself. At the time, there was a debate on whether priority inversion was a problem RIOT should worry about or not.
After FreeRTOS, and then RIOT, I found TNEO (https://dmitryfrank.com/articles/how_i_ended_up_writing_my_o...). It's been a dream and rock solid for us. It does not come with a large make/build system. It's just C code. You tweak a couple of things, include one or two bits, and I was off to the races. Coupling it with Unity for testing and then writing ~200 unit tests that stressed all of its functionality was straightforward, helped me understand it even better, and gave me a high degree of confidence in its abilities. It's been super solid for us since. It does the one thing I need it to: MicroKernel for dealing with processes and related synchronization primitives.
Having used both contiki and riot, I found contiki (NG) very good with supporting more devices and peripherals and much easier to integrate and work with. Riot is difficult in that half the times their tutorials themselves didn't work and there was no support online (this was about a year back and maybe it is better now).
We are currently using RIOT in a 3 year research project(2 years in atm) and they do have some issues, however not on the scale you describe. Especially with the online support, if you ask the community, you will generally get an answer relatively quick. With the examples, your experience will vary greatly depending on what board you use, as not all support every test.
It is however clear, that they started as a research project and had, and still have, some maturing to do before they can be considered for a production environment. In my opinion, they are however on the right track for that. The list of buried corpses tends to get shorter and over the last year, a lot of interesting features were added. They are also currently working on fixing their documentation and getting automated HIL tests running for their supported boards, which should fix alot of problems people getting started with RIOT currently have.
We are likely not going to use RIOT in the next project, however we will continue to use it for teaching.
The OS comparison table leaves me somewhat puzzled.
Contiki's "C Support" is listed as "partial" without any explanation. What does this even mean? It seems to me that this should be between you and your compiler.
Linux is supposedly "partially modular", whereas RIOT is fully modular. That's a bold statement.
I get that they are trying to differentiate their product. But without motivations, statements like these sound a bit hollow to me.
Obviously one needs to provide bold statements to get people's attention these days. I guess the details and precise statements are in the research papers, such as:
Contiki uses protothreads, so I would class that as 'partial' C support, since you can't really use local variables (due to the hair-raising nature of how protothreads works).
I disagree. Sure, it is a monolith as apposed to a micro-kernel, i.e. everything in the kernel shares the same address space. But it is highly modular in that most drivers and filesystems can be dynamically loaded/unloaded.
Dynamic loading and unloading of drivers in a single-purpose embedded system is not a good point.
In this level of devices, everything should be statically allocated for the worst-case situations. This is for reliability, simplicity, determinism, etc.
The space taken by the code involved in handling dynamic loading could be better used by giving you a cheaper, simpler, more reliable device.
What seems 'nice' on a desktop system is (in a lot of cases) inappropriate for a resource-constrained embedded system.
Interesting points, but have to challenge your strong assertions here.
1) Dynamic loading and unloading of drivers in a single-purpose embedded system is not a good point.
- Yes, but who said anything about Riot being used only for single-purpose embedded systems?
2) In this level of devices, everything should be statically allocated for the worst-case situations.
You seem to be conflating embedded systems with safety-critical systems. There are plenty of embedded systems that are neither safety critical nor single-purpose. Also, plenty of non safety-critical embedded systems use microcontrollers that don't have the resources (RAM) to run Linux effectively, even MMU-less Linux, but could benefit from dynamic loading and unloading of drivers for hot-plugging events to support external hardware/accessories.
3) The space taken by the code involved in handling dynamic loading could be better used by giving you a cheaper, simpler, more reliable device.
- For certain problems, sure. But I'd challenge your assertion that resource-constrained always equals single-purpose device where dynamic loading for things like hot-plugging events isn't useful
What seems 'nice' on a desktop system is (in a lot of cases) inappropriate for a resource-constrained embedded system.
- It's hard to argue this point with the caveat "in a lot of cases", but I'll try. What makes this inappropriate for resource-constrained embedded systems? Again, it seems you're conflating resource-constrained embedded systems with safety-critical embedded systems, where formal methods/functional verification/regulatory certification are necessary and therefore dynamic code loading/unloading isn't feasible
Safety-critical systems imply a lot more than that, been there, done that.
Its an accepted, general premise in embedded systems to always avoid dynamic behaviour, that includes memory allocation, storage behaviour, etc.
Implying that that kind of behaviour is only for safety-critical systems is not correct, its mainly for robustness and simplicity reasons. I would argue that it applies to the vast majority of embedded systems (prototypes excepted).
An embedded system is also (generalising again here) always single-purpose. By definition its a part of another system and you do not interact with the device itself, you interact with the system.
You could argue that not all embedded systems are resource-constrained (again, been there, done that). but typically for this level of system (i.e. microcontroller based), if you're not resource-constrained, you've not optimised your BOM cost and hence you're in prototype stage.
RIOT does not allow dynamic loading and unloading of modules/drivers at run time.
You can choose which modules/drivers you want to include at compile-time.
I never argued that it was. Though, "embedded" covers a wide spectrum of systems. For microcontroller-based projects, then no. For bigger SoCs paired with hundreds of MBs of FLASH/RAM it can be very useful.
My original point was that claiming that Linux is partially modular while RIOT is fully modular feels like a stretch to show N lines of red crosses and orange circles, followed by one line of only green check marks.
Linux's FLASH/RAM requirements are enough to disqualify it from projects targeted by RIOT, IMHO. No need to make dubious claims of limited modularity.
And in IoT some would not need a modular kernel at all. A monolith kernel would fit the purpose. Because an IoT device normally has a fixed purpose depending on the additional hardware. There is no plug'n'play requirement.
The only problem with the Linux kernel these days is its sizes. Because with Cortex M3/M4 processors we still have sizes constraints.
Most examples I looked at were just a few lines. None of them showed multi-tasking, nor using sensors or similar SPI/I2C communication or similar.
Looking at the source code it seemed very Arduino-like in the sense that there's few knobs to turn, which is nice for simple stuff but means you'll have to go straight to registers for anything beyond.
Overall a bit hard to get a feel for what it could do.
There's also the pkg directory (https://github.com/RIOT-OS/RIOT/tree/master/pkg) which incorporates a bunch of third-party libraries and projects without a significant amount of code changes (see the patches/ directory in each pkg). Maybe this gives a good impression of what it takes to port things to RIOT.
Show stopper :( . I can't justify an OS more complex than a simple message queue-based scheduler if the target isn't internetted. And I get arguments about the scheduler ("You should use a commutator/superloop"). Don't wish to be a downer though, seems like a great academic project.
Not to mention most business networks, the vast majority of industrial control networks. So basically damn near anywhere you would want to deploy a networked embedded system. Many systems have IPv6 support but you will still need to talk to IPv4 devices, and you need an IPv4 stack to do that.
What is the benefit of using riot os on Arduino instead pure Arduino? I mean what is the reason to add an extra layer since I have to write the same code with and without riot.
Using RIOT you'll get at least multi-threading, power management, a choice of network stacks, a bunch of community-supported libraries and drivers with an extensive test suite, ...
Also, you'd have a clear upgrade path, as applications written for RIOT's API will compile (almost) unchanged for all of RIOT's target hardware. Arduino becomes to slow? Change some pin defines, re-compile for a fast Cortex-M. Intrigued by RISC-V's openness? Recompile for the Hivife1 to find out it's real-world performance with your application.
They still work as libraries. 'Pure arduino' with it's setup(), loop(), pinMode(), etc is hiding a lot of firmware from you. If your device has wifi, it becomes more difficult to use it without an OS since you must manage the wifi yourself inside your loop(). Failure to respond to radio events within certain time frames is an error, so you would have to be careful to not spend too much time in your loop before dealing with the radio. With an OS, the radio has it's own thread and you don't have to deal with it. To you it works like a library.
As someone in the CE field doing this work everyday, I’m getting kind of sick of abstraction layers. Every device mfg takes a crack at it, every RTOS does, ARM itself does, and many IDEs are as well.
I kind of just want my RTOS to do threading, preemption, and resource locking. Middlewares line Amazon’s MQTT are good but I’d really prefer to handle the peripherals myself or use the device mfg’s libs. I am not a believer in write once use everywhere embedded code, it just doesn’t work, because the peripherals are just so different.
> I am not a believer in write once use everywhere embedded code, it just doesn’t work, because the peripherals are just so different.
Are they though?
You got SPI, I2C, UART Timers and DMA and while their flavor may differ depending on the vendor, they should do about the same no matter what platform you are on.
Sometimes you might have to do some exotic stuff when a chip uses non-standard interfaces, but most of the time the behavior should be fairly generic.
Yes, I think they are. Nordic 832’s SPI has an 8bit for remaining count in its hardware register meaning a transfer has a max of 255chars. Atmel’s SPI is all event based. STM32 F3 SPI has an entirely different setup procedure than those two and that has a difference I can’t remeber the F0 and F7 are different.
Just at a base level how you work with peripherals is going to be different. Some are better with events, some better as ISRs, some better with polling.
There is no middleware that’s just going to abstract those and get a great implementation of each. They were just designed so differently.
> Yes, I think they are. Nordic 832’s SPI has an 8bit for remaining count in its hardware register meaning a transfer has a max of 255chars. Atmel’s SPI is all event based. STM32 F3 SPI has an entirely different setup procedure than those two and that has a difference I can’t remeber the F0 and F7 are different.
How does this prevent you from hiding all those implementation details behind an API like spi_transfer(spi_t dev, const void* buffer_tx, void* buffer_rx, size_t size)?
RIOT manages to get by with that - granted, most implementations there rely on polling, it's not a conceptual problem.
It's not that we don't see how you could create an abstraction that works, it's that we're questioning the value of doing so.
In your example, if I want a DMA-based transfer it's fine to just hide it and block the thread waiting for the DMA interrupt. But if the hardware designer expects me to poll, now I have to find a way to put the thread to sleep and schedule it. And there are performance and timing implications for either scheme that your abstraction hides from me. At some point I'm going to stumble on those implications and get screwed badly enough by your abstraction that I need to rip it out or work around it to solve a problem. This has happened to me too many times before.
On systems that are small enough to be considered "embedded" I would much rather the OS gets the hell out of my way and lets me pick a sensible abstraction myself.
I don't see much value in having an abstraction layer in these systems unless it's for a hobby project. In an established project you would never recompile your code for a completely different hardware platform "just to see how it performs." You'd stick with the hardware you know, and you'd already have de-risked the performance questions early enough in the project that you wouldn't have written a bunch of application code yet. It's not a problem that needs to be solved in a professional environment.
There is just no way an abstraction is going to pick the best process for my needs.
And yea, while it might be a nice idea to switch chips... I’d have to go through sourcing to figure out if they make a chip in the package i would need at a price I’m willing to pay at a lead time I’m willing to wait, a week or two reading data sheets and reference manuals, updating the board hardware, changing the BOM, getting with purchasing to update the next PO, changing the assembly programming and stencils... plus documentation, plus fragmenting our hardware variations, etc.
The abstraction to switch chips, might be nice for prototyping, but yea, it’s not really useful for production, so I think I would rather my OS developers focus on other things.
It also talks about 8-bit support, but only lists AVR... I mean, AVR is compiler target optimized arch, more like a 16/32 bit system with register width chopped to 8 bits, not limited like other 8-bit MCUs. 8051 support wouldn't hurt.
Plus side there's support for 16-bit MSP430, important due to its low power consumption.
Anyways, it's good to see more OS competition in IoT space.
There simply look for merged pullrequests of the features you are looking for. That will give a better overview of the degree of support then the documentation.
Recently needed multi-threading with Arduino. Going to give this a shot. I'm sure people more well-versed with non-POSIX may have gripes, but I'm just doing this for hobby purposes. I'm familiar with Linux, I need multi-threading and maybe a network stack. Given that, this seems like the thing for me.
I also recommend taking a look at Zephyr RTOS [1].
I've been using it in production for almost a year, and no major complaints so far. There seems to have support for POSIX threads and BSD sockets, but I don't use them, so I can't tell you if they are production-ready or not.
What language do you suggest? The GCed languages aren't real time (yes there are hard real time GCs, but they come at the cost of overall perf and close to an order of magnitude more memory usage which is a non starter for many embedded boards). Rust support for AVR isn't mainlined and is frequently broken. It's Xtensa (ie. esp8266) support is basically non-existent.
And there are some options. You could achieve barebones VM protections with Pawn(the language enforces very little otherwise, though). You could use something ref-counted like a TCL implementation, which would allow you to design around linear allocation times. The vast majority are using some kind of tracing collector of course, but I wouldn't say this is a total loss for achieving a combination of real time and memory protection.
As far as I'm aware, the focus of RIOT is to make embedded development as similar as possible to the way you'd write an application on Linux, including most of the relevant POSIX APIs. This is what, as far as I know, sets it apart from other systems such as Contiki or FreeRTOS, which have non-POSIX APIs for interaction with the OS.
Someone on this thread asked why RIOT vs FreeRTOS is not something being compared. FreeRTOS doesn't really come with all the drivers, network stack etc that you need in order to write an embedded IoT application. That is not to say that it's not possible to do so, but it requires more work of picking the right drivers and libraries, whereas RIOT tries to work out of the box for supported devices.
Finally, RIOT was born as a university project, and many of the people working on it are either university students or former students. In that sense, there are many, often conflicting interests and no single governing body. Depending on your point of view, this might either be good or bad.