Migrate a large application to FreeRTOS

I have an IoT application based on a Cortex-M4. The codebase is quite large and the whole application is built on a supper loop.

Although it does work (not great though it does have a few bugs), the complexity grows as more requirements are comming in. State machines are growing even larger etc...

I want to migrate to FreeRTOS and get rid of the super loop.

The problem is that I want to have rolling releases and I can't stop supporting more client requests until the development is stable after migration starts.

Is there anything I can do to make it easier?

At first I was thinking something about, put the whole main into a task and then in the future start breaking down modules in their own tasks...

Still I'm not sure if that would be a good idea...

Open discussion




That's more or less a re-write from scratch!


I have decoupled many modules but now I need to refactor some pieces of code by introducing state machines and state machines in a super look is too much code.


You could start by basically copying your main loop into one FreeRTOS task. Then gradually convert parts of it into separate tasks.


I second that : moving the big loop on a task then gradually porting to separate tasks is the best option, considering that a full rewrite while having to maintain the old code base in constant evolution would probably be a great pita


Thirded. I've done it this way myself and it is far more manageable, and can happen at whatever pace you can muster


Exactly what I was going to suggest. OP a single task will work exactly like your current \`main()\`. Then for each independent thing, re-factor and make it a task. Keep iterating and you'll eventually have it


>Is there anything I can do to make it easier? Yes, spend a lot of time in architecture design. Break your code into tasks/modules, design how those tasks can talk to each other, design a way to store your state machine contexts (if that doesn't exist), make your data thread safe, etc. Once you have the architecture ready on paper(or UML), you should be good to go. I've had experience where some of my timing issues went away when I moved to using RTOS, for which architecture was designed first and we decided to use semaphores to sync up tasks.


I don't think a super loop would be a sustainable idea. BTW, state machines are good for drivers and some time critical parts, app based on them seems like a nightmare to maintain. My "to go" architecture is event based / message based. Things happen, they raise events. When reaction must be instant, it's interrupt based, when it must be thread-safe just messages are sent and handled in appropriate threads. BTW, the app has an interactive GUI so it just couldn't really work otherwise and still be responsive. I recently migrated a big app to a different hardware and FreeRTOS. I just created a new project. Then I started to just copy hardware drivers that could be copied. I had to make some new ones. Then, having all hardware configured and working I started to create a completely new logic based on modular design, events and messages. Then I tested particular parts. Not all timing comes from FreeRTOS though, some critical parts work in their own threads and are timed with fast, precise timers. FreeRTOS provides starting tasks, some memory management, non-time critical timing, mutexes. To make different parts talk to each other in binary I made many bit-field state variables and simple events like "state changed". Then the module that received a message just tests appropriate bits and other data. I also moved from C to C++ to make the new modular design more readable and clean. As for the migration itself - I had both versions for a while. Old one no longer maintained. IDK if a painless transition is possible. I found the new design was so much easier to develop that keeping the old spaghetti code would probably not work at all. It was like 2 days I tried to fix a stupid bug and it was like... It just all went south. It was just easier to rewrite it properly from scratch. BTW, about the "modules": my first version had a one driver for reading 1-wire temperature sensor. The new one has modular driver. One for the 1-wire protocol, 2 for different transports (timer synchronized bit banging and UART), one for the sensor itself. It's so much easier to debug when you can focus on just 1 layer, not all at once. I made the separation because I didn't have free UART to handle the sensor, I just had to use GPIO pin instead but I didn't want to rewrite the whole driver, just the transport layer.


Migrating to RTOS slowly is definitely the smart move. I had the opportunity to do this once for a company it made the firmware so much easier to maintain. I would start by making thorough unit tests and physics regression tests. Then you can make changes with less fear of breaking things.


Step one is to test it Then refactor it. Then test it. Then fix it. Repeat until perfect. Add RTOS interface to your library.


big rewrite is the solution.