Tic I2C get_variables always returns 255 (-1) in Python

I am attempting to control & monitor two stepper motors using two tic T500 drivers set up for I2C communication in Python with a Raspberry Pi. Controlling the motors works flawlessly, I can energize, set position, home, de-energize, etc… with no issues. However, I cannot seem to use any of the read-type I2C commands.

Specifically, the get_variables command will always return a list of [255, 255, …] depending on the specified length. This is in reference to the example program in section 12.9 of the tic handbook, shown below.

I heeded the warnings about using I2C on Raspberry Pi and followed the instructions in 12.8 to provide stable access to the i2c-gpio overlay. For my device, /dev/i2c-1 shows the connected tics with proper device numbers, not the usual /dev/i2c-3 mentioned in the user guide.

Running the demo program below doesn’t throw any errors, and indeed drives the motor with tic device number 14, but the printed position is always -1. After some investigating with get_variables, it appears to always return a list of 255s, which the get_current_position function translates to a position of -1.

I’m not an expert in I2C communications or linux here, so any help or insight is appreciated.

# Uses the smbus2 library to send and receive data from a Tic.
# Works on Linux with either Python 2 or Python 3.
#
# NOTE: The Tic's control mode must be "Serial / I2C / USB".
# NOTE: For reliable operation on a Raspberry Pi, enable the i2c-gpio
# overlay and use the I2C device it provides (usually /dev/i2c-3).
# NOTE: You might nee to change the 'SMBus(3)' line below to specify the
# correct I2C device.
# NOTE: You might need to change the 'address = 11' line below to match
# the device number of your Tic.
from smbus2 import SMBus, i2c_msg
class TicI2C(object):
def __init__(self, bus, address):
self.bus = bus
self.address = address
# Sends the "Exit safe start" command.
def exit_safe_start(self):
command = [0x83]
write = i2c_msg.write(self.address, command)
self.bus.i2c_rdwr(write)
# Sets the target position.
#
# For more information about what this command does, see the
# "Set target position" command in the "Command reference" section of the
# Tic user's guide.
def set_target_position(self, target):
command = [0xE0,
target >> 0 & 0xFF,
target >> 8 & 0xFF,
target >> 16 & 0xFF,
target >> 24 & 0xFF]
write = i2c_msg.write(self.address, command)
self.bus.i2c_rdwr(write)
# Gets one or more variables from the Tic.
def get_variables(self, offset, length):
write = i2c_msg.write(self.address, [0xA1, offset])
read = i2c_msg.read(self.address, length)
self.bus.i2c_rdwr(write, read)
return list(read)
# Gets the "Current position" variable from the Tic.
def get_current_position(self):
b = self.get_variables(0x22, 4)
position = b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24)
if position >= (1 << 31):
position -= (1 << 32)
return position
# Open a handle to "/dev/i2c-3", representing the I2C bus.
bus = SMBus(1)
# Select the I2C address of the Tic (the device number).
address = 14
tic = TicI2C(bus, address)
position = tic.get_current_position()

print("Current position is {}.".format(position))
new_target = -200 if position > 0 else 200
print("Setting target position to {}.".format(new_target));
tic.exit_safe_start()
tic.set_target_position(new_target)

Hello.

Could you post some pictures of your setup that show all of your connections?

By the way, the operation of Python code depends on indentation and it looks like you posted your code in a way that broke all the indentation. However, it looks like it might just be our code from the “Example I²C code for Linux in Python” section of the Tic user’s guide, but with SMBus(3) changed to SMBus(1), which should be fine.

Brandon

Thanks for the reply Brandon. Below are photos of the jumpers connected to the SDA & SCL pins on both the RPI & the T500. Each wire is about 16in long.

I don’t think it’s a wiring/physical connection issue though, as the motor commands work consistently over the same connection. My only issues occur attempting to read variables from the tic.

And yes, sorry the indenting got messed up, but the code is a direct copy from the user guide’s example.

(Yellow & purple jumpers)

(Other lines are configured limit switches, which work with no issue)

After some more searching, I was able to solve the issue with a simple code change! It looks like an identical issue was reported here a few years ago and their solution worked for me too.

It wasn’t a hardware connection or RPI issue, it was the example code from the Tic user guide! The get_variables function attempts to send the write & read commands at the same time and thus always reads back high values. The single line, self.bus.i2c_rdwr(write, read) needs to instead be separated into two calls as shown below.

def get_variables(self, offset, length):
    write = i2c_msg.write(self.address, [0xA1, offset])
    read = i2c_msg.read(self.address, length)
    self.bus.i2c_rdwr(write)
    self.bus.i2c_rdwr(read)
    return list(read)

This function now performs as expected and without issue. I’ve always appreciated the great documentation Pololu provides for these drivers, so I’m curious if this is a system-specific bug or if the example code was never tested.

It looks like you are still using the hardware I2C on the Raspberry Pi, which has a bug that causes our example code to not work reliably with it. You can find more about that in the “Example I2C code for Linux in C” section of the Tic user’s guide (which is referenced in the section I linked to in my previous post). We expect our code to work fine if you follow the instructions in the guide.

Brandon

You’re right, I missed the line recommending different GPIO pins to use. Thanks.