使用smbus替代i2ctransfer发送字节数组至I2C设备遇IOError求助
问题根源分析
你遇到的IOError: [Errno 121] Remote I/O error,本质是因为smbus.write_i2c_block_data的行为和你原来用i2ctransfer的纯字节写逻辑完全不匹配:
- 你的
i2ctransfer命令w12@0x03是直接向设备0x03发送12字节原始数据,没有任何额外的前置字节; - 但
write_i2c_block_data(device_address, 0, data)会按照SMBus协议的规则,先发送一个寄存器地址字节(你传的0x00),再发送数据块的长度(0x0C,对应12字节),最后才是你要发送的12字节数据。设备实际收到的是14字节(0x00 + 0x0C + 12个数据字节),和它预期的指令完全不符,所以返回了I/O错误。
解决方案
针对你的需求(发送无寄存器地址的纯字节流),这里提供两种可靠的实现方式:
方案1:改用smbus2库(推荐,更灵活易用)
smbus2是smbus模块的增强版,支持构造原始I2C消息,完美匹配i2ctransfer的行为。先安装库:
pip install smbus2
然后用以下代码实现:
from smbus2 import SMBus, i2c_msg # 初始化I2C总线(对应你原来的i2cbus=1) bus = SMBus(1) device_address = 0x03 # 你要发送的12字节数据 data = [0x23, 0x04, 0x49, 0x74, 0x24, 0x00, 0x23, 0x05, 0x49, 0x74, 0x24, 0x00] # 构造纯写操作的I2C消息:指定设备地址和要发送的字节数组 write_msg = i2c_msg.write(device_address, data) # 执行消息发送 bus.i2c_rdwr(write_msg) # 记得用完后关闭总线(可选,程序退出时会自动释放) bus.close()
这段代码会直接把12字节数据发送给设备0x03,和你原来的i2ctransfer命令逻辑完全一致。
方案2:直接操作I2C设备文件(无需额外库)
如果无法安装第三方库,你可以直接通过Linux的/dev/i2c-*设备文件发送数据,这和i2ctransfer的底层实现逻辑一致:
import os # 对应你的i2cbus=1 device_path = "/dev/i2c-1" device_address = 0x03 # 把数据转为字节对象 data = bytes([0x23, 0x04, 0x49, 0x74, 0x24, 0x00, 0x23, 0x05, 0x49, 0x74, 0x24, 0x00]) # 打开I2C设备文件(需要root权限或对应设备权限) fd = os.open(device_path, os.O_RDWR) try: # 设置要通信的目标设备地址(I2C_SLAVE的ioctl命令码是0x0703) os.ioctl(fd, 0x0703, device_address) # 发送数据 os.write(fd, data) finally: # 确保关闭文件描述符 os.close(fd)
注意:这段代码需要程序拥有访问/dev/i2c-1的权限,通常需要以root用户运行,或者给设备文件添加对应权限(比如chmod 666 /dev/i2c-1)。
为什么不用内置smbus的其他方法?
内置的smbus模块所有写操作方法(比如write_bytes、write_block_data)都是基于SMBus协议设计的,强制要求先发送寄存器地址(command byte),无法直接发送纯字节流,所以无法满足你的需求。
内容的提问来源于stack exchange,提问作者user2261062
相关产品推荐
相关产品推荐

