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

Question:

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 rn".encode()) #Asks the Lock-in for x-value
    ser.write("++readrn".encode())
    x=ser.readline()
    print (x)
if __name__ == '__main__': main()

I get a byte string like b'-3.7486e-008r'. 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 rn") #Asks the Lock-in for x-value
    sio.write("++readrn")
    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 rn".encode()) #Asks the Lock-in for x-value
    ser.write("++readrn".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()
Asked By: Calum Beck

||

Answers:

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.

Answered By: Erik Šťastný

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()
Answered By: Eric

Use Serial.read_until() instead:

ser.read_until(b'r')

NB: don’t forget the b to specify a bytes object rather than a standard string. Otherwise, even if it reads the 'r', the function will block until the connection times out.

Answered By: Girl Spider