RepoPilotOpen in app →

rust-embedded/embedded-hal

A Hardware Abstraction Layer (HAL) for embedded systems

Healthy

Healthy across the board

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 2mo ago
  • 25+ active contributors
  • Distributed ownership (top contributor 35% of recent commits)
Show all 6 evidence items →
  • Apache-2.0 licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/rust-embedded/embedded-hal)](https://repopilot.app/r/rust-embedded/embedded-hal)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/rust-embedded/embedded-hal on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: rust-embedded/embedded-hal

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/rust-embedded/embedded-hal shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 2mo ago
  • 25+ active contributors
  • Distributed ownership (top contributor 35% of recent commits)
  • Apache-2.0 licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live rust-embedded/embedded-hal repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/rust-embedded/embedded-hal.

What it runs against: a local clone of rust-embedded/embedded-hal — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in rust-embedded/embedded-hal | Confirms the artifact applies here, not a fork | | 2 | License is still Apache-2.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 97 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>rust-embedded/embedded-hal</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of rust-embedded/embedded-hal. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/rust-embedded/embedded-hal.git
#   cd embedded-hal
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of rust-embedded/embedded-hal and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "rust-embedded/embedded-hal(\\.git)?\\b" \\
  && ok "origin remote is rust-embedded/embedded-hal" \\
  || miss "origin remote is not rust-embedded/embedded-hal (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is Apache-2.0" \\
  || miss "license drift — was Apache-2.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "embedded-hal/src/lib.rs" \\
  && ok "embedded-hal/src/lib.rs" \\
  || miss "missing critical file: embedded-hal/src/lib.rs"
test -f "embedded-hal/src/digital.rs" \\
  && ok "embedded-hal/src/digital.rs" \\
  || miss "missing critical file: embedded-hal/src/digital.rs"
test -f "embedded-hal/src/spi.rs" \\
  && ok "embedded-hal/src/spi.rs" \\
  || miss "missing critical file: embedded-hal/src/spi.rs"
test -f "embedded-hal/src/i2c.rs" \\
  && ok "embedded-hal/src/i2c.rs" \\
  || miss "missing critical file: embedded-hal/src/i2c.rs"
test -f "Cargo.toml" \\
  && ok "Cargo.toml" \\
  || miss "missing critical file: Cargo.toml"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 97 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~67d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/rust-embedded/embedded-hal"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

embedded-hal is a Rust Hardware Abstraction Layer that defines a standardized set of traits for microcontroller peripherals (I2C, SPI, GPIO, CAN, delay, digital I/O) across different platforms. It enables platform-agnostic driver libraries to work with any microcontroller that implements these traits, eliminating vendor lock-in and letting driver authors support Cortex-M, AVR, RISC-V, and embedded Linux from a single codebase. Workspace monorepo with 8 focused crates: embedded-hal (core blocking traits), embedded-hal-async (async/await versions), embedded-hal-nb (nb-crate polling versions), embedded-hal-bus (SPI/I2C sharing utilities across i2c/atomic.rs, critical_section.rs, mutex.rs, refcell.rs, rc.rs and spi/), embedded-can (CAN bus abstraction), embedded-io and embedded-io-async (I/O traits), and embedded-io-adapters (glue code). Each crate is independently versioned with its own Cargo.toml and CHANGELOG.md.

👥Who it's for

Embedded systems engineers and driver library authors who need to write once and support multiple microcontroller platforms (STM32, nRF52, ESP32, etc.). Also target platform maintainers who implement the HAL traits to enable ecosystem drivers for their specific hardware.

🌱Maturity & risk

Production-ready and actively maintained. v1.0 was officially released with a migration guide (docs/migrating-from-0.2-to-1.0.md), indicating years of real-world use and API stabilization. The workspace includes multiple crates with CI workflows (clippy.yml, rustfmt.yml, test.yml, rustdoc.yml) and is governed by the rust-embedded organization with a designated HAL team.

Low risk for a HAL project of this scope. The modular workspace design (embedded-hal, embedded-hal-async, embedded-hal-nb, embedded-hal-bus, embedded-can, embedded-io) means breaking changes in one crate don't necessarily break others. Main risk is the API surface area—with five interconnected crates, adding new trait methods requires careful deprecation planning, though the v1.0 stabilization and migration docs show the team handles this deliberately.

Active areas of work

The repository is past the major v1.0 release milestone and appears to be in maintenance/incremental improvement mode. The file structure shows well-established CI (GitHub Actions workflows) and the presence of docs/how-to-add-a-new-trait.md suggests the team is accepting structured proposals for new abstractions. No specific PR or milestone data is visible, but the dual-workspace model (async + blocking + nb) indicates work on supporting multiple execution models.

🚀Get running

git clone https://github.com/rust-embedded/embedded-hal.git
cd embedded-hal
cargo build
cargo test --all

Daily commands: This is a library crate, not an application. Build with cargo build --all or run tests with cargo test --all. To use in your project, add a crate to your Cargo.toml: embedded-hal = "1.0" or embedded-hal-async = "1.0". Run individual workspace member tests: cargo test -p embedded-hal or cargo test -p embedded-hal-bus.

🗺️Map of the codebase

  • embedded-hal/src/lib.rs — Core library entry point that re-exports all HAL traits; every contributor must understand the module organization and stability guarantees.
  • embedded-hal/src/digital.rs — Defines fundamental digital I/O traits (InputPin, OutputPin, StatefulOutputPin) that are foundational to most embedded drivers.
  • embedded-hal/src/spi.rs — Defines SPI communication traits that enable synchronous serial peripheral interface abstraction across platforms.
  • embedded-hal/src/i2c.rs — Defines I2C communication traits, one of the most commonly used bus abstractions in embedded systems.
  • Cargo.toml — Workspace configuration defining all member crates and resolver strategy; essential for understanding crate dependencies and versioning.
  • docs/how-to-add-a-new-trait.md — Contribution guide documenting the process for adding new HAL traits, ensuring consistency with project conventions.
  • embedded-hal-bus/src/lib.rs — Bus sharing infrastructure that enables safe concurrent access to I2C and SPI buses, critical for multi-driver scenarios.

🛠️How to make changes

Add a new synchronous HAL trait

  1. Create a new module or extend existing module (e.g., src/uart.rs) with trait definition following the documentation at docs/how-to-add-a-new-trait.md (embedded-hal/src/uart.rs)
  2. Export the new trait from the library root using pub use (embedded-hal/src/lib.rs)
  3. Add integration tests demonstrating the trait's usage in embedded-hal/tests/ if complex (embedded-hal/tests/new_trait_test.rs)
  4. Document the trait with examples showing both platform implementer and driver author perspectives (embedded-hal/src/uart.rs)
  5. Update CHANGELOG.md with the new trait addition (embedded-hal/CHANGELOG.md)

Add an async variant of an existing trait

  1. Define async trait in embedded-hal-async using async fn syntax (matching sync counterpart structure) (embedded-hal-async/src/uart.rs)
  2. Export new async trait from library root (embedded-hal-async/src/lib.rs)
  3. Implement any necessary adapter traits if bridging to embedded-io-async (embedded-io-adapters/src/lib.rs)
  4. Document the async variant and note differences from sync version in trait docs (embedded-hal-async/src/uart.rs)

Add a new bus sharing strategy

  1. Create new strategy file (e.g., embedded-hal-bus/src/i2c/rwlock.rs) implementing the BusManager pattern (embedded-hal-bus/src/i2c/rwlock.rs)
  2. Implement generic Device wrapper and I2c trait for the synchronization primitive (embedded-hal-bus/src/i2c/rwlock.rs)
  3. Export from module root with feature gate if needed for optional dependencies (embedded-hal-bus/src/i2c/mod.rs)
  4. Add example usage and document when to use this strategy vs alternatives (embedded-hal-bus/examples/i2c_rwlock_example.rs)

Add adapter for new async runtime

  1. Create adapter module (e.g., embedded-io-adapters/src/async_runtime.rs) providing From/Into implementations (embedded-io-adapters/src/async_runtime.rs)
  2. Add feature flag in embedded-io-adapters/Cargo.toml for conditional compilation (embedded-io-adapters/Cargo.toml)
  3. Implement Read and Write traits for runtime's I/O types (embedded-io-adapters/src/async_runtime.rs)
  4. Add tests demonstrating runtime integration (embedded-io-adapters/tests/runtime_adapter_test.rs)

🔧Why these technologies

  • Trait-based abstraction — Enables platform-agnostic drivers to work across vastly different hardware (ARM, AVR, RISC-V, embedded Linux) without compile-time or runtime overhead
  • Workspace with multiple crates — Allows incremental adoption; users can depend only on sync traits (embedded-hal) or add async (embedded-hal-async) or bus sharing (embedded-hal-bus) as needed without bloating minimal deployments
  • Critical section & mutex abstractions in embedded-hal-bus — Provides platform-agnostic bus sharing without forcing a specific synchronization primitive, letting platforms choose interrupt-free, mutex, or other strategies
  • Non-blocking (nb) crate integration — Bridges bare-metal environments (no_std) with cooperative multitasking patterns for systems too constrained for async/await runtime

⚖️Trade-offs already made

  • Synchronous blocking API as primary trait set

    • Why: Simplest mental model for driver authors and most widely applicable across all platforms
    • Consequence: Requires separate embedded-hal-async crate for async use cases; some duplicate trait definitions
  • Bus sharing in separate crate rather than core

    • Why: Keeps embedded-hal minimal (no std/alloc dependencies); bus sharing is optional for simple single-device scenarios
    • Consequence: Users building multi-device systems must import and understand embedded-hal-bus; adds small learning curve
  • No built-in driver registry or discovery

    • Why: HAL remains focused on abstraction layer; driver ecosystem (crates.io) handles discoverability
    • Consequence: Ecosystem fragmentation possible; driver quality/compatibility varies; no central catalog
  • Platform implementations live outside this repo

    • Why: Allows platform crates (e.g., stm32h7xx-hal) to evolve at their own pace and focus on hardware-specific optimizations
    • Consequence: Fragmentation in implementation quality; some platforms may lag behind trait evolution

🚫Non-goals (don't propose these)

  • Does not provide concrete hardware implementations (e.g., no GPIO driver for STM32)
  • Does not include platform-specific optimizations; platform HAL crates handle those
  • Does not define higher-level protocols beyond basic I2C/SPI/CAN (application-specific libraries build on top)
  • Not a real-time operating system or scheduler
  • Does not specify timing guarantees or hard-deadline semantics

🪤Traps & gotchas

MSRV (Minimum Supported Rust Version) varies by crate and is documented in docs/msrv.md—verify before depending on a specific Rust version. The workspace uses resolver = "2", so cargo build from the root builds all crates; use cargo build -p <crate> to isolate. embedded-hal-bus requires careful synchronization primitive choice (atomic vs. critical_section vs. mutex)—pick wrong and you'll either have overhead or deadlocks on your platform. No default implementations for traits means every platform integration must be explicit.

🏗️Architecture

💡Concepts to learn

  • Trait-based Hardware Abstraction — This is the core pattern of embedded-hal: each peripheral capability (GPIO, I2C, SPI) is a Rust trait that implementers provide, not a monolithic driver; this enables composition and platform portability.
  • Execution Model Agnosticism (Blocking vs. Async vs. Polling) — embedded-hal offers three trait variants (blocking in main crate, async in embedded-hal-async, polling via nb crate) so drivers work with any executor model; understanding which one your platform uses is critical.
  • Critical Section and Atomic Synchronization Primitives — embedded-hal-bus uses critical_section crate and atomic operations to safely share SPI/I2C buses without OS-level mutexes; knowing when to use atomic vs. critical_section vs. mutex is essential for embedded applications.
  • Type-State Pattern — Many HAL implementations use Rust's type system to encode state (e.g., GPIO pins as Uninitialized → Configured → Readable) at compile time, preventing runtime errors; embedded-hal traits lean on this pattern.
  • Platform-Agnostic Drivers — By implementing embedded-hal traits, a sensor or wireless chip driver library (e.g., an HTU21D humidity driver) works on any microcontroller, not just one vendor; this is the ecosystem value proposition.
  • Peripheral Access Crate (PAC) — HAL implementations sit atop auto-generated PACs (via svd2rust) that provide low-level register access; understanding the PAC → HAL → Driver stack clarifies abstraction boundaries.
  • Non-Blocking (nb) Crate Pattern — embedded-hal-nb uses the nb crate's WouldBlock error to express non-blocking operations; this pattern avoids async overhead while still allowing polling-based drivers on resource-constrained devices.
  • rust-embedded/rtic — RTIC is a real-time framework that often sits on top of embedded-hal implementations, providing task scheduling and interrupt-safe abstractions for projects using these traits.
  • rust-embedded/cortex-m — Lower-level Cortex-M crate for bare-metal Rust on ARM; many embedded-hal implementations for STM32/nRF52 depend on cortex-m for register access.
  • stm32-rs/stm32f4xx-hal — Canonical example of an embedded-hal implementation for STM32F4 microcontrollers; reference implementation showing how to provide all core traits for a real chip family.
  • embassy-rs/embassy — Modern async Rust runtime for embedded systems that uses embedded-hal-async traits; the primary consumer/driver of the async variant of this HAL.
  • rust-embedded/svd2rust — Tool that auto-generates PAC (Peripheral Access Crates) from vendor SVD files; many HAL implementations (like stm32f4xx-hal) layer embedded-hal traits on top of svd2rust-generated registers.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add MSRV enforcement in CI pipeline for all workspace members

The repo has docs/msrv.md documenting minimum supported Rust version, but there's no automated CI check to enforce it across all workspace members (embedded-hal, embedded-hal-async, embedded-hal-nb, embedded-hal-bus, embedded-can, etc.). This prevents accidental MSRV breakage in PRs. Adding a cargo-msrv or cargo +nightly check workflow would catch violations early.

  • [ ] Create new .github/workflows/msrv.yml that runs cargo msrv verify or cargo +<msrv> check for each workspace member
  • [ ] Ensure workflow tests against the version documented in docs/msrv.md
  • [ ] Configure the workflow to fail if any member breaks MSRV compatibility
  • [ ] Document the MSRV enforcement approach in docs/msrv.md

Add comprehensive trait implementation examples for embedded-hal-bus shared bus patterns

embedded-hal-bus/src contains multiple synchronization strategies (atomic.rs, critical_section.rs, mutex.rs, rc.rs, refcell.rs) for both I2C and SPI buses, but there are no concrete examples showing how to use each pattern. New contributors and users struggle to understand when to use each strategy. Adding example code and tests would significantly improve adoption.

  • [ ] Create embedded-hal-bus/examples/ directory with example files: i2c_critical_section.rs, i2c_mutex.rs, spi_shared.rs, spi_exclusive.rs
  • [ ] Each example should demonstrate real-world use: multiple devices on same bus, synchronization strategy rationale, and expected behavior
  • [ ] Add doctests in embedded-hal-bus/src/i2c/mod.rs and embedded-hal-bus/src/spi/mod.rs showing basic usage of each strategy
  • [ ] Update embedded-hal-bus/README.md with a decision matrix: when to use Atomic vs CriticalSection vs Mutex vs Rc vs RefCell

Create integration tests validating trait compatibility across async/blocking/nb variants

The workspace has 5+ core crate variants (embedded-hal, embedded-hal-async, embedded-hal-nb) with overlapping trait definitions for SPI, I2C, Digital I/O, etc. There's no test suite verifying that implementations can correctly bridge between these variants or that breaking changes don't create incompatibilities. This is critical for ecosystem stability.

  • [ ] Create embedded-hal/tests/trait_compatibility.rs with mock implementations that satisfy multiple trait variants simultaneously (e.g., a mock SPI device implementing both blocking::spi::Transfer and async spi::Operation)
  • [ ] Add tests in embedded-hal-bus/tests/ validating that bus abstractions correctly preserve trait bounds across all synchronization strategies
  • [ ] Document in docs/how-to-add-a-new-trait.md the compatibility checklist: ensure new traits work with embedded-hal-async and embedded-hal-nb equivalents
  • [ ] Add a CI check (.github/workflows/test.yml enhancement) that runs these integration tests in addition to unit tests

🌿Good first issues

  • Add missing trait documentation examples in embedded-hal/src/digital.rs and embedded-hal/src/spi.rs—check which trait methods lack runnable code examples showing the intended usage pattern.
  • Audit embedded-hal-bus/src/spi/ for feature coverage parity with embedded-hal-bus/src/i2c/—the SPI module has fewer synchronization strategies (atomic.rs, critical_section.rs, exclusive.rs) than I2C (atomic, critical_section, mutex, refcell, rc); propose and implement missing adapters.
  • Expand embedded-can/src/blocking.rs and embedded-can/src/nb.rs with integration tests showing real-world CAN device patterns (send-then-receive, arbitration handling)—currently the test coverage is minimal.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • e6b10a6 — Merge pull request #729 from hermit-os/embedded-io-typo (Dirbaio)
  • 193c0ed — docs(embedded-io): fix typo (mkroening)
  • ed5b5a7 — Merge pull request #728 from eldruin/update-e-h-async-changelog (MabezDev)
  • 8888a0a — Update embedded-hal-async changelog (eldruin)
  • c281ae5 — Merge pull request #726 from Kobzol/remove-triagebot-toml (therealprof)
  • cf7fe41 — Remove triagebot.toml (Kobzol)
  • b8fd20d — Merge pull request #721 from rootdiae/copyright (eldruin)
  • acf8fc5 — update copyright year to 2026 (rootdiae)
  • cb1f73e — Merge pull request #718 from t-wallet/spi-device-error-into-infallible (MabezDev)
  • 19802b3 — Implement Into<Infallible> when spi::DeviceError is infallible (t-wallet)

🔒Security observations

The embedded-hal repository demonstrates strong security posture for a Hardware Abstraction Layer library. As a foundational embedded systems library, it has minimal attack surface. No critical vulnerabilities were identified in the static analysis. The codebase follows security best practices with proper licensing, code review workflows (GitHub Actions for clippy, rustfmt), and clear documentation. The modular workspace structure isolates concerns effectively. Minor recommendations focus on dependency auditing and documentation practices.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · rust-embedded/embedded-hal — RepoPilot