5

I am trying to read and write to a sensor via serial using pySerial. I have no software or hardware flow control.

I am able to send a string of hex to the device, but I only receive one byte back instead of the two-to-ten bytes I should see. The sensor is working -- I've verified this using Realterm.

I've tried using ser.readline() (instead of the inWaiting loop), and ser.read(2); this just causes the program to hang. I've also tried increasing the sleep time, and experimented with different baud rates (on both the PC and sensor), but nothing seems to work.

Does anyone have any advice?

import time
import serial

# configure the serial connections
ser = serial.Serial(
    port='COM1',
    baudrate=115200,
    parity=serial.PARITY_EVEN,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS
)

ser.isOpen()

print 'Enter your commands below.\r\nInsert "exit" to leave the application.'

while 1 :
    # get keyboard input
    data_in = raw_input(">> ")

    if data_in == 'exit':
        ser.close()
        exit()
    else:
        # send the character to the device
        ser.write(data_in.decode('hex') + '\r\n')

        out = ''
        time.sleep(1)
        while ser.inWaiting() > 0:
            out += ser.read(1)

        if out != '':
            print ">>" + " ".join(hex(ord(n)) for n in out)

(I slightly modified the code from that found on Full examples of using pySerial package)

anothermh
  • 9,815
  • 3
  • 33
  • 52
anon
  • 53
  • 1
  • 1
  • 3

1 Answers1

6

Your read statement is explicitly requesting 1 byte:

ser.read(1)

If you know how many bytes to read, you can specify here. If you are unsure, then you can specify a larger number. For example, doing

ser.read(10)

will read up to 10 bytes. If only 8 are available, then it will only return 8 bytes (following the timeout, see below).

It is also worth setting a timeout to prevent the program from hanging. Just add a timeout parameter to your Serial constructor. The following will give you a 2 second timeout:

ser = serial.Serial(
    port='COM1',
    baudrate=115200,
    parity=serial.PARITY_EVEN,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=2
)

The documentation states:

read(size=1) Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.

So if you do not know how many bytes to expect, then set a small timeout (if possible) so your code does not hang.

If your code does not return the full number of bytes expected, then it is likely the device you are connected to is not sending all the bytes you are expecting. Since you have verified it should be working separately, have you verified the data you are sending is correct? Perhaps encode to bytes first using struct.pack(). As an example, to send a byte with a decimal value of 33 (hex 0x21)

import struct
bytes_to_send = struct.pack('B', 33)

I have also never found it necessary to append the end of line chars \r\n to a message before sending

Sam
  • 111
  • 3
  • I removed the ser.inWaiting() loop and wrote `out = ser.read(10)` but I still only receive the first byte. – anon Jun 19 '17 at 21:25
  • I'd focus on what you are transmitting. It may be that you are not sending what you think you are. Firstly, as a test, I'd remove the `\r\n`. Secondly, I'd try using struct to send exactly the bytes that you intend to. How many bytes are you sending to the device as part your ser.write (excluding the newline formatting)? – Sam Jun 19 '17 at 21:32
  • I removed the `\r\n` and it worked! Do you know why this is the case? – anon Jun 19 '17 at 21:38
  • `\r\n` will be appending two bytes to your transmitted message. My guess is that this was confusing the device receiving your message. I'm glad it worked. – Sam Jun 19 '17 at 21:43