I'm using the following code to communicate with a Digital Multi Meter (DMM).
It works ok. I can send commands and read results.
I don't use readline because I read binary data.
The problem:
The problem is that it is very very slow.
Same code written in Ruby is much faster.
When it takes 30 sec in python, it takes 2 or 3 sec in ruby (using same speed).
So it's not a hardware issue.
The only difference between Ruby code and Python is that in Python I use inWaiting() for reading all characters available. In Ruby, read() function reads all of them and not just one.
The code:
Here's the code:
In read_retry function I check how many characters I have to read.
I read them, and then call data_is_ok function to check if it's finished or not.
As you can see, there are '\r' embedded in the data returned.
Reading is finished when the last character is '\r' (no more data available).
So there's a loop to read numerous chunks.
import serial
[...]
def data_is_ok(data):
# No status code yet
if len(data) < 2: return False
# Non-OK status
if len(data) == 2 and data[0] != "0" and data[1] == "\r": return True
# Non-OK status with extra data on end
if len(data) > 2 and data[0] != "0":
raise ValueError('Error parsing status from meter (Non-OK status with extra data on end)')
# We should now be in OK state
if data[0] != "0" or data[1] != "\r":
raise ValueError('Error parsing status from meter (status:%c size:%d)' % (data[0], len(data)))
return len(data) >= 4 and data[-1] == "\r"
def read_retry():
retry_count = 0
data = ""
while retry_count < 500 and not data_is_ok(data):
bytesToRead = ser.inWaiting()
data += ser.read(bytesToRead)
if data_is_ok(data): return data
time.sleep (0.001)
retry_count += 1
raise ValueError('Error parsing status from meter: %c %d %r %r' % (data[0],len(data),data[1] == '\r', data[-1] == '\r'))
[...]
# Serial port settings
try:
ser = serial.Serial(port='/dev/cu.usbserial-AK05FTGH', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=5, rtscts=False, dsrdtr=False)
except serial.serialutil.SerialException, err:
print "Serial Port /dev/cu.usbserial-AK05FTGH doesn't respond"
print err
sys.exit()
[...]
ser.write(cmd+'\r')
data = read_retry()
I have used cProfile profiler. Most of the time is spent in time.sleep
Here's an extract:
363096 function calls (363085 primitive calls) in 28.821 seconds
Ordered by: internal time
List reduced from 127 to 10 due to restriction <10>
ncalls tottime percall cumtime percall filename:lineno(function)
19050 25.245 0.001 25.245 0.001 {time.sleep}
1 1.502 1.502 1.502 1.502 {posix.open}
The question:
Is it possible to make the code faster ?