How to proceed, when in need of a custom I2C master-slave system?

What are the design criteria to apply?

What are the debugging tools one can use to troubleshoot problems?

  • This is a reference question. I have deleted a couple of comments that made this less obvious. (The question is answered by the asker).
    – Nick Gammon
    Apr 21, 2016 at 21:09

1 Answer 1


This tutorial I gave at the Embedded Linux Conference tries to answer the questions, providing links to more detailed description of the topics addressed and using the practical example of driving a 4WD drone, where an Arduino Mini Pro acts as slave and controls the 4 independent wheels. The original document can be found here.

Note: This answer is currently work in progress, as I adapt the highlights from the link.

Typical Applications of the I2C bus

  • Interfacing with relatively slow peripherals. Ex: sensors, mechanical actuators.
  • Controlling “fast” peripherals, that use other channels for exchanging data. Ex: codecs.

    In a PC, the Operating System usually interacts over I2C with:

    • temperature and battery voltage meters;
    • fan speed controllers;
    • audio codecs.

In case multiple bus controllers are available, peripherals are grouped by speed, so that fast ones are not penalized by slower ones.

A quick introduction to the I2C bus - key features

  • Serial bus.
  • Only 2 lines: Serial CLock and Serial DAta (plus ground).
  • 4 speeds: 100kHz, 400kHz, 1MHz, 3.2MHz.
  • Typically, 1 master device and 1 or more slaves.
  • Communications are always initiated by a master device.
  • Multiple masters can co-exist on the same bus (multi-master).
  • Open-Drain: both SDA and SCL need pull-up resistors.
  • “Clock Stretching”
    • The master controls SCL, but a slave can hold it down (because open drain), if it needs to adjust the speed.
    • The master must check for this scenario.
    • A slave can get stuck and jam the bus: need for reset lines from the master to the slave.
  • Typically 7-bit addressing, but also 10 bit is supported.
  • Logical protocol: actual voltage levels are not specified and depend on individual implementations. Ex: 1.8V / 3.3V / 5.0V

Reference URLs:

Example of Bus Configuration

Example of Bus Configuration

Characteristics of the Protocol (simplified)

  • 2 message types: read and write
  • Start / Stop bit - represented as “[“ and “]” in the rest of the answer
  • Address: 7 or 10 bits
  • R/W bit: R = 1 / W = 0 Used to discriminate the type of message sent.
  • Data on the bus: (Address << 1 | R/W)
  • Registers as information handlers, within the selected device.

Example of Bus traffic

Example of Bus Traffic Example of Bus Write Cycle Example of Bus Read Cycle Part1 Example of Bus Read Cycle Part2

Custom Slaves

Why create a custom I2C slave?

  • Desired sensor/actuator unavailable with I2C interface.
  • Less unique addresses available than slaves needed.
  • Desired custom functionality on the slave:
    • Semi-autonomous reactions to stimuli.
    • Filtering/preprocessing input data.
  • Power optimization: custom “sensor hub” does the housekeeping while the main processor is idle.
  • Realtime response to inputs.
  • [your imagination here]

How to design a custom I2C slave?

  • Define requirements (see previous slide).
  • Choose microcontroller or microprocessor.
  • Choose Scheduler or Operating System (if any).
  • Define communication sub-protocol:
    • Define parameters and commands to be exchanged.
    • Organize them into “registers” and choose a free address.

Design of the I2C Master

Key design criteria:

  • Weight/Dimensions.
  • Required computational power and average latency.
  • PC-like device
    • Embedded device, typically headless.
    • Preferred programming language: interpreted vs compiled.
  • Availability of busses/gpios for driving the slave(s):
    • GPIOs only: bitbang the protocol
    • I2C: user-space application vs kernel driver.
    • No GPIOs/I2C interfaces available: USB to I2C adapter.

Debugging: Divide and Conquer

Take direct control of the bus with an ad-hoc device. Examples:

  • Bus Pirate (useful also for other busses)
  • USB to I2C Master adapter, also based on the FTDI FT232R chip.
  • Custom device (could be a separate project).
  • Snoop the bus with a logic analyzer or a scope/advanced meter. Examples:

    • sigrok/pulseview with compatible logic analyzer
    • 2-channels standalone scope/meter
    • Use slave-specific In Circuit Debugger/In Circuit Emulator.

      Example: AVR Dragon for AVR chips (Arduino UNO, Nano, Mini, MiniPro)

