Python serial (pySerial) Reading lines with EOL \r instead of \n

18,116

Solution 1

What about use simple loop for reading?

def readData():
    buffer = ""
    while True:
        oneByte = ser.read(1)
        if oneByte == b"\r":    #method should returns bytes
            return buffer
        else:
            buffer += oneByte.decode("ascii")

You can check the serialutil.py file from Pyserial package, They use the same way to achieve method read_until.

Solution 2

Use read_until() instead:

ser.read_until(b'\r')

Be careful, don't forget the b. Otherwise even if it reads the '\r' the function won't return until the timeout set on the port is reached.

Solution 3

From the docs for readline():

The line terminator is always b'\n' for binary files; for text files, the newline argument to open() can be used to select the line terminator(s) recognized.

Of course, you can't use open here. But what you can do is use io.TextIOWrapper to convert the byte stream into a text stream:

ser_text = io.TextIOWrapper(ser, newline='\r')
ser_text.readline()
Share:
18,116
Calum Beck
Author by

Calum Beck

I'm probably the laziest person I know, but I can't be arsed to find out.

Updated on July 17, 2022

Comments

  • Calum Beck
    Calum Beck almost 2 years

    I am communicating with an SR830 lock-in amplifier via an RS232 cable. When reading the data as in the following code:

    import serial
    
    def main():
        ser = serial.Serial(
            port='COM6',
            baudrate=19200,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            bytesize=serial.EIGHTBITS)
        ser.timeout=1
        ser.write("OUTP? 1 \r\n".encode()) #Asks the Lock-in for x-value
        ser.write("++read\r\n".encode())
        x=ser.readline()
        print (x)
    if __name__ == '__main__': main()
    

    I get a byte string like b'-3.7486e-008\r'. However the ser.readline() function does not recognise the \r as an EOL. So I have to wait for the timeout every time I read data, which will be troublesome as I want to take a lot of points as fast as I can. And the length of the number changes a lot so I cannot just use ser.read(12) for example. I have tried using io.TextIOWrapper but it's not clear to me how to implement it. Here's my attempt:

    import serial
    import io
    def main():
        ser = serial.Serial(
            port='COM6',
            baudrate=19200,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            bytesize=serial.EIGHTBITS)
        ser.timeout=1
        sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
        sio.write("OUTP? 1 \r\n") #Asks the Lock-in for x-value
        sio.write("++read\r\n")
        x=sio.readline()
        print (x)
    if __name__ == '__main__': main()
    

    Which just prints a blank space. Any help will be much appreciated, thanks.

    EDIT: Here's my working code after the answers, using the loop:

    import serial
    def main():
        ser = serial.Serial(
            port='COM6',
            baudrate=19200,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            bytesize=serial.EIGHTBITS)
        ser.timeout=5
        ser.write("OUTP? 1 \r\n".encode()) #Asks the Lock-in for x-value
        ser.write("++read\r\n".encode())
        buffer = ""
        while True:
            oneByte = ser.read(1)
            if oneByte == b"\r":    #method should returns bytes
                print (buffer)
                break
            else:
                buffer += oneByte.decode()
    if __name__ == '__main__': main()
    
  • juhist
    juhist almost 7 years
    I wouldn't recommend that as a general solution due to the terrible performance, but now that we're talking about low-speed serial interfaces, it'll probably work.
  • Błotosmętek
    Błotosmętek almost 7 years
    You mean \r, not \n.
  • Erik Šťastný
    Erik Šťastný almost 7 years
    @juhist I use this method for communication with multimeter etc. via RS232 where responses are longer than 100 ms and this has absolutely no impact on performance in this cases.
  • juhist
    juhist almost 7 years
    AttributeError: 'str' object has no attribute 'append'
  • Erik Šťastný
    Erik Šťastný almost 7 years
    @juhist fixed again.
  • Calum Beck
    Calum Beck almost 7 years
    Yeah, I added += and .decode() and it worked! I'll just check the other answers to see what's the fastest. Thanks a lot Erik.
  • Erik Šťastný
    Erik Šťastný almost 7 years
    @Calum Beck I have updated answer, the pyserial use same aproach for the read_until method.
  • Calum Beck
    Calum Beck almost 7 years
    I also added a break function
  • Eric
    Eric almost 7 years
    Is the BufferedRWPair needed?
  • Calum Beck
    Calum Beck almost 7 years
    Hmm, actually, just realised even with that code it still waits for the timeout. I'll just stick with the loop. I can see what's going on that way.
  • Eric
    Eric almost 7 years
    Why not set the timeout to 0 then?
  • spitz
    spitz about 5 years
    Keep getting a AttributeError: 'Serial' object has no attribute 'read1' error...
  • Eric
    Eric about 5 years
    @spitz: I'd file a bug/feature request against pyserial for that