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   * @param baudRate The baud rate to configure the serial port.
088   * @param port The Serial port to use
089   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
090   * @param parity Select the type of parity checking to use.
091   * @param stopBits The number of stop bits to use as defined by the enum StopBits.
092   */
093  public SerialPort(
094      final int baudRate, Port port, final int dataBits, Parity parity, StopBits stopBits) {
095    m_portHandle = SerialPortJNI.serialInitializePort((byte) port.value);
096    SerialPortJNI.serialSetBaudRate(m_portHandle, baudRate);
097    SerialPortJNI.serialSetDataBits(m_portHandle, (byte) dataBits);
098    SerialPortJNI.serialSetParity(m_portHandle, (byte) parity.value);
099    SerialPortJNI.serialSetStopBits(m_portHandle, (byte) stopBits.value);
100
101    // Set the default read buffer size to 1 to return bytes immediately
102    setReadBufferSize(1);
103
104    // Set the default timeout to 5 seconds.
105    setTimeout(5.0);
106
107    // Don't wait until the buffer is full to transmit.
108    setWriteBufferMode(WriteBufferMode.kFlushOnAccess);
109
110    disableTermination();
111
112    HAL.report(tResourceType.kResourceType_SerialPort, port.value + 1);
113  }
114
115  /**
116   * Create an instance of a Serial Port class. Defaults to one stop bit.
117   *
118   * @param baudRate The baud rate to configure the serial port.
119   * @param port The serial port to use.
120   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
121   * @param parity Select the type of parity checking to use.
122   */
123  public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity) {
124    this(baudRate, port, dataBits, parity, StopBits.kOne);
125  }
126
127  /**
128   * Create an instance of a Serial Port class. Defaults to no parity and one stop bit.
129   *
130   * @param baudRate The baud rate to configure the serial port.
131   * @param port The serial port to use.
132   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
133   */
134  public SerialPort(final int baudRate, Port port, final int dataBits) {
135    this(baudRate, port, dataBits, Parity.kNone, StopBits.kOne);
136  }
137
138  /**
139   * Create an instance of a Serial Port class. Defaults to 8 databits, no parity, and one stop bit.
140   *
141   * @param baudRate The baud rate to configure the serial port.
142   * @param port The serial port to use.
143   */
144  public SerialPort(final int baudRate, Port port) {
145    this(baudRate, port, 8, Parity.kNone, StopBits.kOne);
146  }
147
148  @Override
149  public void close() {
150    SerialPortJNI.serialClose(m_portHandle);
151  }
152
153  /**
154   * Set the type of flow control to enable on this port.
155   *
156   * <p>By default, flow control is disabled.
157   *
158   * @param flowControl the FlowControl m_value to use
159   */
160  public void setFlowControl(FlowControl flowControl) {
161    SerialPortJNI.serialSetFlowControl(m_portHandle, (byte) flowControl.value);
162  }
163
164  /**
165   * Enable termination and specify the termination character.
166   *
167   * <p>Termination is currently only implemented for receive. When the terminator is received, the
168   * read() or readString() will return fewer bytes than requested, stopping after the terminator.
169   *
170   * @param terminator The character to use for termination.
171   */
172  public void enableTermination(char terminator) {
173    SerialPortJNI.serialEnableTermination(m_portHandle, terminator);
174  }
175
176  /**
177   * Enable termination with the default terminator '\n'
178   *
179   * <p>Termination is currently only implemented for receive. When the terminator is received, the
180   * read() or readString() will return fewer bytes than requested, stopping after the terminator.
181   *
182   * <p>The default terminator is '\n'
183   */
184  public void enableTermination() {
185    enableTermination('\n');
186  }
187
188  /** Disable termination behavior. */
189  public final void disableTermination() {
190    SerialPortJNI.serialDisableTermination(m_portHandle);
191  }
192
193  /**
194   * Get the number of bytes currently available to read from the serial port.
195   *
196   * @return The number of bytes available to read.
197   */
198  public int getBytesReceived() {
199    return SerialPortJNI.serialGetBytesReceived(m_portHandle);
200  }
201
202  /**
203   * Read a string out of the buffer. Reads the entire contents of the buffer
204   *
205   * @return The read string
206   */
207  public String readString() {
208    return readString(getBytesReceived());
209  }
210
211  /**
212   * Read a string out of the buffer. Reads the entire contents of the buffer
213   *
214   * @param count the number of characters to read into the string
215   * @return The read string
216   */
217  public String readString(int count) {
218    byte[] out = read(count);
219    return new String(out, StandardCharsets.US_ASCII);
220  }
221
222  /**
223   * Read raw bytes out of the buffer.
224   *
225   * @param count The maximum number of bytes to read.
226   * @return An array of the read bytes
227   */
228  public byte[] read(final int count) {
229    byte[] dataReceivedBuffer = new byte[count];
230    int gotten = SerialPortJNI.serialRead(m_portHandle, dataReceivedBuffer, count);
231    if (gotten == count) {
232      return dataReceivedBuffer;
233    }
234    byte[] retVal = new byte[gotten];
235    System.arraycopy(dataReceivedBuffer, 0, retVal, 0, gotten);
236    return retVal;
237  }
238
239  /**
240   * Write raw bytes to the serial port.
241   *
242   * @param buffer The buffer of bytes to write.
243   * @param count The maximum number of bytes to write.
244   * @return The number of bytes actually written into the port.
245   */
246  public int write(byte[] buffer, int count) {
247    if (buffer.length < count) {
248      throw new IllegalArgumentException("buffer is too small, must be at least " + count);
249    }
250    return SerialPortJNI.serialWrite(m_portHandle, buffer, count);
251  }
252
253  /**
254   * Write a string to the serial port.
255   *
256   * @param data The string to write to the serial port.
257   * @return The number of bytes actually written into the port.
258   */
259  public int writeString(String data) {
260    return write(data.getBytes(StandardCharsets.UTF_8), data.length());
261  }
262
263  /**
264   * Configure the timeout of the serial m_port.
265   *
266   * <p>This defines the timeout for transactions with the hardware. It will affect reads if less
267   * bytes are available than the read buffer size (defaults to 1) and very large writes.
268   *
269   * @param timeout The number of seconds to wait for I/O.
270   */
271  public final void setTimeout(double timeout) {
272    SerialPortJNI.serialSetTimeout(m_portHandle, timeout);
273  }
274
275  /**
276   * Specify the size of the input buffer.
277   *
278   * <p>Specify the amount of data that can be stored before data from the device is returned to
279   * Read. If you want data that is received to be returned immediately, set this to 1.
280   *
281   * <p>It the buffer is not filled before the read timeout expires, all data that has been received
282   * so far will be returned.
283   *
284   * @param size The read buffer size.
285   */
286  public final void setReadBufferSize(int size) {
287    SerialPortJNI.serialSetReadBufferSize(m_portHandle, size);
288  }
289
290  /**
291   * Specify the size of the output buffer.
292   *
293   * <p>Specify the amount of data that can be stored before being transmitted to the device.
294   *
295   * @param size The write buffer size.
296   */
297  public void setWriteBufferSize(int size) {
298    SerialPortJNI.serialSetWriteBufferSize(m_portHandle, size);
299  }
300
301  /**
302   * Specify the flushing behavior of the output buffer.
303   *
304   * <p>When set to kFlushOnAccess, data is synchronously written to the serial port after each call
305   * to either print() or write().
306   *
307   * <p>When set to kFlushWhenFull, data will only be written to the serial port when the buffer is
308   * full or when flush() is called.
309   *
310   * @param mode The write buffer mode.
311   */
312  public final void setWriteBufferMode(WriteBufferMode mode) {
313    SerialPortJNI.serialSetWriteMode(m_portHandle, (byte) mode.value);
314  }
315
316  /**
317   * Force the output buffer to be written to the port.
318   *
319   * <p>This is used when setWriteBufferMode() is set to kFlushWhenFull to force a flush before the
320   * buffer is full.
321   */
322  public void flush() {
323    SerialPortJNI.serialFlush(m_portHandle);
324  }
325
326  /**
327   * Reset the serial port driver to a known state.
328   *
329   * <p>Empty the transmit and receive buffers in the device and formatted I/O.
330   */
331  public void reset() {
332    SerialPortJNI.serialClear(m_portHandle);
333  }
334}