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