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()
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
.
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()
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.
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()
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
.
From the docs for readline()
:
The line terminator is always
b'n'
for binary files; for text files, thenewline
argument toopen()
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()
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.