Change serial timeout per read

R

rowan

I'm writing a driver in Python for an old fashioned piece of serial
equipment. Currently I'm using the USPP serial module. From what I can
see all the serial modules seem to set the timeout when you open a
serial port. This is not what I want to do. I need to change the
timeout each time I do a "read" on the serial port, depending on
which part of the protocol I've got to. Sometimes a return character
is expected within half a second, sometimes within 2 seconds, and
sometimes within 20 seconds. How do I do this in USPP, or Pyserial, or
anything else? Currently I'm working in Windows, but I'd prefer a
platform independent solution if possible...

Thanks - Rowan
 
H

Hendrik van Rooyen

I'm writing a driver in Python for an old fashioned piece of serial
equipment. Currently I'm using the USPP serial module. From what I can
see all the serial modules seem to set the timeout when you open a
serial port. This is not what I want to do. I need to change the
timeout each time I do a "read" on the serial port, depending on
which part of the protocol I've got to. Sometimes a return character
is expected within half a second, sometimes within 2 seconds, and
sometimes within 20 seconds. How do I do this in USPP, or Pyserial, or
anything else? Currently I'm working in Windows, but I'd prefer a
platform independent solution if possible...

Yikes!

you will probably have to make the port non blocking, and roll your own
using different time.sleep(n) values between invocations to port.read(1) calls

Unless you can afford to close and open the port each time - but that way leads
to missed characters...

- Hendrik
 
R

rowan

you will probably have to make the port non blocking, and roll your own
using different time.sleep(n) values between invocations to port.read(1) calls

What I actually want to do is to respond immediately if the expected
string comes in, but not raise a timeout unless it takes longer than
the maximum time. So if the device I'm communicating with usually
responds in a second, but _can_ take up to 20 seconds, I don't want to
do a sleep(20) then read the port since this will slow everything down
a lot in an average world. I want to keep checking for the expected
string, and act upon it as soon as I've got it, only raising a timeout
if I haven't got it after 20 seconds. I guess to do this using non-
blocking calls I have to do something like:
timesofar = 0
returnstring = port.read(1)
while len(returnstring)<expectedlength:
if timesofar >= timeout:
raise SerialException('Timeout')
time.sleep(checkportinterval)
timesofar += checkpointinterval
returnstring += port.read(1)

This seems rather messy. What I've tried this morning is to produce a
modified version of uspp with a second optional timeout parameter in
its read() function. If this is present, the timeout given is sent to
the port using SetCommTimeouts(). If it's not present, the timeouts
specified when the port was opened are sent. At first sight, with
minimal testing on Windows, this seems to be working, and will leave
my application code a lot cleaner than the non-blocking plus sleep
approach. Of course I don't know whether my method will work on Linux,
and there may be problems I haven't found yet.

Rowan
 
H

Hendrik van Rooyen

What I actually want to do is to respond immediately if the expected
string comes in, but not raise a timeout unless it takes longer than
the maximum time. So if the device I'm communicating with usually
responds in a second, but _can_ take up to 20 seconds, I don't want to
do a sleep(20) then read the port since this will slow everything down
a lot in an average world. I want to keep checking for the expected
string, and act upon it as soon as I've got it, only raising a timeout
if I haven't got it after 20 seconds. I guess to do this using non-
blocking calls I have to do something like:
timesofar = 0
returnstring = port.read(1)
while len(returnstring)<expectedlength:
if timesofar >= timeout:
raise SerialException('Timeout')
time.sleep(checkportinterval)
timesofar += checkpointinterval
returnstring += port.read(1)

This seems rather messy. What I've tried this morning is to produce a
modified version of uspp with a second optional timeout parameter in
its read() function. If this is present, the timeout given is sent to
the port using SetCommTimeouts(). If it's not present, the timeouts
specified when the port was opened are sent. At first sight, with
minimal testing on Windows, this seems to be working, and will leave
my application code a lot cleaner than the non-blocking plus sleep
approach. Of course I don't know whether my method will work on Linux,
and there may be problems I haven't found yet.

If it works it works - no problem with that - fight the
dragons as you meet them, one at a time.

I normally put something like this in a read function
(from memory, not tested):

error = 0
k = ''
try:
k = port.read(1)
except IoError:
error = 1
return error,k

For this to work, you have to first unblock the port using fcntl:

