I2C (Grove connector)

hub.i2c is a machine.I2C singleton — the same object returned by machine.I2C(0). It speaks the internal I2C bus that drives the PCA9685, SC16IS750 (I2C variant), BNO085 IMU and the on-board Grove connector pads.

Because hub.i2c and machine.I2C(0) are the same singleton, creating machine.I2C(0) explicitly is safe and returns the same instance:

import hub, machine
assert hub.i2c is machine.I2C(0)   # True

Pins are fixed to the board’s I2C_SDA / I2C_SCL definitions in Board.h. Passing different sda / scl kwargs to .init() is accepted but silently ignored.

Quickstart

Scan the bus and print anything that ACKs:

import hub
print("devices:", [hex(a) for a in hub.i2c.scan()])

Change the clock:

hub.i2c.init(freq=100_000)     # slow down to 100 kHz
hub.i2c.init(freq=400_000)     # back to default

Raw write / read

ADDR = 0x40
hub.i2c.writeto(ADDR, b"\\x00\\x01")     # single transaction
data = hub.i2c.readfrom(ADDR, 4)         # read 4 bytes

For a scatter/gather write in one transaction:

hub.i2c.writevto(ADDR, [b"\\x10", payload])

Register access

Convenience wrappers around the common register-file pattern (write memaddr, repeated-start, read/write):

who = hub.i2c.readfrom_mem(0x68, 0x75, 1)          # MPU6050 WHO_AM_I
hub.i2c.writeto_mem(0x68, 0x6B, b"\\x00")          # PWR_MGMT_1 = 0

buf = bytearray(6)
hub.i2c.readfrom_mem_into(0x68, 0x3B, buf)         # 6-byte accel burst

16-bit register addresses:

hub.i2c.writeto_mem(0x50, 0x1234, b"\\xaa", addrsize=16)
hub.i2c.readfrom_mem(0x50, 0x1234, 1, addrsize=16)

Full example — MPU-6050 accelerometer on Grove

examples/grove_i2c_mpu6050.py
"""MPU-6050 accelerometer over the Grove I2C connector.

Reads WHO_AM_I, wakes the chip, then streams 6-byte accel bursts.
Uses the shared internal I2C bus via ``hub.i2c`` — coexists with the
PCA9685, SC16IS750, BNO085 and everything else already on Wire1.
"""

import hub, struct, time

ADDR = 0x68           # MPU-6050 / MPU-6500 default (AD0 low)
WHO_AM_I = 0x75
PWR_MGMT_1 = 0x6B
ACCEL_XOUT_H = 0x3B

print("scanning I2C bus...")
devs = hub.i2c.scan()
print("addresses:", [hex(a) for a in devs])
if ADDR not in devs:
    print(f"MPU-6050 not found at {hex(ADDR)}")
else:
    who = hub.i2c.readfrom_mem(ADDR, WHO_AM_I, 1)[0]
    print(f"WHO_AM_I: 0x{who:02X}")
    hub.i2c.writeto_mem(ADDR, PWR_MGMT_1, b"\x00")

    _buf = bytearray(6)
    _last_print = 0

    while not hub.buttons.center():
        now = time.ticks_ms()
        if time.ticks_diff(now, _last_print) >= 200:
            _last_print = now
            hub.i2c.readfrom_mem_into(ADDR, ACCEL_XOUT_H, _buf)
            ax, ay, az = struct.unpack(">hhh", _buf)
            print(f"accel  x={ax:+6}  y={ay:+6}  z={az:+6}")

API reference

hub.i2c is a machine.I2C instance — the full API is documented under machine.I2C. Methods available:

  • .init(*, freq=-1) — apply new frequency.

  • .scan() — probe 0x08..0x77, return list of ACK’d addresses.

  • .readfrom(addr, nbytes, stop=True) → bytes.

  • .readfrom_into(addr, buf, stop=True).

  • .writeto(addr, buf, stop=True) → bytes-written count.

  • .writevto(addr, vector, stop=True) — scatter/gather.

  • .readfrom_mem(addr, memaddr, nbytes, *, addrsize=8).

  • .readfrom_mem_into(addr, memaddr, buf, *, addrsize=8).

  • .writeto_mem(addr, memaddr, buf, *, addrsize=8).

Errors:

  • OSError(ENODEV) — device NACK’d (bad address / not present).

  • OSError(ETIMEDOUT) — bus timeout.

  • OSError(EIO) — other bus error.