A drop-in replacement for smbus-cffi/smbus-python in pure Python
npx @tessl/cli install tessl/pypi-smbus2@0.5.0A drop-in replacement for smbus-cffi/smbus-python in pure Python. smbus2 provides a complete implementation of the SMBus (System Management Bus) protocol for I2C communication, enabling comprehensive device interaction for embedded systems, IoT devices, and hardware interfacing applications.
pip install smbus2from smbus2 import SMBus, i2c_msg, I2cFuncIndividual component import:
from smbus2 import SMBus # Main SMBus interface
from smbus2 import i2c_msg # I2C message class for combined transactions
from smbus2 import I2cFunc # I2C functionality flags enumfrom smbus2 import SMBus
# Basic usage with context manager (recommended)
with SMBus(1) as bus:
# Read a byte from address 0x50, register 0x00
data = bus.read_byte_data(0x50, 0x00)
print(f"Read value: {data}")
# Write a byte to address 0x50, register 0x00
bus.write_byte_data(0x50, 0x00, 0xFF)
# Manual connection management
bus = SMBus(1) # Open I2C bus 1
try:
# Read word data from register
word_data = bus.read_word_data(0x50, 0x10)
print(f"Word data: {word_data}")
finally:
bus.close() # Always close the connection
# Enable Packet Error Checking (PEC)
with SMBus(1) as bus:
bus.pec = 1 # Enable PEC
data = bus.read_byte_data(0x50, 0x00)smbus2 is built on a layered architecture:
The library supports both traditional SMBus operations (limited to 32 bytes) and advanced I2C transactions (unlimited size) through the i2c_rdwr interface, making it suitable for both simple sensor communication and complex data transfer scenarios.
SMBus connection initialization, opening, and closing with support for context managers and manual resource management.
class SMBus:
def __init__(self, bus=None, force=False):
"""
Initialize SMBus instance.
Parameters:
- bus: int or str, I2C bus number (e.g. 1) or device path (e.g. '/dev/i2c-1')
- force: bool, force using slave address even when driver is already using it
"""
def __enter__(self):
"""Context manager entry, returns self."""
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit, closes connection."""
def open(self, bus):
"""
Open I2C bus connection.
Parameters:
- bus: int or str, I2C bus number or device path
Raises:
- TypeError: if bus type is not int or str
"""
def close(self):
"""Close I2C connection."""Fundamental SMBus operations including quick commands and single byte read/write operations.
def write_quick(self, i2c_addr, force=None):
"""
Perform SMBus Quick Command.
Parameters:
- i2c_addr: int, I2C slave address
- force: bool, optional override for force flag
Raises:
- IOError: if operation fails
"""
def read_byte(self, i2c_addr, force=None):
"""
Read single byte from I2C device.
Parameters:
- i2c_addr: int, I2C slave address
- force: bool, optional override for force flag
Returns:
- int: byte value (0-255)
"""
def write_byte(self, i2c_addr, value, force=None):
"""
Write single byte to I2C device.
Parameters:
- i2c_addr: int, I2C slave address
- value: int, byte value to write (0-255)
- force: bool, optional override for force flag
"""SMBus byte and word data operations for reading from and writing to specific device registers.
def read_byte_data(self, i2c_addr, register, force=None):
"""
Read byte from specified register.
Parameters:
- i2c_addr: int, I2C slave address
- register: int, register address (0-255)
- force: bool, optional override for force flag
Returns:
- int: byte value from register
"""
def write_byte_data(self, i2c_addr, register, value, force=None):
"""
Write byte to specified register.
Parameters:
- i2c_addr: int, I2C slave address
- register: int, register address (0-255)
- value: int, byte value to write (0-255)
- force: bool, optional override for force flag
"""
def read_word_data(self, i2c_addr, register, force=None):
"""
Read 16-bit word from specified register.
Parameters:
- i2c_addr: int, I2C slave address
- register: int, register address (0-255)
- force: bool, optional override for force flag
Returns:
- int: 16-bit word value from register
"""
def write_word_data(self, i2c_addr, register, value, force=None):
"""
Write 16-bit word to specified register.
Parameters:
- i2c_addr: int, I2C slave address
- register: int, register address (0-255)
- value: int, 16-bit word value to write (0-65535)
- force: bool, optional override for force flag
"""SMBus Process Call operations for atomic write-then-read transactions.
def process_call(self, i2c_addr, register, value, force=None):
"""
Execute SMBus Process Call (write 16-bit value, read 16-bit response).
Parameters:
- i2c_addr: int, I2C slave address
- register: int, register address
- value: int, 16-bit value to send
- force: bool, optional override for force flag
Returns:
- int: 16-bit response value
"""
def block_process_call(self, i2c_addr, register, data, force=None):
"""
Execute SMBus Block Process Call (write block, read block response).
Parameters:
- i2c_addr: int, I2C slave address
- register: int, register address
- data: list of int, data bytes to send (max 32 bytes)
- force: bool, optional override for force flag
Returns:
- list of int: response data bytes
Raises:
- ValueError: if data length exceeds 32 bytes
"""SMBus block data operations for reading and writing multiple bytes in a single transaction.
def read_block_data(self, i2c_addr, register, force=None):
"""
Read block of data from register (up to 32 bytes).
Parameters:
- i2c_addr: int, I2C slave address
- register: int, start register address
- force: bool, optional override for force flag
Returns:
- list of int: data bytes read
"""
def write_block_data(self, i2c_addr, register, data, force=None):
"""
Write block of data to register (up to 32 bytes).
Parameters:
- i2c_addr: int, I2C slave address
- register: int, start register address
- data: list of int, data bytes to write (max 32 bytes)
- force: bool, optional override for force flag
Raises:
- ValueError: if data length exceeds 32 bytes
"""
def read_i2c_block_data(self, i2c_addr, register, length, force=None):
"""
Read I2C block data with specified length.
Parameters:
- i2c_addr: int, I2C slave address
- register: int, start register address
- length: int, number of bytes to read (max 32)
- force: bool, optional override for force flag
Returns:
- list of int: data bytes read
Raises:
- ValueError: if length exceeds 32 bytes
"""
def write_i2c_block_data(self, i2c_addr, register, data, force=None):
"""
Write I2C block data.
Parameters:
- i2c_addr: int, I2C slave address
- register: int, start register address
- data: list of int, data bytes to write (max 32 bytes)
- force: bool, optional override for force flag
Raises:
- ValueError: if data length exceeds 32 bytes
"""Combined I2C read/write operations using i2c_msg for transactions beyond SMBus limitations.
def i2c_rdwr(self, *i2c_msgs):
"""
Execute combined I2C read/write operations with repeated start.
Parameters:
- i2c_msgs: i2c_msg instances created with i2c_msg.read() or i2c_msg.write()
Note: Allows bulk transfers beyond SMBus 32-byte limit
"""
class i2c_msg:
"""I2C message for combined transactions."""
@staticmethod
def read(address, length):
"""
Create I2C read message.
Parameters:
- address: int, I2C slave address
- length: int, number of bytes to read
Returns:
- i2c_msg: configured for read operation
"""
@staticmethod
def write(address, buf):
"""
Create I2C write message.
Parameters:
- address: int, I2C slave address
- buf: list of int or str, data to write
Returns:
- i2c_msg: configured for write operation
"""
def __iter__(self):
"""Iterate over message data bytes."""
def __len__(self):
"""Get message length."""
def __bytes__(self):
"""Get message data as bytes."""
def __repr__(self):
"""String representation of message."""
def __str__(self):
"""Decoded string representation of message data."""
# Properties
addr: int # I2C slave address
flags: int # Message flags (e.g., I2C_M_RD for read)
len: int # Message length in bytes
buf: pointer # Message data bufferDevice capability detection and configuration management including Packet Error Checking.
def enable_pec(self, enable=True):
"""
Enable or disable Packet Error Checking (SMBus 1.1+).
Parameters:
- enable: bool, True to enable PEC, False to disable
Raises:
- IOError: if device doesn't support PEC
"""
# Properties
pec: int # PEC setting (0=disabled, 1=enabled)
funcs: I2cFunc # Device capability flags
fd: int # File descriptor for I2C device
address: int # Current I2C slave address
force: bool # Force flag setting
class I2cFunc:
"""IntFlag enum for I2C/SMBus capability flags."""
# Basic I2C capabilities
I2C = 0x00000001
ADDR_10BIT = 0x00000002
PROTOCOL_MANGLING = 0x00000004
SMBUS_PEC = 0x00000008
NOSTART = 0x00000010
SLAVE = 0x00000020
# SMBus operation capabilities
SMBUS_QUICK = 0x00010000
SMBUS_READ_BYTE = 0x00020000
SMBUS_WRITE_BYTE = 0x00040000
SMBUS_READ_BYTE_DATA = 0x00080000
SMBUS_WRITE_BYTE_DATA = 0x00100000
SMBUS_READ_WORD_DATA = 0x00200000
SMBUS_WRITE_WORD_DATA = 0x00400000
SMBUS_PROC_CALL = 0x00800000
SMBUS_READ_BLOCK_DATA = 0x01000000
SMBUS_WRITE_BLOCK_DATA = 0x02000000
SMBUS_READ_I2C_BLOCK = 0x04000000
SMBUS_WRITE_I2C_BLOCK = 0x08000000
SMBUS_BLOCK_PROC_CALL = 0x00008000
SMBUS_HOST_NOTIFY = 0x10000000
# Combined capability flags
SMBUS_BYTE = 0x00060000
SMBUS_BYTE_DATA = 0x00180000
SMBUS_WORD_DATA = 0x00600000
SMBUS_BLOCK_DATA = 0x03000000
SMBUS_I2C_BLOCK = 0x0c000000
SMBUS_EMUL = 0x0eff0008from smbus2 import SMBus
# Read temperature from sensor at address 0x48
with SMBus(1) as bus:
# Read 2-byte temperature value
temp_raw = bus.read_word_data(0x48, 0x00)
# Convert to Celsius (example conversion for specific sensor)
temperature = (temp_raw >> 8) * 0.5
print(f"Temperature: {temperature}°C")from smbus2 import SMBus
# Read configuration block from EEPROM
with SMBus(1) as bus:
# Read 16 bytes starting from address 0x00
config_data = bus.read_i2c_block_data(0x50, 0x00, 16)
print(f"Config: {[hex(b) for b in config_data]}")
# Write new configuration
new_config = [0x01, 0x02, 0x03, 0x04, 0x05]
bus.write_i2c_block_data(0x50, 0x00, new_config)from smbus2 import SMBus, i2c_msg
# Advanced I2C operations beyond SMBus limits
with SMBus(1) as bus:
# Write command and read large response in single transaction
write_cmd = i2c_msg.write(0x40, [0x01, 0x02])
read_response = i2c_msg.read(0x40, 64) # Read 64 bytes
bus.i2c_rdwr(write_cmd, read_response)
# Access response data
response_data = list(read_response)
print(f"Response: {response_data}")from smbus2 import SMBus, I2cFunc
with SMBus(1) as bus:
# Check device capabilities
if bus.funcs & I2cFunc.SMBUS_PEC:
print("Device supports Packet Error Checking")
bus.pec = 1 # Enable PEC
if bus.funcs & I2cFunc.SMBUS_BLOCK_DATA:
print("Device supports SMBus block operations")
if bus.funcs & I2cFunc.I2C:
print("Device supports I2C operations")# I2C device control constants
I2C_SLAVE = 0x0703 # Use this slave address
I2C_SLAVE_FORCE = 0x0706 # Use this slave address, even if already in use by a driver
I2C_FUNCS = 0x0705 # Get the adapter functionality mask
I2C_RDWR = 0x0707 # Combined R/W transfer (one STOP only)
I2C_SMBUS = 0x0720 # SMBus transfer. Takes pointer to i2c_smbus_ioctl_data
I2C_PEC = 0x0708 # != 0 to use PEC with SMBus
# SMBus transfer direction markers
I2C_SMBUS_WRITE = 0
I2C_SMBUS_READ = 1
# SMBus operation size identifiers
I2C_SMBUS_QUICK = 0
I2C_SMBUS_BYTE = 1
I2C_SMBUS_BYTE_DATA = 2
I2C_SMBUS_WORD_DATA = 3
I2C_SMBUS_PROC_CALL = 4
I2C_SMBUS_BLOCK_DATA = 5 # Not supported by Pure-I2C drivers with SMBUS emulation
I2C_SMBUS_BLOCK_PROC_CALL = 7 # Not supported by Pure-I2C drivers either
I2C_SMBUS_I2C_BLOCK_DATA = 8
I2C_SMBUS_BLOCK_MAX = 32
# I2C message flags
I2C_M_RD = 0x0001Common exceptions that may be raised:
Specific error conditions:
ValueError if data length > 32 bytesenable_pec() raises IOError if device doesn't support SMBUS_PEC functionalityopen() raises TypeError if bus parameter is not int or strAlways use try-except blocks for robust error handling:
from smbus2 import SMBus, I2cFunc
try:
with SMBus(1) as bus:
# Check PEC support before enabling
if bus.funcs & I2cFunc.SMBUS_PEC:
bus.pec = 1
data = bus.read_byte_data(0x50, 0x00)
except IOError as e:
print(f"I2C communication error: {e}")
except ValueError as e:
print(f"Invalid parameter: {e}")
except TypeError as e:
print(f"Invalid type: {e}")