def unblock(f):
"""given file f sets unblock flag to true"""

fcntl.fcntl(f.fileno(),f.F_SETFL, os.O_NONBLOCK)

Then you put a call to the read in a loop, and use time.time()
to do your time out, resetting a start_time variable at the start,
and every time you get a char, and using short sleeps
(millisec or so) after unsuccessful calls to make it less of a
busy loop.

The side benefit of this is that once you have received a char,
you can change gears and use a shorter time out to detect the
end of message, in a protocol and state agnostic way. - when
the device has stopped sending chars for a little while it has
finished saying what it wants to say. - so its easy to write a
get_a_reply routine with variable time out, moving the action
from the char to the message level:

start_time=time.time()
s = ''
while time.time()-start_time < time_out:
error,k = get_a_char(port)
if error:
time.sleep(0.001)
continue
s += k # keep the first char
start_time = time.time()
while time.time() - start_time < 0.005: # inter char time out
status,k = get_a_char(port)
if error:
time.sleep(0.001)
continue
s +=k
start_time = time.time()
break
return s # return empty string or what was received

Something similar works for me on Suse Linux - not sure if
fcntl works on windows.

And no it isn't pretty. - but then very little of what I write is...

- Hendrik
 
J

James T. Dennis

I'm writing a driver in Python for an old fashioned piece of serial
equipment. Currently I'm using the USPP serial module. From what I can
see all the serial modules seem to set the timeout when you open a
serial port. This is not what I want to do. I need to change the
timeout each time I do a "read" on the serial port, depending on
which part of the protocol I've got to. Sometimes a return character
is expected within half a second, sometimes within 2 seconds, and
sometimes within 20 seconds. How do I do this in USPP, or Pyserial, or
anything else? Currently I'm working in Windows, but I'd prefer a
platform independent solution if possible...
Thanks - Rowan

I'm not familiar with the USPP serial module. However, I used
the PySerial package successfully for several months on one job
to write a full regression testing framework and suite for testing
a series of different VOIP/SIP phone models (little embedded Linux
systems) via their diagnostics/JTAG serial headers.

It had timeout and writeTimeout settings which I recall were fully
configurable to fractions of a second, None, and 0 (non-blocking).

Under Debian it's available as a simple:

apt-get -f install python-serial.

... and I seem to recall that it worked fine under MS Windows, too.
(In fact it was written in pure Python, no C-lib or .so/.DLL under it).

Ahh ... here's the URL:

http://pyserial.sourceforge.net/

... with backends for CPython (Windows and Linux/UNIX/Posix), and Jython.

Under Python just use:

>>> import serial

... then use something like:

>>> s0 = serial.Serial(0) # Open first serial port: default settings

... or:

>>> s0 = serial.Serial('/dev/ttyS0', 38400, timeout=None) \
# port by device node/name, setting speed and no timeout
>>> s1 = serial.Serial(3, 19200, timeout=0) \
# another by number, non-blocking

Then use:

>>> s0.read() # or s0.readline() for '\n' terminated

... and various other methods on these ports.

BTW: you can change the timeouts on these connection objects
simply by using s0.timeout=X ... and you can set them to floating
point values --- so you can use tenths of a second.

I remember I was also able to adapt my framework classes to add
support for telnetlib in about an hour of spare time one evening; so
we could use the serial port to manage testing on one block of phones
and we could enable the optional telnet port access built-into a
bank of the other phones and use almost all the same test code with
them. (The only difference was that we couldn't capture reboot
output over the telnet interface; while I could capture and log it
via the serial ports --- in other words it was a natural limitation of
the hardware --- and their embedded system didn't enable something like
a dmesg to capture that after the fact).

The reason I mention the telnetlib angle serves some purpose other than
mere rambling and bragging ... I seem to recall that the methods
supported by the two modules were very closely matched ... so my
adaptation was extremely straightforward.

I see that USPP (univ. serial port for Python) is available at:

http://ibarona.googlepages.com/uspp

... and it's supposed to have most of the same features (from a
quick glance at the web page). However, there's much more info
available at the PySerial page ... and PySerial seems to be far
more recently maintained (with 2.2 "slots' support, for example).

In addition PySerial seems to be linked to a PyParallel package
that's "under development" (presumably by the same author).
(I'm guessing that this latter development can't be done in pure
Python, though).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,120
Latest member
ShelaWalli
Top