Odd return from serial port

I am using pySerial in Python 3.10 to do a command exchange with a motor controller.

I send:
SN[4]\n\r
and I receive back the serial number
12345678\n

Before I go on I will say that this motor controller has a poor data sheet. It doesn’t actually say how the terminal interface works(!) so I am making a bit of this up, however largely what I’m writing works and I think once I get this oddity explained I’ll be able to work the rest out.

However - when I get the response back from the controller, the byte array when converted to a string does this:

length is: 11
21511772;
result is S #
result is #
result is 2
result is 1
result is 5
result is 1
result is 1
result is 7
result is 7
result is 2
result is ;

from the code:

    print("length of serial number: " + str(len(value)))
    print(value)

    for i in value:
        print ("result is " + i)

Where value is the variable with the serial number in.

The code that converts the byte array from the serial port is this:
string_response = response.decode('ASCII')

From what I’ve seen of the protocol, the response I get should be as shown when the string is displayed in full (ie <12345678;> with that semicolon terminator). The same happens with other commands but they’re less conventional (ie they just return a ; with the two additional characters which I’ve marked with the # symbol).

In summary, I’m not sure why there are two additional characters at the start of the string when I try and access the elements of the string individually or try to do a comparison but these don’t display when I show the string in a more conventional way. I’m certain they are not being transmitted by the remote device as I’ve put it on a standard terminal and they’re not there.

There is always some link between the letter (the S here comes from SN…) and the command so that’s not random. It happens running through different pythons (eg Anaconda, from the Windows command line etc.).

Thanks for any comments. Perhaps now I’ve described the problem, if it’s not obvious it will be easier to post snippets which might help.

The code says "length of serial number: " but the output says length is:, so that output doesn’t come from that code.

Do you have a link to the datasheet?

Do you need both \r and \n on the command?
If both are required the convention would be \r\n not \n\r.

I send:
SN[4]\n\r

As Barry says, the convention would normally be \r\n, not \n\r, if
the \r is needed at all. (Which it may be.)

and I receive back the serial number
12345678\n
[…]
The code that converts the byte array from the serial port is this:
string_response = response.decode('ASCII')

Can you show the result of:

 print(repr(string_response))

before going on to the value?

However - when I get the response back from the controller, the byte
array when converted to a string does this:

length is: 11
21511772;

Try print(repr(value)) here - check what’s really in it. Printing a
string to your display inherent has your own terminal interpreting the
characters in the string, so your want a repr() to see things without
that interpretation.

result is S #
result is #

It would be good to see these # for real. So go print("result is", repr(i)) instead.

from the code:

   print("length of serial number: " + str(len(value)))

You can write this:

 print("length of serial number:", len(value))

The print() function automatically calls str() on every one of its
arguments.

print(value)

As mentioned, if value contains control characters, your terminal will
obey them. Use print(repr(value)).

@MRAB the datasheet is behind an NDA and is almost useless. Also, yes! I did change the code to shorten the output! I hadn’t realised I had done it at this unopportune moment.

I have no idea about the terminators, as indicated, the datasheet doesn’t say! Do you think there might be a feature in pySerial that corrupts the output because of this? Interesting thought.

@cameron ah, repr() is a tool I need! thanks. I’ve done a lot of data processing stuff in Python but much less with the hardware libraries like pySerial. Just to clarify, the # symbols are to indicate the presence of the extra characters and were added by me to refer to the footnote. The ‘S’ and the ’ ’ are the characters which are printed.

For additional information, I’ve focussed on the task in Spyder and using a terminal pointing at Anaconda terminal set up with a Python 3.10 environment.

I’m not back with the hardware until next week. Appreciate the pointers on the additional tools for viewing the raw data. That’s more than I had before this thread. After trying to reverse engineer in the protocol in the first place (and a USB to serial failure which got me for a bit) I lost focus and have some back now. Thanks.

Sorry Barry Scott, I can only mention two people apparently.

If you use serial.Serial.readline to receive data, there could be an issue if the terminator is indeed \n\r and not \r\n. readline reads until \n, so if the device terminates with \n\r, half the terminator would remain in the buffer until the next read. This could have strange effects on the output.

When I use pyserial, I set the timeout and read as much as I can in one go.

Also, I haven’t seen any corruption occur when using it.

Hi chaps,

Thanks for your help here, you pushed me in the right direction and it does of course make perfect sense but the tools are a bit different in Python and I am not as familiar with them.

Sooo…
S\r21511772;
is the output from repl() which of course print() does a carriage return and then helpfully overwrites the S\r with the serial number (as it should)!

I still haven’t worked out why I get the S\r but that’s nothing to do with Python (I didn’t think it was) though at least I have the tools to get it sorted now or know how to parse it if this daft controller is going to make life difficult!

Thanks again folks.

You could write out what you send and what the reply is. That might help you understand how it works.

It might be that the device returns "S" to indicate success and then the value or, say, "F" to indicate failure if you send an unknown/invalid command.

Morning,

With the repr() function it was easy to see what was happening. The controller echoes the outgoing command but in my “C” like debug, the non printing characters didn’t print(!). repr() shows it correctly as a linefeed or carriage return etc. It turns out you just send a \r and then in the response which includes what was sent, split on the \r and trim off a trailing semicolon and that’s your result. All of which is fairly easy to do in Python. Obviously I had the benefit of a few extra commands too which I hadn’t listed here for comparison.

If it’s any consolation, the protocol is totally mad. It has very little repeatable structure! Hopefully that’s just down to maintaining backwards compatibility but if that’s how it was designed they could do with a few lessons in elegant protocols!

Thanks again.