i2cperipheral – Two wire serial protocol peripheral

The i2cperipheral module contains classes to support an I2C peripheral.

Example emulating a peripheral with 2 addresses (read and write):

import board
from i2cperipheral import I2CPeripheral

regs = [0] * 16
index = 0

with I2CPeripheral(board.SCL, board.SDA, (0x40, 0x41)) as device:
    while True:
        r = device.request()
        if not r:
            # Maybe do some housekeeping
        with r:  # Closes the transfer if necessary by sending a NACK or feeding dummy bytes
            if r.address == 0x40:
                if not r.is_read:  # Main write which is Selected read
                    b = r.read(1)
                    if not b or b[0] > 15:
                    index = b[0]
                    b = r.read(1)
                    if b:
                        regs[index] = b[0]
                elif r.is_restart:  # Combined transfer: This is the Main read message
                    n = r.write(bytes([regs[index]]))
                    # A read transfer is not supported in this example
                    # If the microcontroller tries, it will get 0xff byte(s) by the ctx manager (r.close())
            elif r.address == 0x41:
                if not r.is_read:
                    b = r.read(1)
                    if b and b[0] == 0xde:
                        # do something

This example sets up an I2C device that can be accessed from Linux like this:

$ i2cget -y 1 0x40 0x01
$ i2cset -y 1 0x40 0x01 0xaa
$ i2cget -y 1 0x40 0x01


I2CPeripheral makes use of clock stretching in order to slow down the host. Make sure the I2C host supports this.

Raspberry Pi in particular does not support this with its I2C hw block. This can be worked around by using the i2c-gpio bit banging driver. Since the RPi firmware uses the hw i2c, it’s not possible to emulate a HAT eeprom.

Available on these boards
  • AITHinker ESP32-C3S_Kit
  • AITHinker ESP32-C3S_Kit_2M
  • ATMegaZero ESP32-S2
  • Adafruit Camera
  • Adafruit EdgeBadge
  • Adafruit Feather ESP32-S2 TFT
  • Adafruit Feather ESP32S2
  • Adafruit Feather ESP32S3 No PSRAM
  • Adafruit Feather M4 CAN
  • Adafruit Feather M4 Express
  • Adafruit FunHouse
  • Adafruit Grand Central M4 Express
  • Adafruit Hallowing M4 Express
  • Adafruit ItsyBitsy M4 Express
  • Adafruit MagTag
  • Adafruit Matrix Portal M4
  • Adafruit Metro ESP32S2
  • Adafruit Metro M4 Airlift Lite
  • Adafruit Metro M4 Express
  • Adafruit Monster M4SK
  • Adafruit PyGamer
  • Adafruit PyPortal
  • Adafruit PyPortal Pynt
  • Adafruit PyPortal Titano
  • Adafruit Pybadge
  • Adafruit QT Py ESP32-S3 no psram
  • Adafruit QT Py ESP32C3
  • Adafruit QT Py ESP32S2
  • Adafruit Trellis M4 Express
  • AloriumTech Evo M51
  • Artisense Reference Design RD00
  • BastWiFi
  • CP32-M4
  • CircuitBrains Deluxe
  • CrumpS2
  • Cytron Maker Feather AIoT S3
  • ESP 12k NodeMCU
  • ESP32-C3-DevKitM-1
  • ESP32-S2-DevKitC-1-N4
  • ESP32-S2-DevKitC-1-N4R2
  • ESP32-S3-Box-2.5
  • ESP32-S3-DevKitC-1-N8
  • ESP32-S3-DevKitC-1-N8R2
  • ESP32-S3-DevKitC-1-N8R8
  • ESP32-S3-DevKitM-1-N8
  • ESP32-S3-USB-OTG-N8
  • Feather ESP32S2 without PSRAM
  • FeatherS2
  • FeatherS2 Neo
  • FeatherS2 PreRelease
  • FeatherS3
  • Franzininho WIFI w/Wroom
  • Franzininho WIFI w/Wrover
  • Gravitech Cucumber M
  • Gravitech Cucumber MS
  • Gravitech Cucumber R
  • Gravitech Cucumber RS
  • HMI-DevKit-1.1
  • HexKyS2
  • IoTs2
  • Kaluga 1
  • LILYGO TTGO T8 ESP32-S2 w/Display
  • MORPHEANS MorphESP-240
  • MicroDev microC3
  • MicroDev microS2
  • Mini SAM M4
  • Oak Dev Tech PixelWing ESP32S2
  • ProS3
  • PyCubedv04
  • PyCubedv04-MRAM
  • PyCubedv05
  • PyCubedv05-MRAM
  • Robo HAT MM1 M4
  • S2Mini
  • S2Pico
  • SAM E54 Xplained Pro
  • SAM32v26
  • Saola 1 w/Wroom
  • Saola 1 w/Wrover
  • Seeeduino Wio Terminal
  • Silicognition LLC M4-Shim
  • SparkFun MicroMod SAMD51 Processor
  • SparkFun Thing Plus - SAMD51
  • Sprite_v2b
  • TG-Boards' Datalore IP M4
  • Targett Module Clip w/Wroom
  • Targett Module Clip w/Wrover
  • The Open Book Feather
  • TinyS2
  • TinyS3
  • UARTLogger II
  • nanoESP32-S2 w/Wrover
  • nanoESP32-S2 w/Wroom

class i2cperipheral.I2CPeripheral(scl: microcontroller.Pin, sda: microcontroller.Pin, addresses: Sequence[int], smbus: bool = False)

Two wire serial protocol peripheral

I2C is a two-wire protocol for communicating between devices. This implements the peripheral (sensor, secondary) side.

  • scl (Pin) – The clock pin

  • sda (Pin) – The data pin

  • addresses (list[int]) – The I2C addresses to respond to (how many is hw dependent).

  • smbus (bool) – Use SMBUS timings if the hardware supports it

deinit() None

Releases control of the underlying hardware so other classes can use it.

__enter__() I2CPeripheral

No-op used in Context Managers.

__exit__() None

Automatically deinitializes the hardware on context exit. See Lifetime and ContextManagers for more info.

request(timeout: float = -1) I2CPeripheralRequest

Wait for an I2C request.


timeout (float) – Timeout in seconds. Zero means wait forever, a negative value means check once


I2C Slave Request or None if timeout=-1 and there’s no request

Return type


class i2cperipheral.I2CPeripheralRequest(peripheral: I2CPeripheral, address: int, is_read: bool, is_restart: bool)

Information about an I2C transfer request This cannot be instantiated directly, but is returned by I2CPeripheral.request().

  • peripheral – The I2CPeripheral object receiving this request

  • address – I2C address

  • is_read – True if the main peripheral is requesting data

  • is_restart – Repeated Start Condition

address :int

The I2C address of the request.

is_read :bool

The I2C main controller is reading from this peripheral.

is_restart :bool

Is Repeated Start Condition.

__enter__() I2CPeripheralRequest

No-op used in Context Managers.

__exit__() None

Close the request.

read(n: int = -1, ack: bool = True) bytearray

Read data. If ack=False, the caller is responsible for calling I2CPeripheralRequest.ack().

  • n – Number of bytes to read (negative means all)

  • ack – Whether or not to send an ACK after the n’th byte


Bytes read

write(buffer: circuitpython_typing.ReadableBuffer) int

Write the data contained in buffer.


buffer (ReadableBuffer) – Write out the data in this buffer


Number of bytes written

ack(ack: bool = True) None

Acknowledge or Not Acknowledge last byte received. Use together with I2CPeripheralRequest.read() ack=False.


ack – Whether to send an ACK or NACK