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