pythontwistedi2ctwisted.internet

How to define an i2c address using twisted's SerialPort/Protocol classes


I am trying to access to I2C devices using twisted. Both devices are on the same I2C port /dev/i2c-1 and have different addresses (0x68,0x69). My issue is that I struggle to understand where in my code logic I should specify the I2C address for my devices.

I have an existing working script that takes inputs from different sensors using twisted SerialPort, Protocol and Factory. What the below code is missing, is a way of indicating which I2C address to read the incoming data from. I found a I2cProtocol extension to twisted's Protocol class on GitHub and tried adapting it for my purposes, however the i2c address in that extension is accessed on data received. I wouldn't expect this to work if I had two devices on the same I2C port.

This is a reduced version of my script for simplicity, showing the sections that concern the I2C devices:

from twisted.internet import reactor
from twisted.internet.serialport import SerialPort
from twisted.internet.protocol import Protocol

class IMU_Protocol(Protocol):
#Class to log IMU 
    def constants(self, sensor, I2C_ADDR):
        self.sensor    = sensor
        self.I2C_ADDR  = I2C_ADDR

    def dataReceived(self, data):
        print 'dataReceivedIMU called', time.time()
        #how do I specify which address to take the data from?
        i2c_address, value = data 

imu_numbers    = (1,2)
i2c_addresses  = (0x68,0x69)

sen_log_instances = {} #dictionary of logging instances

for imu_no,imu_addr in zip(imu_numbers,i2c_addresses):
    sen_log_instances['IMU_'+imu_no]= IMU_Protocol()
    sen_log_instances['IMU_'+imu_no].constants(imu_no,imu_addr)
    SerialPort(sen_log_instances['IMU_'+imu_no],'/dev/i2c-1', reactor, baudrate='115200')
    sensor_numbers += ('IMU_'+imu_no,)

fact = EchoClientFactory(sen_log_instances,sensor_numbers, field_name)
reactor.run()

Solution

  • I don't think it would be easy to implement I2C support in twisted context by simulating the mechanism in SerialPort or other out-of-the-box twisted modules.

    The key step to make dataReceived be triggered is to make the reactor aware that an I2C device is there that needs to be watched and as long as some data is ready it should read data from there and delivers to the protocol.

    The reactor as a part of a high level application knows nothing about the low level stuff like serial or I2C, but it knows a lot on how to manipulate a FileDescriptor, which is the way that SerialPort works. So, if you do want your I2C to be workable in twisted you have to do similar things.

    1. Find a Python library that can manipulate I2C in a manner of a file descriptor (unfortunately smbus2 cannot)
    2. Subclass twisted.internet.abstract.FileDescriptor, and re-write the methods like fileno(), doRead(), doWrite(), etc.
    3. Create an instance for each I2C addr upon initialization

    The I2C Protocol Extension code that you mentioned above would not help you a lot, as it is based on a very old version of Twisted where SerialPortEndpoint is still used. The latest version of Twisted supports limited types of endpoints so you'll have to come up with your own.