CircuitPython APIs for non-CircuitPython versions of Python such as CPython on Linux and MicroPython.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Hardware and software implementations of I2C, SPI, and UART communication protocols with automatic resource locking and CircuitPython compatibility. Provides unified interfaces across diverse hardware platforms with automatic platform detection and driver loading.
The I2C class provides Inter-Integrated Circuit (I2C) communication with automatic platform detection and resource management. Supports both hardware and software implementations depending on the platform.
class I2C(Lockable):
def __init__(self, scl, sda, frequency: int = 100000):
"""
Initialize I2C bus.
Args:
scl: SCL (clock) pin object from board module
sda: SDA (data) pin object from board module
frequency: Bus frequency in Hz (default 100000, ignored on Linux)
Raises:
ValueError: If no hardware I2C available on specified pins
"""
def deinit(self) -> None:
"""Release I2C bus resources"""
def scan(self) -> list[int]:
"""
Scan for I2C devices on the bus.
Returns:
list[int]: List of 7-bit device addresses found on the bus
"""
def readfrom_into(self, address: int, buffer: bytearray, *, start: int = 0, end: int = None) -> None:
"""
Read from I2C device into existing buffer.
Args:
address: 7-bit I2C device address
buffer: Buffer to read data into
start: Starting index in buffer (default 0)
end: Ending index in buffer (default None for full buffer)
"""
def writeto(self, address: int, buffer: bytes, *, start: int = 0, end: int = None) -> int:
"""
Write data to I2C device.
Args:
address: 7-bit I2C device address
buffer: Data to write (bytes or str)
start: Starting index in buffer (default 0)
end: Ending index in buffer (default None for full buffer)
Returns:
int: Number of bytes written
"""
def writeto_then_readfrom(
self,
address: int,
buffer_out: bytes,
buffer_in: bytearray,
*,
out_start: int = 0,
out_end: int = None,
in_start: int = 0,
in_end: int = None,
stop: bool = False
) -> None:
"""
Write to device then read from device (common for register reads).
Args:
address: 7-bit I2C device address
buffer_out: Data to write first
buffer_in: Buffer to read response into
out_start: Start index for output buffer
out_end: End index for output buffer
in_start: Start index for input buffer
in_end: End index for input buffer
stop: Send stop condition between write and read
"""The SPI class provides Serial Peripheral Interface (SPI) communication with configurable parameters and automatic platform detection. Includes resource locking for thread safety.
class SPI(Lockable):
def __init__(self, clock, MOSI=None, MISO=None):
"""
Initialize SPI bus.
Args:
clock: SCK (clock) pin object from board module (required)
MOSI: MOSI (Master Out Slave In) pin object (optional for read-only)
MISO: MISO (Master In Slave Out) pin object (optional for write-only)
Raises:
ValueError: If no hardware SPI available on specified pins
"""
def configure(self, baudrate: int = 100000, polarity: int = 0, phase: int = 0, bits: int = 8) -> None:
"""
Configure SPI parameters. Must be called after try_lock().
Args:
baudrate: Clock frequency in Hz (default 100000)
polarity: Clock polarity - 0 (idle low) or 1 (idle high)
phase: Clock phase - 0 (sample on leading edge) or 1 (trailing edge)
bits: Data word size in bits (default 8)
Raises:
RuntimeError: If SPI bus is not locked
"""
def deinit(self) -> None:
"""Release SPI bus resources"""
@property
def frequency(self) -> int:
"""
Current SPI frequency in Hz.
Returns:
int: Current baudrate
Raises:
NotImplementedError: If not supported on platform
"""
def write(self, buf: bytes, start: int = 0, end: int = None) -> None:
"""
Write data to SPI bus.
Args:
buf: Data to write
start: Starting index in buffer (default 0)
end: Ending index in buffer (default None for full buffer)
"""
def readinto(self, buf: bytearray, start: int = 0, end: int = None, write_value: int = 0) -> None:
"""
Read data from SPI bus into buffer.
Args:
buf: Buffer to read data into
start: Starting index in buffer (default 0)
end: Ending index in buffer (default None for full buffer)
write_value: Value to write during read (default 0)
"""
def write_readinto(
self,
buffer_out: bytes,
buffer_in: bytearray,
out_start: int = 0,
out_end: int = None,
in_start: int = 0,
in_end: int = None
) -> None:
"""
Simultaneously write and read data (full-duplex).
Args:
buffer_out: Data to write
buffer_in: Buffer to read response into
out_start: Start index for output buffer
out_end: End index for output buffer
in_start: Start index for input buffer
in_end: End index for input buffer
"""The UART class provides Universal Asynchronous Receiver-Transmitter (UART) serial communication. Not supported on Linux platforms - use pyserial instead.
class UART(Lockable):
class Parity:
ODD: Parity # Odd parity checking
EVEN: Parity # Even parity checking
def __init__(
self,
tx,
rx,
baudrate: int = 9600,
bits: int = 8,
parity = None,
stop: int = 1,
timeout: int = 1000,
receiver_buffer_size: int = 64,
flow = None
):
"""
Initialize UART interface.
Args:
tx: TX (transmit) pin object from board module
rx: RX (receive) pin object from board module
baudrate: Communication speed in baud (default 9600)
bits: Data bits per character (default 8)
parity: Parity checking (None, UART.Parity.ODD, UART.Parity.EVEN)
stop: Stop bits (default 1)
timeout: Read timeout in milliseconds (default 1000)
receiver_buffer_size: RX buffer size in bytes (default 64)
flow: Flow control (not implemented, raises NotImplementedError)
Raises:
RuntimeError: On Linux platforms (use pyserial instead)
ValueError: If no hardware UART available on specified pins
NotImplementedError: If flow control is specified
"""
def deinit(self) -> None:
"""Release UART resources"""
def read(self, nbytes: int = None) -> bytes:
"""
Read data from UART.
Args:
nbytes: Number of bytes to read (None for all available)
Returns:
bytes: Data read from UART
"""
def readinto(self, buf: bytearray, nbytes: int = None) -> int:
"""
Read data from UART into buffer.
Args:
buf: Buffer to read data into
nbytes: Number of bytes to read (None for buffer size)
Returns:
int: Number of bytes actually read
"""
def readline(self) -> bytes:
"""
Read a line of characters up to newline.
Returns:
bytes: Line data including newline character
"""
def write(self, buf: bytes) -> int:
"""
Write data to UART.
Args:
buf: Data to write
Returns:
int: Number of bytes written
"""import board
import busio
import time
# Initialize I2C bus
i2c = busio.I2C(board.SCL, board.SDA)
# Scan for devices
with i2c:
devices = i2c.scan()
print(f"I2C devices found: {[hex(d) for d in devices]}")
# Communicate with device at address 0x48
device_addr = 0x48
if device_addr in devices:
with i2c:
# Write register address
i2c.writeto(device_addr, bytes([0x00]))
# Read response
buffer = bytearray(2)
i2c.readfrom_into(device_addr, buffer)
print(f"Device response: {buffer}")
# Combined write-then-read operation
with i2c:
result = bytearray(2)
i2c.writeto_then_readfrom(device_addr, bytes([0x01]), result)
print(f"Register 0x01 value: {result}")
# Cleanup
i2c.deinit()import board
import busio
import digitalio
# Initialize SPI bus
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
# Setup chip select pin
cs = digitalio.DigitalInOut(board.D5)
cs.direction = digitalio.Direction.OUTPUT
cs.value = True
# Configure and use SPI
while not spi.try_lock():
pass
try:
spi.configure(baudrate=1000000, polarity=0, phase=0)
# Write data
cs.value = False
spi.write(bytes([0x01, 0x02, 0x03]))
cs.value = True
time.sleep(0.001)
# Read data
cs.value = False
result = bytearray(3)
spi.readinto(result)
cs.value = True
print(f"SPI response: {result}")
finally:
spi.unlock()
# Cleanup
spi.deinit()
cs.deinit()# Note: UART not supported on Linux - use pyserial library instead
import board
import busio
try:
# Initialize UART (MicroPython/embedded platforms only)
uart = busio.UART(board.TX, board.RX, baudrate=115200)
# Send data
uart.write(b"Hello, UART!\n")
# Read response
data = uart.read(10)
if data:
print(f"Received: {data}")
# Read line
line = uart.readline()
if line:
print(f"Line: {line}")
# Cleanup
uart.deinit()
except RuntimeError as e:
print(f"UART not supported: {e}")
print("Use pyserial library for serial communication on Linux")import board
import busio
# Using context managers for automatic cleanup
with busio.I2C(board.SCL, board.SDA) as i2c:
devices = i2c.scan()
print(f"Found devices: {devices}")
# Manual locking for SPI
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
# Lock before configuration
if spi.try_lock():
try:
spi.configure(baudrate=500000)
spi.write(b"test data")
finally:
spi.unlock()
else:
print("Could not acquire SPI lock")
spi.deinit()import board
import busio
try:
i2c = busio.I2C(board.SCL, board.SDA)
# Use I2C...
i2c.deinit()
except ValueError as e:
print(f"Hardware not available: {e}")
except RuntimeError as e:
print(f"Platform limitation: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-adafruit-blinka