You are browsing as a guest. Sign up (or log in) to start making projects!

Guagyyy

@Guagyyy

Joined June 1st, 2026

  • 10Devlogs
  • 2Projects
  • 1Ships
  • 15Votes
Open comments for this post

7h 10m 51s logged

1. Fixing the Stack Overflows at Boot

After an entire 25-hour debugging period across 4 days, the RTOS kernel is finally done (for the scheduled tasks). I tracked down a silent bug where several critical scheduled tasks—like powerManagement, lowLevelFailSafe, and telemetryTX—were crashing the MCU on boot. The issue came down to a boot-time dependency lock: tasks were trying to poll hardware registers (like UART TX flags) and read shared drone state variables before the peripherals or producer tasks had time to initialize on Tick 0. By adding an initial yieldCurrentTask() right at the top of these methods, Task Phase Alignment was officially added. This gives the hardware a full cycle to stabilize, preventing buffer overflows and eliminating the cascading task state corruptions that were locking up the scheduler.

2. Custom Stack Sizing & Real-Time Guard Checks

Alongside the scheduler fix, I pushed major upgrades to memory safety and task management. Instead of assigning a static stack array to every task, I transitioned to dynamic per-task stack allocations during initialization (new (std::nothrow)). Heavy string-parsing and math tasks get larger stacks, while simple toggle tasks stay lightweight. To catch memory issues before they corrupt the heap, I implemented a custom dual guarding system: 0xDEADBEEF is appended at the bottom of the stack to catch standard stack overflows, while a new 0xBEEFCAFE canary value is written to the very top index (stackSizeWords) to instantly flag memory breaches in the SysTick_Handler. Furthermore, I refactored powerManagement to use ADC2 in Circular DMA Mode, writing battery telemetry directly to RAM in the background for zero CPU overhead.

3. Transitioning to Walksnail Avatar HD

Looking ahead, my and my friend are officially transitioning our existing analog video setup over to a fully integrated Walksnail Avatar HD digital ecosystem. Rather than keeping my custom telemetry task (telemetryTX) with heavy string formatting over raw UART, Walksnail allows me to stream standard MSP (MultiWii Serial Protocol) DisplayPort frames straight to the VTX. The Walksnail system will serve as our primary onboard logging source, giving us full OSD telemetry, high-framerate HD video, and precise blackbox data logging. With the RTOS core now stable and non-blocking, our next focus shifts toward “premium” flight controller features, e.g. including custom RC stick expo curves, an expanded USB CLI, and an integrated simulator interface.

0
0
6
Open comments for this post

9h 4m 46s logged

I just finished expanding the RTOS architecture, filling out all of the scheduled background tasks to handle real-time flight telemetry, peripheral hardware, and terminal configuration. On the telemetry side, I built out telemetryTX to package battery, status, and GPS data into standard CRSF frames over UART, alongside an NMEA gpsParser that decodes $GNGGA and $GNRMC sentences for live location fixes. I also made powerManagement into its own standalone task so ADC voltage and current metrics can feed into the failsafe logic and telemetry concurrently. To make bench testing and tuning significantly easier, I added usbCLI over USART1, giving me a full interactive shell to tweak PID gains (Kp, Ki, Kd), dump raw IMU data, and inspect hardware states on the fly without needing to reflash firmware.

To keep the physical quad safe and responsive during flight, I added updatePeripherals for LED status patterns and buzzer warnings, along with a dedicated Independent Watchdog (IWDG) task running as a low-priority fail-safe to trigger hardware MCU resets if the scheduler ever freezes. On the project structure side, CMake now routes all output binaries straight into a clean ./bin/ directory so nobody has to compile the source manually just to run the .elf target in Renode. I also introduced a -DENABLE_DEBUG flag, allowing me to cleanly toggle between complete USART log prints across every loop or a blank terminal environment meant purely for the CLI shell.

While all the background subsystems and scheduled task priorities are fully mapped out, I’m currently figuring out a few lingering RTOS bugs—specifically around stack allocation, causing stack overflow flags on certain scheduled tasks, along with scheduling starvation where context switching isn’t releasing task blocks properly. Because I want to ensure the scheduler kernel is 100% complete before pushing broken stack frames to the main branch, the GitHub repository will be updated with the complete, bug-fixed code later today. Everything is moving in the right direction, and getting these background tasks finalized brings me one step closer to full flight hardware deployment!

0
0
2
Open comments for this post

2h 30m 50s logged

