DEV Community

Cover image for Using I2C in MicroPython ๐ŸŒก
Andy Piper
Andy Piper

Posted on • Updated on

Using I2C in MicroPython ๐ŸŒก

The flashy NeoPixel-decorated ESP32-C3 board I've been learning about has no published schematic, so one of the things I've been doing as I play with it, is document how to access the various LEDs, buttons, and pins.

In the last post, I mentioned that I was curious about the JST-SH connector on the edge of the board.

ESP32-C3FH4-RGB overview

ESP32-C3FH4-RGB overview

End view showing 4 pin JST-SH connector

End view showing 4 pin JST-SH connector


The MicroPython documentation for the ESP32-C3 indicates that the default pin assignments for Hardware I2C ports 0 and 1 are 18/19 and 25/26, but attempting to use those pins in the REPL resulted in errors, or no data.

The clue is in the etching on the board itself. Just above the connector, we can see the text G3V01 - as one of the commenters on the CNX post about the board points out, this likely means "GND, 3V, I/O 0, I/O 1".

Inserting a JST-SH cable reveals the other part I had a query about:

JST-SH connector showing Black,Red,Blue,Yellow cables

JST-SH connector showing Black,Red,Blue,Yellow cables


The Adafruit site has a handy technical guide to StemmaQT / Qwiic connectors, from which we learn...

For the STEMMA QT cables, we follow the Qwiic convention:

  1. Black for GND
  2. Red for V+
  3. Blue for SDA
  4. Yellow for SCL

SDA and SCL are the important pieces here - these are the I2C data lines. From the labelling G3V01 and the wire ordering Black / Red / Blue / Yellow we can map SDA to pin 0 and SCL to pin 1. This actually makes sense as I look at the board, where the labels for visible holes for other connections (on the other side of the board from the image above) start from 2. 0 and 1 are for this I2C port, which accepts 4-pin JST-SH / Sparkfun Qwiic / Adafruit StemmaQT peripherals.

This is where I admit that it took me the longest time to work all of the above out, even though it sounds so logical and obvious! What can I tell you... I'm not an expert at any of this...

I actually sat trying I2C.scan() in the REPL against various combinations of possibilities for SDA and SCL for quite a long time, either hanging/crashing the board and requiring a reset, or just getting a blank [] result... as I said, now, it all seems so clear. Well, I'm on this MicroPython and ESP32-C3 journey to learn, and I'm learning.

OK, so we've figured out another part of the schematic. My reference card now looks like this (latest version will be in the GitHub repo in various formats):

Reference for the ESP32-C3FH4-RGB board

Annotated image of pin assignments


This has moved along slightly since the previous post in the series. Now, let's get some data in.

Although I have a few Adafruit and Sparkfun sensors on hand, I've typically used them with CircuitPython or Python rather than in MicroPython, and it turns out that the Sparkfun Qwiic library is not available for MicroPython. However, in the case of one sensor - the AHT20 temperature sensor - there is a MicroPython library. The example looked fairly straightforward, so I went ahead and installed the library to one of my boards (... I should mention, by now, I have a few of them - they are reasonably cheap, and I'm trying things in MicroPython and in C, and have some other hardware-related ideas as well as this exploration).

This time, for variety and to try out some different tooling, I used Adafruit's ampy tool, which can be installed via pip:

$ pip3 install adafruit-ampy
... ...
$ ampy -p /dev/cu.usbmodem11301 mkdir lib
$ ampy -p /dev/cu.usbmodem11301 put lib/
Enter fullscreen mode Exit fullscreen mode

This installed the micropython-ahtx0 library to lib/ on the board.

Adafruit AHT20 sensor board attached to ESP32-C3 via JST-SH cable

Adafruit AHT20 sensor board attached to ESP32-C3 via JST-SH cable


The code from there to get a temperature is really, really straightforward (but, the library is ~14 months old, and MicroPython has moved along since then, so I did have to change a few things).

