Sep 5, 2026 · microcontrollers · embedded-robotics · robotics-middleware · beginners
Do Microcontrollers Need Middleware Too?
Mostly no. A microcontroller running one control loop wants plain firmware and one clear agreement with the main computer, not a message-passing layer.
Mostly no: a microcontroller running one control loop wants plain firmware or a small real-time operating system, not a message-passing middleware like micro-ROS. The board usually has one job with hard timing, and a message layer buys coordination the board does not need. That flips when several boards must agree with each other and with the main computer, where a message layer starts earning its keep. The rest of this post is for someone wiring a small board to a bigger computer and wondering whether ROS 2, micro-ROS or HORUS belongs anywhere near it.
There is a small board on your robot. It reads an encoder, drives two motors and answers a stream of commands coming down a USB cable from the computer that does the thinking. For a while it was the simplest part of the project.
Then you added a second sensor, and the motor loop began to stutter every time that sensor was read. You added a status message going the other way, and now commands sometimes arrive late enough that the robot lurches. You added a print statement to find out why, and the problem moved somewhere else.
Searching does not settle it. Half the results say a board like yours should be running a real-time operating system. Half say to put ROS 2 on it, or something called micro-ROS. Everybody sounds certain, and none of them are describing your robot.
Underneath sits a question nobody states plainly. The small board is a computer running programs too, so does everything people say about middleware apply down there as well, or is it a different world with different rules?
Do microcontrollers need middleware too?
Most microcontrollers do not, and the ones that appear to are usually attached to a robot that has outgrown a single board. A microcontroller earns its place by doing one small job with timing you can reason about: read the encoder, work out a motor command, repeat, forever, without surprises. A message layer exists so that many programs on one computer can find each other, agree on message shapes and survive one another's slowness. A board running one program has none of those problems, so a middleware there adds vocabulary, memory pressure and a build system while taking nothing off your plate.
What the board does need is a clear agreement with whatever sits on the other end of the wire: what a message looks like, what happens when one goes missing, and who is allowed to command what. That agreement deserves to be written down and tested. It is not the same act as adopting a framework.
The question becomes genuinely open in two situations. One is several boards that must agree with each other. The other is one board carrying jobs that cannot wait for each other.
What does middleware mean on a board this small?
On a microcontroller, middleware means a library that carries named messages between parts of your code and out over a wire to other computers, with rules about who receives what and what happens when nobody is listening. On a Linux machine, that same idea arrives with process isolation, a networking stack and memory to spare. On a board whose entire memory would be a rounding error on a laptop, none of it is free, and what you get in return is names instead of byte offsets, plus the ability to add a listener later without editing the sender.
It helps to separate three things that get called the same word. There is the scheduler that decides which piece of your firmware runs next, which is an operating system job. There is the transport that moves bytes over serial, CAN or Wi-Fi, which is a driver job. And there is the message layer that gives those bytes names and delivery rules, which is the middleware part.
Plenty of robots want the first two and not the third. What middleware actually does in a robot is the longer version of that distinction.
What do most people try first, and why does it stop working?
Almost everyone starts with one loop on the board and lines of text over USB, and it works beautifully until the board has two jobs with different timing. The first version is honest and readable: read, compute, drive the motors, repeat. Then a distance sensor arrives that takes its time to answer, and the motor update waits for it. The wheels twitch at the end of every cycle.
So you move the sensor read into a timer interrupt, and now two pieces of code touch the same variable. Once in a while you read a value that is half old and half new, and the robot swerves for no reason you can find. It looks exactly like a hardware fault, which is where the week goes.
The next thing people try is a longer text protocol, because debugging by reading is comfortable. That survives until the messages crowd the control loop, or until the host misses a line and the two ends quietly disagree.
Neither failure means you need middleware. They mean you need a scheduler, a queue, or an agreed message format, which are pieces a middleware bundles and which you can have separately.
What are your actual options for the microcontroller side?
Six options cover nearly every robot, and only two involve anything called middleware. A bare loop with timer interrupts is the smallest and, for one job, the best. A small real-time operating system such as FreeRTOS or Zephyr gives you separate tasks with their own timing and queues between them, which solves the stuttering without messaging across machines. micro-ROS puts a cut-down ROS 2 client on the board, paired with an agent process on the main computer. Your own fixed protocol over serial or CAN keeps the board dumb and the agreement explicit. An off-the-shelf motor controller that speaks a documented protocol removes the firmware question.
The middleware decision that matters lives on the far side of the wire, on the computer where perception, planning and logging run. There the choice is ROS 2, with its large ecosystem, or a lighter layer such as HORUS, an open-source real-time robotics middleware for Rust, Python and C++ where the three languages share the same shared-memory ring buffers, so messages between processes on one machine are not serialised. Apache-2.0, validated in simulation, and no use on the small board, which has no shared memory to share.
How do the options compare?
Read the last column first.
| Option | Who it is for | What it assumes you know | When to pick it | When not to |
|---|---|---|---|---|
| A bare loop with timer interrupts | Anyone whose board has one job | C, interrupts, and your board's timers | The loop reads, decides and drives, and nothing else | Two jobs on the board need different timing |
| A small real-time operating system | Boards juggling several timed jobs | Tasks, queues, priorities, stack sizing | A slow sensor is delaying a motor update | The single loop still fits comfortably |
| micro-ROS on the board | Robots already living inside ROS 2 | ROS 2 concepts, cross-compiling, agents | Board data must appear as a topic like any other | Nobody on the team runs ROS 2 today |
| Your own protocol over serial or CAN | Teams wanting an explicit, small agreement | Framing, checksums, versioning, timeouts | The host thinks and the board obeys | Many teams must share one vocabulary |
| An off-the-shelf motor controller | Builders who want the motors solved | Wiring and reading a vendor document | Firmware is not where your value is | You need custom behaviour close to the motors |
| ROS 2 on the main computer | Robots needing maps, drivers and tools | Workspaces, launch files, message packages | Borrowed packages are most of the robot | The whole robot is your own code on one machine |
| HORUS on the main computer | Teams whose own programs crowd one computer | Rust, Python or C++, and where processes split | Python thinking sits above a loop that must keep time | You still need drivers and navigation from elsewhere |
Does the answer change if you are a hobbyist rather than a company?
Yes, though not in the direction people expect: the hobbyist has more reason to keep the board simple, and the company has more reason to write the agreement down. On a weekend robot, the firmware is something you can hold entirely in your head, and every layer you add is an evening spent on plumbing rather than on the robot doing its trick. Keep the loop small enough to read in one sitting and you will find your own bugs by looking.
A company faces a different problem, which is that the person who wrote the firmware will one day be on holiday, or gone. What protects the project is not a framework but a written message contract, a versioned protocol and a test that runs the board against a fake host. Companies also carry duties the hobbyist does not, starting with field updates and a safe behaviour when the host stops talking.
Both end up in the same place. The board stays boring, and the interesting software lives where there is room for it.
Which boards and robots change the answer?
Hardware changes the answer twice: once when the board becomes large enough to run Linux, and once when the robot gains enough boards that they must talk to each other rather than only to a host. The smallest classic boards have room for a loop and little else, and a message layer there spends scarce memory on bookkeeping. The middle class, the common thirty-two bit boards with Wi-Fi or a spare serial port, comfortably hold a real-time operating system and can carry micro-ROS if there is a reason.
A board that runs Linux is not a microcontroller at all, and the ordinary middleware conversation applies to it directly. That matters more than the chip family, because it decides whether processes, shared memory and drivers exist at all.
The other change comes with count. One board and one host is a conversation. Six boards along a robot arm is a network, and networks want addresses, priorities and an agreed vocabulary, which is what a bus like CAN provides. Whether your robot should run one computer or several is the same question one level up.
What if you need the robot moving in a few weeks?
Do not add a message layer to the firmware; write the smallest protocol that carries what you need and spend the remaining time on the robot's actual job. Bringing up micro-ROS is a project of its own, with an agent process, a transport choice and a cross-compiled build. A deadline is the worst moment to learn that, and the learning does not make the robot do anything new.
What pays for itself in a fortnight is smaller. Fix the message format early and write it in a comment both ends can read. Give every message a length and a checksum so a dropped byte does not turn into a lurch. Add a heartbeat, so that when the host goes quiet the board stops the motors rather than continuing with its last command forever.
That last one is the difference between a demo that ends and a demo that ends against a wall. It costs a few lines. Teams that skip it under deadline pressure add it the day after the first crash.
What if you have never written firmware before?
Start with a single loop and a timer, learn what an interrupt does to your assumptions, and leave middleware alone until you have hit the wall it removes. The beginner's real obstacles are not missing frameworks. They are blocking calls that stall everything, a variable written in two places, and a motor that behaves differently on a fresh battery.
None of those are solved by adopting a message layer, and all of them are easier to find in a program short enough to read end to end. Work in the environment with the fastest edit-and-flash cycle you can get, because on a small board debugging is mostly about how quickly you can try the next idea.
There is one habit worth adopting immediately, and it costs nothing. Keep the code that touches pins separate from the code that decides what should happen. Then the deciding part can be tested on your laptop with made-up inputs, which is the only way to check the awkward cases without breaking hardware. Whether beginners need middleware at all is the same argument at the level above.
What do you give up by running middleware on the microcontroller?
You give up memory, build simplicity and the ability to hold the whole board in your head, and you take on a version dependency between the firmware and the host software. The memory cost is obvious, and on a small board it decides whether the firmware fits. The build cost is quieter and worse: what used to be one file you flashed in seconds becomes a toolchain, a configuration and a second program on the host that must be running before anything works.
Timing gets harder to reason about too, because a message layer now runs between your loops, deciding when to serialise, when to send and when to wait. The board that used to be the part you trusted becomes another suspect when the robot misbehaves.
Then there is the coupling. Message definitions must match at both ends, so upgrading the host can silently break a board sitting inside a sealed robot. Teams handle this with versioned messages and a refusal to talk when versions disagree, which is more discipline than the one-file protocol ever asked of them.
When is ROS 2 the better choice?
ROS 2 is the better choice the moment your microcontroller has to look like the rest of the robot to software you did not write. If the robot already runs ROS 2 upstairs, having the board publish its sensor as a topic means the data lands in your recordings, your visualiser and your existing nodes without a translator in the middle, and micro-ROS is the supported route. If several people work on the robot, one shared message definition beats a private protocol only its author understands.
ROS 2 also wins whenever the board is really a small Linux computer, when a vendor ships a ROS 2 driver for the hardware you bought, and when your project will be handed to a lab or a company that speaks ROS 2 and nothing else. HORUS is not the answer in any of those cases, and it is not an answer on the board at all.
The honest cost: micro-ROS brings a build system, an agent process on the host and a memory budget to manage, none of which makes the motors turn better. Adopt it because the robot lives in that ecosystem, not because it sounds more serious.
Is a microcontroller too small to deserve any structure?
No, and here is why: the structure a small board needs is a handful of decisions rather than a framework, and skipping them is what turns a board into a mystery. Decide which job owns which variable, and let nothing else write to it. Decide what happens when a command is late or absent, and make the safe answer automatic. Decide what the safe state is and drive the hardware into it on every reset, because a board that wakes with the motors enabled will eventually wake at the wrong moment.
Add a watchdog, so a stuck loop restarts instead of holding full power at a jammed joint. Give the board a way to say what it is doing that does not disturb its timing, even if that is one status message per cycle rather than free-form printing.
None of this is middleware, and all of it is engineering. Small does not mean careless. It means the discipline lives in your habits rather than in a library you can point at.
Will you have to rewrite the firmware once the robot grows?
Partly, but not the way you think. What gets rewritten is the transport and the message plumbing, and the plumbing was never the valuable part. The valuable part is everything the board learned about your hardware: that the left motor needs a little more push to match the right one, that the encoder jumps once at power-up, that the gripper must not close while the arm is moving.
That knowledge survives any restructuring, provided it lives in functions that take values and return values rather than being tangled into the code that reads the wire. The rewrite that genuinely hurts is the one where sensing, deciding and communicating are braided through hundreds of lines, because untangling them is the work.
So protect yourself with a habit instead of a framework. Keep one file that talks to the outside world, and let the control code take plain numbers and hand plain numbers back. Do that and swapping serial for CAN, or adding a message layer later, is an afternoon rather than a rebuild.
How do you decide whether your board needs middleware?
Count the jobs on the board that cannot wait for each other, then count the computers that must agree with each other, and if both counts are one, you are finished. That is the whole test, and paper beats argument for it. Write each job down: reading an encoder, holding a wheel speed, answering a host, blinking a light. Next to each, write how long it may be delayed before something visible goes wrong.
If everything can wait for everything else, keep the single loop. If one job cannot wait while another takes its time, you need a scheduler on the board, which is a real-time operating system, not a message layer. Only when separate computers must share a vocabulary is a message layer the missing piece, and even then it usually belongs on the larger computer.
- If your robot is one board with one loop -> plain firmware, because a message layer adds work and no capability.
- If a slow sensor is delaying your motor update -> a small real-time operating system, because the problem is scheduling, not messaging.
- If the robot already runs ROS 2 and you want the board's data inside it -> micro-ROS, because the tooling is the payoff.
- If the board obeys and the host thinks -> your own small protocol with a heartbeat, because an explicit agreement beats a framework here.
- If the crowding is on the main computer instead -> a middleware up there, because that is where the traffic actually is.
When two options stay close, weigh them on the five axes of the HORUS Fit Framework — ecosystem size, setup effort, team size fit, deployment target, and licence — and take the one that loses on the fewest. No scores, just five honest questions about your situation. And when the crowded computer turns out to be the big one rather than the small one, star HORUS on GitHub so it is in your list when you start building.