I’ve spent the last 20 hours of coding basically staring at bare-metal memory allocations and tracking down why the drone’s context switcher was triggering CPU drops under the Renode simulation environment. Moving away from manual Python injection scripts to a cleaner C++ conditional compilation framework using CMake was supposed to simplify the testing layout, but instead, it exposed structural design flaws in how the scheduler kernel boots up. Running a multi-sample sensorCalibration() routine at cold boot was forcing hidden task yields before the OS kernel was fully mapped out, which was throwing unmapped register address errors and wiping out index-0 entries.

To solve this, I decoupled the entire state machine sequence so that hardware allocations, peripheral registers, and core tracking counters now clear before any operational loops drop into the registry. I also added a critical rtosStarted gatekeeper flag to yieldCurrentTask() so calibration math runs cleanly in sequence without triggering early PendSV handler switches. On top of that, the task stack pointers are completely realigned. The assembly context swapper now maps a precise 32-word boundary allocation utilizing 0xFFFFFFFD as a basic exception return code, which completely eliminates the data corruption that was misaligning our Process Stack Pointer.

With the fundamental kernel stabilized, I shifted entirely into clean architecture organization. The massive, cluttered code blocks have been split up into independent tracking files (BufferPopulation.cpp, Tasks.cpp, and SimInjector.cpp). High-speed SPI reads for the IMU, UART ring buffers for decoding CRSF radio channel frames, battery ADC sampling, and DShot600 motor output mixers are now fully broken out and functional. There is still a nagging preemption lockout issue inside the simulator where a single interrupt task attempts to starve out the other concurrent loops during a millisecond window, but the baseline operating system is locked in, meaning I can confidently verify the PID flight mixing tables and safety code blocks safely before the code ever touches a physical quadcopter.

If you would like to run your own tasks, I also added support in the .bat and .sh files to build code for the simulation (flag=sim), user task code (flag=user), and actual drone upload code (no flags).

Example MacOS Commands:
./build.sh sim
./build.sh user
./build.sh

This makes it so that I don’t have to constantly modify code to run either the user tasks or simulation code, instead using a flag. Overall, this RTOS is not done yet, however, I plan to add the rest of the scheduled tasks later today, and fixing the sole bug keeping this simulation back!

I expect a full ship to be comming out in the next week or so, along with all ESC, RM, and TM code complete!

0
0
7
Open comments for this post

9h 14m 46s logged

I’ve been doing a lot of debugging for the RTOS scheduler inside the simulation environment, and it’s been very difficult for me to identify many bugs in teh core architecture of my tasks. Moving away from Python scripts to a cleaner C++ conditional compilation framework using CMake was supposed to simplify things, but instead, it exposed some nasty architectural bugs in how tasks and context switching interact. There has also been a massive priority inversion and preemption lockup where the CRSF parsing loop completely starves out the lower-priority threads. Also, running a raw sensor calibration loop at boot was forcing thread switches before the OS kernel was even fully initialized, completely corrupting task indexes, throwing null pointer warnings, and leaving half of my PID calculations at zero. Still working on many fixes, but it has been a lot of work (9 hours), but I hope to get the full RTOS with at least bare minimum flight tasks out by tomorrow! The GitHub repo still won’t be fully up-to-date until these structural bugs are figured out, but I plan to push the corrected scheduler files and mock drivers very soon.

The screenshot below shows an image of the CRSF task completely dominating, without any processes interrupting it.

0
0
10
Open comments for this post

9h 31m 14s logged

I just wrapped up the core architecture of the drone’s flight controller, including mapping out all 7 interrupt methods into a far more optimized RTOS scheduler. The updated version makes use of a FIFO stack, to keep track of nested interrupts. I also protected the main code from data corruption during pendSV switches by enabling and disabling irq in certain areas of vulnerable code. Other small changes to the assembly to save floating point registers as well as better memory allocation was also done in this update.

From high-speed IMU sensor integration and state estimation calculations to CRSF packet decoding and DShot motor generation, the essential tasks are fully written and compiled. I’ve officially shifted gears into the testing phase, working on simulated environments to debug DMA transfers, registers, and the flight state machine’s arming sequences. As of now, I am writing python scripts by hand, in order to inject “dummy” values into the GPIO, SPI, and USART registers in order to populate the buffers, and simulate the calculations.

As the code is becoming longer and longer, I will try to refactor a lot of the files into smaller more organized portions, to increase readability (often I have a hard time traversing everything).