from machine import Pin, SoftI2C
import utime
import ahtx0

i2c = SoftI2C(scl=Pin(1), sda=Pin(0))

sensor = ahtx0.AHT20(i2c)

while True:
    print("\nTemperature: %0.2f C" % sensor.temperature)
    print("Humidity: %0.2f %%" % sensor.relative_humidity)
Enter fullscreen mode Exit fullscreen mode

(this program is available in my "Five by Five" repo as

The original example for micropython-ahtx0 was for a Wemos D1 Mini, and used the hardware I2C implementation. Running that on this board on MicroPython 1.18 nightly (we are using nightly builds to make use of the RMT fixes introduced recently) resulted in a warning that hardware I2C is deprecated, and to use SoftI2C instead. In this case, it's a 1:1 replacement. We now know that the relevant SDA and SCL values are 0 and 1, so we can pass them in, and then, simply read the temperature and humidity every 5 seconds. Works great!

I sent a Pull Request upstream for the micropython-ahtx0 code to make the change to SoftI2C, as well as to fix an MQTT-related issue in another of the samples - another good deed I've done while learning about this board ๐Ÿ˜‡

Finally, I wanted to play with some other I2C things...

BCRobotics Qwiic/STEMMA QT Port Expander, wired to Adafruit BH1750 Light Sensor and APDS-9960 Proximity/Gesture/Color Sensor

BCRobotics Qwiic/STEMMA QT Port Expander, wired to Adafruit BH1750 Light Sensor and APDS-9960 Proximity/Gesture/Color Sensor


Here's what happens when we connect this to the board, and scan on the I2C bus:

MicroPython v1.18 on 2022-02-14; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> from machine import Pin, SoftI2C
>>> i2c = SoftI2C(scl=Pin(1),sda=Pin(0))
>>> i2c.scan()
[35, 56]
Enter fullscreen mode Exit fullscreen mode

Great! We know that these two peripherals are visible. I've not written code for these yet, and a more general set of drivers may or may not already exist, but this is all part of the fun!

I've been adding the various things I've learned to my repo as I go along, including details like the I2C IDs of the devices I have, for when I want to do something with them.

I'll close out this part of the series with something a bit different - Ciro Cattuto Tweeted about his "Wordle Device" built using this board...

This is really, really cool - running C code to call the Twitter API v2 directly on the board, and display results (there are a LOT of people playing Wordle right now!). I made a quick video running the code on one of mine, and I sent him some PRs to help the project, too! Check it out: look how rapidly those Tweets are coming in!

I hope you're enjoying this series - I'm learning so much just by really digging deep into the various topics here. So far I've learned a lot about: the ESP32-C3 chip itself, MicroPython, Thonny and plugins, various command line tools, I2C and the Qwiic "standard", some C, Espressif tooling, and FreeRTOS in the Wordle example, and I've sent Pull Requests to different projects, and met some really nice people in the community!

Thanks for reading! Thoughts and questions are always welcome! What would YOU do with a board like this? What would you like me to try out?

You can also follow me on Twitter for more. If you enjoyed this post, you could help to fund my gadget obsession via a small tip on Ko-Fi โ˜•๏ธ

Top comments (2)

fivdi profile image
Brian Cooke

Hi, nice post :)

There may be a misunderstanding related to the deprecation of hardware I2C in MicroPython as hardware I2C hasnโ€™t actually been deprecated.

The post doesn't explicitly mention what warning message related to I2C was seen, but assuming it was "Warning: I2C(-1, ...) is deprecated, use SoftI2C(...) instead", this means that the syntax I2C(-1, ...) which actually creates a SoftI2C instance has been deprecated. To create a SoftI2C instance the syntax SoftI2C(...) should now be used instead.

To create an I2C instance for hardware I2C on the ESP32-C3, the syntax I2C(0, ...) should be used. The 0 here identifies the hardware I2C peripheral.

andypiper profile image
Andy Piper

This is great, thanks for clarifying! Iโ€™ll try this over again :-)