A professional, test-driven embedded systems development template supporting multiple microcontroller platforms with comprehensive DevOps tooling, automated testing, and continuous integration.
This project template provides a robust foundation for embedded software development with a focus on Test-Driven Development (TDD), code quality, and DevOps best practices. It demonstrates a layered architecture approach suitable for production-grade embedded systems.
- β Multi-Platform Support: STM32F4 (ARM Cortex-M4) and Raspberry Pi RP2040
- π§ͺ Test-Driven Development: Full unit testing with Ceedling, Unity, and CMock
- π Code Coverage: Automated coverage reporting with gcov/gcovr
- π Static Analysis: clang-tidy with customizable rulesets
- π¨ Code Formatting: clang-format for consistent code style
- π Security Analysis: Flawfinder and Lizard for vulnerability and complexity analysis
- π³ Containerized Builds: Docker-based development environment
- π CI/CD Pipeline: Jenkins pipeline with comprehensive quality gates
- π¦ CMake Build System: Cross-platform build configuration
- ποΈ Layered Architecture: Clean separation of concerns
Embedded-Project-Template/
βββ source/ # Production source code
β βββ 00_MCU_SDK/ # MCU-specific SDK and startup code
β β βββ stm32/ # STM32F4 HAL and CMSIS
β β βββ rp2040/ # Raspberry Pi Pico SDK
β βββ 01_MCU_PORT/ # MCU abstraction layer (MP)
β β βββ stm32/ # STM32 port implementations
β β βββ rp2040/ # RP2040 port implementations
β βββ 02_HW_API/ # Hardware API abstraction (HA)
β β βββ ha_gpio/ # GPIO abstraction
β β βββ ha_iic/ # I2C abstraction
β β βββ ha_uart/ # UART abstraction
β β βββ ha_timer/ # Timer abstraction
β βββ 03_DEV_DRV/ # Device drivers (DD)
β β βββ dd_bmp388/ # BMP388 barometric pressure sensor driver
β βββ 03_PFM_SVC/ # Platform services (PS)
β β βββ ps_iic_bus_scanner/ # I2C bus scanner utility
β β βββ ps_logger/ # Logging service
β β βββ ps_timer/ # Timer service
β βββ 04_APP_SW/ # Application software
β β βββ inc/ # Application headers
β β βββ src/ # Application source
β βββ SW_UTILS/ # Software utilities
β
βββ tests/ # Unit tests
β βββ 01_MCU_PORT/ # MCU port layer tests
β βββ 02_HW_API/ # Hardware API tests
β βββ 03_DEV_DRV/ # Device driver tests
β βββ cmock/ # CMock configuration and stubs
β βββ support/ # Test support files
β
βββ tools/ # Development tools
β βββ clang-format/ # Code formatting configuration
β βββ clang-tidy/ # Static analysis configuration
β βββ jenkins/ # Jenkins-specific tools
β
βββ build/ # Build artifacts (generated)
βββ docs/ # Documentation
βββ Dockerfile # Development container definition
βββ Jenkinsfile # CI/CD pipeline definition
βββ Makefile # Top-level build orchestration
βββ project.yml # Ceedling configuration
βββ LICENSE # GPL v3 License
The project follows a layered architecture pattern:
- 00_MCU_SDK: Vendor-provided SDKs and low-level hardware definitions
- 01_MCU_PORT (MP): Microcontroller abstraction layer - direct hardware access
- 02_HW_API (HA): Hardware API layer - platform-independent hardware interface
- 03_DEV_DRV (DD): Device drivers - external component drivers
- 03_PFM_SVC (PS): Platform services - higher-level utilities and services
- 04_APP_SW: Application layer - business logic
This layered approach ensures:
- Portability: Easy migration between different MCU platforms
- Testability: Each layer can be tested independently with mocks
- Maintainability: Clear separation of concerns
- Reusability: Components can be reused across projects
- Build Tools: CMake 4.0+, Make, GCC
- ARM Toolchain: arm-none-eabi-gcc 14.2+
- RP2040 SDK: Pico SDK 2.1.1+ (if targeting RP2040)
- Testing: Ruby, Ceedling 1.0.1+
- Code Quality: clang-tidy-19, clang-format-19
- Coverage: gcov, gcovr
- Security: flawfinder, lizard, junitparser
- Python: 3.x with pip
- Docker or compatible container runtime
- Make
-
Clone the repository
git clone <repository-url> cd Embedded-Project-Template
-
Build for STM32
make cont-build MCU=stm32
-
Build for RP2040
make cont-build MCU=rp2040
-
Run unit tests
make cont-test MCU=stm32
-
Generate code coverage
make cont-cov MCU=stm32
-
Configure Python environment (required for Ceedling)
pip install gcovr flawfinder lizard junitparser
-
Install Ruby and Ceedling
gem install ceedling
-
Install ARM toolchain (platform-specific)
- Download from ARM Developer
- Add to PATH
-
Install clang tools
# Ubuntu/Debian apt-get install clang-tidy-19 clang-format-19 # macOS brew install llvm@19
-
Build the project
make build MCU=stm32 BUILD_TYPE=Debug
| Command | Description |
|---|---|
make build-image |
Build the Docker development image |
make cont-build MCU=<mcu> |
Build firmware in container |
make cont-test MCU=<mcu> MODULE=<module> |
Run unit tests |
make cont-cov MCU=<mcu> |
Generate test coverage report |
make cont-analyse MCU=<mcu> TYPE=<type> |
Run static analysis |
make cont-analyse-all MCU=<mcu> |
Run all static analysis checks |
make cont-analyse-flaws |
Run security analysis |
make cont-format |
Format source code |
make cont-format-tests |
Format test code |
make cont-start |
Start interactive container shell |
make cont-exec CMD="<command>" |
Execute custom command in container |
| Command | Description |
|---|---|
make build MCU=<mcu> BUILD_TYPE=<type> |
Build firmware natively |
ceedling test:all |
Run all unit tests |
ceedling gcov:all |
Generate coverage report |
make analyse TYPE=<type> |
Run specific static analysis |
make analyse-all |
Run all static analysis |
make analyse-flaws |
Run security analysis |
make format |
Format source code |
make format-tests |
Format test code |
make clean-all |
Clean all build artifacts |
- MCU:
stm32orrp2040 - BUILD_TYPE:
DebugorRelease - MODULE: Specific test module or
all - TYPE: Static analysis type:
bug,cert,core,misc,perf,read
The project uses Ceedling (Unity + CMock) for comprehensive unit testing.
# Run all tests for STM32
export MCU=stm32
ceedling test:all
# Run specific test module
ceedling test:ha_gpio
# Run with coverage
ceedling gcov:all
# Run in container
make cont-test MCU=stm32 MODULE=all- Unit Tests: Located in
tests/directory - Mocks: Auto-generated CMock mocks in
build/test/mocks/ - Test Runners: Auto-generated by Unity in
build/test/runners/ - Reports: JUnit XML and HTML reports in
build/artifacts/
Coverage reports are generated in multiple formats:
- HTML:
build/artifacts/gcov/TestCoverageReport.html - Cobertura XML:
build/artifacts/gcov/TestCoverageReport.xml - JSON:
build/artifacts/gcov/GcovCoverage.json
The project uses clang-tidy with multiple analysis categories:
# Analyze specific category
make analyse TYPE=bug MCU=stm32
# Run all categories (bug, cert, core, misc, perf, read)
make analyse-all MCU=stm32
# In container
make cont-analyse-all MCU=stm32Analysis results are saved to build/source/<mcu>/Debug/clang-tidy.txt
Automated code formatting with clang-format:
# Format source code
make format
# Format test code
make format-tests
# In container
make cont-format
make cont-format-testsConfiguration files:
- Source code:
tools/clang-format/configs/sources.clang-format - Test code:
tools/clang-format/configs/tests.clang-format
Security and complexity analysis with Flawfinder and Lizard:
make analyse-flaws
# In container
make cont-analyse-flawsReports generated:
- Flawfinder:
build/logs/ff_report.log - Lizard HTML:
build/logs/liz_report.html - Lizard warnings:
build/logs/liz_warns.log
Thresholds:
- Cyclomatic Complexity: CCN β€ 15
- Function Length: NLOC β€ 60
- Parameters: β€ 7
The project includes a multi-architecture Docker image supporting both x86_64 and ARM64.
Base: Ubuntu 24.04
Included Tools:
- ARM GCC Toolchain 14.2.rel1
- CMake 4.0.2
- Raspberry Pi Pico SDK 2.1.1
- Ceedling (Ruby gem)
- clang-tidy-19, clang-format-19
- gcovr, flawfinder, lizard, junitparser
- Git, Python 3, build essentials
# Build with current user permissions
make build-image
# Manual build with custom UID/GID
docker build -t naimmas/embedded-dev-img:multi_mcu \
--build-arg USER_UID=$(id -u) \
--build-arg USER_GID=$(id -g) .# Interactive shell
make cont-start
# Run single command
make cont-exec CMD="ls -la"
# Build project
make cont-build MCU=stm32The project includes a comprehensive Jenkins pipeline with multiple quality gates.
- Checkout: Clean workspace and fetch source code
- Build: Compile firmware for all target platforms
- Unit Tests: Run test suite with coverage
- Static Analysis: clang-tidy analysis
- Security Analysis: Flawfinder and Lizard checks
- Build logs with GCC warnings
- JUnit test results
- Code coverage reports (Cobertura format)
- Static analysis warnings (clang-tidy)
- Security vulnerabilities (Flawfinder)
- Complexity metrics (Lizard HTML)
# Simulate CI build
make cont-build MCU=stm32 BUILD_TYPE=Debug
make cont-build MCU=rp2040 BUILD_TYPE=Debug
make cont-cov MCU=stm32
make cont-cov MCU=rp2040
make cont-analyse-all MCU=stm32
make cont-analyse-all MCU=rp2040
make cont-analyse-flaws-
Install required Jenkins plugins:
- Warnings Next Generation
- JUnit
- Code Coverage API
- HTML Publisher
-
Create pipeline job pointing to
Jenkinsfile -
Configure post-build actions for reports
The template includes a demo application that:
- Initializes the BMP388 barometric pressure sensor
- Configures sensor settings (oversampling, ODR, IIR filter)
- Reads temperature and pressure data in a loop
- Demonstrates logging and I2C communication
STM32F401xC:
- GPIO control (LED)
- I2C communication
- UART logging
- Hardware timers
Raspberry Pi RP2040:
- GPIO control
- I2C communication
- UART logging
- Timer functionality
-
Generate module structure:
ceedling module:create[module_name]
-
Write tests first (TDD approach):
// tests/02_HW_API/test_new_module.c #include "unity.h" #include "new_module.h" void test_new_module_should_initialize(void) { TEST_ASSERT_EQUAL(RET_OK, new_module_init()); }
-
Implement functionality:
// source/02_HW_API/new_module/new_module.c response_status_t new_module_init(void) { // Implementation return RET_OK; }
-
Run tests:
ceedling test:new_module
-
Check coverage:
ceedling gcov:new_module
- All unit tests pass
- Code coverage β₯ 80%
- No clang-tidy warnings
- Code formatted with clang-format
- No security vulnerabilities (Flawfinder)
- Cyclomatic complexity < 15
- Function length < 60 lines
- Build succeeds on all platforms
- Architecture: See docs/ for detailed architecture documentation
- API Documentation: Generated from source code comments
- Ceedling Guide: ThrowTheSwitch.org
- CMock Guide: ThrowTheSwitch.org
- Unity Guide: ThrowTheSwitch.org
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Ensure all quality checks pass
- Submit a pull request
<type>(<scope>): <subject>
<body>
<footer>
Types: feat, fix, docs, style, refactor, test, chore
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
- Ceedling/Unity/CMock: ThrowTheSwitch.org
- ARM Toolchain: ARM Limited
- Raspberry Pi Pico SDK: Raspberry Pi Foundation
- STM32 HAL: STMicroelectronics
For questions or support, please open an issue on GitHub.
Note: This is a template project designed to be customized for your specific embedded application needs. The example application demonstrates capabilities but should be replaced with your actual application logic.