Note: The current code pushed to GitHub isn’t quite the latest since I’m in the middle of troubleshooting a few simulation-specific register bugs, but I wanted to get this update out to meet the 10-hour devlog requirement on Stardance. Everything is moving in the right direction, and having a virtual environment all working means I can safely verify the PID mixer and safety flags before this code ever touches a physical quad. This officially marks the end of both phases 2 and 3, and now its a matter of finishing extra scheduled telemetry tasks (e.g. OSD and EdgeTX logging), but I estimate that the flight controller portion of the code (around 60% of all logic in the drone) will be done between now and Sunday, July 19th!

I will also post a much more in depth devlog about how the methods work, as well as an updated README.md on Github ASAP.

0
0
6
Ship Pending review

## Phase 1 (The Custom RTOS Kernel)

I am building a custom FPV drone from scratch with my friend in an attempt to achieve **200 mph**. At those speeds, standard super loops are far too slow, as a single microsecond of lag means the drone travels feet before making a PID correction. To solve this, I built a custom, hybrid cooperative/preemptive Real-Time Operating System (RTOS) from scratch for the ARM Cortex-M7 (STM32F7)!

### What Was Challenging?
The absolute hardest part of this phase was writing the low-level assembly context switcher (`PendSV_Handler.S`). Hacking the ARM Cortex-M7 registers to physically hot-swap stack pointers (`PSP`) mid-execution without crashing the CPU or corrupting the program state was a massive headache.

I had to ensure that when an asynchronous preemption event fires, the assembly code perfectly preserves the execution registers (`R4-R11` plus the hardware-stacked registers), jumps to the high-priority task, and then seamlessly restores the background task right where it left off. Getting the compiler-enforced task overloading working so the kernel cleanly separates periodic tasks from raw interrupts was another huge hurdle, but it makes the whole codebase infinitely more robust.

### Want to Test It or Write Your Own Tasks?
You don't need physical hardware to watch this run! I’ve packaged the entire project with a **Renode** simulation script. You can run the interactive emulator with a single command to see the preemption scheduler hijack our counter task live, or you can jump into the user sandbox file and write your own custom tasks.

