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
A chip ID of 0x58 was returned. According to the BMP280 datasheet this is the correct value.