1 | """
|
---|
2 | Test serial port baudrates on multiple platforms.
|
---|
3 |
|
---|
4 | Requires:
|
---|
5 |
|
---|
6 | - PySerial (`pip3 install {--user} pyserial`, or use `pkgman install pyserial`)
|
---|
7 | - A loopback wire (Rx<->Tx) on the serial port.
|
---|
8 |
|
---|
9 | For Haiku versions older than hrev56354:
|
---|
10 |
|
---|
11 | Due to bug https://dev.haiku-os.org/ticket/17861, you need to comment out
|
---|
12 | line 344: "self._reset_input_buffer()" on PySerial's serialposix.py file.
|
---|
13 |
|
---|
14 | Normal test duration: < 20 seconds.
|
---|
15 |
|
---|
16 | For each tested baudrate:
|
---|
17 | - Write a string to the loopback port. Ensure all bytes get written.
|
---|
18 | - Split the reading back of that string on 3 parts, making sure that on each step:
|
---|
19 | - the read data is correct.
|
---|
20 | - the input buffer has the correct number of bytes left.
|
---|
21 |
|
---|
22 | Supossedly supported baudrates on each platform:
|
---|
23 |
|
---|
24 | Win32: 50 to 115200 bps - Confirmed working
|
---|
25 | Linux: 50 to 4000000 bps - Confirmed working
|
---|
26 | Haiku: 50 to 230400 bps - only 50 - 115200 seems to work.
|
---|
27 |
|
---|
28 | For Haiku versions older than hrev56354:
|
---|
29 | Make sure you don't call any function that call:
|
---|
30 | - termios.tcflush() - like serial.reset_input_buffer() and serial.reset_output_buffer()
|
---|
31 | - termios.tcdrain() - like serial.flush()
|
---|
32 | - termios.tcsendbreak() - like serial.send_break()
|
---|
33 | """
|
---|
34 |
|
---|
35 | import os
|
---|
36 | import serial
|
---|
37 | import sys
|
---|
38 | import time
|
---|
39 |
|
---|
40 | #
|
---|
41 | # Default port names. Used if not given on the command line
|
---|
42 | #
|
---|
43 | if sys.platform == 'win32':
|
---|
44 | PORT = 'COM1'
|
---|
45 | elif sys.platform == 'linux':
|
---|
46 | PORT = '/dev/ttyS0'
|
---|
47 | elif sys.platform == 'haiku1':
|
---|
48 | PORT = '/dev/ports/pc_serial0'
|
---|
49 | else:
|
---|
50 | exit('unsupported platform')
|
---|
51 |
|
---|
52 |
|
---|
53 | USAGE_TEXT = """Test serial ports read/write via a loopback cable.
|
---|
54 |
|
---|
55 | Usage:
|
---|
56 |
|
---|
57 | > test_serial.py [-h] [port] [baudrate]
|
---|
58 |
|
---|
59 | -h Show this text and exit.
|
---|
60 | [port] Name of the port to test. Default: '%s'.
|
---|
61 | [baudrate] Baudrate to test. Tests all the supported ones if not given.
|
---|
62 | """ % PORT
|
---|
63 |
|
---|
64 |
|
---|
65 | TEST_STRING = b'Hello World!'
|
---|
66 | TS_FIRST_HALF = TEST_STRING.split()[0]
|
---|
67 | TS_SECOND_HALF = TEST_STRING.split()[-1]
|
---|
68 |
|
---|
69 | # print('Test strings:\n\t%s\n\t%s\n\t%s\n' % (TEST_STRING, TS_FIRST_HALF, TS_SECOND_HALF))
|
---|
70 |
|
---|
71 | def test(port, baudrates=None):
|
---|
72 | print('Testing port: %s.' % port)
|
---|
73 |
|
---|
74 | with serial.Serial(port) as s:
|
---|
75 | if not s.isOpen():
|
---|
76 | exit('Could not open the serial port.')
|
---|
77 |
|
---|
78 | if baudrates is None:
|
---|
79 | baudrates = s.BAUDRATES
|
---|
80 |
|
---|
81 | for b in baudrates[::-1]:
|
---|
82 | # 230400 hangs on Haiku, and it doesn't supports setting custom baudrates on pc_serial.
|
---|
83 | if sys.platform == 'haiku1' and int(b) >= 230400:
|
---|
84 | continue
|
---|
85 |
|
---|
86 | if (int(b) == 0):
|
---|
87 | print('Skipping tests for baudrate = 0 bps.')
|
---|
88 | continue
|
---|
89 |
|
---|
90 | s.baudrate = b
|
---|
91 | s.bytesize = serial.SEVENBITS
|
---|
92 | s.timeout = 200000
|
---|
93 | assert s.baudrate == b, b
|
---|
94 | in_wating = s.in_waiting
|
---|
95 | assert in_wating == 0, in_wating
|
---|
96 |
|
---|
97 | bytes_written = s.write(TEST_STRING)
|
---|
98 | # Give it time to send the data:
|
---|
99 | if sys.platform == 'linux':
|
---|
100 | # PySerial flush() calls tcdrain(). This is enough on Linux, as it blocks until all the
|
---|
101 | # bytes are written.
|
---|
102 | s.flush()
|
---|
103 | print('Testing baudrate: %d bps.' % b)
|
---|
104 | else:
|
---|
105 | # On Win32 flush() returns right away, so we need to wait an BPS appropiate time here
|
---|
106 | # (same on Haiku, at least for now):
|
---|
107 | s.flush()
|
---|
108 | print('Testing baudrate: %d bps.' % b, end='')
|
---|
109 | sleep_time = (1 / (b / 8) * bytes_written) * 2 # we need to write AND read, thus: time x 2.
|
---|
110 | if sys.version_info < (3, 11) and sleep_time < 0.001:
|
---|
111 | sleep_time = 0.001 # time.sleep() is not THAT precise on earlier Python versions.
|
---|
112 | print(' - Sleeping for %s seconds.' % sleep_time)
|
---|
113 | time.sleep(sleep_time)
|
---|
114 |
|
---|
115 | assert bytes_written == len(TEST_STRING), bytes_written
|
---|
116 | in_waiting = s.in_waiting
|
---|
117 | assert in_waiting == bytes_written, '%d != %d' % (in_waiting, bytes_written)
|
---|
118 |
|
---|
119 | data = s.read(len(TS_FIRST_HALF))
|
---|
120 | assert data == TS_FIRST_HALF, data
|
---|
121 | in_waiting = s.in_waiting
|
---|
122 | assert in_waiting == (len(TEST_STRING) - len(TS_FIRST_HALF)), in_waiting
|
---|
123 |
|
---|
124 | # Eat away the expected ' ' separator.
|
---|
125 | data = s.read(1)
|
---|
126 | assert data == b' ', data
|
---|
127 |
|
---|
128 | data = s.read(len(TS_SECOND_HALF))
|
---|
129 | assert data == TS_SECOND_HALF, data
|
---|
130 | in_waiting = s.in_waiting
|
---|
131 | assert in_waiting == 0, in_waiting
|
---|
132 |
|
---|
133 |
|
---|
134 | if __name__ == '__main__':
|
---|
135 | port = PORT
|
---|
136 | baudrates = None
|
---|
137 |
|
---|
138 | if len(sys.argv) > 1:
|
---|
139 | if '-h' in sys.argv:
|
---|
140 | print(USAGE_TEXT)
|
---|
141 | exit(0)
|
---|
142 |
|
---|
143 | if os.path.exists(sys.argv[1]):
|
---|
144 | port = sys.argv[1]
|
---|
145 | elif sys.argv[1].isnumeric():
|
---|
146 | baudrates = [int(sys.argv[1])]
|
---|
147 | else:
|
---|
148 | exit('Couldn\'t find port "%s"' % sys.argv[1])
|
---|
149 |
|
---|
150 | if len(sys.argv) > 2 and sys.argv[2].isnumeric():
|
---|
151 | baudrates = [int(sys.argv[2])]
|
---|
152 |
|
---|
153 | test(port, baudrates)
|
---|