Some links in this post may be affiliate links. We may get paid if you buy something or take an action after clicking one of these, but without addictional costs for you compared to direct buying.

Using I2C devices with Raspberry PI Pico and MicroPython

Raspberry PI Pico i2C featured image
Follow my latest articles on Twitter: . Or subscribe my newsletter (top right in this page) to be notified when new projects are available!
4.2
(6)

I2C (Inter-Integrated Circuit) communication allows to reduce wiring difficulty when connecting devices able to support this protocol. It is widely used in electronics industry and also Raspberry PI Pico can use I2C

In this tutorial I’m going to show you how works and how to use I2C with Raspberry PI Pico.

Explaining How I2C Works

I2C (pronounced “I squared C”) is a synchronous communication protocol invented in 1982 by Philips Semiconductors. It supports multi-master, multi-slave, packet switched, single-ended, serial communications between supporting devices. I2C allows to connect lower-speed peripheral to processors and microcontrollers.

It uses a simple 2 wire communication bus, Serial Data Line (SDA) and Serial Clock Line (SCL), pulled up with resistors. Typical voltages used are +5 V or +3.3 V.

Master devices generates the clock, so keeping communication management ownership. All devices share same wires:

I2C generic schematic
By en:user: Cburnett – Own work made with Inkscape, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=1472017; Ref: Wikipedia I2C page

Common I2C bus connections can work at 100 kbit/s (also known as standard mode) or at 400 kbit/s (known as Fast mode). You can theoretically use also arbitrarily low clock frequencies. On the other side, faster speeds are allowed on more recent I2C revisions, raising speed up to 1 Mbit/s (Fast mode plus), 3.4 Mbit/s (High Speed mode), and 5 Mbit/s (Ultra Fast-mode).

I2C uses basic transactions to let devices communicate between them. Each transaction begins with a START and ends with a STOP signal:

  • Single message – Master writes data to Slave
  • Single message – Master reads data from Slave
  • Combined format – Master sends at least two reads or writes to one or more Slaves

The I2C uses a 7-bit address space to make each device on bus identified and send requests to correct slave listening on bus.

More info about this communication protocol can be found in Wikipedia I2C page.

Common I2C MicroPython Commands

Micropython includes an I2C class in its “machine” module, which makes it simple using this communication protocol. Common methods are below reported and refer to MicroPython I2C documentation.

  • Constructors
    • machine.I2C(id, *, scl, sda, freq=400000) – creates a new I2C object
  • General Methods
    • I2C.init(scl, sda, *, freq=400000) – initialise the I2C bus with the given arguments
    • I2C.deinit() – turn off the I2C bus
    • I2C.scan() – scan all I2C addresses (between 0x08 and 0x77 inclusive) and return a list of those that respond
  • Primitive I2C operations
    • I2C.start() – generate a START condition on the bus
    • I2C.stop() – generate a STOP condition on the bus
    • I2C.readinto(buf, nack=True, /) – reads bytes from the bus and stores them into “buf” variable
    • I2C.write(buf) – write the bytes from “buf” variable to the bus
  • Standard bus operations
    • I2C.readfrom(addr, nbytes, stop=True, /) – read nbytes from the slave specified by addr
    • I2C.readfrom_into(addr, buf, stop=True, /) – Read into buf from the slave specified by addr
    • I2C.writeto(addr, buf, stop=True, /) – Write the bytes from buf to the slave specified by addr
    • I2C.writevto(addr, vector, stop=True, /) – Write the bytes contained in vector to the slave specified by addr (vector should be a tuple or list of objects with the buffer protocol)
  • Memory operations
    • I2C.readfrom_mem(addr, memaddr, nbytes, *, addrsize=8) – Read nbytes from the slave specified by addr starting from the memory address specified by memaddr (addrsize specifies the address size in bits)
    • I2C.readfrom_mem_into(addr, memaddr, buf, *, addrsize=8) – Read into buf from the slave specified by addr starting from the memory address specified by memaddr. The number of bytes read is the length of buf. (addrsize specifies the address size in bits)
    • I2C.writeto_mem(addr, memaddr, buf, *, addrsize=8) – Write buf to the slave specified by addr starting from the memory address specified by memaddr (addrsize specifies the address size in bits)

In the following example, I will show you how to use Raspberry PI Pico with I2C, connecting a generic device, and scanning I2C bus to find slave addresses. I will use a generic I2C LCD screen, but this applies to all I2C compatible devices.

What We Need

raspberry pi pico microcontroller

As usual, I suggest adding from now to your favourite e-commerce shopping cart all needed hardware, so that at the end you will be able to evaluate overall costs and decide if continuing with the project or removing them from the shopping cart. So, hardware will be only:

Check hardware prices with following links:

amazon raspberry pi pico box

Wiring Diagram

Following picture shows how to wire a generic I2C device to Raspberry PI Pico:

Raspberry PI Pico generic i2C wiring

Note that SDA connects to SDA and SCL connects to SCL.

Step-by-Step procedure

Connect RPI Pico to Thonny (you can refer my tutorial about First steps with Raspberry PI Pico). Download my rpi_pico_i2c_scan.py script in your computer and open it with Thonny.

Following paragraphs will describe my code line by line. At the end, you will find scipt expected results.

Required modules are imported:

import machine

SDA and SCL PINs are defined as variables, set to corresponding PIN numbers:

sdaPIN=machine.Pin(0)
sclPIN=machine.Pin(1)

An I2C instance is set, associated to variable i2c. This requires the i2c block number (0 – zero, in our wiring). It also requires the SDA and SCL PIN variables and, finally, the bus frequency:

i2c=machine.I2C(0,sda=sdaPIN, scl=sclPIN, freq=400000)

Following lines notify user that scanning is going to start and uses “devices” variable (a list) to store scan results, which return a list of all devices address (in decimal):

print('Scanning i2c bus')
devices = i2c.scan()

If no devices have answered, then devices list will not include any number, so its lenght will be 0:

if len(devices) == 0:
 print("No i2c device !")

On the other hand, if at least one device answered to scan, then device length will be equal to the number of devices found:

else:
 print('i2c devices found:',len(devices))

Finally, for each device in list, its address will be printed (both in decimal and hexadecimal) with a for loop:

for device in devices:
 print("Decimal address: ",device," | Hexa address: ",hex(device))

Running the rpi_pico_i2c_scan Script

Run this script on Thonny (F5 key), selecting “This computer” as Save Location (if requested).

This will output following:

Scanning i2c bus
i2c devices found: 1
Decimal address:  39  | Hexa address:  0x27

Found address will be useful for your programs to set correct identifier and send data to right slave device.

Enjoy!

How useful was this post?

Click on a star to rate it anonymously!

Average rating 4.2 / 5. Vote count: 6

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Leave a Reply

Your email address will not be published. Required fields are marked *

I accept the Privacy Policy