Using I2C on the Pyboard with 'pyb.I2C'

Contents

What is I2C?

I²C (Inter-Integrated Circuit, pronounced "eye-squared-C") is a common, two-wire serial communication protocol for short-distance data transfer between microcontrollers and peripherals like sensors, displays, or memory.

It uses only two lines, SDA (Serial Data) for data and SCL (Serial Clock) for timing, allowing many devices (masters and slaves, each with a unique address) to share a single bus, making it space-efficient and flexible.

It is synchronous serial communication because there is only a single clock source, maintained by the master, on the I2C bus.

I2C has a written international standard with universal adherence. This provides developers with a high degree of confidence while working on projects involving devices from multiple suppliers.

I2C Hardware on the Pyboard

The Pyboard V1 has two separate hardware I2C units, configured as shown in the following table:

I2C Name SCL Pin SDA Pin
I2C('X') X9 X10
I2C('Y') Y9 Y10

NOTE: They can also be named I2C(1) and I2C(2) but the official MicroPython documentation[1] recommends using the names shown in the table above.

MicroPython pyb.I2C Class

This section discusses the pyb.I2C class and its methods. This class is provided for specific use with the Pyboard. It's very simplistic, providing only the basic set of methods for the master (i.e. Pyboard) to interact with slave devices on the I2C bus.

It doesn't provide primitive I2C controller bus operations. As an example the pyb.I2C class does not allow control over whether a STOP condition is generated or not at the end of a transfer. This level of control is crucial when dealing with some sensor boards.

In theses cases where finer control over the I2C bus is needed then the machine.I2C class should be used. For further details see this article, Using I2C on the Pyboard with 'machine.I2C'.

Initialising the I2C Object

The first step is to declare and initialise an I2C object.


Syntax:
<i2c_variable> = pyb.I2C(bus,
                         mode,*,
                         baudrate=400000,
                         gencall=False)

Creates an I2C object.

Where:
  bus : One of 'X' or 'Y' based on the above table.

  mode : One of I2C.CONTROLLER or I2C.PERIPHERAL

  baudrate : The SCL clock rate.
             Usually 400 kHz or 100 kHz for
             some older devices.
  
  gencall : Whether to support general call mode.
            

Syntax:
I2C.deinit()

Turns off the I2C bus.

Examples:
from pyb import I2C

i2c1 = I2C('X', I2C.CONTROLLER)
i2c2 = I2C('Y', I2C.CONTROLLER, baudrate=100000)
i2c2.deinit()
            

I2C Methods

The I2C object provides some basic methods to scan for slaves, check the status of a slave, read and write to the bus.


Syntax:
I2C.scan()

Scan all I2C addresses from 0x01 to 0x7f and return
a list of those that respond.
            

Syntax:
I2C.is_ready(addr)

Where:
  addr : The I2C device address.

Returns: True if the I2C object
         is ready else False.
            

Syntax:
I2C.recv(recv, addr=0x00)

Where:
  recv : Number of bytes to receive.
  addr : The address to receive from.

Returns: Buffer containing the bytes read in.
            

Syntax:
I2C.send(send, addr=0x00)

Where:
  send : The data to send.
  addr : The address to send to.
            

Note: The sent or received data must be one or more bytes; each byte having a value between 0 and 255 inclusive. This is usually a string or bytes object or bytearray object.

Reading the BMP280 Sensor's Chip ID

The BMP280 is a barometric pressure sensor that remains popular though it is no longer being actively manufactured by Bosch Sensortec.

As with most Bosch Sensortec sensors the BMP280 has a chip ID burnt to non-volatile memory at point of manufacture. The example MicroPython program will read and report this ID value. The algorithm is simple. Write the ID register address of 0xD0 to the BMP280 then read back the ID as a single byte.

The Pyboard's I2C('X') will be used. The hookup to the BMP280 is simple:

Pyboard BMP280
3.3V VCC
GND GND
Pin X9 SCL
Pin X10 SDA

Copy the following code to a suitable IDE such as Thonny and flash to the Pyboard.


Code:
# A BMP280 barometric pressure sensor is attached to the
# 'X' I2C bus of the Pyboard.

# The I2C bus is scanned for the BMP280 address. This will
# be either 0x76 or 0x77.

# Then the BMP280 is queried for its chip ID. This should
# return 0x58.

from pyb import I2C

# Initialise I2C
i2c = I2C('X', mode=I2C.CONTROLLER)

# Scan the I2C bus and get the BMP280 address.
L = i2c.scan()
Addr = L[0]
print('I2C slave address found:', hex(Addr))

# Query the BMP280 for its Chip ID
CMD_CHIP_ID = bytes([0xD0]) # Command to get the Chip ID
if i2c.is_ready(Addr):
    i2c.send(CMD_CHIP_ID, Addr)
    buf = i2c.recv(1, Addr)
    chipID = hex(buf[0])
    print('Chip ID:', chipID)
else:
    print('I2C not ready')           
          

Output:
I2C slave address found: 0x76
Chip ID: 0x58
          
Pyboard's I2C('X') connected to the I2C interface of the BMP280 sensor.
Fig 1 - Pyboard's I2C('X') connected to the I2C interface of the BMP280 sensor

A chip ID of 0x58 was returned. According to the BMP280 datasheet this is the correct value.