Trouble with uart.read()

The official PYBD running MicroPython, and its accessories.
Target audience: Users with a PYBD
Post Reply
kbrenner
Posts: 46
Joined: Mon Jan 20, 2020 8:05 pm

Trouble with uart.read()

Post by kbrenner » Wed Aug 05, 2020 2:49 pm

Hi,

I am trying to run consecutive uart.write calls in a row to a motor and return one uart.read that contains multiple messages in it. Specifically, I am using the Pyboard D-series SF6W. The motors that I am writing to are Dynamixel XM430-W210-R motors (https://emanual.robotis.com/docs/en/dxl/x/xm430-w210/). My code looks like this:

def readMotors(self):
self.wrist_pin_out.high()
pyb.udelay(100)

write_time = utime.ticks_us()
self.uart.write(self.final_rot_pos)
self.uart.write(self.final_flex_pos)
self.uart.write(self.final_rot_curr)
self.uart.write(self.final_flex_curr)
print("time in uart.write " + str(utime.ticks_us()-write_time))


pyb.udelay(1000)
self.wrist_pin_out.low()
pyb.udelay(1000)

read_time = utime.ticks_us()
tmp = self.uart.read()
print("time in uart.read: " + str(utime.ticks_us()-read_time))
print("tmp: " + str(tmp))

where self.final_rot_pos, self.final_flex_pos, self.final_rot_curr, and self.final_flex_curr are instructional packets for the motors to know to respond with the correct positions and currents. They are in the correct hexadecimal form (which has been confirmed). The issue lies in the uart.write()/uart.read() sequence. I am not sure if it has to do with delays; however, I am returning readings that has some of the correct bytes. For example, it will return one or two of those readings correctly but then will also include a bunch of random bytes jumbled inside.

Any thoughts?

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Trouble with uart.read()

Post by dhylands » Wed Aug 05, 2020 4:53 pm

I've got micropythyon code for talking with Dyanmixel AX-12's (and family). It looks like the XM430's use the same packet layout.

It has a parser that will read one byte at a time and spit out the packets as they are parsed.

It looks like the XM430's use D+/D- (full duplex) whereas the AX-12's use half-duplex comms over the same wire.

My code can be found here: https://github.com/dhylands/bioloid3

I never got around to creating much documentation, but there is some in the README.md file. You'd want to create a much simpler version of https://github.com/dhylands/bioloid3/bl ... rt_port.py that wouldn't need to use inline assembler (it would wind up being much closer to https://github.com/dhylands/bioloid3/bl ... sb_port.py)

https://github.com/dhylands/bioloid3/bl ... /packet.py contains the packet parser.
https://github.com/dhylands/bioloid3/blob/master/bus.py has the commands for sending commands to the servos

A bunch of the code in the bioloid3 repository can run on the host or on the device, depending on what you're trying to do (I find testing on the host works faster than doing everything on the device).

I also have some code https://github.com/dhylands/Bioloid which runs on the host gives a CLI for talking to the bioloid devices. It uses register maps to describe the EEPROM layout. This is the register map for the AX-12: https://github.com/dhylands/Bioloid/blo ... -servo.bld

Feel free to ask questions.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Trouble with uart.read()

Post by dhylands » Wed Aug 05, 2020 5:04 pm

I found the comms protocol for the XM-430 here: https://emanual.robotis.com/docs/en/dxl/protocol2/

It appears to be similar but not identical to the that used by the AX-12: https://emanual.robotis.com/docs/en/dxl/protocol1/

I'm not 100% sure, but it looks like the XM430 can use protocol version 1, but I don't have one to play with to confirm.

So anyways, in order to talk to the servos you'll need to create a packet for the 2.0 protocol and send that and parse the 2.0 response packets which come back. Rather that writing one byte at a time, I prefer to arrange the entire packet in memory and then issue a single write (this simplifies calculating the CRC and ensures that the entire packet it written without any gaps).

kbrenner
Posts: 46
Joined: Mon Jan 20, 2020 8:05 pm

Re: Trouble with uart.read()

Post by kbrenner » Thu Aug 06, 2020 5:24 am

Hi Dhylands, thanks for the information. We are using protocol 2.0 and following the Dynamixel instructions on their site as well. I have tried creating individual byte arrays with their own individual CRC's then adding them together for the uart.write() as such:

Position Reading Command for Motor 1: b'\xff\xff\xfd\x00\x01\x07\x00\x02\x84\x00\x04\x00\x1d\x15'
Position Reading Command for Motor 2: b'\xff\xff\xfd\x00\x02\x07\x00\x02\x84\x00\x04\x00\x17%'
Current Reading Command for Motor 1: b'\xff\xff\xfd\x00\x01\x07\x00\x02~\x00\x02\x005I'
Current Reading Command for Motor 2: b'\xff\xff\xfd\x00\x02\x07\x00\x02~\x00\x02\x00?y'

Thus I call uart.write(b'\xff\xff\xfd\x00\x01\x07\x00\x02\x84\x00\x04\x00\x1d\x15\xff\xff\xfd\x00\x02\x07\x00\x02\x84\x00\x04\x00\x17%\xff\xff\xfd\x00\x01\x07\x00\x02~\x00\x02\x005I\xff\xff\xfd\x00\x02\x07\x00\x02~\x00\x02\x00?y')

Then I have a pyboard delay before the uart.read(). However, even with different delay values, I cannot get the uart.read() to return adequate results. I can see parts of the correct values in there but they are incomplete and jumbled. I have also tried combining the instructional packets for all 4 readings prior to calculating the CRC and then sending that through uart.write() but that resulted in an even more nonsensical uart.read(). The first method I tried seems promising as I can see a lot of the correct bytes in the uart.read(). I am just missing something...

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Trouble with uart.read()

Post by dhylands » Thu Aug 06, 2020 6:07 pm

I always just read single characters using uart.read and assemble the packets. I've found that trying to do anything else inevitably breaks down. There is an RX buffer that you can control the size of when you open the UART. If you're expecting multiple messages then it should be big enough to hold all of the messages you expect.

http://docs.micropython.org/en/latest/l ... .UART.init (the rxbuf parameter). It you don't specify an rxbuf then it will default to 64 bytes. If more that 64 bytes is received and unread then data after that gets dropped.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Trouble with uart.read()

Post by dhylands » Thu Aug 06, 2020 6:14 pm

When you send the command to servo 1, you need to wait for the response before sending the command to servo 2.

Otherwise your command to servo 2 and the response from servo 1 will trample each other. Only one device can transmit at a time, and the host needs to orchestrate that.

I believe that there are EEPROM parameters that can be set to disable the responses from the servos, but I'd recommend that you structure your code to wait for the response before sending the next command.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Trouble with uart.read()

Post by dhylands » Thu Aug 06, 2020 6:23 pm

Also, how are you controlling the direction?

With RS-485 you need to have a GPIO line or something controlling the direction.

I found that controlling that line from python wasn't fast enough and I would miss response characters from the servo.

So I had to write the sending portion in assembler to get adequate response times. See: https://github.com/dhylands/bioloid3/bl ... rt_port.py

My solution above doesn't use a GPIO since the AX-12's don't need the external driver chip for D+/D-. So you'd still need to do something a little different. My code enables/disables the receiver. Your code needs to toggle a GPIO line instead.

I mentioned earlier that the XM430's using D+/D- were full duplex, but I realized that's wrong and its still only half duplex.

Post Reply