Valvy
Valvy

Reputation: 42

python2 to python3 differences with byte additions and sending to serial port

Currently, we are building a robot with a raspberry pi and the AX-12 dynamixel Servo's. We found a python2 library we a currently porting to python3, we pinpointed the problem in a specific method that gives an error in python3.

The Python 2 version actually works like a charm

AX_GOAL_LENGTH = 5
AX_WRITE_DATA = 3
AX_GOAL_POSITION_L = 30
AX_START = 255
AX_REG_WRITE = 4

def move(self, id, position):
    self.direction(Ax12.RPI_DIRECTION_TX)
    Ax12.port.flushInput()
    p = [position&0xff, position>>8]
    checksum = (~(id + Ax12.AX_GOAL_LENGTH + Ax12.AX_WRITE_DATA + Ax12.AX_GOAL_POSITION_L + p[0] + p[1]))&0xff
    outData = chr(Ax12.AX_START)
    outData += chr(Ax12.AX_START)
    outData += chr(id)
    outData += chr(Ax12.AX_GOAL_LENGTH)
    outData += chr(Ax12.AX_WRITE_DATA)
    outData += chr(Ax12.AX_GOAL_POSITION_L)
    outData += chr(p[0])
    outData += chr(p[1])
    outData += chr(checksum)
    Ax12.port.write(outData)

What we have tried is adjust this variable:

Ax12.port.write(bytes(outData,'utf-8'))

Now the script runs, sadly the Servo won't work anymore.

We also tried to place the bytes in a byte array

result = bytes([Ax12.AX_START, Ax12.AX_START, 
                Ax12.AX_GOAL_LENGTH,Ax12.AX_REG_WRITE,
                Ax12.AX_GOAL_POSITION_L, p[0], p[1], checksum
])
Ax12.port.write(result)

The script runs but the servo won't run.

My believe is that the operations done to outData is different in python3 then in python2. I can't find out what should be adjusted or different.

Anybody sees what I am currently doing wrong?

Upvotes: 0

Views: 267

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121714

Your bytes are not UTF-8 data; for any of your chr() values you create outside the range 0 - 127, encoding to UTF-8 produces two bytes.

You were creating Unicode codepoints from integer values; these create Latin-1 codepoints if you limited those to integers between 0 and 255; in principle you could encode to 'latin1' to get the bytes again, but it is much easier to just create bytes in the first place.

In Python 3, use the bytes type, created from a list of integers:

def move(self, id, position):
    self.direction(Ax12.RPI_DIRECTION_TX)
    Ax12.port.flushInput()
    p = [, position>>8]
    checksum = (~(id + Ax12.AX_GOAL_LENGTH + Ax12.AX_WRITE_DATA + Ax12.AX_GOAL_POSITION_L + p[0] + p[1]))&0xff
    outData = bytes([
        Ax12.AX_START, Ax12.AX_START, id,
        Ax12.AX_GOAL_LENGTH, Ax12.AX_WRITE_DATA,
        Ax12.AX_GOAL_POSITION_L, p[0], p[1], checksum])
    Ax12.port.write(outData)

Your own attempt did not include the id value and you used AX_REG_WRITE rather than AX_WRITE_DATA.

You could also use a bytearray() object, which would let you append additional bytes; you could use that to calculate the checksum by referencing the bytes produced so far, avoiding having to repeat yourself (a common source of errors):

def move(self, id, position):
    self.direction(Ax12.RPI_DIRECTION_TX)
    Ax12.port.flushInput()
    outData = bytearray([
        Ax12.AX_START, Ax12.AX_START, id,
        Ax12.AX_GOAL_LENGTH, Ax12.AX_WRITE_DATA,
        Ax12.AX_GOAL_POSITION_L, position & 0xff, position >> 8])
    checksum = ~sum(outData[2:]) & 0xff
    outData.append(checksum)
    Ax12.port.write(outData)

You could use concatenation too of course (outData = bytes([...]), outData += bytes([checksum])) but bytearray is also available in Python 2, so the above version is compatible with both major Python versions.

Upvotes: 2

Related Questions