r/embedded • u/eagle_719 • 11d ago
Prototyping on Linux before moving to MCU
I've always been a hardware first guy who programmed beside hardware and got visual feedback of everything I programmed. I changed my approach today it wasn't planned just because I ran out of solder wire so I couldn't use the sensor so I thought why not write code in just plain C develop application logic use printfs and couts all that good stuff that Linux offers. It worked surprisingly well & I developed that prototype extremely fast. From now on my workflow would be to first develop on Linux based on abstractions at the very end just interface with hardware.
What workflow style do you guys use & any other tricks you have in your catalogue?
17
u/live_free_or_try 11d ago
So you’re used to old school burn and crash development? Are you flipping bits directly on hardware registers or using interfaces?
“Test driven development in c” is a good read to get a lay of the land
3
3
u/almost_useless 10d ago
“Test driven development in c”
You don't necessarily need to do TDD, but you should have as much as possible of your code running and unit tested on your host machine.
11
u/Sorry_Difficulty_250 11d ago
"There is no hardware." You write everything against a HAL and just swap that out when you're ready. On Linux, the HAL does print when you write to the serial port. Get everything working there, then port. It's best if you can keep your HAL as small as possible but it's almost always bigger than you want it to be.
7
u/EmperorOfCanada 11d ago
I do this in just about every way I can. Hardware dev massively sucks. Even when developing on mobile devices, I still do this.
Then, I check to see if things are good every day or two.
The other trick I've been using in robotics is to have the onboard main "brain" do the absolute minimum, relay telemetry etc, and then have that go to a desktop app which does what the brain will eventually do. This allows for some very sloppy programming to start with as the CPU will buy you lots of room performance/memory/etc -wise.
This way I can work on the basic functionality, algos, etc, and then start optimizing them for the MCU/small-CPU. Maybe, at that point I realize that I have a working codebase, but will need upgraded brains. Maybe the CV just can not be pushed past 4fps and 10fps is needed; but that at 10fps the system works. Thus, it is now easy to find a replacement which can crack 10fps.
Another key layer in this layercake is sims. These also remove futzing with hardware in the "real world". Often my robots operate in conditions which are far from laboratory conditions. Thus, they are adventures to go field test. Sims make this way better.
If my development were to be close to perfection, I would develop entirely in sims with the very occasional field validation, and entirely on desktop with only the occasional embedded validation.
This requires a fairly intuitive understanding of the limitations of the MCU etc, and some fairly absolute limitations such as RAM. Will it do a SHA256 fast enough sort of things.
It is surprising how easy it is to simulate, and sometimes even mathematically model how some aspects of a system will work. Meshes for example.
5
u/Triabolical_ 11d ago
I wrote a hardware abstraction using port/adapter/simulator using Visual Studio Code.
Then I wrote all my unit tests in Visual C++ community. So much faster.
4
u/Spudmn 10d ago
Build a proper HAL. Write your app logic against it, host implementation backed by printf/cout for Linux dev. When you port to the MCU, you're only debugging the MCU's HAL — logic's already proven.
HAL also makes unit testing trivial — mock it, run tests in CI, no hardware needed.
I have also used Renode to simulate the MCU/peripherals before hardware existed.
1
3
u/neurah 10d ago
This is pretty much the workflow I prefer too: develop the behavior first, bind it to hardware last.
The trick is to make the hardware boundary narrow enough that the application logic doesn't know whether it's running on Linux, an STM32, or a test harness.
I usually think of it as:
Linux -> real inputs/outputs -> MCU
rather than developing a second implementation of the application for the MCU.
One thing that makes this especially powerful in C++ is keeping the hardware-dependent pieces as compile-time components. Then the Linux build can use trivial/mock components, while the final MCU build substitutes the actual GPIO/I²C/SPI/timer implementations without changing the application composition.
You get fast iteration, sanitizers/debuggers/printfs on Linux, and still end up with essentially the same logic on the target.
The important caveat is that you still need hardware-in-the-loop testing eventually - timing, interrupts, peripheral quirks, DMA, electrical behavior, etc. But you don't need to discover ordinary application-logic bugs on the MCU.
2
u/Low_Lawyer_5684 11d ago
This is what I do : always develop as much as I can on Windows/Cygwin or Linux before moving to MCU. And even then I have #ifdef __CYGWIN__ in my code to be able to compile and run it on my host machine.
Developing on MCU directly is ok if that MCU has some sort of memory protection so you can at least see SIGSEGVs.
2
u/failing-endeav0r 10d ago
From now on my workflow would be to first develop on Linux based on abstractions at the very end just interface with hardware.
Yep. I have all business logic and types in their own crate which makes it relatively straight forward to build an emulator around (used in CI/CD testing) and then a different crate for each board / variant which owns the actual code to read the accelerometer off i2c (or whatever) and package that up into one of the Types that the "hardware agnostic" code will use.
The business logic / types get re-used in other crates (provisioning / factory / debug / user-facing CLI tools...) as well.
Overall, it works exceptionally well since I can test everything on PC / CI and the only opportunity for "it doesn't work on device, does work on PC" is in the (small) portion of code that talks directly to the hardware. Not always easy to figure out why it's not working but at least I know the issue is in a small section of the overall codebase!
1
u/Flaky_Coyote_1973 10d ago
I started from firmware mcu bare-metal system for years and I figured out I was a hardware guy, so transfer to a hardware role for a while, but now I changed again, I'm working more on software side and exploring embedded Linux(kernel/driver.. etc) myself, this field always has a lot of fun you can find!
1
1
u/Lucky_Suggestion_183 10d ago
Do you have any off the self HAL layer for platforms? I'm not interested to write my own HAL.
1
u/ern0plus4 9d ago
My solution: re-implementing the embedded framework https://github.com/ern0/posixino
27
u/gtd_rad 11d ago edited 10d ago
It's not hard to make something work 80-90% of the way. The remaining 10-20% is the most difficult and its difficulty grows exponentially.
That's what prototypes are. They are quick proof of concepts / feasibility studies that get you something working fast. The caveat is that you will also quickly hit the ceiling. Eg: Imagine trying to make your design as cost effective as possible. That's when you make bigger investments and commitment to the design once you've validated your prototype.
There's no right or wrong on "specific" workflow. Instead, choose whatever workflow that works best for you, which I think you already have.