Display & bus drivers

SSD1306 (OLED display)

SSD1306 OLED driver — thin wrapper around micropython-lib’s ssd1306.SSD1306_I2C.

The micropython-lib driver is already battle-tested and subclasses framebuf.FrameBuffer. We re-expose its core methods (text, pixel, fill, show) explicitly and delegate the rest (rect, line, hline, scroll, contrast, …) via __getattr__. An extra clear() convenience erases the buffer and pushes a blank frame.

The ssd1306 module is frozen into each board’s firmware image (see native/boards/*/manifest.py), so import works out of the box on flashed hardware.

A Display is an external I2C component, not part of the hub — instantiate one directly wherever you want to draw text / pixels.

class openbricks.drivers.ssd1306.SSD1306(i2c, addr=0x3C, width=128, height=64)[source]

Bases: object

128×64 (or 128×32) SSD1306 OLED on I2C.

text(s, x, y, c=1)[source]

Draw string s with the 8x8 font at pixel (x, y); c=1 lit, c=0 dark. Buffered — call show().

pixel(x, y, c=None)[source]

Set pixel (x, y) to c (1 lit / 0 dark), or return its current value when c is None.

fill(c)[source]

Fill the whole buffer with c (1 lit / 0 dark).

show()[source]

Push the buffer to the panel (nothing appears until this).

clear()[source]

Blank the display immediately (fill 0 + show).

WS2812 / WS2812B (RGB LED strip)

WS2812 / WS2812B RGB LED strip driver (NeoPixel protocol).

Targets the common addressable-LED modules — the 8-LED “stick” (WS2812B x8), rings, and cut-to-length strips — on a single data GPIO. The bit-banged 800 kHz protocol itself comes from MicroPython’s built-in neopixel module; this wrapper adds what user code actually wants on top of it:

  • Brightness scaling. Raw WS2812s at full duty are dazzling and hot; brightness=0.2 is a comfortable indoor default (same convention as the hub’s onboard status LED). Colors are stored unscaled and multiplied only at show(), so strip[i] reads back exactly what you assigned and changing brightness later re-scales everything on the next show().

  • Buffered updates. Item assignment only touches the buffer; show() pushes the whole strip in one wire transaction — an animation frame is N assignments + one show(), not N flickery writes. fill() / clear() are one-call conveniences that push immediately.

Usage:

from openbricks.drivers.ws2812 import WS2812

strip = WS2812(pin=21, n=8)         # WS2812B x8 stick
strip.fill((0, 60, 0))              # everything green (pushed)
strip[0] = (255, 0, 0)              # buffer only...
strip[7] = (0, 0, 255)
strip.show()                        # ...pushed together

Wiring the x8 stick: DIN → the data GPIO, 5V → 5 V supply, GND → common ground. The ESP32’s 3.3 V data line is out of spec for a 5 V-supplied WS2812B (V_IH = 0.7 × VDD = 3.5 V) but works with virtually every module in practice; if you see glitches, power the stick from 3.3 V (fine for the small x8 boards) or add a level shifter.

class openbricks.drivers.ws2812.WS2812(pin, n=8, brightness=0.2)[source]

Bases: object

A strip of n WS2812/WS2812B RGB LEDs on one data pin.

show()[source]

Push the buffered colors to the strip (one transaction), applying the current brightness scale.

fill(color)[source]

Set every LED to color (an (r, g, b) tuple) and push immediately.

clear()[source]

All LEDs off, pushed immediately.

property brightness

Global brightness scale 0.0 – 1.0. Assigning re-scales the whole strip on the next show() (or right now via show() — colors are stored unscaled).

TCA9548A (I2C multiplexer)

TI TCA9548A 8-channel I2C multiplexer / switch.

Some I2C sensors have a fixed address with no way to change it — the TCS34725 colour sensor is stuck at 0x29, for instance — so you cannot put two of them on one bus without an address collision. The TCA9548A sits between the host and the sensors and fans one bus out to eight electrically-isolated channels (SD0/SC0 .. SD7/SC7); each channel can host its own copy of the same-address device.

The mux itself answers at 0x70 by default (0x70..``0x77`` via the A0/A1/A2 address pins). Channel selection is a single-byte write to that address: bit N enables channel N. Multiple bits enable several channels at once; 0x00 disables all of them.

The drop-in path is mux[n]: it returns an object that quacks like a machine.I2C bus but transparently selects channel n before every operation, so existing drivers construct against it unchanged:

from machine import I2C, Pin
from openbricks.drivers.tca9548a import TCA9548A
from openbricks.drivers.tcs34725 import TCS34725

i2c = I2C(0, sda=Pin(21), scl=Pin(22), freq=400_000)
mux = TCA9548A(i2c)
left  = TCS34725(mux[0])   # 0x29 on channel 0
right = TCS34725(mux[1])   # 0x29 on channel 1

Reference: TCA9548A datasheet (Texas Instruments), section 8.3.

class openbricks.drivers.tca9548a.TCA9548A(i2c, address=_DEFAULT_ADDR)[source]

Bases: object

8-channel I2C multiplexer for same-address sensors.

Index it like a list: mux[n] returns an I2C-compatible handle for channel n (0-7) that selects the channel transparently before every transaction — pass it to any driver in place of the real machine.I2C.

Parameters:
  • i2c – the upstream machine.I2C bus the mux sits on.

  • address – mux’s own I2C address, 0x70-0x77 via A0/A1/A2 straps (default 0x70).

select(channel)[source]

Enable exactly channel (0..7), disabling the rest.

disable()[source]

Disable every channel (control byte 0x00).