I2C write command to a sensor device

My sensor device has I2C interface. I need some help in I2C writing and reading back from the same register.
Here is my code.

import board
import busio

# Initialize
i2c = busio.I2C(board.SCL, board.SDA)

# Device Address
Device_Address = 0x40
######################################################
## Writing and Reading Alert Limit Channel-1 
######################################################

# Alert Limit Channel-1 REG 
ALERT_LIMIT_CH1_REG = 0x07

# Wait until the I2C bus is locked
while not i2c.try_lock():
    pass
try:

# Write to the device specific register to write.
    i2c.writeto(Device_Address, bytes([ALERT_LIMIT_CH1_REG]))

# Create a bytearray buffer of two bytes to store the data.
    in_buf = bytearray(2)
    out_buf = bytearray(2)

# Initialize in_buf   
    in_buf[0] = 0xEE
    in_buf[1] = 0xFF

# Print the in buffer data.
    print("The Alert Limit CH 1 will be set to = 0x", in_buf.hex())

    i2c.writeto_then_readfrom(Device_Address, out_buf, in_buf, out_start=0, out_end=None, in_start=0, in_end=None)
    
# Print the out buffer data.
    print("The Alert Limit CH 1 is set to = 0x", out_buf.hex())

# Unlock the bus 
finally:
    i2c.unlock()

I basically want to perform I2C write operation and read back from same register to confirm the data is written correctly.
The sequence will be first send device address, then register address within the device, then actual data that I would like to write to the resister. I guess there is something wrong in my code. Please have a look and let me know the issue.

You have a try and a finally in your script implying that you want exception handling. What you are missing is the except keyword and the potential action to take in the event of an error/exception occuring.

A good way to test your script to is to force an error. In the traceback message, look for the name of the exception that was generated. Include that name as the exception in your script. It is better to be specific so that you may provide a taylored solution and or error message as opposed to the generic Exception keyword which handles all errors / exceptions. Note that you may use multiple types.

try:
    # do what you want here

except specific_exception_here:
    # Action to take in case of an error / exception

finally:
    # Mandatory action to execute

Here is a handy tutorial that may be useful:

The OP doesn’t necessarily want exception handling, but just guarantee that i2c will be unlocked, even if an exception happens to occur.

There’s a method to write to the device and a method to read from the device. There isn’t a method to do one and then the other, so just do one and then the other and compare what was read to what was written.

As a sanity check, can you include a print statement in the while loop body to verify it is being locked. Also include a print statementn immediately after the loop. This way you know if the error is a locking error or not.

I think you need to read the manual for the sensor chip, to see how it actually interacts with those registers. On one of the chips I work with, writes are made to Output Latches, and there’s some small real world delta t before those values’re readable in the actual GPIO Port register.

I know you’d have to rework your whole code. But I tried a few different options for I2C recently, and found smbus worked the best.

1 Like

What makes you say that? What behavior do you expect, and what do you see instead?

Based on the device address (0x40) and ALERT_LIMIT_CH1_REG = 0x07, I’m guessing you’re working with a TI-INA3221. From the datasheet, section 7.5.2 “Writing To and Reading From the INA3221“:

Register writes begin with the first byte transmitted by the controller. This byte is the target address, with the R/ W bit low. The INA3221 then acknowledges receipt of a valid address. The next byte transmitted by the controller is the register address that data are written to. This register address value updates the register pointer to the desired register. The next two bytes are written to the register addressed by the register pointer. The INA3221 acknowledges receipt of each data byte. The controller terminates data transfer by generating a start or stop condition.

I don’t recall the specifics of busio.i2c off the top of my head, but what you’re doing appears slightly different from what the datasheet says to do.

You first set the register pointer via a writeto operation:

i2c.writeto(Device_Address, bytes([ALERT_LIMIT_CH1_REG]))

but you do not follow up with data. At this point, you are already in undefined behavior territory.

Then you do a writeto_then_readfrom operation:

i2c.writeto_then_readfrom(Device_Address, out_buf, in_buf, out_start=0, out_end=None, in_start=0, in_end=None)

but this time without specifying the register address (sincebusio.i2c.writeto_then_readfrom does not support specifying a register address in this operation). The INA3221 likely does not consider this a valid command format.

There is a circuitpython driver for the INA3221: GitHub - adafruit/Adafruit_CircuitPython_INA3221: CircuitPython driver for the INA3221 Triple 0-26 VDC, ±3.2 Amp Power Monitor · GitHub

Try and see if it works for you. You can also check the source code to understand how to properly talk to the device.

1 Like

In fact I am interested to learn and practice I2C read and write commands.

I manage with I2C write command.

Now I am just using this command for I2C write and read back to confirm the data is written correctly.

i2c.writeto_then_readfrom(DEVICE_ADDRESS, write_buffer, read_buffer)

The write_buffer is a 3 byte array. I put register address, byte 0 and byte 1 to write in the write_buffer.

The return is read_buffer which is a 2 byte array. I get the same value byte 0 and byte 1 in read_buffer that confirm the write operation.

Would you please suggest I2C command that I can run and test ?