0

I'm using pyserial to communicate with some sensors which use the Modbus protocol. In Python 2.7, this works perfectly:

import serial

c = serial.Serial('port/address')  # connect to sensor
msg = "\xFE\x44\x00\x08\x02\x9F\x25"  # 7 hex bytes(?)
c.write(msg)  # send signal
r = c.read(7)  # read 7 hex bytes (?).

In Python 3, this does not work. I know it's something to do with differences in how Python 2/3 handle binary vs. unicode strings. I've found numerous other threads suggesting the solution should be to simply prepend a b on my message (msg=b""\xFE\x44\x00\x08\x02\x9F\x25") to specify it as a binary string but this does not work for my case.

Any insights? What should I be sending in Python 3 so the sensor recieves the same signal? I'm at my wit's end...

I should add that I'm totally new to serial connections (well... 1 week old), and (despite reading around quite a bit) I struggle with understanding different character/string formats... Hence question marks in comments above. Please pitch answers appropriately :).

Thanks in advance!

oscarbranson
  • 3,877
  • 1
  • 13
  • 15

2 Answers2

1

write expects argument to be str, not bytes, so passing b"\xFE\x44\x00\x08\x02\x9F\x25" directly to it won't work. You need to convert bytes to str first: c.write(b"\xFE\x44\x00\x08\x02\x9F\x25".decode()) should work.

Błotosmętek
  • 12,717
  • 19
  • 29
  • Thanks. Unfortunately no good: `'utf-8' codec can't decode byte 0xfe in position 0: invalid start byte`. Which (from my *limited* knowledge of character encoding) makes sense, because it's a hex string, not a unicode/utf-8 string. I've seen people using `.decode('hex')`, but that gives me `'hex' is not a text encoding; use codecs.decode() to handle arbitrary codecs`. So I tried `codecs.decode(b"\xFE\x44\x00\x08\x02\x9F\x25", 'hex')`, which gave me `decoding with 'hex' codec failed (Error: Odd-length string)`... I'm lost! – oscarbranson Jul 01 '17 at 10:33
  • Uhm, sorry. According to documentation: http://pyserial.readthedocs.io/en/latest/pyserial_api.html `write` as defined in pySerial accepts `bytes` after all. So, you should use just `c.write(b"\xFE\x44\x00\x08\x02\x9F\x25")` (and if it doesn't work, please specify **in what way** it doesn't work). – Błotosmętek Jul 01 '17 at 11:31
  • OK... so, embarrassingly, I just tried c.write(b"\xFE\x44\x00\x08\x02\x9F\x25") and it worked... Errors initially must have come from a typo in the string, and then secondarily from how I was handling the outputs (`ord()` is no longer necessary to process the outputs in Python 3). I somehow missed that in the last 2 days I spent trying to troubleshoot this... Thanks for your help anyway! – oscarbranson Jul 01 '17 at 12:31
1

Solution

It turned out that specifying the input as a byte string (msg=b""\xFE\x44\x00\x08\x02\x9F\x25") did work. Initial error was from a typo in the msg string...

Secondary errors arose from how the outputs were handled - in Python 2 ord() had to be applied to the indexed output to return integers, in Python 3 integers can be extracted directly from the output by indexing (i.e. no ord() necessary).

Hope this might help someone in the future...

oscarbranson
  • 3,877
  • 1
  • 13
  • 15