Top 20 GitHub repos for embedded engineers
Real code from the RTOS kernels, vendor HALs, bootloaders, test frameworks and debuggers that ship inside millions of devices. Read them in the right order and they teach you the whole firmware stack.
Why source beats tutorialsRead the code that already ships
Most embedded courses stop at blinking an LED. Production firmware has to boot securely, schedule dozens of tasks, survive a power cut halfway through a flash write, talk over a radio, draw a UI on a 320×240 display, and still be testable on a laptop before anyone touches a board. Nobody learns all of that from a textbook. You learn it by reading code that has already solved it.
The twenty repositories below are that code. Some are kernels you will run directly in products. Some are thin hardware layers that show how a register write becomes a GPIO toggle. Others are tools that make the work survivable: unit-test frameworks, an on-chip debugger, and two full-system simulators. Together they cover every layer between the silicon and the application.
Each entry below explains what the project is, what it will teach you, and the one file or folder to open first. The links point to the official repositories.
MapWhere each repo sits in the firmware stack
Before diving in, it helps to see how these projects relate. Firmware is built in layers. Hardware abstraction code sits right above the silicon. An RTOS or scheduler sits above that. Middleware such as file systems, serialization and graphics sits above the OS. Tooling runs alongside all of it on your development machine.
Quick referenceAll 20 repositories at a glance
The table keeps the original ranking. The Layer column tells you which part of Fig. 1 each repo belongs to, so you can jump straight to the gap in your own knowledge.
| No. | Repository | What you can learn | Layer | GitHub link |
|---|---|---|---|---|
| 01 | FreeRTOS | RTOS, tasks, queues, semaphores | RTOS | FreeRTOS/FreeRTOS-Kernel |
| 02 | Zephyr Project | Modern RTOS, drivers, Bluetooth, IoT | RTOS | zephyrproject-rtos/zephyr |
| 03 | ArduinoCore-avr | AVR microcontrollers, GPIO, timers | HAL | arduino/ArduinoCore-avr |
| 04 | CMSIS_5 | ARM Cortex-M, DSP, embedded libraries | HAL | ARM-software/CMSIS_5 |
| 05 | libopencm3 | Low-level ARM Cortex-M development | HAL | libopencm3/libopencm3 |
| 06 | ESP-IDF | ESP32 firmware, Wi-Fi, Bluetooth, IoT | SDK + RTOS | espressif/esp-idf |
| 07 | STM32CubeF4 | STM32 drivers, peripheral examples | HAL | STMicroelectronics/STM32CubeF4 |
| 08 | Apache NuttX | RTOS, POSIX APIs, embedded systems | RTOS | apache/nuttx |
| 09 | Mbed OS | RTOS, connectivity, IoT development | RTOS | ARMmbed/mbed-os |
| 10 | RIOT OS | IoT operating systems, networking | RTOS | RIOT-OS/RIOT |
| 11 | LVGL | Embedded GUI, displays, touch interfaces | Middleware | lvgl/lvgl |
| 12 | LittleFS | Flash file systems for microcontrollers | Middleware | littlefs-project/littlefs |
| 13 | MCUboot | Secure bootloaders and firmware updates | Boot | mcu-tools/mcuboot |
| 14 | nanopb | Protocol Buffers for embedded C | Middleware | nanopb/nanopb |
| 15 | Unity | Unit testing for embedded C | Tooling | ThrowTheSwitch/Unity |
| 16 | Ceedling | Automated testing for C projects | Tooling | ThrowTheSwitch/Ceedling |
| 17 | OpenOCD | JTAG/SWD debugging and flashing | Tooling | openocd-org/openocd |
| 18 | Renode | Embedded hardware and firmware simulation | Tooling | renode/renode |
| 19 | QEMU | System emulation and firmware testing | Tooling | qemu/qemu |
| 20 | Embedded Template Library (ETL) | C++ data structures for embedded systems | Middleware | ETLCPP/etl |
Layer 1 · Operating systemsThe RTOS kernels
An RTOS is the first big leap after bare-metal loops. Instead of one while(1) that polls everything, you split work into tasks with priorities and let a scheduler decide who runs. These six projects show very different answers to the same problem.
FreeRTOS
FreeRTOS is the most widely deployed small RTOS. The kernel is only a handful of C files, which makes it the best place to learn how a scheduler really works. You will see how a context switch saves registers on the stack, how the ready list is sorted by priority, and how a queue blocks a task until data arrives.
Once you understand tasks.c and queue.c, every other RTOS on this list becomes easier to read, because they all solve the same scheduling and synchronization problems.
tasks.c around vTaskSwitchContext(), then portable/GCC/ARM_CM4F/port.c to see the PendSV handler that actually swaps tasks.Zephyr Project
Zephyr is a full RTOS ecosystem rather than a bare kernel. It supports hundreds of boards and borrows Devicetree and Kconfig from Linux, so the same driver code runs on boards from Nordic, NXP, ST, Espressif and many others. It also ships a complete Bluetooth Low Energy stack, networking, and power management.
Reading Zephyr teaches you how large firmware projects stay portable. You will learn how a board file describes hardware, how drivers bind to that description at build time, and how a professional project organizes tests and CI.
samples/basic/blinky, then trace how DT_ALIAS(led0) resolves through the board’s .dts file to a real GPIO pin.ESP-IDF
ESP-IDF is Espressif’s official framework for the ESP32 family. It wraps a modified FreeRTOS with Wi-Fi, Bluetooth, TLS, OTA updates, and a component-based CMake build system. It is the fastest way to learn connected-device firmware on cheap, widely available hardware.
Pay attention to how it runs FreeRTOS on dual-core chips and how its event loop decouples Wi-Fi events from application code. Those patterns carry over to any IoT product.
examples/wifi/getting_started/station, then read components/esp_event to see how events are posted and dispatched.Apache NuttX
NuttX aims to be a small Unix. It implements POSIX and ANSI interfaces, so code that uses open(), read(), pthread_create() and file descriptors runs on a microcontroller. It even has a shell, NuttShell, and a virtual file system where drivers appear under /dev.
It is a great bridge for Linux developers moving into firmware. It is also the operating system underneath the PX4 drone autopilot, which shows it can handle demanding real-time work.
sim:nsh configuration and run NuttX as a process on your PC, no board required.Mbed OS
Mbed OS combined an RTOS (built on Keil RTX), a clean C++ driver API and connectivity stacks for Cortex-M boards. Its DigitalOut, Thread and EventQueue classes are still a tidy example of how to wrap hardware in object-oriented C++ without heavy overhead.
events/ to see how EventQueue defers work out of interrupt context.RIOT OS
RIOT targets very constrained IoT nodes, devices with a few kilobytes of RAM running on batteries for years. Its strength is networking: it includes a full IPv6 stack with 6LoWPAN, CoAP, and support for low-power radios.
Study RIOT to learn how a network stack fits into tiny memory, and how a tickless scheduler lets the CPU sleep between events to save power.
examples/gnrc_networking on the native board to try IPv6 networking from a Linux terminal.CompareChoosing between the RTOS options
The kernels overlap, so here is how they differ in practice. Scroll the table sideways on a phone.
| Project | API style | Footprint | Built-in networking | Best for |
|---|---|---|---|---|
| FreeRTOS | Own C API | Very small | Add-on libraries | Learning schedulers, simple products |
| Zephyr | Own C API, Devicetree | Small to medium | BLE, IPv4/IPv6, Thread | Multi-board professional products |
| ESP-IDF | FreeRTOS + Espressif APIs | Medium | Wi-Fi, BLE, TLS | ESP32 IoT devices |
| NuttX | POSIX | Small to medium | TCP/IP, USB | Linux-style code, drones |
| RIOT | POSIX-like C | Very small | 6LoWPAN, CoAP | Battery-powered sensor nodes |
| Mbed OS | C++ classes | Medium | Ethernet, cellular, LoRa | Study only (end of life) |
Layer 2 · Hardware abstractionClose to the registers
These repositories sit directly above the silicon. They are where a line like digitalWrite(13, HIGH) turns into a single store to a memory-mapped port register. Reading them removes the magic from microcontrollers.
ArduinoCore-avr
This is the code behind every Arduino Uno sketch. It is short and readable, which makes it ideal for a first look under the hood. You will see how millis() is driven by a Timer0 overflow interrupt, how analogWrite() configures hardware PWM, and how the serial port uses a ring buffer filled from an interrupt.
cores/arduino/wiring.c for timers and wiring_digital.c for GPIO. Then compare it with the ATmega328P datasheet.CMSIS_5
CMSIS is Arm’s standard interface for Cortex-M processors. Every vendor HAL for Cortex-M builds on it. It defines how the core registers (NVIC, SysTick, SCB) are named and accessed, provides the startup code pattern, and includes CMSIS-DSP for filters and FFTs plus CMSIS-RTOS as a portable RTOS API.
CMSIS/Core/Include/core_cm4.h to see exactly how NVIC_EnableIRQ() writes to hardware.libopencm3
libopencm3 is a community firmware library for many Cortex-M families, including STM32, NXP LPC, and others. It is thinner than vendor HALs, so each function maps almost one-to-one onto register writes. That makes it an excellent study companion while you read a reference manual.
lib/stm32/common/gpio_common_f0234.c and match each line with the GPIO chapter of an STM32F4 reference manual.STM32CubeF4
This is ST’s official MCU package for the STM32F4 series. It contains the HAL and LL (low-layer) drivers, board support packages, middleware such as USB and FatFS, and hundreds of example projects for real evaluation boards.
Comparing the HAL and LL drivers side by side is one of the best lessons in abstraction cost: the HAL is portable and safe, while LL is lean and close to the metal.
Projects/ and open a UART DMA example. Follow the call into stm32f4xx_hal_uart.c.Layer 3 · Boot and updateGetting new firmware onto a device safely
MCUboot
MCUboot is a secure bootloader used by Zephyr, NuttX, ESP-IDF, and many commercial products. It checks a cryptographic signature on the application image before running it, and handles over-the-air updates by keeping two image slots in flash.
It teaches the most important rule of field updates: a device must never be left without a working image, even if power fails mid-update. The swap-and-confirm flow in Fig. 3 is how MCUboot guarantees that.
boot/bootutil/src/loader.c and the design document docs/design.md.Layer 4 · MiddlewareGraphics, storage, data and C++
Middleware turns a running kernel into a product. These four libraries are portable, so you can drop them into almost any project on this list.
LVGL
LVGL (Light and Versatile Graphics Library) builds smartphone-style interfaces with buttons, charts, sliders, animations and anti-aliased fonts on microcontrollers with as little as tens of kilobytes of RAM. It only needs two things from you: a function that pushes pixels to the display and a function that reads touch input.
The source teaches partial screen redraws, dirty-area tracking and draw buffers, which are the techniques that make smooth UIs possible without a GPU.
src/core/lv_refr.c to see how invalidated areas are redrawn.LittleFS
LittleFS is a small file system built for raw NOR and NAND flash. It is power-loss resilient, so a sudden reset never corrupts the file system, and it spreads writes across blocks to avoid wearing out one area of flash. Its RAM usage is bounded and does not grow with the size of the storage.
The design notes are some of the best writing on copy-on-write data structures for flash. Read them even if you never use the library.
DESIGN.md first, then the 5-line block-device config in README.md.nanopb
nanopb implements Google Protocol Buffers in plain C, with no dynamic memory allocation. You write a .proto file, a generator produces C structs, and the library encodes them into a compact binary format that a phone app or cloud server can decode in any language.
It teaches you how to design firmware messages that can evolve over time without breaking older devices in the field.
examples/simple and read the generated .pb.h file. Use a .options file to set fixed array sizes.Embedded Template Library (ETL)
The C++ standard library containers allocate memory from the heap, which many safety and real-time projects forbid. ETL provides fixed-capacity replacements such as etl::vector, etl::string, etl::queue and etl::map whose maximum size is set at compile time, so memory use is fully predictable.
It is a good way to learn modern C++ techniques like templates and constexpr applied to embedded constraints.
std::vector in a project with etl::vector<T, 16> and check the map file to see memory move from heap to static.Layer 5 · ToolingTest, debug and simulate
Strong embedded teams find most bugs before firmware ever reaches a board. These five tools make that possible: two for unit testing on your PC, one for debugging real hardware, and two for running firmware on simulated hardware.
Unity
Unity is a unit-test framework for C made of a single source file and two headers. It runs on a PC or directly on a microcontroller, and offers assertions built for embedded work, such as checking integers in hex, bit masks and memory arrays.
examples/example_1. Write one test for a ring buffer before you write the ring buffer.Ceedling
Ceedling is the build system that makes Unity practical at scale. It finds test files, generates test runners, and uses CMock to create mocks from your header files automatically. With mocks you can test a driver’s logic by faking the HAL calls underneath it, so you never need the real chip for unit tests.
ceedling new myproject, then add #include "mock_gpio.h" to a test and watch the mock appear.OpenOCD
OpenOCD (Open On-Chip Debugger) connects your PC to a microcontroller through a debug probe such as ST-LINK, J-Link or a CMSIS-DAP adapter. It speaks JTAG and SWD to the chip, programs flash, and exposes a GDB server so you can set breakpoints and inspect memory from your editor.
Reading its target and flash drivers shows exactly how a debugger halts a core and writes flash through the debug port.
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg, then connect with arm-none-eabi-gdb and target extended-remote :3333.Renode
Renode, from Antmicro, simulates complete embedded systems, including the CPU, peripherals, sensors and even multiple networked nodes. It runs your unmodified firmware binary, so you can test a full Zephyr or FreeRTOS build in CI without any physical boards.
Its Robot Framework integration lets you write tests like “send this UART input, expect this output”, which is how teams catch regressions on every commit.
.repl file to see how a board is described.QEMU
QEMU is the general-purpose machine emulator used across the industry. For embedded work it emulates Arm, RISC-V and other architectures, with machine models for boards such as the BBC micro:bit and several Cortex-M and Cortex-A platforms. Zephyr and many Linux-based projects use it as a default test target.
Compared with Renode, QEMU is faster and broader in CPU support, while Renode models more peripherals and multi-node systems. Learn both.
west build -b qemu_cortex_m3 samples/hello_world and west build -t run.RoadmapA 12-week order for reading these repos
Twenty codebases is a lot. The order below builds each skill on the previous one, and every stage ends with something you can run.
- 1
Registers first. Blink an LED with ArduinoCore-avr, then do it again on a Cortex-M board using only CMSIS register definitions. Finish by comparing libopencm3 with the STM32 HAL for the same peripheral.
- 2
Then scheduling. Write two FreeRTOS tasks that share a queue, and step through a context switch with OpenOCD and GDB. Port the same idea to Zephyr to see Devicetree in action.
- 3
Then product features. Build an ESP32 sensor that stores readings in LittleFS, sends them as nanopb messages over Wi-Fi, shows them on an LVGL screen, and updates itself through MCUboot.
- 4
Then quality. Move your driver logic under Ceedling tests with mocked hardware, and run the whole firmware image in Renode or QEMU inside a CI pipeline.
How to readGetting value from a large codebase
Opening a repository with thousands of files can feel overwhelming. A few habits make it manageable.
Start from a sample, not from the root. Every project here ships examples. Build one, flash it or run it in a simulator, then follow the function calls inward. You will learn the architecture by tracing one real path through it.
Keep the datasheet open. HAL code only makes sense next to the reference manual that describes each register bit. When you see a write to RCC->AHB1ENR, look up what that bit enables.
Read the tests and CI files. Zephyr’s tests/ folder, LittleFS’s test suite and the GitHub Actions workflows in these repos show you how professional firmware is verified, which interviewers often ask about.
Read the issue tracker and pull requests. Closed bugs explain why odd-looking code exists. A race condition fixed in a FreeRTOS port or a flash wear issue in LittleFS teaches more than any tutorial.
Contribute something small. Fix a typo in documentation, add a board definition, or improve an example. Your first merged pull request to Zephyr or LVGL is a strong signal on an embedded résumé.
Wrap-upThe short version
If you only have time for three repositories, read FreeRTOS to understand scheduling, CMSIS to understand Cortex-M hardware access, and MCUboot to understand safe updates. Those three cover the core of what makes firmware different from application software.
With time for more, follow the 12-week roadmap above. By the end you will have touched every layer of a modern embedded product, from the register write that toggles a pin to the CI pipeline that tests a full firmware image on simulated hardware. That breadth is exactly what separates someone who can write a driver from someone who can ship a device.
