Raspberry Pi Pico I2C

Pico has several I2C lines. In Arduino, when I scan I2C lines it is scanned only on 0 and 1, as default. How to force Wire.begin() to take say, 6 and 7? According to this page, it should be addressed, but I don't know how. For 2 and 3, the address is 0x30.

I checked the datasheet, but obviously had no luck.

Which Arduino boards platform are you using to add support for the Pico to the Arduino IDE.

I know of two prominent ones:

Both ways can not compile if I add int as a pin number in Wire.begin(). I can with hex, but I don't know what hex value to use for 6 and 7.

It sound like you're using the 3rd party (earlephilhower) library. If you try with the mbed (official Arduino) library, take a look at this thread, which deals with modifying the pins_arduino.h file, found here in Windows installations of Arduino IDE: C:\Users\USERNAME\AppData\Local\Arduino15\packages\arduino\hardware\mbed_rp2040\2.5.2\variants\RASPBERRY_PI_PICO, to specify Wire and Wire1 pins for use in I2C connections:

https://github.com/arduino/ArduinoCore-mbed/issues/194#issuecomment-825490996

The code referenced is for Earle's core. The 0x30 is the sensor/device I2C address being connected

// Simple I2C master and slave demo - Earle F. Philhower, III
// Released into the public domain
// 
// Using both onboard I2C interfaces, have one master and one slave
// and send data both ways between them
//
// To run, connect GPIO0 to GPIO2, GPIO1 to GPIO3 on a single Pico


#include <Wire.h>


void setup() {
    Serial.begin(115200);
    delay(5000);
    Wire.begin();
    Wire1.begin(0x30);
    Wire1.onReceive(recv);
    Wire1.onRequest(req);
}


static char buff[100];
void loop() {
    static int p;
    char b[90];


    // Write a value over I2C to the slave
    Serial.println("Sending...");
    Wire.beginTransmission(0x30);
    sprintf(b, "pass %d", p++);
    Wire.write(b, strlen(b));
    Wire.endTransmission();


    // Ensure the slave processing is done and print it out
    delay(1000);
    Serial.printf("buff: '%s'\r\n", buff);


    // Read from the slave and print out
    Wire.requestFrom(0x30, 6);
    Serial.print("\nrecv: '");
    while(Wire.available()) {
        Serial.print((char)Wire.read());
    }
    Serial.println("'");
    delay(1000);
}


// These are called in an **INTERRUPT CONTEXT** which means NO serial port
// access (i.e. Serial.print is illegal) and no memory allocations, etc.


// Called when the I2C slave gets written to
void recv(int len) {
    int i;
    // Just stuff the sent bytes into a global the main routine can pick up and use
    for (i=0; i<len; i++) buff[i] = Wire1.read();
    buff[i] = 0;
}


// Called when the I2C slave is read from
void req() {
    static int ctr = 765;
    char buff[7];
    // Return a simple incrementing hex value
    sprintf(buff, "%06X", (ctr++) % 65535);
    Wire1.write(buff, 6);
}

