Pure Python FTDI device driver for USB-to-serial/GPIO/SPI/I2C/JTAG bridge devices
—
I2C master controller implementation for communication with I2C devices. Supports standard and fast mode speeds, device addressing, register access, bus scanning, and simultaneous GPIO control for additional device management.
Configure and manage I2C controller with device detection and bus management.
class I2cController:
def configure(self, url, **kwargs):
"""
Configure I2C controller with FTDI device.
Parameters:
- url: FTDI device URL (e.g., 'ftdi:///1')
- frequency: I2C clock frequency in Hz (default: 100kHz)
- clockstretching: Enable clock stretching support
- debug: Enable debug output
Raises:
- I2cIOError: Configuration failed
- FtdiError: Device access error
"""
def terminate(self):
"""Terminate I2C controller and release device."""
def get_frequency(self) -> float:
"""Get actual configured I2C frequency."""
def get_gpio(self):
"""
Get GPIO controller for unused pins.
Returns:
GpioMpsseController: GPIO controller for remaining pins
"""Get I2C port instances for communication with specific device addresses.
def get_port(self, address: int) -> 'I2cPort':
"""
Get I2C port for specific device address.
Parameters:
- address: 7-bit I2C device address (0x08-0x77)
Returns:
I2cPort: Port instance for device communication
Raises:
- I2cIOError: Invalid address or device not responding
"""Scan and manage I2C bus with device detection.
def scan(self, start: int = 0x08, end: int = 0x77) -> list:
"""
Scan I2C bus for responding devices.
Parameters:
- start: Starting address to scan
- end: Ending address to scan
Returns:
list: List of responding device addresses
"""
def detect_devices(self) -> dict:
"""
Detect and identify I2C devices on bus.
Returns:
dict: Device addresses with identification info
"""Perform I2C transactions with proper start/stop conditions and ACK handling.
class I2cPort:
def read(self, readlen: int, relax: bool = True) -> bytes:
"""
Read data from I2C device.
Parameters:
- readlen: Number of bytes to read
- relax: Use relaxed timing for slow devices
Returns:
bytes: Data read from device
Raises:
- I2cNackError: Device did not acknowledge
- I2cTimeoutError: Operation timed out
"""
def write(self, out: bytes, relax: bool = True):
"""
Write data to I2C device.
Parameters:
- out: Data bytes to write
- relax: Use relaxed timing for slow devices
Raises:
- I2cNackError: Device did not acknowledge
- I2cTimeoutError: Operation timed out
"""
def exchange(self, out: bytes, readlen: int, relax: bool = True) -> bytes:
"""
Write data then read response in single transaction.
Parameters:
- out: Data bytes to write
- readlen: Number of bytes to read
- relax: Use relaxed timing
Returns:
bytes: Data read from device
"""Convenient methods for I2C register-based device communication.
def read_from(self, regaddr: int, readlen: int, relax: bool = True) -> bytes:
"""
Read from device register.
Parameters:
- regaddr: Register address (8-bit or 16-bit)
- readlen: Number of bytes to read
- relax: Use relaxed timing
Returns:
bytes: Register data
"""
def write_to(self, regaddr: int, out: bytes, relax: bool = True):
"""
Write to device register.
Parameters:
- regaddr: Register address (8-bit or 16-bit)
- out: Data to write to register
- relax: Use relaxed timing
"""
def read_from_word(self, regaddr: int, wordsize: int = 2,
bigendian: bool = True, relax: bool = True) -> int:
"""
Read word (16/32-bit) from register with endianness handling.
Parameters:
- regaddr: Register address
- wordsize: Word size in bytes (2 or 4)
- bigendian: True for big-endian, False for little-endian
- relax: Use relaxed timing
Returns:
int: Word value
"""
def write_to_word(self, regaddr: int, value: int, wordsize: int = 2,
bigendian: bool = True, relax: bool = True):
"""
Write word (16/32-bit) to register with endianness handling.
Parameters:
- regaddr: Register address
- value: Word value to write
- wordsize: Word size in bytes (2 or 4)
- bigendian: True for big-endian, False for little-endian
- relax: Use relaxed timing
"""I2C port with additional GPIO control for device management.
class I2cGpioPort(I2cPort):
"""I2C port with GPIO capabilities for control signals."""
def set_gpio_direction(self, pins: int, direction: int):
"""Set GPIO pin directions (0=input, 1=output)."""
def read_gpio(self) -> int:
"""Read current GPIO pin states."""
def write_gpio(self, value: int):
"""Write GPIO pin states."""
def exchange_gpio(self, out: bytes, readlen: int, gpio_dir: int,
gpio_out: int) -> tuple:
"""
Perform I2C exchange with simultaneous GPIO control.
Returns:
tuple: (i2c_data, gpio_state)
"""Standard FTDI I2C pin assignments:
FT232H:
FT2232H/FT4232H:
Supported I2C frequencies:
from pyftdi.i2c import I2cController
# Configure I2C controller
i2c = I2cController()
i2c.configure('ftdi:///1')
# Connect to device at address 0x48
device = i2c.get_port(0x48)
# Simple read/write
device.write([0x01, 0xFF]) # Write 0xFF to register 0x01
data = device.read_from(0x01, 1) # Read back from register 0x01
print(f"Register value: 0x{data[0]:02x}")
# Clean up
i2c.terminate()from pyftdi.i2c import I2cController
i2c = I2cController()
i2c.configure('ftdi:///1')
# Scan for devices
devices = i2c.scan()
print(f"Found devices at addresses: {[hex(addr) for addr in devices]}")
# Connect to first found device
if devices:
device = i2c.get_port(devices[0])
# ... communicate with device
i2c.terminate()from pyftdi.i2c import I2cController
i2c = I2cController()
i2c.configure('ftdi:///1', frequency=400000) # Fast mode
# Temperature sensor example (TMP102)
temp_sensor = i2c.get_port(0x48)
# Read temperature register (16-bit, big-endian)
temp_raw = temp_sensor.read_from_word(0x00, wordsize=2, bigendian=True)
temperature = (temp_raw >> 4) * 0.0625 # Convert to Celsius
print(f"Temperature: {temperature:.2f}°C")
# Configure sensor
config = (1 << 15) | (1 << 10) # One-shot mode, 12-bit resolution
temp_sensor.write_to_word(0x01, config, wordsize=2, bigendian=True)
i2c.terminate()from pyftdi.i2c import I2cController
import time
i2c = I2cController()
i2c.configure('ftdi:///1')
# 24C32 EEPROM at address 0x50
eeprom = i2c.get_port(0x50)
# Write data to address 0x0100 (16-bit addressing)
write_addr = 0x0100
data_to_write = b"Hello, I2C!"
# Write with 16-bit address
eeprom.write([(write_addr >> 8) & 0xFF, write_addr & 0xFF] + list(data_to_write))
# Wait for write cycle to complete
time.sleep(0.01)
# Read data back
eeprom.write([(write_addr >> 8) & 0xFF, write_addr & 0xFF]) # Set address
read_data = eeprom.read(len(data_to_write))
print(f"Read: {read_data.decode()}")
i2c.terminate()from pyftdi.i2c import I2cController
i2c = I2cController()
i2c.configure('ftdi:///1')
# Multiple devices on same bus
rtc = i2c.get_port(0x68) # Real-time clock
temp = i2c.get_port(0x48) # Temperature sensor
eeprom = i2c.get_port(0x50) # EEPROM
# Read from each device
current_time = rtc.read_from(0x00, 7) # RTC time registers
temperature = temp.read_from_word(0x00) # Temperature
eeprom_data = eeprom.read_from(0x00, 16) # First 16 bytes
print(f"RTC: {current_time.hex()}")
print(f"Temp: {temperature}")
print(f"EEPROM: {eeprom_data.hex()}")
i2c.terminate()from pyftdi.i2c import I2cController
i2c = I2cController()
i2c.configure('ftdi:///1')
device = i2c.get_port(0x48)
gpio = i2c.get_gpio()
# Use GPIO for device control
gpio.set_direction(0xF0, 0xF0) # Upper 4 bits as outputs
# Power on device
gpio.write(0x10) # Enable power
time.sleep(0.1)
# Configure device via I2C
device.write_to(0x01, [0x80]) # Configuration register
# Read data with status LED
gpio.write(0x30) # Turn on LED
data = device.read_from(0x00, 4)
gpio.write(0x10) # Turn off LED
i2c.terminate()from pyftdi.i2c import I2cController, I2cNackError, I2cTimeoutError, I2cIOError
from pyftdi.ftdi import FtdiError
try:
i2c = I2cController()
i2c.configure('ftdi:///1')
device = i2c.get_port(0x48)
data = device.read_from(0x00, 2)
except I2cNackError as e:
print(f"Device not responding: {e}")
except I2cTimeoutError as e:
print(f"I2C timeout: {e}")
except I2cIOError as e:
print(f"I2C communication error: {e}")
except FtdiError as e:
print(f"FTDI device error: {e}")
finally:
if 'i2c' in locals():
i2c.terminate()# Exception types
class I2cIOError(IOError):
"""I2C communication error"""
class I2cNackError(I2cIOError):
"""I2C device not acknowledged (NACK)"""
class I2cTimeoutError(TimeoutError):
"""I2C operation timeout"""
# I2C speed constants
I2C_STANDARD_MODE = 100000 # 100 kHz
I2C_FAST_MODE = 400000 # 400 kHz
I2C_FAST_MODE_PLUS = 1000000 # 1 MHzInstall with Tessl CLI
npx tessl i tessl/pypi-pyftdi