Skip to main content Docker | IoT Worker

Docker

Why Embedded Development Needs Docker?

Solving Cross-Compilation Environment Issues

  • Toolchain Version Conflicts: Different projects require different gcc-arm-none-eabi versions.
  • Dependency Management: Avoid system pollution and isolate various libraries.
  • Team Collaboration: Ensure all developers use the identical compilation environment.

Rapid Environment Setup

  • One-Click Launch: Get a complete embedded development environment in seconds.
  • Version Control: Development environments can also be versioned.
  • Cross-Platform: Unified development experience on Windows, Linux, and macOS.

Core Concepts

Image

Think of it as a “development environment installer” containing all necessary tools and libraries.

Container

An “instance of your development environment” launched from an image, where you write code and compile programs.

Volume Mounting

“Mounting” your host machine’s code directory into the container, so any code changes are saved synchronously.

Quick Installation

Ubuntu/Debian Installation

# One-line installation script
curl -fsSL https://get.docker.com | sh

# Configure user permissions (to avoid sudo every time)
sudo usermod -aG docker $USER

# Log out and log back in, or run:
newgrp docker

Verify Installation

docker --version
docker run hello-world

Common Commands for Embedded Development

Get Embedded Development Images

# ARM development environment
docker pull arm32v7/gcc

# STM32 development environment
docker pull stm32duino/arduino-core-stm32

# ESP32 development environment
docker pull espressif/idf

# Generic cross-compilation environment
docker pull multiarch/crossbuild

Start a Development Container

# Basic launch (mount current directory)
docker run -it -v $(pwd):/workspace arm32v7/gcc bash

# With port mapping (for debugging servers)
docker run -it -v $(pwd):/workspace -p 3333:3333 arm32v7/gcc bash

# Run container in the background
docker run -d --name my-dev -v $(pwd):/workspace arm32v7/gcc tail -f /dev/null

# Enter a background container
docker exec -it my-dev bash

Common Operations

# View running containers
docker ps

# Stop a container
docker stop my-dev

# Delete a container
docker rm my-dev

# View local images
docker images

Hands-on: STM32 Development Environment

Create an STM32 Development Image

Create Dockerfile.stm32:

FROM ubuntu:20.04

# Prevent interactive installation
ENV DEBIAN_FRONTEND=noninteractive

# Install basic tools
RUN apt-get update && apt-get install -y \
    build-essential \
    git \
    wget \
    curl \
    unzip \
    python3 \
    python3-pip

# Install ARM toolchain
RUN apt-get install -y gcc-arm-none-eabi

# Install STM32CubeMX (optional)
RUN apt-get install -y default-jre

# Set working directory
WORKDIR /workspace

# Set entrypoint
CMD ["/bin/bash"]

Build and Use

# Build image
docker build -f Dockerfile.stm32 -t stm32-dev .

# Start development environment
docker run -it -v $(pwd):/workspace stm32-dev

# Compile inside the container
cd /workspace
make clean && make

Hands-on: ESP32 Development Environment

Use Official ESP-IDF Environment

# Pull ESP-IDF image
docker pull espressif/idf:latest

# Start ESP32 development environment
docker run --rm -v $PWD:/project -w /project -it espressif/idf:latest

# Operate inside the container
idf.py build
idf.py flash

Simplify Script

Create esp-dev.sh:

#!/bin/bash
docker run --rm -v $PWD:/project -w /project -it \
  --device=/dev/ttyUSB0 \
  espressif/idf:latest bash

Usage:

chmod +x esp-dev.sh
./esp-dev.sh

Data Persistence

Mount Project Directory

# Mount current directory to /workspace in the container
docker run -it -v $(pwd):/workspace image_name

# Mount a specific directory
docker run -it -v /home/user/projects:/workspace image_name

# Read-only mount (prevent container from modifying host files)
docker run -it -v $(pwd):/workspace:ro image_name

Data Volumes (for toolchain caching)

# Create a data volume to store compilation cache
docker volume create build-cache

# Use the data volume
docker run -it -v $(pwd):/workspace -v build-cache:/cache image_name

Multi-Project Management

Using Docker Compose

Create docker-compose.yml:

version: '3.8'

services:
    stm32-dev:
        build:
            context: .
            dockerfile: Dockerfile.stm32
        volumes:
            - ./stm32-project:/workspace
        working_dir: /workspace
        stdin_open: true
        tty: true

    esp32-dev:
        image: espressif/idf:latest
        volumes:
            - ./esp32-project:/project
        working_dir: /project
        devices:
            - /dev/ttyUSB0:/dev/ttyUSB0
        stdin_open: true
        tty: true

Usage:

# Start STM32 development environment
docker-compose run stm32-dev

# Start ESP32 development environment
docker-compose run esp32-dev

Handy Tips

Simplify Commands

Add aliases to .bashrc:

# STM32 Development
alias stm32='docker run -it --rm -v $(pwd):/workspace stm32-dev'

# ESP32 Development
alias esp32='docker run -it --rm -v $(pwd):/project -w /project espressif/idf:latest'

# ARM Cross-compilation
alias arm-gcc='docker run -it --rm -v $(pwd):/workspace arm32v7/gcc'

Device Access

# Access USB devices (debugger, programmer)
docker run -it --device=/dev/ttyUSB0 -v $(pwd):/workspace image_name

# Access multiple devices
docker run -it \
  --device=/dev/ttyUSB0 \
  --device=/dev/ttyACM0 \
  -v $(pwd):/workspace image_name

Network Debugging

# Map GDB debug port
docker run -it -p 3333:3333 -v $(pwd):/workspace image_name

# Map Web service port (for online debugging tools)
docker run -it -p 8080:8080 -v $(pwd):/workspace image_name

Common Issues

USB Device Access Permissions

# Add user to dialout group
sudo usermod -aG dialout $USER

# Or temporarily modify device permissions
sudo chmod 666 /dev/ttyUSB0

Slow Compilation Inside Container

# Use compilation cache volume
docker run -it -v build-cache:/tmp -v $(pwd):/workspace image_name

# Limit parallel compilation processes
make -j$(nproc)

Cleaning Up Space

# Clean up stopped containers
docker container prune

# Clean up unused images
docker image prune

# One-command clean up all unused resources
docker system prune

Practical Examples

Embedded Linux Development

# Use cross-compilation environment
docker run -it --rm \
  -v $(pwd):/workspace \
  -e CROSS_COMPILE=arm-linux-gnueabihf- \
  multiarch/crossbuild bash

RTOS Project Compilation

# FreeRTOS Project
docker run -it --rm \
  -v $(pwd):/workspace \
  -w /workspace \
  arm32v7/gcc \
  make -f freertos.mk

Summary

Docker’s main value in embedded development:

  1. Environment Isolation: Different projects use different toolchain versions.
  2. Rapid Deployment: Team members quickly get consistent development environments.
  3. Cross-Platform: Windows/Linux/macOS unified development experience.
  4. Version Management: Development environments can also be version controlled.

Just remember these commands:

# Start development environment
docker run -it -v $(pwd):/workspace image_name

# View containers
docker ps

# Stop container
docker stop container_name

# Clean up resources
docker system prune

Focus on solving real problems; don’t get bogged down by complex concepts. Docker is just a better “virtual machine” that simplifies embedded development.