001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import java.io.UnsupportedEncodingException;
011
012import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
013import edu.wpi.first.wpilibj.hal.HAL;
014import edu.wpi.first.wpilibj.hal.SerialPortJNI;
015
016/**
017 * Driver for the RS-232 serial port on the roboRIO.
018 *
019 * <p>The current implementation uses the VISA formatted I/O mode. This means that all traffic goes
020 * through the formatted buffers. This allows the intermingled use of print(), readString(), and the
021 * raw buffer accessors read() and write().
022 *
023 * <p>More information can be found in the NI-VISA User Manual here: http://www.ni
024 * .com/pdf/manuals/370423a.pdf and the NI-VISA Programmer's Reference Manual here:
025 * http://www.ni.com/pdf/manuals/370132c.pdf
026 */
027public class SerialPort {
028  private byte m_port;
029
030  public enum Port {
031    kOnboard(0), kMXP(1), kUSB(2), kUSB1(2), kUSB2(3);
032
033    @SuppressWarnings("MemberName")
034    public int value;
035
036    Port(int value) {
037      this.value = value;
038    }
039  }
040
041  /**
042   * Represents the parity to use for serial communications.
043   */
044  public enum Parity {
045    kNone(0), kOdd(1), kEven(2), kMark(3), kSpace(4);
046
047    @SuppressWarnings("MemberName")
048    public final int value;
049
050    Parity(int value) {
051      this.value = value;
052    }
053  }
054
055  /**
056   * Represents the number of stop bits to use for Serial Communication.
057   */
058  public enum StopBits {
059    kOne(10), kOnePointFive(15), kTwo(20);
060
061    @SuppressWarnings("MemberName")
062    public final int value;
063
064    StopBits(int value) {
065      this.value = value;
066    }
067  }
068
069  /**
070   * Represents what type of flow control to use for serial communication.
071   */
072  public enum FlowControl {
073    kNone(0), kXonXoff(1), kRtsCts(2), kDtsDsr(4);
074
075    @SuppressWarnings("MemberName")
076    public final int value;
077
078    FlowControl(int value) {
079      this.value = value;
080    }
081  }
082
083  /**
084   * Represents which type of buffer mode to use when writing to a serial m_port.
085   */
086  public enum WriteBufferMode {
087    kFlushOnAccess(1), kFlushWhenFull(2);
088
089    @SuppressWarnings("MemberName")
090    public final int value;
091
092    WriteBufferMode(int value) {
093      this.value = value;
094    }
095  }
096
097  /**
098   * Create an instance of a Serial Port class.
099   *
100   * @param baudRate The baud rate to configure the serial port.
101   * @param port     The Serial port to use
102   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
103   * @param parity   Select the type of parity checking to use.
104   * @param stopBits The number of stop bits to use as defined by the enum StopBits.
105   */
106  public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity,
107                    StopBits stopBits) {
108    m_port = (byte) port.value;
109
110    SerialPortJNI.serialInitializePort(m_port);
111    SerialPortJNI.serialSetBaudRate(m_port, baudRate);
112    SerialPortJNI.serialSetDataBits(m_port, (byte) dataBits);
113    SerialPortJNI.serialSetParity(m_port, (byte) parity.value);
114    SerialPortJNI.serialSetStopBits(m_port, (byte) stopBits.value);
115
116    // Set the default read buffer size to 1 to return bytes immediately
117    setReadBufferSize(1);
118
119    // Set the default timeout to 5 seconds.
120    setTimeout(5.0);
121
122    // Don't wait until the buffer is full to transmit.
123    setWriteBufferMode(WriteBufferMode.kFlushOnAccess);
124
125    disableTermination();
126
127    HAL.report(tResourceType.kResourceType_SerialPort, 0);
128  }
129
130  /**
131   * Create an instance of a Serial Port class. Defaults to one stop bit.
132   *
133   * @param baudRate The baud rate to configure the serial port.
134   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
135   * @param parity   Select the type of parity checking to use.
136   */
137  public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity) {
138    this(baudRate, port, dataBits, parity, StopBits.kOne);
139  }
140
141  /**
142   * Create an instance of a Serial Port class. Defaults to no parity and one stop bit.
143   *
144   * @param baudRate The baud rate to configure the serial port.
145   * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
146   */
147  public SerialPort(final int baudRate, Port port, final int dataBits) {
148    this(baudRate, port, dataBits, Parity.kNone, StopBits.kOne);
149  }
150
151  /**
152   * Create an instance of a Serial Port class. Defaults to 8 databits, no parity, and one stop
153   * bit.
154   *
155   * @param baudRate The baud rate to configure the serial port.
156   */
157  public SerialPort(final int baudRate, Port port) {
158    this(baudRate, port, 8, Parity.kNone, StopBits.kOne);
159  }
160
161  /**
162   * Destructor.
163   */
164  public void free() {
165    SerialPortJNI.serialClose(m_port);
166  }
167
168  /**
169   * Set the type of flow control to enable on this port.
170   *
171   * <p>By default, flow control is disabled.
172   *
173   * @param flowControl the FlowControl m_value to use
174   */
175  public void setFlowControl(FlowControl flowControl) {
176    SerialPortJNI.serialSetFlowControl(m_port, (byte) flowControl.value);
177  }
178
179  /**
180   * Enable termination and specify the termination character.
181   *
182   * <p>Termination is currently only implemented for receive. When the the terminator is received,
183   * the read() or readString() will return fewer bytes than requested, stopping after the
184   * terminator.
185   *
186   * @param terminator The character to use for termination.
187   */
188  public void enableTermination(char terminator) {
189    SerialPortJNI.serialEnableTermination(m_port, terminator);
190  }
191
192  /**
193   * Enable termination with the default terminator '\n'
194   *
195   * <p>Termination is currently only implemented for receive. When the the terminator is received,
196   * the read() or readString() will return fewer bytes than requested, stopping after the
197   * terminator.
198   *
199   * <p>The default terminator is '\n'
200   */
201  public void enableTermination() {
202    enableTermination('\n');
203  }
204
205  /**
206   * Disable termination behavior.
207   */
208  public void disableTermination() {
209    SerialPortJNI.serialDisableTermination(m_port);
210  }
211
212  /**
213   * Get the number of bytes currently available to read from the serial port.
214   *
215   * @return The number of bytes available to read.
216   */
217  public int getBytesReceived() {
218    return SerialPortJNI.serialGetBytesReceived(m_port);
219  }
220
221  /**
222   * Read a string out of the buffer. Reads the entire contents of the buffer
223   *
224   * @return The read string
225   */
226  public String readString() {
227    return readString(getBytesReceived());
228  }
229
230  /**
231   * Read a string out of the buffer. Reads the entire contents of the buffer
232   *
233   * @param count the number of characters to read into the string
234   * @return The read string
235   */
236  public String readString(int count) {
237    byte[] out = read(count);
238    try {
239      return new String(out, 0, out.length, "US-ASCII");
240    } catch (UnsupportedEncodingException ex) {
241      ex.printStackTrace();
242      return "";
243    }
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_port, 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_port, 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(), 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 to wait for I/O.
294   */
295  public void setTimeout(double timeout) {
296    SerialPortJNI.serialSetTimeout(m_port, 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 void setReadBufferSize(int size) {
311    SerialPortJNI.serialSetReadBufferSize(m_port, 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_port, 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
329   * call to either print() or write().
330   *
331   * <p>When set to kFlushWhenFull, data will only be written to the serial port when the buffer
332   * is full or when flush() is called.
333   *
334   * @param mode The write buffer mode.
335   */
336  public void setWriteBufferMode(WriteBufferMode mode) {
337    SerialPortJNI.serialSetWriteMode(m_port, (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_port);
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_port);
357  }
358}