blob: 6f2145bb166c6af713058b808fd98bf47e561f1f [file] [log] [blame]
Chris Liechti33f0ec52015-08-06 16:37:21 +02001#!/usr/bin/env python
2
3# RS485 support
4#
5# (C) 2015 Chris Liechti <cliechti@gmx.net>
Chris Liechtifbdd8a02015-08-09 02:37:45 +02006#
7# SPDX-License-Identifier: BSD-3-Clause
Chris Liechti33f0ec52015-08-06 16:37:21 +02008
9"""\
10The settings for RS485 are stored in a dedicated object that can be applied to
11serial ports (where supported).
12NOTE: Some implementations may only support a subset of the settings.
13"""
14
15import time
16import serial
17
18class RS485Settings(object):
19 def __init__(self,
20 rts_level_for_tx=True,
21 rts_level_for_rx=False,
22 loopback=False,
23 delay_before_tx=None,
24 delay_before_rx=None):
25 self.rts_level_for_tx = rts_level_for_tx
26 self.rts_level_for_rx = rts_level_for_rx
27 self.loopback = loopback
28 self.delay_before_tx = delay_before_tx
29 self.delay_before_rx = delay_before_rx
30
31
32class RS485(serial.Serial):
33 """\
34 A subclass that replaces the write method with one that toggles RTS
35 according to the RS485 settings.
36
37 NOTE: This may work unreliably on some serial ports (control signals not
38 synchronized or delayed compared to data). Using delays may be
39 unreliable (varying times, larger than expected) as the OS may not
40 support very fine grained delays (no smaller than in the order of
41 tens of milliseconds).
42
43 NOTE: Some implementations support this natively. Better performance
44 can be expected when the native version is used.
45
46 NOTE: The loopback property is ignored by this implementation. The actual
47 behavior depends on the used hardware.
48
49 Usage:
50
51 ser = RS485(...)
52 ser.rs485_mode = RS485Settings(...)
53 ser.write(b'hello')
54 """
55
56 def __init__(self, *args, **kwargs):
57 super(RS485, self).__init__(*args, **kwargs)
58 self._alternate_rs485_settings = None
59
60
61 def write(self, b):
62 """Write to port, controlling RTS before and after transmitting."""
63 if self._alternate_rs485_settings is not None:
64 # apply level for TX and optional delay
65 self.setRTS(self._alternate_rs485_settings.rts_level_for_tx)
66 if self._alternate_rs485_settings.delay_before_tx is not None:
67 time.sleep(self._alternate_rs485_settings.delay_before_tx)
68 # write and wait for data to be written
69 super(RS485, self).write(b)
70 super(RS485, self).flush()
71 # optional delay and apply level for RX
72 if self._alternate_rs485_settings.delay_before_rx is not None:
73 time.sleep(self._alternate_rs485_settings.delay_before_rx)
74 self.setRTS(self._alternate_rs485_settings.rts_level_for_rx)
75 else:
76 super(RS485, self).write(b)
77
78
79 # redirect where the property stores the settings so that underlying Serial
80 # instance does not see them
81 @property
82 def rs485_mode(self):
83 """\
84 Enable RS485 mode and apply new settings, set to None to disable.
85 See serial.rs485.RS485Settings for more info about the value.
86 """
87 return self._alternate_rs485_settings
88
89 @rs485_mode.setter
90 def rs485_mode(self, rs485_settings):
91 self._alternate_rs485_settings = rs485_settings
92
93