BUS Pirate

Bus Pirate

  • Primarily for development purposes.
  • Can both sniff the bus and drive it.
  • Console interface over serial (ttyACM) port, including macros, or programmatic access for several programming languages.
  • Built-in pullup resistors and voltage sources (5V / 3.3V)
  • Supports many other protocols.
  • References: Wikipedia, main page

USB to I2C Adapter


  • Small footprint.
  • Suitable for permanent installations.
  • No need for special connections on the host: it can be used to interface with a typical PC.
  • Variant available that is also SPI-capable.
  • No console interface, only serial binary protocol.
  • Requires protocol wrapper.
  • Reference: protocol

sigrok and pulseview

sigrok(bakend component) logo


pulseview (visualizer) example


Example of low end logic Analyzer


  • De-facto standard for PC-driven measurements on linux (but available on other OSes too).
  • Support for vast range of logic analyzers, scopes and meters.
  • Various protocol decoders, including I2C.
  • Useful for visualizing the logical signals and debugging protocol errors.
  • Even very low end, inexpensive HW can provide a whole new dimension to debugging.
  • References: sigrok, pulseview, supported hardware

Example: steering a 4WD drone

Prototype built using 2 Arduino Mini Pro. Drone

What does the slave do in the example?

The I2C slave:

  • Controls the amount of torque applied to each wheel.
  • Controls the direction each wheel spins.
  • Measures the rotation speed of each wheel through an optical encoder (Odometer).
  • Exposes the parameters above to the I2C Master.

Slave Role

High level block diagram of the I2C Slave.

Selecting the Slave: Arduino Mini Pro


  • Enough pins/functions to provide for each wheel:
    • 1 PWM output with independent configuration of the duty-cycle.
    • 1 GPIO for registering odometer input as IRQ.
    • 2 GPIOs for selecting:
      • Forward
      • Reverse
      • Idle
      • Lock
  • I2C HW block for interrupt-driven i2c exchanges.
  • Dedicated pins for SPI-based programming.
  • Small footprint.
  • Low Cost.
  • The board layout of the clone represented in the picture is optimized for mounting on a DIL socket.

Slave-specific ICD: AVR Dragon

AVR Dragon

Selecting the OS: ChibiOS


  • RTOS: preemption, tasks, semaphores, dynamic system tic, etc.
  • Small footprint: link only used code/data.
  • Distinction between RTOS and BSP through HAL.
  • GPLv3 for non-commercial use.
  • Actively developed, but already mature.
  • Supports 8bit AVR.

However it had limited BSP support for AVR, lack of: - interrupts driver for AVR GPIOs (added). - I2C support for AVR slave mode (custom). Which had to be developed separately as part of the Drone SW for the AVR.

Defining the Communication Parameters

For each wheel:

  • Duty Cycle of the PWM signal used to drive it - 1 byte. 0xFF = max torque / 0x00 = no torque.

  • Direction of rotation - 1 byte.

    • 0x00 = idle
    • 0x01 = reverse
    • 0x02 = forward
    • 0x03 = locked
  • Average period in between slots of the optical encoder - 2 bytes.

    • Writing anything resets the measurement.
  • Parameter Index - 1 nibble:

    • 0 = Duty Cycle
    • 1 = Direction
    • 2 = Average Period
  • Wheel indexes - 1 nibble:

    • 0 = Left Rear
    • 1 = Right Rear
    • 2 = Right Front
    • 3 = Left Front
    • 4 = All

Sub Protocol: Defining the Registers

Register format: 0xαβ - α = Parameter Index - β = Wheel Index

Address (chosen arbitrarily): 0x10

Bus Pirate format: - [ = start bit - ] = end bit - r = read byte - address times 2 (left shift 1), for R/W bit

Example - in Bus Pirate Format

[ i2c_addr reg_addr=(parm,wheel) reg_value ]

[0x20 0x20 0x02]  Left Rear Forward
[0x20 0x21 0x01]  Right Rear Backward
[0x20 0x22 0x01]  Right Front Backward
[0x20 0x23 0x02]  Left Front Forward
[0x20 0x14 0xFF]  Wheels set to max torque

The car spins clockwise.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.