**[Check out the GitHub Repository to Run the Demo!](https://github.com/Guag914/200-MPH-Drone-RTOS)**

The README has a quick 5-minute setup guide to get you compiling and simulating your own code in no time.

  • 5 devlogs
  • 15h
Try project → See source code →
Open comments for this post

1h 27m 37s logged

To make my hybrid RTOS accessible and easy to test, I designed a modular sandbox task registry system that isolates the core kernel from custom user code. Developers can write standard non-returning tasks in an isolated file (User_Tasks/user_tasks.cpp), while the kernel exposes a registration hook that allocates a dedicated 1024-word stack space. This decoupling means anyone can plug in their own cooperative or preemptive tasks, set execution periods or trigger bounds, and run them instantly on our virtual STM32F7 without risking memory corruption or touching the underlying context-switching assembly. For more information on how to make your own tasks refer to the README.md.

0
0
7
Open comments for this post

1h 5m 32s logged

I just updated a few build files and restructured the project, so code and simulation separation would be better. I also added a few executable files (build.sh, run.bat, and run.sh) to easily compile and run the project.

0
0
5
Open comments for this post

5h 35m 39s logged

I finally reached a massive milestone with the kernel architecture and got the scheduler firing exactly how it needs to. Even though I thought the RTOS portion was completely wrapped up a bit ago, now it is officially done. I ended up doing a massive revamp on how scheduled tasks execute, routing them through PendSV so that the background worker states get completely backed up and saved by my assembly code just like the preemptive ones. I also cleaned up the initialization API by throwing in an overloaded function to separate timed loops from raw hardware events (the interrupt tasks). Overall most of the testing logic is now gone, and now the system is being more and more focused on the actual drone tasks.

On the hardware side of things, I built out the actual drone tasks and the underlying SPI and UART DMA drivers that populate my local data packets. Even though they are not in use right now as all the tasks (which are not fully completed) require the support of each other for full testing.

With the core OS completely checked off, the focus is shifting entirely over to the actual flight control logic. Over the next couple of days, I’m going to wrap up the final flight controller tasks, and get the core stabilization algorithms running. Once the flight controller application layer is fully put together, I’m moving straight onto building out complete simulations for the receiver, transmitter, and finally the ESC modules to get the whole drone network interacting together!

0
0
12
Open comments for this post
Reposted by @Guagyyy

3h 49m 44s logged

I finally finished the most difficult part of the project so far and got a hybrid cooperative/preemptive scheduler running from scratch. After a ton of debugging with the assembly memory layout, the core RTOS kernel shell is officially stable, verified inside the Renode simulator, and completely ready to handle flight data.

The architecture divides the CPU’s brain into two execution pathways to maximize calculation speeds while keeping latency near zero. Standard background tasks, like handling low-priority peripheral updates, run sequentially inside a cooperative loop based on their relative priority. They execute and then drop out without wasting an overhead time. But for high-priority hardware event such as a critical radio packet arriving over UART or raw sensor data from the IMU, the system completely bypasses the standard queue. It pulls a physical hardware lever (PendSV) that forces an instant stack swap.

When an interrupt task fires through PendSV, the CPU pauses the background scheduled loop, and my assembly code packs up the core registers, saves that exact memory address to RAM, and hot-swaps the Process Stack Pointer to launch the interrupt task. Right now, there is just a dummy delay loop inside the interrupt task to visually show the execution break in the console logs. Once it finishes printing, it hits a yield function that throws the assembly switch backward, pops the background registers, and hands control straight back to the flight loop.

The repository currenly has a lot of diagnostic test code because I needed to prove the pointer indirection and stack-faking logic worked without blowing up the memory alignment. Now that the plumbing is completely verified, the next step is to strip out the test code and start writing the actual flight infrastructure. I am moving straight into coding the PID control loops, signal mixers, Kalman filters, and live motor generation.

0
1
10
Open comments for this post

3h 49m 44s logged

I finally finished the most difficult part of the project so far and got a hybrid cooperative/preemptive scheduler running from scratch. After a ton of debugging with the assembly memory layout, the core RTOS kernel shell is officially stable, verified inside the Renode simulator, and completely ready to handle flight data.

The architecture divides the CPU’s brain into two execution pathways to maximize calculation speeds while keeping latency near zero. Standard background tasks, like handling low-priority peripheral updates, run sequentially inside a cooperative loop based on their relative priority. They execute and then drop out without wasting an overhead time. But for high-priority hardware event such as a critical radio packet arriving over UART or raw sensor data from the IMU, the system completely bypasses the standard queue. It pulls a physical hardware lever (PendSV) that forces an instant stack swap.

When an interrupt task fires through PendSV, the CPU pauses the background scheduled loop, and my assembly code packs up the core registers, saves that exact memory address to RAM, and hot-swaps the Process Stack Pointer to launch the interrupt task. Right now, there is just a dummy delay loop inside the interrupt task to visually show the execution break in the console logs. Once it finishes printing, it hits a yield function that throws the assembly switch backward, pops the background registers, and hands control straight back to the flight loop.

The repository currenly has a lot of diagnostic test code because I needed to prove the pointer indirection and stack-faking logic worked without blowing up the memory alignment. Now that the plumbing is completely verified, the next step is to strip out the test code and start writing the actual flight infrastructure. I am moving straight into coding the PID control loops, signal mixers, Kalman filters, and live motor generation.

0
1
10
Open comments for this post

2h 49m 22s logged

I finally added minimum task scheduling for the RTOS. I had originally designed a simple kernel to create a buffer of scheduled tasks in QEMU, but after learning that the simulation didn’t actually support the specific MCU that my friend and I are using for the real drone, I had to pivot. I switched over to using STM32CubeMX for the actual bare-metal configuration and Renode to handle running the emulation. Right now, the kernel just executes a task based on a basic timing loop and priority, waits for it to completely finish, and then moves on to the next task.

To be honest, it isn’t super useful yet because it only covers tasks in what I call the “scheduled zone” of the drone. This includes background stuff like blackbox flight data management, but it leaves out the most critical parts like the PID control loops and streaming real-time UART data from the IMU. Those features need to fire their own asynchronous interrupt signals at the highest possible hardware priority so they can instantly pause the optional background tasks and execute the critical code that actually keeps the drone stable and flying.

Next up, I need to write a short piece of assembly to handle saving the current CPU state and registers of whatever task is currently running when an interrupt hits. It shouldn’t be too bad since it will only take about 30 lines of code. I know I haven’t been posting devlogs for the past couple of weeks even though I’ve been grinding on this, but that’s mostly because we spent a ton of time initially planning out the architecture of the drone. We wanted to map out the software boundaries early so my friend and I could go our separate ways working on different systems without having massive merge conflicts later on.

0
0
13

Followers

Loading…