Overview
In the evolving landscape of embedded systems development, workflows that support automation and efficiency are becoming more essential. This article explores how to build a robust CI/CD pipeline tailored for embedded projects using GitHub Actions, Docker, the MCUXpresso SDK, and Visual Studio Code. By integrating these tools, developers can automate builds, run tests, and ensure consistent firmware delivery across teams and environments.
We begin by outlining the benefits of CI/CD in embedded workflows, including faster iteration cycles, reduced human error, and improved collaboration. Then, we dive into the practical setup: using Docker to encapsulate the build environment, GitHub Actions to orchestrate builds and tests, and MCUXpresso SDK within VS Code to manage and develop firmware projects. Real-world examples and reusable templates will guide readers through creating a pipeline that’s scalable, maintainable, and optimized for NXP-based development boards.
Whether you're an embedded engineer looking to modernize your workflow or a product manager aiming to improve delivery timelines, this guide will help you harness the power of automation in your development lifecycle.
Prerequisites
MCUXpresso for VS Code
MCUXpresso SDK 24.12 or later
Git
GitHub account
Docker
Table of Contents
Benefits of CI/CD in Embedded Workflows
Containers - Docker
Automation - GitHub Actions
Using the pipeline
Conclusion
1. Benefits of CI/CD in Embedded Workflows
Implementing Continuous Integration and Continuous Deployment (CI/CD) in embedded systems development offers transformative advantages, especially when working with complex toolchains like the MCUXpresso SDK and hardware-specific constraints. Here are the key benefits:
Automated Builds and Testing
CI/CD pipelines eliminate manual build steps by automating compilation, linking, and flashing processes. This ensures that every code change is validated against a consistent build environment, reducing the risk of human error and saving valuable engineering time.
Early Detection of Issues
By integrating automated unit tests, static analysis, and hardware-in-the-loop (HIL) testing into the pipeline, developers can catch bugs and regressions early before they reach production hardware. This leads to more stable firmware and fewer surprises during integration.
Consistent Environments with Docker
Using Docker to containerize the build environment ensures consistency across development machines and CI runners. Developers no longer need to worry about mismatched toolchain versions or missing dependencies—everything is defined and reproducible.
Improved Collaboration and Code Quality
CI/CD encourages frequent commits and pull requests, which are automatically validated. This fosters better collaboration among team members, enforces coding standards, and ensures that only tested code is merged into the main branch.
Faster Iteration and Deployment
With automated pipelines, firmware updates can be built, tested, and deployed to target devices or staging environments quickly. This accelerates development cycles and enables rapid prototyping, especially useful in agile or iterative development models.
Traceability and Auditability
CI/CD systems log every build, test result, and deployment, providing a clear history of changes. This is crucial for debugging, compliance, and maintaining high-quality standards in regulated industries like automotive or medical devices.
Scalability Across Projects
Once a pipeline is established, it can be reused or adapted across multiple embedded projects. This scalability reduces setup time for new boards or applications and promotes best practices across teams.
2. Containers - Docker
What Are Containers?
Containers are lightweight, portable units of software that package up code along with all its dependencies, libraries, and configuration files—so it can run reliably across different computing environments.
Think of a container as a self-contained box that includes everything your application needs to run. There are several platforms that can be used to containerize a workspace. For this guide, we focus on Docker.
What Is Docker?
Docker is an open-source platform that enables developers to build, package, and run applications in containers. It simplifies the process of creating isolated environments that include everything an application needs such as code, libraries, tools, and settings to run consistently across different systems.
At its core, Docker helps solve the problem of "it works on my machine" by ensuring that the development, testing, and deployment environments are identical, whether you're working locally or in the cloud.
Steps to Containerize the MCUXpresso SDK and Build System using Docker
The following components are required to containerize the MCUXpresso SDK and build system with Docker.
Dockerfile - This is a text file that defines the steps to build a Docker image such as installing packages, copying files, and setting environment variables.
Docker Image - This is a snapshot of a container environment. It’s built from a Dockerfile and used to create containers.
Docker Container - A running instance of a Docker image. It’s isolated, lightweight, and portable.
Writing the dockerfile
1. Open a text editor (e.g VS Code)
2. Create a new text file. Name it dockerfile. Save this file as a Docker file type.
3. When creating a dockerfile to containerize the MCUXpresso SDK and build system, you must specify all of the components you need. We have provided a template below which you can copy and paste it in your dockerfile.
What the template does:
- Uses Ubuntu 22.04 as the foundation for the container. This provides a stable Linux environment for building and running embedded tools.
- Prevents interactive prompts during the installation
- Installs all the packages necessary (some optional) to work with the MCUXpresso SDK
- Installs the ARM GNU 13.2 toolchain
- Sets up a workspace to clone the MCUXpresso SDK in with West
- Configures the toolchain path environment variable
# Use Ubuntu 22.04 as the base image
FROM ubuntu:22.04
# Set environment variables for non-interactive installations
ENV DEBIAN_FRONTEND=noninteractive
# Install necessary packages /some optional
RUN apt update && apt install -y \
curl \
wget \
ca-certificates \
xz-utils \
libncurses5 \
cmake \
ninja-build \
git \
python3 \
python3-pip \
build-essential \
device-tree-compiler \
unzip \
&& rm -rf /var/lib/apt/lists/*
# ===========================================================================================================
# Notes on flags used:
# (-LO) follows http redirects and saves the downloaded file with same name as in URL
# (-k) ignores SSL certificate verification. This is needed when system security prevents certain actions
# Contact IT to whitelist arm servers if needed.
# ============================================================================================================
RUN curl -LO -k https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz && \
tar xf arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz && \
rm arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz
# Install additional Python packages
RUN pip3 install --upgrade west imgtool requests
# Set the workspace directory
WORKDIR /workspace
# Clone the mcuxsdk-manifests repository
RUN git clone https://github.com/nxp-mcuxpresso/mcuxsdk-manifests.git
# Set the MCUXpresso SDK path environment variable
ENV MCUX_SDK_PATH=/workspace/mcuxsdk-manifests
# Initialize and update the west workspace
RUN cd $MCUX_SDK_PATH && \
west init -l . && \
west update
# ARMGCC ENV variable
ENV ARMGCC_DIR=/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi
# Default command: Start a shell
CMD ["/bin/bash"]
Building the Container Image
Before we continue, it is a good idea to set up a GitHub repository and configure credentials to use in the later steps.
1. Create a new repository. Leave it empty for now. Later, it will contain the following items:
.github/workflows/docker-build.yml
my_app
dockerfile
README.md
2. Generate a Personal Access Token (PAT). This will allow you to access the GitHub API.
- Click on your profile icon
- Select Settings > Developer settings > Personal access tokens > Tokens(classic)
- Select Generate new token (classic)
- The scopes can be custom for your needs. Use the following scopes for this guide: delete:packages, repo, write:packages
- Click Generate Token. Make sure to copy and save the token once it is generated.
- Next, store the personal access token (PAT) as a GitHub secret. The GitHub Secret will be used for authentication in the workflow file. This is done to enhance reusability, security, and rotation without modifying the workflow file.
- Navigate to the repository that was created and click on Setttings.
- Select Secrets and variables and click on Actions.
- Here we will add 2 secrets. One for our username and the second for our personal access token. Click New repository secret.
Secret for username - This can be personalized. However, in the workflow file that will be covered later in the guide, the variable is set as GH_USERNAME. Set the name as GH_USERNAME. In the Secret field enter your GitHub username.
Secret for token - The workflow has the variable set as GH_PAT. Set the name as GH_PAT. In the Secret field paste your personal access token.
3. We can now build the container image from the command line.
- Clone a local copy of your repository. Open the command line interface to that location.
- Save the dockerfile to the root directory of your cloned repository.
- Login to GitHub Container Registry. Run:
echo <your personal access token> | docker login ghcr.io -u <your GitHub username> --password-stdin
Ouput:
-Build the container image. This is the lengthiest step in this guide, but images only need to be built once before they are pushed to ghcr to be used. Run:
docker build -t <your image name> .
Output:
- To verify your image details run:
docker images
- Tag the Docker image. This command doesn't create a new image; it just gives an existing image a new name and tag. This is especially useful when you're preparing to push the image to a registry like GHCR. Docker requires the image to be tagged with the registry URL and repository name before it can be pushed. Run:
docker tag <your image tag> <your location>
- Push the image to the container registry. Run:
docker push <your location>
**Note: The Output might show as failed due to a server error. If it does, simply attempt by running the command once more.
Running the command once more:
Congratulations! The MCUXpresso SDK and build system are now in a container image are ready to use to build your projects. Next, we will configure GitHub for automation.
3. Automation - GitHub Actions
Why use GitHub Actions?
GitHub Actions is a built-in automation tool in GitHub that lets you define workflows to build, test, and deploy your code based on events like pushes or pull requests. It uses YAML files to configure these workflows, making it easy to set up CI/CD pipelines directly in your repository.
1. Clone your repository locally. Then navigate to its root directory.
- Create a .github/workflows directory inside your project root.
- Navigate to VS Code and create a new file. Name it: docker-build.yml
- We have provided a template below which you can copy and paste it in your docker-build.yml.
What the template does:
Runs on push or PR
Uses a container with MCUXpresso SDK tools
Checks out your repo
Copies your app into the west workspace
Builds it for the FRDM-MCXA153 using west
name: Build MCUXpresso Project
on:
push:
# branches: [ main ]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
container:
image: ghcr.io/nxp-jose/mcuxpresso-sdk:latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Copy my_app into west workspace
run: |
cp -r $GITHUB_WORKSPACE/my_app /workspace/mcuxsdk-manifests/my_app
- name: Build project using west
working-directory: /workspace/mcuxsdk-manifests
run: |
echo "Building project..."
west build -b frdmmcxa153 my_app
4. Using the Pipeline
Complete your workspace setup
The last step in the process is creating a project to use in our pipeline.
1. Open MCUXpresso for VS Code
2. Click Import example from Repository
3. Import a project from MCUXpresso SDK 24.12 or later as a freestanding example. Set the import location to the root directory of your cloned repository.
4. Stage, commit and push the changes to your repository. Your repository should now contain:
*Note: You will see a .vscode directory if you build the project locally. It is completely optional to push this directory to your repository.
5. Once the push is completed, enable workflows on GitHub. Once the workflow is enabled. Successive pushes or pull requests will trigger automatic project builds.
6. Examine the build details.
- Navigate to the Actions tab on GitHub
- The Actions tab will show the all the instances where the workflow has run.
- Click on a build to view the details.
- The details displayed are individual steps executed during the build process. Click on the steps to view specific details.
5. Conclusion
The CI/CD pipeline outlined in this guide provides a simple yet effective starting point for automating builds with Docker, the MCUXpresso SDK, and GitHub Actions. While the example focuses on a basic workflow, it can be extensively customized to meet project-specific needs such as integrating automated tests, adding quality checks, or using custom MCUXpresso SDK manifests. By leveraging these tools, teams can streamline development, ensure consistency, and scale their processes with minimal manual intervention.
查看全文