001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj;
006
007import edu.wpi.first.hal.FRCNetComm.tResourceType;
008import edu.wpi.first.hal.HAL;
009import edu.wpi.first.hal.SerialPortJNI;
010import java.nio.charset.StandardCharsets;
011
012/** Driver for the serial ports (USB, MXP, Onboard) on the roboRIO. */
013public class SerialPort implements AutoCloseable {
014  private int m_portHandle;
015
016  public enum Port {
017    kOnboard(0),
018    kMXP(1),
019    kUSB(2),
020    kUSB1(2),
021    kUSB2(3);
022
023    public final int value;
024
025    Port(int value) {
026      this.value = value;
027    }
028  }
029
030  /** Represents the parity to use for serial communications. */
031  public enum Parity {
032    kNone(0),
033    kOdd(1),
034    kEven(2),
035    kMark(3),
036    kSpace(4);
037
038    public final int value;
039
040    Parity(int value) {
041      this.value = value;
042    }
043  }
044
045  /** Represents the number of stop bits to use for Serial Communication. */
046  public enum StopBits {
047    kOne(10),
048    kOnePointFive(15),
049    kTwo(20);
050
051    public final int value;
052
053    StopBits(int value) {
054      this.value = value;
055    }
056  }
057
058  /** Represents what type of flow control to use for serial communication. */
059  public enum FlowControl {
060    kNone(0),
061    kXonXoff(1),
062    kRtsCts(2),
063    kDtsDsr(4);
064
065    public final int value;
066
067    FlowControl(int value) {
068      this.value = value;
069    }
070  }
071
072  /** Represents which type of buffer mode to use when writing to a serial port. */
073  public enum WriteBufferMode {
074    kFlushOnAccess(1),
075    kFlushWhenFull(2);
076
077    public final int value;
078
079    WriteBufferMode(int value) {
080      this.value = value;
081    }
082  }
083
084  /**
085   * Create an instance of a Serial Port class.
086   *
087   * <p>Prefer to use the constructor that doesn't take a port name, but in some cases the automatic
088   * detection might not work correctly.
089   *
090   * @param baudRate The baud rate to configure the serial port.
091   * @param port The Serial port to use
092   * @param portName The direct portName to use
093   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
094   * @param parity Select the type of parity checking to use.
095   * @param stopBits The number of stop bits to use as defined by the enum StopBits.
096   * @deprecated Will be removed for 2019
097   */
098  @Deprecated
099  public SerialPort(
100      final int baudRate,
101      String portName,
102      Port port,
103      final int dataBits,
104      Parity parity,
105      StopBits stopBits) {
106    m_portHandle = SerialPortJNI.serialInitializePortDirect((byte) port.value, portName);
107    SerialPortJNI.serialSetBaudRate(m_portHandle, baudRate);
108    SerialPortJNI.serialSetDataBits(m_portHandle, (byte) dataBits);
109    SerialPortJNI.serialSetParity(m_portHandle, (byte) parity.value);
110    SerialPortJNI.serialSetStopBits(m_portHandle, (byte) stopBits.value);
111
112    // Set the default read buffer size to 1 to return bytes immediately
113    setReadBufferSize(1);
114
115    // Set the default timeout to 5 seconds.
116    setTimeout(5.0);
117
118    // Don't wait until the buffer is full to transmit.
119    setWriteBufferMode(WriteBufferMode.kFlushOnAccess);
120
121    disableTermination();
122
123    HAL.report(tResourceType.kResourceType_SerialPort, port.value + 1);
124  }
125
126  /**
127   * Create an instance of a Serial Port class.
128   *
129   * @param baudRate The baud rate to configure the serial port.
130   * @param port The Serial port to use
131   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
132   * @param parity Select the type of parity checking to use.
133   * @param stopBits The number of stop bits to use as defined by the enum StopBits.
134   */
135  public SerialPort(
136      final int baudRate, Port port, final int dataBits, Parity parity, StopBits stopBits) {
137    m_portHandle = SerialPortJNI.serialInitializePort((byte) port.value);
138    SerialPortJNI.serialSetBaudRate(m_portHandle, baudRate);
139    SerialPortJNI.serialSetDataBits(m_portHandle, (byte) dataBits);
140    SerialPortJNI.serialSetParity(m_portHandle, (byte) parity.value);
141    SerialPortJNI.serialSetStopBits(m_portHandle, (byte) stopBits.value);
142
143    // Set the default read buffer size to 1 to return bytes immediately
144    setReadBufferSize(1);
145
146    // Set the default timeout to 5 seconds.
147    setTimeout(5.0);
148
149    // Don't wait until the buffer is full to transmit.
150    setWriteBufferMode(WriteBufferMode.kFlushOnAccess);
151
152    disableTermination();
153
154    HAL.report(tResourceType.kResourceType_SerialPort, port.value + 1);
155  }
156
157  /**
158   * Create an instance of a Serial Port class. Defaults to one stop bit.
159   *
160   * @param baudRate The baud rate to configure the serial port.
161   * @param port The serial port to use.
162   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
163   * @param parity Select the type of parity checking to use.
164   */
165  public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity) {
166    this(baudRate, port, dataBits, parity, StopBits.kOne);
167  }
168
169  /**
170   * Create an instance of a Serial Port class. Defaults to no parity and one stop bit.
171   *
172   * @param baudRate The baud rate to configure the serial port.
173   * @param port The serial port to use.
174   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
175   */
176  public SerialPort(final int baudRate, Port port, final int dataBits) {
177    this(baudRate, port, dataBits, Parity.kNone, StopBits.kOne);
178  }
179
180  /**
181   * Create an instance of a Serial Port class. Defaults to 8 databits, no parity, and one stop bit.
182   *
183   * @param baudRate The baud rate to configure the serial port.
184   * @param port The serial port to use.
185   */
186  public SerialPort(final int baudRate, Port port) {
187    this(baudRate, port, 8, Parity.kNone, StopBits.kOne);
188  }
189
190  @Override
191  public void close() {
192    SerialPortJNI.serialClose(m_portHandle);
193  }
194
195  /**
196   * Set the type of flow control to enable on this port.
197   *
198   * <p>By default, flow control is disabled.
199   *
200   * @param flowControl the FlowControl m_value to use
201   */
202  public void setFlowControl(FlowControl flowControl) {
203    SerialPortJNI.serialSetFlowControl(m_portHandle, (byte) flowControl.value);
204  }
205
206  /**
207   * Enable termination and specify the termination character.
208   *
209   * <p>Termination is currently only implemented for receive. When the the terminator is received,
210   * the read() or readString() will return fewer bytes than requested, stopping after the
211   * terminator.
212   *
213   * @param terminator The character to use for termination.
214   */
215  public void enableTermination(char terminator) {
216    SerialPortJNI.serialEnableTermination(m_portHandle, terminator);
217  }
218
219  /**
220   * Enable termination with the default terminator '\n'
221   *
222   * <p>Termination is currently only implemented for receive. When the the terminator is received,
223   * the read() or readString() will return fewer bytes than requested, stopping after the
224   * terminator.
225   *
226   * <p>The default terminator is '\n'
227   */
228  public void enableTermination() {
229    enableTermination('\n');
230  }
231
232  /** Disable termination behavior. */
233  public void disableTermination() {
234    SerialPortJNI.serialDisableTermination(m_portHandle);
235  }
236
237  /**
238   * Get the number of bytes currently available to read from the serial port.
239   *
240   * @return The number of bytes available to read.
241   */
242  public int getBytesReceived() {
243    return SerialPortJNI.serialGetBytesReceived(m_portHandle);
244  }
245
246  /**
247   * Read a string out of the buffer. Reads the entire contents of the buffer
248   *
249   * @return The read string
250   */
251  public String readString() {
252    return readString(getBytesReceived());
253  }
254
255  /**
256   * Read a string out of the buffer. Reads the entire contents of the buffer
257   *
258   * @param count the number of characters to read into the string
259   * @return The read string
260   */
261  public String readString(int count) {
262    byte[] out = read(count);
263    return new String(out, 0, out.length, StandardCharsets.US_ASCII);
264  }
265
266  /**
267   * Read raw bytes out of the buffer.
268   *
269   * @param count The maximum number of bytes to read.
270   * @return An array of the read bytes
271   */
272  public byte[] read(final int count) {
273    byte[] dataReceivedBuffer = new byte[count];
274    int gotten = SerialPortJNI.serialRead(m_portHandle, dataReceivedBuffer, count);
275    if (gotten == count) {
276      return dataReceivedBuffer;
277    }
278    byte[] retVal = new byte[gotten];
279    System.arraycopy(dataReceivedBuffer, 0, retVal, 0, gotten);
280    return retVal;
281  }
282
283  /**
284   * Write raw bytes to the serial port.
285   *
286   * @param buffer The buffer of bytes to write.
287   * @param count The maximum number of bytes to write.
288   * @return The number of bytes actually written into the port.
289   */
290  public int write(byte[] buffer, int count) {
291    if (buffer.length < count) {
292      throw new IllegalArgumentException("buffer is too small, must be at least " + count);
293    }
294    return SerialPortJNI.serialWrite(m_portHandle, buffer, count);
295  }
296
297  /**
298   * Write a string to the serial port.
299   *
300   * @param data The string to write to the serial port.
301   * @return The number of bytes actually written into the port.
302   */
303  public int writeString(String data) {
304    return write(data.getBytes(StandardCharsets.UTF_8), data.length());
305  }
306
307  /**
308   * Configure the timeout of the serial m_port.
309   *
310   * <p>This defines the timeout for transactions with the hardware. It will affect reads if less
311   * bytes are available than the read buffer size (defaults to 1) and very large writes.
312   *
313   * @param timeout The number of seconds to to wait for I/O.
314   */
315  public void setTimeout(double timeout) {
316    SerialPortJNI.serialSetTimeout(m_portHandle, timeout);
317  }
318
319  /**
320   * Specify the size of the input buffer.
321   *
322   * <p>Specify the amount of data that can be stored before data from the device is returned to
323   * Read. If you want data that is received to be returned immediately, set this to 1.
324   *
325   * <p>It the buffer is not filled before the read timeout expires, all data that has been received
326   * so far will be returned.
327   *
328   * @param size The read buffer size.
329   */
330  public void setReadBufferSize(int size) {
331    SerialPortJNI.serialSetReadBufferSize(m_portHandle, size);
332  }
333
334  /**
335   * Specify the size of the output buffer.
336   *
337   * <p>Specify the amount of data that can be stored before being transmitted to the device.
338   *
339   * @param size The write buffer size.
340   */
341  public void setWriteBufferSize(int size) {
342    SerialPortJNI.serialSetWriteBufferSize(m_portHandle, size);
343  }
344
345  /**
346   * Specify the flushing behavior of the output buffer.
347   *
348   * <p>When set to kFlushOnAccess, data is synchronously written to the serial port after each call
349   * to either print() or write().
350   *
351   * <p>When set to kFlushWhenFull, data will only be written to the serial port when the buffer is
352   * full or when flush() is called.
353   *
354   * @param mode The write buffer mode.
355   */
356  public void setWriteBufferMode(WriteBufferMode mode) {
357    SerialPortJNI.serialSetWriteMode(m_portHandle, (byte) mode.value);
358  }
359
360  /**
361   * Force the output buffer to be written to the port.
362   *
363   * <p>This is used when setWriteBufferMode() is set to kFlushWhenFull to force a flush before the
364   * buffer is full.
365   */
366  public void flush() {
367    SerialPortJNI.serialFlush(m_portHandle);
368  }
369
370  /**
371   * Reset the serial port driver to a known state.
372   *
373   * <p>Empty the transmit and receive buffers in the device and formatted I/O.
374   */
375  public void reset() {
376    SerialPortJNI.serialClear(m_portHandle);
377  }
378}