Tips and Tricks
Community-sourced recipes for common tasks. Many of these originate from questions raised in the issue tracker.
Signed Integer Conversion
smbus2 always returns unsigned integer values, matching the raw bytes from the bus.
To interpret a value as a signed integer, use Python’s ctypes
(#11):
from ctypes import c_int8, c_uint8, c_int16, c_uint16
from smbus2 import SMBus
with SMBus(1) as bus:
# Read an unsigned byte and convert to signed
unsigned_byte = bus.read_byte_data(0x50, 0x00) # e.g. 200
signed_byte = c_int8(unsigned_byte).value # → -56
# Convert a signed Python integer to the unsigned byte value sent on the wire
signed_value = -123
bus.write_byte_data(0x50, 0x00, c_uint8(signed_value).value) # writes 133
# Same pattern for 16-bit words
unsigned_word = bus.read_word_data(0x50, 0x02)
signed_word = c_int16(unsigned_word).value
Endianness / Byte-Order Conversion
read_word_data returns a 16-bit integer assembled by the Linux kernel following the
SMBus convention (low byte first). If your device sends the high byte first (big-endian),
swap the bytes manually
(#86):
# Bit-manipulation swap
value = bus.read_word_data(0x50, 0x00)
swapped = ((value & 0xFF) << 8) | ((value >> 8) & 0xFF)
# Alternatively, use the struct module
import struct
raw = bus.read_word_data(0x50, 0x00)
(big_endian_value,) = struct.unpack('>H', struct.pack('<H', raw))
Reading Devices That Have No Register Address
Devices like some ADCs, sensors, and actuators do not use the SMBus register-addressing
convention. The *_data family of functions always prepend a register/offset write,
which confuses these devices or triggers an error
(#19,
#84,
#117).
Use i2c_rdwr with a bare i2c_msg instead:
from smbus2 import SMBus, i2c_msg
with SMBus(1) as bus:
msg = i2c_msg.read(0x40, 2) # read 2 bytes — no register byte sent
bus.i2c_rdwr(msg)
data = list(msg)
print(data)
For a single-byte write without a register:
with SMBus(1) as bus:
bus.write_byte(0x40, 0x8C) # write_byte sends only the data byte
Write-Then-Read (Repeated Start)
Many sensors require you to write a command or register-select byte and then read the
response without a STOP condition (repeated start) in between. Use dual i2c_rdwr
(#25):
from smbus2 import SMBus, i2c_msg
addr = 0x50
command = 0xAC
with SMBus(1) as bus:
write = i2c_msg.write(addr, [command])
read = i2c_msg.read(addr, 6)
bus.i2c_rdwr(write, read) # single ioctl — repeated start between messages
data = list(read)
print(data)
Checking Adapter Capabilities
Use bus.funcs and the I2cFunc IntFlag to query what the adapter supports
(#64):
import smbus2
with smbus2.SMBus(1) as bus:
funcs = bus.funcs
if funcs & smbus2.I2cFunc.I2C:
print("Raw I2C (i2c_rdwr) is supported")
if funcs & smbus2.I2cFunc.ADDR_10BIT:
print("10-bit addressing is supported")
if funcs & smbus2.I2cFunc.SMBUS_PEC:
print("Packet Error Checking (PEC) is supported")
asyncio / Async Support
smbus2 itself is synchronous. For asyncio applications, a community library
wraps smbus2 with async/await support
(#18):
smbus2_asyncio — https://github.com/jabdoa2/smbus2_asyncio (Python 3.4+)
Note that because I2C operations are typically short and infrequent, running them in a
thread pool executor (asyncio.get_event_loop().run_in_executor) is often sufficient
without a dedicated async library:
import asyncio
from smbus2 import SMBus
async def read_sensor():
loop = asyncio.get_event_loop()
with SMBus(1) as bus:
value = await loop.run_in_executor(
None, bus.read_byte_data, 0x50, 0x00
)
return value
Setting I2C Bus Clock Speed (Baud Rate)
smbus2 does not expose a clock-speed setting (#77). The I2C clock is controlled at the kernel/hardware level:
Raspberry Pi — add
dtparam=i2c_arm_baudrate=100000to/boot/config.txt(replacesi2c_armoverlay parameter).Other SBCs — consult the board’s device-tree documentation.
Kernel module parameter — some drivers accept a
baudrateorspeedparameter viamodprobe.
Clock stretching (the slave holding SCL low to pause the master) is also handled entirely by the kernel driver.