Can Python serial support run at 45.45 baud?

J

John Nagle

Can Python's serial port support be made to run at 45.45 baud, the old
"60 speed" Teletype machine speed? I've restored a Model 15 teletype from
WWII. Works great after cleaning, oiling, and adjustment. There's
Perl support for this, and a Perl program ("http://www.buzbee.net/heavymetal")
that runs the things. I'd like to do something similar in Python, both
on Windows and Linux.

I'm proposing to use Python 2.6, PyWin32-212, and PySerial 2.4.
The PySerial interface allows requesting a specific baud rate, but does that
propagate properly all the way down into Windows? And what about
Linux, where the "stty" interface is quite different?

John Nagle
 
M

MRAB

Grant said:
If your hardware and OS supports it, Python can be made to
support it.
[snip]
I had a quick look at the Windows API. The struct member for the baud
rate is of type DWORD, ie an integer.
 
M

MRAB

John said:
OK, tried to open the port, using Python 2.6, latest PySerial
and PyWin32:

ser = serial.Serial(port, baudrate=baud,
bytesize=serial.FIVEBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_TWO)

Traceback (most recent call last):
File "serialtest.py", line 27, in <module>
main()
File "serialtest.py", line 24, in main
ser = openportbaudot(0,45.45)
File "serialtest.py", line 17, in openportbaudot
stopbits=serial.STOPBITS_TWO)
File "D:\python26\lib\site-packages\serial\serialutil.py", line 171,
in __init__
self.open()
File "D:\python26\lib\site-packages\serial\serialwin32.py", line 63,
in open
self._reconfigurePort()
File "D:\python26\lib\site-packages\serial\serialwin32.py", line 171,
in _reconfigurePort
raise ValueError("Cannot configure port, some setting was wrong.
Original message: %s" % e)
ValueError: Cannot configure port, some setting was wrong. Original
message: (87, 'SetCommState', 'The parameter is incorrect.')

Something doesn't like "serial.FIVEBITS". That's a valid value,
according
to "http://pyserial.wiki.sourceforge.net/pySerial". If changed to
"serial.EIGHTBITS", the code will execute, but of course does the wrong
thing. That looks like a bug.

I tried various values for "baud". PySerial will accept "45", and
even "45.45", although I doubt that it's really calculating the serial
port divisor
values from a floating point value. (Standard serial port hardware can do
45.45 baud, and most PCs with non-USB serial ports will do it quite well.)
For my application, 45 baud should work, with two stop bits; the
tolerances aren't that tight.
I don't think it's a Python bug. The MSDN website says this:

(http://msdn.microsoft.com/en-us/library/aa363214(VS.85).aspx.)

When a DCB structure is used to configure the 8250, the following
restrictions apply to the values specified for the ByteSize and StopBits
members:

* The number of data bits must be 5 to 8 bits.
* The use of 5 data bits with 2 stop bits is an invalid
combination, as is 6, 7, or 8 data bits with 1.5 stop bits.
 
J

John Nagle

Grant said:
If your hardware and OS supports it, Python can be made to
support it.

OK, tried to open the port, using Python 2.6, latest PySerial
and PyWin32:

ser = serial.Serial(port, baudrate=baud,
bytesize=serial.FIVEBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_TWO)

Traceback (most recent call last):
File "serialtest.py", line 27, in <module>
main()
File "serialtest.py", line 24, in main
ser = openportbaudot(0,45.45)
File "serialtest.py", line 17, in openportbaudot
stopbits=serial.STOPBITS_TWO)
File "D:\python26\lib\site-packages\serial\serialutil.py", line 171, in __init__
self.open()
File "D:\python26\lib\site-packages\serial\serialwin32.py", line 63, in open
self._reconfigurePort()
File "D:\python26\lib\site-packages\serial\serialwin32.py", line 171, in
_reconfigurePort
raise ValueError("Cannot configure port, some setting was wrong. Original
message: %s" % e)
ValueError: Cannot configure port, some setting was wrong. Original message:
(87, 'SetCommState', 'The parameter is incorrect.')

Something doesn't like "serial.FIVEBITS". That's a valid value, according
to "http://pyserial.wiki.sourceforge.net/pySerial". If changed to
"serial.EIGHTBITS", the code will execute, but of course does the wrong
thing. That looks like a bug.

I tried various values for "baud". PySerial will accept "45", and even
"45.45", although I doubt that it's really calculating the serial port divisor
values from a floating point value. (Standard serial port hardware can do
45.45 baud, and most PCs with non-USB serial ports will do it quite well.)
For my application, 45 baud should work, with two stop bits; the
tolerances aren't that tight.

John Nagle
 
M

MRAB

John said:
OK, here's what's wrong. The allowed numbers for stop bits in
Windows are

ONESTOPBIT 0 1 stop bit.
ONE5STOPBITS 1 1.5 stop bits.
TWOSTOPBITS 2 2 stop bits.

The Python interface, however, only exports STOPBITS_ONE and STOPBITS_TWO.
See "serialutil.py", at line 9, and "serialwin32.py" at lines 141-146.

Microsoft documentation
("http://msdn.microsoft.com/en-us/library/aa363214(VS.85).aspx") says:

* The use of 5 data bits with 2 stop bits is an invalid combination,
as is 6, 7, or 8 data bits with 1.5 stop bits.

So the correct combination, 5 bits with 1.5 stop bits, isn't supported in
Python. 1 stop bit will not physically work on Baudot teletypes; the
main camshaft doesn't come around fast enough. (Yes, there's an actual
mechanical reason for 1.5 stop bits.) Requesting 2 stop bits at the
Python level gets a reject at the Win32 level. (Not sure why Win32
doesn't allow that; extra stop bits just add delay, but don't hurt
anything. But it's not supported.)

Linux has a different set of restrictions; Linux offers only 1 or 2 stop
bits, and won't do arbitrary baud rates via the "termios" data structure,
although there are other ways to request that. At the hardware level,
there's a clock rate, a counter, and a divisor, so arbitrary baud
rates can be set.
If 5+2 is invalid, could you work around it with 6+1 instead (one of the
data bits masquerading as a stop bit)? Presumably the device on the
other end won't care as long as it gets the bits it expects...
 
M

MRAB

John Nagle wrote:
[snip]
So the correct combination, 5 bits with 1.5 stop bits, isn't supported in
Python. 1 stop bit will not physically work on Baudot teletypes; the
main camshaft doesn't come around fast enough. (Yes, there's an actual
mechanical reason for 1.5 stop bits.) Requesting 2 stop bits at the
Python level gets a reject at the Win32 level. (Not sure why Win32
doesn't allow that; extra stop bits just add delay, but don't hurt
anything. But it's not supported.)
I forgot to add that what Windows allows depends on what the hardware
(the serial chip) allows: the MSDN webpage mentions that the DCB struct
configures the 8250.
 
J

John Nagle

John said:
OK, tried to open the port, using Python 2.6, latest PySerial
and PyWin32:

ser = serial.Serial(port, baudrate=baud,
bytesize=serial.FIVEBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_TWO)

ValueError: Cannot configure port, some setting was wrong. Original
message: (87, 'SetCommState', 'The parameter is incorrect.')

Something doesn't like "serial.FIVEBITS". That's a valid value,
according
to "http://pyserial.wiki.sourceforge.net/pySerial". If changed to
"serial.EIGHTBITS", the code will execute, but of course does the wrong
thing. That looks like a bug.

OK, here's what's wrong. The allowed numbers for stop bits in Windows are

ONESTOPBIT 0 1 stop bit.
ONE5STOPBITS 1 1.5 stop bits.
TWOSTOPBITS 2 2 stop bits.

The Python interface, however, only exports STOPBITS_ONE and STOPBITS_TWO.
See "serialutil.py", at line 9, and "serialwin32.py" at lines 141-146.

Microsoft documentation
("http://msdn.microsoft.com/en-us/library/aa363214(VS.85).aspx") says:

* The use of 5 data bits with 2 stop bits is an invalid combination,
as is 6, 7, or 8 data bits with 1.5 stop bits.

So the correct combination, 5 bits with 1.5 stop bits, isn't supported in
Python. 1 stop bit will not physically work on Baudot teletypes; the
main camshaft doesn't come around fast enough. (Yes, there's an actual
mechanical reason for 1.5 stop bits.) Requesting 2 stop bits at the
Python level gets a reject at the Win32 level. (Not sure why Win32
doesn't allow that; extra stop bits just add delay, but don't hurt
anything. But it's not supported.)

Linux has a different set of restrictions; Linux offers only 1 or 2 stop
bits, and won't do arbitrary baud rates via the "termios" data structure,
although there are other ways to request that. At the hardware level,
there's a clock rate, a counter, and a divisor, so arbitrary baud
rates can be set.

John Nagle
 
R

Roy Smith

John Nagle said:
At the hardware level, there's a clock rate, a counter, and a divisor,
so arbitrary baud rates can be set.

Is that really true of modern hardware? The last time I looked at serial
port hardware, UARTs took a base clock rate and divided it sequentially
with flip-flops to get all the possible rates (they usually had some ugly
logic to generate 110). You were limited to the specific rates the
hardware gave you. Is that no longer the case?
 
J

John Nagle

MRAB said:
John Nagle wrote:
[snip]
So the correct combination, 5 bits with 1.5 stop bits, isn't supported in
Python. 1 stop bit will not physically work on Baudot teletypes; the
main camshaft doesn't come around fast enough. (Yes, there's an actual
mechanical reason for 1.5 stop bits.) Requesting 2 stop bits at the
Python level gets a reject at the Win32 level. (Not sure why Win32
doesn't allow that; extra stop bits just add delay, but don't hurt
anything. But it's not supported.)

I patched PySerial to support "STOPBITS_ONE5", for 1.5 stop bits,
and the big Teletype Model 15 is now banging out text from Python.

I actually put in "45.45" as the baud rate; it gets rounded down
to 45 for Windows, which works. That won't work on Linux, though;
there's a canned list of speeds for POSIX serial ports. (It's the
same list PDP-11 serial ports had in the 1970s, plus some later
extensions at the high end.)

1.5 or 2 stop bits is acceptable for the old heavy iron, but 1.5
is preferred, because there's less vibration and wear. With 2 stop bits,
the Teletype Model 15 clutch drops out on every character and the drive
train momentarily comes to a halt. With 1.5, everything rotates steadily.

John Nagle
 
J

John Nagle

Roy said:
Is that really true of modern hardware? The last time I looked at serial
port hardware, UARTs took a base clock rate and divided it sequentially
with flip-flops to get all the possible rates (they usually had some ugly
logic to generate 110). You were limited to the specific rates the
hardware gave you. Is that no longer the case?

It is, but the traditional serial clock rate is 115200 Hz, so you can use
any subdivision of that as a baud rate. The divisor for 45.45 baud is
something like 2535.

Some exotic serial port devices use a 16MHz clock; I've used those for
supporting SICK LMS laser rangerfinders at 500,000 baud.

John Nagle
 
H

Hendrik van Rooyen

MRAB said:
Grant said:
If your hardware and OS supports it, Python can be made to
support it.
[snip]
I had a quick look at the Windows API. The struct member for the baud
rate is of type DWORD, ie an integer.

I think there could be other hassles - the baud rate is basically derived
by setting a divisor that divides a high frequency clock that is
fed to the UART chip. Now this means that the frequency,
and the maximum value of the divisor, defines the lowest
rate that can be attained. The chips are standard, so the maximum
divisor is fixed, but the frequency fed the chip is a design parameter
of the motherboard.

So you could be lucky, or you might have to change the hardware...

- Hendrik
 
H

Hendrik van Rooyen

So the correct combination, 5 bits with 1.5 stop bits, isn't supported in
Python. 1 stop bit will not physically work on Baudot teletypes; the
main camshaft doesn't come around fast enough. (Yes, there's an actual
mechanical reason for 1.5 stop bits.) Requesting 2 stop bits at the
Python level gets a reject at the Win32 level. (Not sure why Win32
doesn't allow that; extra stop bits just add delay, but don't hurt
anything. But it's not supported.)

If you can get down so low in baud rate, then you can fudge it in software.
Set the port to the single stop bit, and make a transmit character function
that outputs a single character and a flush(), and then waits for a bit time
or so - time.sleep(0.022) will do the trick - means you have to make
a string transmitter that calls your character transmitter - you cannot
just write a string to the port - but that is no great hardship.

The receive side will not be a problem, as the syncing is done every char.

- Hendrik
 
R

Roy Smith

Grant Edwards said:
My guess is that it was _supposed_ to
be a CRC routine, but somebody botched it. They used the same
botched routine on the host end when they did testing, so
nobody noticed it was broken.

Stuff like this happens all the time. That's why RFC 2026 requires, "a
specification from which at least two independent and interoperable
implementations from different code bases have been developed".

To drag this back on topic, however, it's interesting to note that Python
itself has been successful, while still being essentially a single
implementation.
 
M

MRAB

Roy said:
Stuff like this happens all the time. That's why RFC 2026 requires,
"a specification from which at least two independent and
interoperable implementations from different code bases have been
developed".

To drag this back on topic, however, it's interesting to note that
Python itself has been successful, while still being essentially a
single implementation.
That's because it's open source, so many people can test and debug it.
 
S

Steve Holden

MRAB said:
That's because it's open source, so many people can test and debug it.

IronPython, J(P)ython and PyPy? TinyPython? Jython must be over ten
years old now.

regards
Steve
 
J

John Nagle

John said:
MRAB said:
John Nagle wrote:
[snip]
So the correct combination, 5 bits with 1.5 stop bits, isn't
supported in
Python. 1 stop bit will not physically work on Baudot teletypes; the
main camshaft doesn't come around fast enough. (Yes, there's an actual
mechanical reason for 1.5 stop bits.) Requesting 2 stop bits at the
Python level gets a reject at the Win32 level. (Not sure why Win32
doesn't allow that; extra stop bits just add delay, but don't hurt
anything. But it's not supported.)

I patched PySerial to support "STOPBITS_ONE5", for 1.5 stop bits,
and the big Teletype Model 15 is now banging out text from Python.

Logged in as a PySerial bug, with discussion of fix:

[ 2603052 ] 5-bit mode not properly supported on Windows

Linux support for nonstandard baud rates is possible, but needs a
call to "setserial", which is not standard POSIX.

John Nagle
 

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

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top