Design, contribution, driver writing


periph provides interfaces to assemble components to communicate with hardware peripherals. It is designed to be composable. As such, it splits boards into their individual components: CPU, buses, physical headers, etc, instead of representing each board as a whole object.


  • Goals to understand the project rationale.
  • Detailed Design to understand more about how things are done.

Source Code

The main periph code is located in repositories at

  • conn defines the base interfaces.
  • host implements the drivers for the peripheral on the host, e.g. the SPI/I²C buses.
  • devices implements device drivers for things connected over the buses.
  • cmd contains ready-to-use executables.

Supplemental projects are:

  • The website itself, so you can easily submit a PR to improve the documentation.
  • gohci for hardware smoke testing.
  • periph-tester which is a board used to confirm the buses (I²C, SPI, 1-wire) are correctly working.
  • bootstrap tool to automate the deployment of Raspberry Pis.

Classes of hardware

This document distinguishes two classes of drivers.


They are what make the interconnects work, so that you can then use real stuff. That’s both point-to-point connections (GPIO, UART, TCP) and buses (I²C, SPI, BT) where individual devices can be addressed. They enable you to do something but are not the essence of what you want to do.


They are the end goal, to do something functional. There are multiple subclasses of devices like sensors, output devices, etc.


All the code must fit the following requirements.

Fear not! We know the list is daunting but as you create your pull request to add something we’ll happily guide you in the process to help improve the code to meet the expected standard. The end goal is to write high quality maintainable code and use this as a learning experience.

  • The code must be Go idiomatic.
    • Constructor NewXXX() returns an object of concrete type.
    • Functions accept interfaces.
    • Leverage standard interfaces like io.Writer and image.Image where possible.
    • No interface{} unless strictly required.
    • Minimal use of factories except for protocol level registries.
    • No init() code that accesses peripherals on process startup. These belong to Driver.Init().
  • Exact naming
    • Driver for a chipset must have the name of the chipset or the chipset family. Don’t use oleddisplay, use ssd1306.
    • Driver must use the real chip name, not a marketing name by a third party. Don’t use dotstar (as marketed by Adafruit), use apa102 (as created by APA Electronic co. LTD.).
    • A link to the datasheet must be included in the package doc unless NDA’ed or inaccessible.
  • Testability
    • Code must be testable and unit tested without a device. The unit tests are meant to run as part of go test.
    • When relevant, include a smoke test. The smoke test tests a real device to confirm the driver physically works for devices. It must be under the package being tested, named as foosmoketest for package foo. Modify periph-smoketests/ to expose this smoke test.
  • Usability
    • Provide a standalone executable in cmd/ to expose the functionality. It is acceptable to only expose a small subset of the functionality but the tool must have purpose.
    • Provide a func Example() along your test to describe basic usage of your driver. See the official testing package for more details.
  • Performance
    • Drivers controlling an output device must have a fast path that can be used to directly write in the device’s native format, e.g. io.Writer.
    • Drivers controlling an output device must have a generic path accepting a higher level interface when found in the stdlib, e.g. image.Image.
    • Floating point arithmetic should only be used when absolutely necessary in the driver code. Most of the cases can be replaced by fixed point arithmetic, for example physic. Floating point arithmetic is acceptable in the unit tests and tools in cmd/ but should not be abused.
    • Drivers must be implemented with performance in mind. For example I²C operations should be batched to minimize overhead.
    • Benchmark must be implemented for non trivial processing running on the host.
  • Code must compile on all OSes, with minimal use of OS-specific thunk as strictly needed. Take advantage of constructs like if isArm { ...} where the conditional is optimized away at compile time via dead code elimination and isArm is a boolean constant defined in relevant .go files having a build constraint.
  • Struct implementing an interface must validate at compile time with var _ <Interface> = &<Type>{}.
  • License is Apache v2.0.

Code style

  • The code tries to follow Go code style as described at
  • Top level comments are expected to be wrapped at 80 cols. Indented comments should be wrapped at reasonable width.
  • Comments should start with a capitalized letter and end with a period.
  • Markdown style is a “try to have similar style to the current doc”.