Go developed a fairly large hardware hacker community in part because the language and its tooling have the following properties:

  • Native support in the tool chain to cross compile to ARM/Linux via GOOS=linux GOARCH=arm go build ..
  • Significantly faster to execute than python and node.js.
  • Significantly lighter in term of memory use than Java or node.js.
  • Significantly more productive to code than C/C++.
  • Builds reasonably fast on ARM.
  • Fairly good OS level support.

Many Go packages, both generic and specialized, were created to fill the space. This library came out of the desire to have a designed API (contrary to growing organically) with strict code requirements and a strong, opinionated philosophy to enable long term maintenance.


periph was created as an answer to specific goals:

  • Not more abstract than absolutely needed. Use concrete types whenever possible.
  • Orthogonality and composability
    • Each component must own an orthogonal part of the platform and each components can be composed together.
  • Extensible:
    • Users can provide additional drivers that are seamlessly loaded with a structured ordering of priority.
  • Performance:
    • Execution as performant as possible.
    • Overhead as minimal as possible, i.e. irrelevant driver are not be attempted to be loaded, uses memory mapped GPIO registers instead of sysfs whenever possible, etc.
  • Coverage:
    • Be as OS agnostic as possible. Abstract OS specific concepts like sysfs.
    • Each driver implements and exposes as much of the underlying device capability as possible and relevant.
    • cmd implements useful directly usable tool.
    • devices implements common device drivers. Device drivers is not the core focus of this project for now.
    • host must implement a large base of common platforms that just work. This is in addition to extensibility.
  • Simplicity:
    • Static typing is thoroughly used, to reduce the risk of runtime failure.
    • Minimal coding is needed to accomplish a task.
    • Use of the library is defacto portable.
    • Include fakes for buses and device interfaces to simplify the life of device driver developers.
  • Stability
    • API must be stable without precluding core refactoring.
    • Breakage in the API should happen at a yearly parce at most once the library got to a stable state.
  • Strong distinction about the driver (as a user of a conn.Conn instance) and an application writer (as a user of a device driver). It’s the application that controls the objects' lifetime.
  • Strong distinction between enablers and devices. See Background.


The project was designed with a very clear vision:

  1. Optimize for simplicity, correctness and usability in that order.
    • e.g. everything, interfaces and structs, uses strict typing, there’s no interface{} in sight.
  2. OS agnostic. Clear separation of interfaces in conn, enablers in host and device drivers in devices.
    • e.g. no devfs or sysfs path in sight.
    • e.g. conditional compilation enables only the relevant drivers to be loaded on each platform.
  3. … yet doesn’t get in the way of platform specific code.
    • e.g. A user can use statically typed global variables rpi.P1_3, bcm283x.GPIO2 to refer to the exact same pin on a Raspberry Pi.
  4. The user can chose to optimize for performance instead of usability.
  5. Use a divide and conquer approach. Each component has exactly one responsibility.
    • e.g. instead of having a driver per “platform”, there’s a driver per “component”: one for the CPU, one for the board headers, one for each bus and sensor, etc.
  6. Extensible via a driver registry.
    • e.g. a user can inject a custom driver to expose more pins, headers, etc. A USB device (like an FT232H) can expose headers in addition to the headers found on the board.
  7. The drivers must use the fastest possible implementation.
    • e.g. both allwinner and bcm283x leverage sysfs gpio to expose interrupt driven edge detection, yet use memory mapped GPIO registers to perform single-cycle reads and writes.

Success criteria

  • Preferred library used by first time Go users and by experts.
  • Becomes the defacto HAL library.
  • Becomes the central link for hardware support.


The risks below are being addressed via Go modules versioning, continuous integration testing and having a high quality bar via an explicit list of requirements.

The enablers (boards, CPU, buses) is what will break or make this project. Nobody want to do them but they are needed. You need a large base of enablers so people can use anything yet they are hard to get right. You want them all in the same repo so that when someone builds an app, it supports everything transparently. It just works.

The device drivers do not need to all be in the same repo, that scales since people know what is physically connected, but a large enough set of enablers is needed to be in the base repository to enable seemlessness. People do not care that a Pine64 has a different processor than a Rasberry Pi; both have the same 40 pins header and that’s what they care about. So enablers need to be a great HAL -> the right hardware abstraction layer (not too deep, not too light) is the core here.


  • The library is rejected by users as being too cryptic or hard to use.
  • The device drivers are unreliable or non functional, as observed by users.
  • Poor usability of the core interfaces.
  • Missing drivers.


  • Lack of API stability; high churn rate.
  • Poor fitting of the core interfaces.
  • No uptake in external contribution.
  • Poor quality of contribution.
  • Duplicate ways to accomplish the same thing, without a clear way to define the right way.