The Arduino-Pico framework [[url]https://github.com/earlephilhower/arduino-pico[/url]] now provides a simple and easy Arduino based approach to programming the Raspberry Pi Pico Arduino core [and all RP2040 based boards] . . . in response to the original posters question, the code below demonstrates how the SDA & SCA pins can be specified in the Arduino-Pico (earlephilhower) "Raspberry Pi RP2040 Boards"framework/library using Wire.setSDA & Wire.setSCL for I2C0 pins and Wire1.setSDA & Wire1.setSCL for I2C1 pins.


I have used Arduino-Pico to create a simple, but highly responsive, interrupt triggered i2c Slave/Responder [in about 30 lines of code], as show below, to illustrate how easy this is [based on smithg400's idea for a 256 byte i2c RAM - [url]Pico as I2C slave (solved) - Raspberry Pi Forums]

#include <Wire.h>

const byte PICO_I2C_ADDRESS = 0x55;
const byte PICO_I2C_SDA = 20;
const byte PICO_I2C_SCL = 21;
const byte PICO_LED = 25;

byte volatile rx_flag = 0;
byte volatile tx_flag = 0;
uint8_t volatile ram_addr = 0;
uint8_t volatile ram[256];


void setup() {
  pinMode (PICO_LED, OUTPUT);
  Wire.setSDA(PICO_I2C_SDA);
  Wire.setSCL(PICO_I2C_SCL);
  Wire.begin(PICO_I2C_ADDRESS);    // join i2c bus as slave
  Wire.onReceive(i2c_receive);     // i2c interrupt receive
  Wire.onRequest(i2c_transmit);    // i2c interrupt send
}

void loop() {
  if (rx_flag) {
    rx_flag = 0;
    digitalWrite (PICO_LED, HIGH);
    delay(1);
    digitalWrite (PICO_LED, LOW);
  }

  if (tx_flag) {
    tx_flag = 0;
    digitalWrite (PICO_LED, HIGH);
    delay(1);
    digitalWrite (PICO_LED, LOW);
  }

  delay(1);
}

void i2c_receive(int bytes_count) {     // bytes_count gives number of bytes in rx buffer   
  if (bytes_count == 0) {
    return;
  }
  ram_addr = (uint8_t)Wire.read();      // first byte is ram offset address (0-255)
  for (byte i=1; i<bytes_count; i++) {
    ram[ram_addr] = (uint8_t)Wire.read();
    ram_addr++;
  } 
  rx_flag = bytes_count;
}

void i2c_transmit() {
  tx_flag = 1;
  Wire.write((uint32_t)ram[ram_addr]);
  ram_addr++;
}

This code turns the Raspberry Pico into an i2c Slave/Responder that emulates a 256 byte RAM memory. The first byte written to the Pico over i2c sets an address/offset in the 256 byte RAM and subsequent bytes written are then stored into the RAM, with the address auto-incremented after each write. Similarly, each request for a byte from the Pico over i2c retrieves a byte from memory and sends it back to the Master/Controller over i2c [again with the address auto-incremented after each read] . Setting the address/offset before a request for a byte from the Pico determines which address/offset the next read will use [NOTE: the address/offset automatically wraps back to zero when it exceeds 255 as a result of an auto-increment]

In testing the code I identified a problem specific to multi controller i2c systems that others may have experienced. This is where one Master/Controller uses the i2c restart functionality, rather than issuing a stop and then start, between the write of a register address and the read of it's content to ensure that another Master/Controller does not gain control of the i2c bus . . . this issue is detailed here [[url]https://github.com/earlephilhower/arduino-pico/issues/585[/url]] and has already been fixed in the master branch of Arduino-Pico framework, and is included in the release [as of version 2.0.2] which ensures this functions as expected in a multi-Master/Controller system.

The code, referenced above, needs to be compiled and uploaded to a Raspberry Pi Pico with GPIO 20 & 21 (SDA & SCL) connected to a suitable controller device [e.g. a Raspberry Pi] . . . NOTE: pull-up resistors to +3.3v will be needed on SDA & SCL, if using a Raspberry Pi & Raspberry Pico for this.

i2cTools under the Pi-OS can then be used to interact with the i2c Pico Slave/Responder . . . examples of this are shown below

i2cdetect -y 1                                         \\ i2c device found at 0x55
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- 55 -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

i2cset -y 1 0x55 0x00                                  \\ set RAM address/offset to 0x00
i2cdump -y 1 0x55                                      \\ dump 256 bytes from Pico
No size specified (using byte-data access)
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................

Load 10 bytes ("0123456789") into Pico starting at address 00

i2ctransfer -y 1 w11@0x55 0x00 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39

i2cset -y 1 0x55 0x00                                  \\ set RAM address/offset to 0x00
i2cdump -y 1 0x55                                      \\ dump 256 bytes from Pico
No size specified (using byte-data access)
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 30 31 32 33 34 35 36 37 38 39 00 00 00 00 00 00    0123456789......
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................

i2cset -y 1 0x55 0x00                                  \\ set RAM address/offset to 0x00
i2ctransfer -y 1 r10@0x55                              \\ read 10 bytes from Pico
0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39

i2ctransfer -y 1 w1@0x55 0x00 r10@0x55                 \\ set RAM address/offset to 0x00, i2c restart and read 10 bytes
0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39   

Below is a more complex i2cTools command that combines the write of 10 bytes [auto incrementing from 0x00 to 0x09] to address 0x00, then resets the address/offset to 0x00 and reads back 10 bytes.

i2ctransfer -y 1 w11@0x55 0x00 0x00+ w1@0x55 0x00 r10@0x55
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09

In terms of i2c protocol it actually generates;

  • an i2c ‘start’
  • an i2c Slave/Responder device address of 0x55
  • a write of 11 bytes of data [byte 1 is the RAM address/offset followed by 10 bytes of actual data to be loaded into the Pico]
  • an i2c ‘restart’
  • an i2c Slave/Responder device address of 0x55
  • 1 byte of data [the RAM address/offset]
  • an i2c ‘restart
  • an i2c Slave/Responder device address of 0x55
  • a read request for 10 bytes of data
  • an i2c ‘stop’

NOTE; each i2c ‘restart’ is actioned by the Slave/Responder device as though it were a ‘stop’ followed by a ‘start’ [i.e. a combined ‘end of message’ followed by a ‘start of message’]

Alternatively, the Raspberry Pico as an i2c Slave/Responder can also be demonstrated using the python program shown below, which is designed to run using CircuitPython [i.e. Adafruit Blinka - Overview | CircuitPython on Linux and Raspberry Pi | Adafruit Learning System] on a Raspberry Pi.

#!/usr/bin/env python
import time
import board
import busio

I2C_ADDRESS = 0x55
i2cbyte     = bytearray(1)
buffer      = bytearray(64)

print("Hello CircuitPython blinka!")

# Try to create an I2C device
i2c = busio.I2C(board.SCL, board.SDA)
print("... I2C ok!")

while not i2c.try_lock():
    pass

#  i2c functions
#  -------------
#  writeto(I2C_ADDRESS, buffer, *, start=0, end=len(buffer), stop=True)
#  readfrom_into(I2C_ADDRESS, buffer, *, start=0, end=len(buffer))
#  writeto_then_readfrom(address, out_buffer, in_buffer, *, out_start=0, out_end=None, in_start=0, in_end=None)

try:
    # write multiple bytes [first byte is initial address/register to write]

    i2c.writeto(I2C_ADDRESS, bytes([0x00,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39]) )

    # read a single byte from a specific address/register

    i2c.writeto(I2C_ADDRESS, bytes([0x01]) )    # address/register 0x01
    i2c.readfrom_into(I2C_ADDRESS, i2cbyte)
    print(i2cbyte)

    i2c.writeto(I2C_ADDRESS, bytes([0x03]) )    # address/register 0x03
    i2c.readfrom_into(I2C_ADDRESS, buffer, start=0, end=1)
    print(buffer[0:1])

    # read multiple bytes [6] from specified address/register

    i2c.writeto(I2C_ADDRESS, bytes([0x00]) )    # address/register 0x00
    i2c.readfrom_into(I2C_ADDRESS, buffer, start=0, end=6)
    print(buffer[0:6])

    # the code below combines writing an address with reading 6 byes using 'restart' rather than
    # 'stop' & 'start' betwwen the write and the read [as executed in the examples above]

    i2c.writeto_then_readfrom(I2C_ADDRESS, bytes([0x00]), buffer, in_start=0, in_end=6)
    print(buffer[0:6])

finally:  # unlock the i2c bus when ctrl-c'ing out
    i2c.unlock()
1 Like

Hi mrburnette,
you show a pinout for a RP2040 board which highly resembles the pinout of a Teensy 3.5. I need a Teensy 3.5 replacement because all of them are out of stock, wherever I try to order. I cannot get such a board.

Can you please tell me where such a RP2040 board, as you show here, can be ordered?

IN THE U.S.A.
BOARD CAN BE FOUND
Micro Center Locations & Store Hours (mystore411.com)

MICROCENTER WILL NOT SHIP PROMOTIONAL ITEMS

2x EXPENSIVE, BUT IN STOCK:
Amazon.com: GeeekPi Raspberry Pi Pico Flexible Micro Controller Mini Development Board,Based on The Raspberry Pi RP2040,Dual-Core ARM Cortex M0+ Processor,Running up to 133 MHz, Support C/C++ / Python(3 Pack) : Electronics

Sorry, I found the board you show now but the pinning is different from the one you showed in post #5. The board should fit into a PCB which already exists and was designed for a Teensy 3.5. Therefore the pinning is very important.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.