Hi members! A few days ago I received an ordered Seeed XIAO RP2350 and a Seeed Expansion Board Base. First my thanks to Seeed for publishing a lot of detailed Get Started with … documentation. I also found a useful document from WaveShare: MicroPython machine.I2C function . However, despite this documentation and despite my years of experience with hard- and software for microcontrollers it took me more than a day to be able to have the XIAO RP2350 (installed on top of the Expansion Board Base) “talking” to the OLED display and to sensors connected via one of the two Grove I2C connectors of the Expansion Board (schematic).
Why? Because I got confused with the naming of the SCL and SDA pins of these two Grove connectors. image. Since the helpful document for the part of I2C is focused to sketches for the Arduino IDE. It uses Pin names like “D4”. The Schematic of the Expansion Board Base uses labels like: “D4 SDA” and “D5 SCL”, however, looking at the following, in the Thonny IDE for MicroPython:
MPY: soft reboot
MicroPython v1.24.0-preview.201.g269a0e0e1 on 2024-08-09; Raspberry Pi Pico2 with RP2350
Type "help()" for more information.
>>> from machine import I2C, Pin
>>> dir(Pin)
['__class__', '__name__', 'value', 'ALT', 'ALT_GPCK', 'ALT_I2C', 'ALT_PIO0', 'ALT_PIO1', 'ALT_PWM', 'ALT_SIO', 'ALT_SPI', 'ALT_UART', 'ALT_USB', 'IN', 'IRQ_FALLING', 'IRQ_RISING', 'OPEN_DRAIN', 'OUT', 'PULL_DOWN', 'PULL_UP', '__bases__', '__dict__', 'board', 'cpu', 'high', 'init', 'irq', 'low', 'off', 'on', 'toggle']
>>> dir(Pin.board)
['__class__', '__name__', 'GP0', 'GP1', 'GP10', 'GP11', 'GP12', 'GP13', 'GP14', 'GP15', 'GP16', 'GP17', 'GP18', 'GP19', 'GP2', 'GP20', 'GP21', 'GP22', 'GP25', 'GP26', 'GP27', 'GP28', 'GP3', 'GP4', 'GP5', 'GP6', 'GP7', 'GP8', 'GP9', 'LED', '__bases__', '__dict__']
>>>
It was until I found the XIAO schematic where, in the drawings for “Pin header” and “Expand Pin” came clear to me that:
I2C0 have pins GP16 (SDA) and GP17 (SCL) and
I2C1 have pins GP6 (SDA) and GP7 (SCL)
This is confirmed in the file “pins_arduino.h” that has these definitions for “Wire”:
// Wire
#define __WIRE0_DEVICE (i2c0)
#define PIN_WIRE0_SDA (16u)
#define PIN_WIRE0_SCL (17u)
#define SDA PIN_WIRE0_SDA
#define SCL PIN_WIRE0_SCL
#define I2C_SDA (SDA)
#define I2C_SCL (SCL)
#define __WIRE1_DEVICE (i2c1)
#define PIN_WIRE1_SDA (6u)
#define PIN_WIRE1_SCL (7u)
Conclusion: this version of MicroPython for the RP2350 has in “machine.Pin.board” no definitions for “D4” or “D5” however, “GP1” etcetera.
In a MicroPython script the definition now could be:
PIN_WIRE1_SCL = machine.Pin.board.GP7
PIN_WIRE1_SDA = machine.Pin.board.GP6
At the time I created the scripts in Thonny, I had not seen the machine.Pin.board definitions, so I (successfully) used direct Pin(nr) assignments
Note that the pins for I2C0 (GP16 and GP17) are solder pads on the downside of the XIAO RP2350 board. Not easily accessible. That is why I chose the pins of I2C1 (GP6 and GP7) which are connected to the two Grove I2C connectors on the Expansion Board Base.
With this information I was finally able to successfully create a working script in MicroPython. In this case for controlling the OLED display on the Seeed Expansion Board Base:
import machine
from lib.ssd1306 import SSD1306_I2C
PIN_WIRE1_SCL = machine.Pin.board.GP7
PIN_WIRE1_SDA = machine.Pin.board.GP6
i2c = machine.I2C(id=1, scl=machine.Pin(PIN_WIRE1_SCL), sda=machine.Pin(PIN_WIRE1_SDA), freq=400000)
oled = SSD1306_I2C(128, 32, i2c)
# This is it. Now we can use our OLED display:
oled.fill(1)
oled.show()
# This fills the whole display with white pixels. To clear the display do:
oled.fill(0)
oled.show()
# Now we can also write some text:
oled.text('Hello', 0, 0)
oled.text('World', 0, 10)
oled.show()
With this new knowledge I also was able to successfully create scripts to read values from different sensors: a BME280 and a MCP9808.
I published these MicroPython scripts on Github Gists. See:
mcp9808_test and mcp9808 module
Note: for the mcp9808 I found the library module on internet however I had to repair a lot of errors because the i2c.send() function requires a bytearray buffer and many calls had only an integer (REGister) value as parameter instead of a bytearray buffer with this REGister value as first element. This is why I added the changed mcp9808 module file to this Gist.
For the “visual” results see:
hello world
MCP9808 value on OLED display