Method: OpenC3::Win32SerialDriver#initialize

Defined in:
lib/openc3/io/win32_serial_driver.rb

#initialize(port_name = 'COM1', baud_rate = 9600, parity = :NONE, stop_bits = 1, write_timeout = 10.0, read_timeout = nil, read_polling_period = 0.01, read_max_length = 1000, flow_control = :NONE, data_bits = 8) ⇒ Win32SerialDriver

Returns a new instance of Win32SerialDriver.

Parameters:

  • port_name (String) (defaults to: 'COM1')

    Name of the serial port

  • baud_rate (Integer) (defaults to: 9600)

    Serial port baud rate

  • parity (Symbol) (defaults to: :NONE)

    Must be one of :EVEN, :ODD or :NONE

  • stop_bits (Integer) (defaults to: 1)

    Number of stop bits

  • write_timeout (Float) (defaults to: 10.0)

    Seconds to wait before aborting writes

  • read_timeout (Float|nil) (defaults to: nil)

    Seconds to wait before aborting reads. Pass nil to block until the read is complete.

  • flow_control (Symbol) (defaults to: :NONE)

    Currently supported :NONE and :RTSCTS (default :NONE)

  • data_bits (Integer) (defaults to: 8)

    Number of data bits (default 8)

Raises:

  • (ArgumentError)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/openc3/io/win32_serial_driver.rb', line 25

def initialize(port_name = 'COM1',
               baud_rate = 9600,
               parity = :NONE,
               stop_bits = 1,
               write_timeout = 10.0,
               read_timeout = nil,
               read_polling_period = 0.01,
               read_max_length = 1000,
               flow_control = :NONE,
               data_bits = 8)

  # Verify Parameters
  port_name = '\\\\.\\' + port_name if /^COM[0-9]{2,3}$/.match?(port_name)

  raise(ArgumentError, "Invalid baud rate: #{baud_rate}") unless baud_rate.between?(Win32::BAUD_RATES[0], Win32::BAUD_RATES[-1])
  raise(ArgumentError, "Invalid data bits: #{data_bits}") unless [5, 6, 7, 8].include?(data_bits)
  raise(ArgumentError, "Invalid parity: #{parity}") if parity and !SerialDriver::VALID_PARITY.include?(parity)

  case parity
  when SerialDriver::ODD
    parity = Win32::ODDPARITY
  when SerialDriver::EVEN
    parity = Win32::EVENPARITY
  when SerialDriver::NONE
    parity = Win32::NOPARITY
  end

  raise(ArgumentError, "Invalid stop bits: #{stop_bits}") unless [1, 2].include?(stop_bits)

  if stop_bits == 1
    stop_bits = Win32::ONESTOPBIT
  else
    stop_bits = Win32::TWOSTOPBITS
  end

  @write_timeout = write_timeout
  @read_timeout = read_timeout
  @read_polling_period = read_polling_period
  @read_max_length = read_max_length

  # Open the Comm Port
  @handle = Win32.create_file(port_name,
                              Win32::GENERIC_READ | Win32::GENERIC_WRITE,
                              0,
                              Win32::NULL,
                              Win32::OPEN_EXISTING,
                              Win32::FILE_ATTRIBUTE_NORMAL)

  @mutex = Mutex.new

  # Configure the Comm Port - See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx
  dcb = Win32.get_comm_state(@handle)
  dcb.write('BaudRate', baud_rate)
  dcb.write('ByteSize', data_bits)
  dcb.write('Parity',   parity)
  dcb.write('StopBits', stop_bits)
  if flow_control == :RTSCTS
    # Monitor CTS
    dcb.write('fOutxCtsFlow', 1)

    # 0x00 - RTS_CONTROL_DISABLE - Disables the RTS line when the device is opened and leaves it disabled.
    # 0x01 - RTS_CONTROL_ENABLE - Enables the RTS line when the device is opened and leaves it on.
    # 0x02 - RTS_CONTROL_HANDSHAKE - Enables RTS handshaking. The driver raises the RTS line when the "type-ahead" (input) buffer is less than one-half full and lowers the RTS line when the buffer is more than three-quarters full. If handshaking is enabled, it is an error for the application to adjust the line by using the EscapeCommFunction function.
    # 0x03 - RTS_CONTROL_TOGGLE - Specifies that the RTS line will be high if bytes are available for transmission. After all buffered bytes have been sent, the RTS line will be low.
    dcb.write('fRtsControl', 0x03)
  end
  Win32.set_comm_state(@handle, dcb)

  # Configure Timeouts, the WinAPI structure is COMMTIMEOUTS:
  #   DWORD ReadIntervalTimeout;
  #   DWORD ReadTotalTimeoutMultiplier;
  #   DWORD ReadTotalTimeoutConstant;
  #   DWORD WriteTotalTimeoutMultiplier;
  #   DWORD WriteTotalTimeoutConstant;
  # 0xFFFFFFFF, 0, 0 specifies that the read operation is to return immediately
  # with the bytes that have already been received, even if no bytes have been received.
  # The WriteTotalTimeoutMultiplier is multiplied by the number of bytes to be written
  # and the WriteTotalTimeoutConstant is added to that total (both are in milliseconds).
  bits_per_symbol = data_bits + 1 # 1 start bit
  case stop_bits
  when Win32::ONESTOPBIT
    bits_per_symbol += 1
  when Win32::TWOSTOPBITS
    bits_per_symbol += 2
  end
  case parity
  when Win32::ODDPARITY, Win32::EVENPARITY
    bits_per_symbol += 1
  end
  delay = (1000.0 / (baud_rate / bits_per_symbol.to_f)).ceil
  Win32.set_comm_timeouts(@handle, 0xFFFFFFFF, 0, 0, delay, 1000)
end