CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-adafruit-blinka

CircuitPython APIs for non-CircuitPython versions of Python such as CPython on Linux and MicroPython.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

communication.mddocs/

Communication Protocols

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.

Capabilities

I2C Communication

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
        """

SPI Communication

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
        """

UART Communication

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
        """

Usage Examples

I2C Device Communication

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()

SPI Device Communication

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()

UART Serial Communication

# 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")

Resource Management

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()

Platform Considerations

I2C Support

  • Hardware I2C: Most embedded Linux systems, RP2040, specialized adapters
  • Software I2C: Available via bitbangio for platforms without hardware support
  • Frequency: Linux ignores frequency parameter, uses system defaults

SPI Support

  • Full-duplex: Most platforms support simultaneous read/write operations
  • Pin flexibility: MOSI/MISO pins optional for unidirectional communication
  • Locking required: Must call try_lock() before configure() and operations

UART Limitations

  • Linux: Not supported - use pyserial library instead
  • MicroPython: Full support with hardware flow control limitations
  • Embedded platforms: Variable feature support depending on hardware

Error Handling

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

docs

analog-io.md

bitbangio.md

board-pins.md

communication.md

core-framework.md

digital-io.md

index.md

peripherals.md

pwm-pulse.md

utilities.md

tile.json