001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2012. 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/*----------------------------------------------------------------------------*/ 007package edu.wpi.first.wpilibj; 008 009import java.io.UnsupportedEncodingException; 010 011import java.nio.ByteOrder; 012import java.nio.IntBuffer; 013import java.nio.ByteBuffer; 014 015import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType; 016import edu.wpi.first.wpilibj.communication.UsageReporting; 017import edu.wpi.first.wpilibj.hal.HALLibrary; 018import edu.wpi.first.wpilibj.hal.HALUtil; 019import edu.wpi.first.wpilibj.hal.SerialPortJNI; 020 021/** 022 * Driver for the RS-232 serial port on the RoboRIO. 023 * 024 * The current implementation uses the VISA formatted I/O mode. This means that 025 * all traffic goes through the formatted buffers. This allows the intermingled 026 * use of print(), readString(), and the raw buffer accessors read() and write(). 027 * 028 * More information can be found in the NI-VISA User Manual here: 029 * http://www.ni.com/pdf/manuals/370423a.pdf 030 * and the NI-VISA Programmer's Reference Manual here: 031 * http://www.ni.com/pdf/manuals/370132c.pdf 032 */ 033public class SerialPort { 034 035 private byte m_port; 036 037 public enum Port { 038 kOnboard(0), 039 kMXP(1), 040 kUSB(2); 041 042 private int value; 043 044 private Port(int value){ 045 this.value = value; 046 } 047 048 public int getValue(){ 049 return this.value; 050 } 051 }; 052 053 /** 054 * Represents the parity to use for serial communications 055 */ 056 public static class Parity { 057 058 /** 059 * The integer value representing this enumeration 060 */ 061 public final int value; 062 static final int kNone_val = 0; 063 static final int kOdd_val = 1; 064 static final int kEven_val = 2; 065 static final int kMark_val = 3; 066 static final int kSpace_val = 4; 067 /** 068 * parity: Use no parity 069 */ 070 public static final Parity kNone = new Parity(kNone_val); 071 /** 072 * parity: Use odd parity 073 */ 074 public static final Parity kOdd = new Parity(kOdd_val); 075 /** 076 * parity: Use even parity 077 */ 078 public static final Parity kEven = new Parity(kEven_val); 079 /** 080 * parity: Use mark parity 081 */ 082 public static final Parity kMark = new Parity(kMark_val); 083 /** 084 * parity: Use space parity 085 */ 086 public static final Parity kSpace = new Parity((kSpace_val)); 087 088 private Parity(int value) { 089 this.value = value; 090 } 091 } 092 093 /** 094 * Represents the number of stop bits to use for Serial Communication 095 */ 096 public static class StopBits { 097 098 /** 099 * The integer value representing this enumeration 100 */ 101 public final int value; 102 static final int kOne_val = 10; 103 static final int kOnePointFive_val = 15; 104 static final int kTwo_val = 20; 105 /** 106 * stopBits: use 1 107 */ 108 public static final StopBits kOne = new StopBits(kOne_val); 109 /** 110 * stopBits: use 1.5 111 */ 112 public static final StopBits kOnePointFive = new StopBits(kOnePointFive_val); 113 /** 114 * stopBits: use 2 115 */ 116 public static final StopBits kTwo = new StopBits(kTwo_val); 117 118 private StopBits(int value) { 119 this.value = value; 120 } 121 } 122 123 /** 124 * Represents what type of flow control to use for serial communication 125 */ 126 public static class FlowControl { 127 128 /** 129 * The integer value representing this enumeration 130 */ 131 public final int value; 132 static final int kNone_val = 0; 133 static final int kXonXoff_val = 1; 134 static final int kRtsCts_val = 2; 135 static final int kDtrDsr_val = 4; 136 /** 137 * flowControl: use none 138 */ 139 public static final FlowControl kNone = new FlowControl(kNone_val); 140 /** 141 * flowcontrol: use on/off 142 */ 143 public static final FlowControl kXonXoff = new FlowControl(kXonXoff_val); 144 /** 145 * flowcontrol: use rts cts 146 */ 147 public static final FlowControl kRtsCts = new FlowControl(kRtsCts_val); 148 /** 149 * flowcontrol: use dts dsr 150 */ 151 public static final FlowControl kDtrDsr = new FlowControl(kDtrDsr_val); 152 153 private FlowControl(int value) { 154 this.value = value; 155 } 156 } 157 158 /** 159 * Represents which type of buffer mode to use when writing to a serial port 160 */ 161 public static class WriteBufferMode { 162 163 /** 164 * The integer value representing this enumeration 165 */ 166 public final int value; 167 static final int kFlushOnAccess_val = 1; 168 static final int kFlushWhenFull_val = 2; 169 /** 170 * Flush on access 171 */ 172 public static final WriteBufferMode kFlushOnAccess = new WriteBufferMode(kFlushOnAccess_val); 173 /** 174 * Flush when full 175 */ 176 public static final WriteBufferMode kFlushWhenFull = new WriteBufferMode(kFlushWhenFull_val); 177 178 private WriteBufferMode(int value) { 179 this.value = value; 180 } 181 } 182 183 /** 184 * Create an instance of a Serial Port class. 185 * 186 * @param baudRate The baud rate to configure the serial port. 187 * @param port The Serial port to use 188 * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits. 189 * @param parity Select the type of parity checking to use. 190 * @param stopBits The number of stop bits to use as defined by the enum StopBits. 191 */ 192 public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity, StopBits stopBits) { 193 ByteBuffer status = ByteBuffer.allocateDirect(4); 194 status.order(ByteOrder.LITTLE_ENDIAN); 195 m_port = (byte) port.getValue(); 196 197 SerialPortJNI.serialInitializePort(m_port, status.asIntBuffer()); 198 HALUtil.checkStatus(status.asIntBuffer()); 199 SerialPortJNI.serialSetBaudRate(m_port, baudRate, status.asIntBuffer()); 200 HALUtil.checkStatus(status.asIntBuffer()); 201 SerialPortJNI.serialSetDataBits(m_port, (byte) dataBits, status.asIntBuffer()); 202 HALUtil.checkStatus(status.asIntBuffer()); 203 SerialPortJNI.serialSetParity(m_port, (byte) parity.value, status.asIntBuffer()); 204 HALUtil.checkStatus(status.asIntBuffer()); 205 SerialPortJNI.serialSetStopBits(m_port, (byte) stopBits.value, status.asIntBuffer()); 206 HALUtil.checkStatus(status.asIntBuffer()); 207 208 // Set the default read buffer size to 1 to return bytes immediately 209 setReadBufferSize(1); 210 211 // Set the default timeout to 5 seconds. 212 setTimeout(5.0f); 213 214 // Don't wait until the buffer is full to transmit. 215 setWriteBufferMode(WriteBufferMode.kFlushOnAccess); 216 217 disableTermination(); 218 219 UsageReporting.report(tResourceType.kResourceType_SerialPort,0); 220 } 221 222 /** 223 * Create an instance of a Serial Port class. Defaults to one stop bit. 224 * 225 * @param baudRate The baud rate to configure the serial port. 226 * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits. 227 * @param parity Select the type of parity checking to use. 228 */ 229 public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity) { 230 this(baudRate, port, dataBits, parity, StopBits.kOne); 231 } 232 233 /** 234 * Create an instance of a Serial Port class. Defaults to no parity and one 235 * stop bit. 236 * 237 * @param baudRate The baud rate to configure the serial port. 238 * @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits. 239 */ 240 public SerialPort(final int baudRate, Port port, final int dataBits) { 241 this(baudRate, port, dataBits, Parity.kNone, StopBits.kOne); 242 } 243 244 /** 245 * Create an instance of a Serial Port class. Defaults to 8 databits, 246 * no parity, and one stop bit. 247 * 248 * @param baudRate The baud rate to configure the serial port. 249 */ 250 public SerialPort(final int baudRate, Port port) { 251 this(baudRate, port, 8, Parity.kNone, StopBits.kOne); 252 } 253 254 /** 255 * Destructor. 256 */ 257 public void free() { 258 ByteBuffer status = ByteBuffer.allocateDirect(4); 259 status.order(ByteOrder.LITTLE_ENDIAN); 260 SerialPortJNI.serialClose(m_port, status.asIntBuffer()); 261 HALUtil.checkStatus(status.asIntBuffer()); 262 } 263 264 /** 265 * Set the type of flow control to enable on this port. 266 * 267 * By default, flow control is disabled. 268 * @param flowControl the FlowControl value to use 269 */ 270 public void setFlowControl(FlowControl flowControl) { 271 ByteBuffer status = ByteBuffer.allocateDirect(4); 272 status.order(ByteOrder.LITTLE_ENDIAN); 273 SerialPortJNI.serialSetFlowControl(m_port, (byte) flowControl.value, status.asIntBuffer()); 274 HALUtil.checkStatus(status.asIntBuffer()); 275 } 276 277 /** 278 * Enable termination and specify the termination character. 279 * 280 * Termination is currently only implemented for receive. 281 * When the the terminator is received, the read() or readString() will return 282 * fewer bytes than requested, stopping after the terminator. 283 * 284 * @param terminator The character to use for termination. 285 */ 286 public void enableTermination(char terminator) { 287 ByteBuffer status = ByteBuffer.allocateDirect(4); 288 status.order(ByteOrder.LITTLE_ENDIAN); 289 SerialPortJNI.serialEnableTermination(m_port, terminator, status.asIntBuffer()); 290 HALUtil.checkStatus(status.asIntBuffer()); 291 } 292 293 /** 294 * Enable termination with the default terminator '\n' 295 * 296 * Termination is currently only implemented for receive. 297 * When the the terminator is received, the read() or readString() will return 298 * fewer bytes than requested, stopping after the terminator. 299 * 300 * The default terminator is '\n' 301 */ 302 public void enableTermination() { 303 this.enableTermination('\n'); 304 } 305 306 /** 307 * Disable termination behavior. 308 */ 309 public void disableTermination() { 310 ByteBuffer status = ByteBuffer.allocateDirect(4); 311 status.order(ByteOrder.LITTLE_ENDIAN); 312 SerialPortJNI.serialDisableTermination(m_port, status.asIntBuffer()); 313 HALUtil.checkStatus(status.asIntBuffer()); 314 } 315 316 /** 317 * Get the number of bytes currently available to read from the serial port. 318 * 319 * @return The number of bytes available to read. 320 */ 321 public int getBytesReceived() { 322 int retVal = 0; 323 ByteBuffer status = ByteBuffer.allocateDirect(4); 324 status.order(ByteOrder.LITTLE_ENDIAN); 325 retVal = SerialPortJNI.serialGetBytesRecieved(m_port, status.asIntBuffer()); 326 HALUtil.checkStatus(status.asIntBuffer()); 327 return retVal; 328 } 329 330 /** 331 * Read a string out of the buffer. Reads the entire contents of the buffer 332 * 333 * @return The read string 334 */ 335 public String readString() { 336 return readString(getBytesReceived()); 337 } 338 339 /** 340 * Read a string out of the buffer. Reads the entire contents of the buffer 341 * 342 * @param count the number of characters to read into the string 343 * @return The read string 344 */ 345 public String readString(int count) { 346 byte[] out = read(count); 347 try { 348 return new String(out, 0, count, "US-ASCII"); 349 } catch (UnsupportedEncodingException ex) { 350 ex.printStackTrace(); 351 return new String(); 352 } 353 } 354 355 /** 356 * Read raw bytes out of the buffer. 357 * 358 * @param count The maximum number of bytes to read. 359 * @return An array of the read bytes 360 */ 361 public byte[] read(final int count) { 362 ByteBuffer status = ByteBuffer.allocateDirect(4); 363 status.order(ByteOrder.LITTLE_ENDIAN); 364 ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(count); 365 SerialPortJNI.serialRead(m_port, dataReceivedBuffer, count, status.asIntBuffer()); 366 HALUtil.checkStatus(status.asIntBuffer()); 367 byte[] retVal = new byte[count]; 368 dataReceivedBuffer.get(retVal); 369 return retVal; 370 } 371 372 /** 373 * Write raw bytes to the serial port. 374 * 375 * @param buffer The buffer of bytes to write. 376 * @param count The maximum number of bytes to write. 377 * @return The number of bytes actually written into the port. 378 */ 379 public int write(byte[] buffer, int count) { 380 ByteBuffer status = ByteBuffer.allocateDirect(4); 381 status.order(ByteOrder.LITTLE_ENDIAN); 382 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(count); 383 dataToSendBuffer.put(buffer, 0, count); 384 int retVal = SerialPortJNI.serialWrite(m_port, dataToSendBuffer, count, status.asIntBuffer()); 385 HALUtil.checkStatus(status.asIntBuffer()); 386 return retVal; 387 } 388 389 /** 390 * Write a string to the serial port 391 * 392 * @param data The string to write to the serial port. 393 * @return The number of bytes actually written into the port. 394 */ 395 public int writeString(String data) { 396 return write(data.getBytes(), data.length()); 397 } 398 399 /** 400 * Configure the timeout of the serial port. 401 * 402 * This defines the timeout for transactions with the hardware. 403 * It will affect reads if less bytes are available than the 404 * read buffer size (defaults to 1) and very large writes. 405 * 406 * @param timeout The number of seconds to to wait for I/O. 407 */ 408 public void setTimeout(double timeout) { 409 ByteBuffer status = ByteBuffer.allocateDirect(4); 410 status.order(ByteOrder.LITTLE_ENDIAN); 411 SerialPortJNI.serialSetTimeout(m_port, (float)timeout, status.asIntBuffer()); 412 HALUtil.checkStatus(status.asIntBuffer()); 413 } 414 415 /** 416 * Specify the size of the input buffer. 417 * 418 * Specify the amount of data that can be stored before data 419 * from the device is returned to Read. If you want 420 * data that is received to be returned immediately, set this to 1. 421 * 422 * It the buffer is not filled before the read timeout expires, all 423 * data that has been received so far will be returned. 424 * 425 * @param size The read buffer size. 426 */ 427 public void setReadBufferSize(int size) { 428 ByteBuffer status = ByteBuffer.allocateDirect(4); 429 status.order(ByteOrder.LITTLE_ENDIAN); 430 SerialPortJNI.serialSetReadBufferSize(m_port, size, status.asIntBuffer()); 431 HALUtil.checkStatus(status.asIntBuffer()); 432 } 433 434 /** 435 * Specify the size of the output buffer. 436 * 437 * Specify the amount of data that can be stored before being 438 * transmitted to the device. 439 * 440 * @param size The write buffer size. 441 */ 442 public void setWriteBufferSize(int size) { 443 ByteBuffer status = ByteBuffer.allocateDirect(4); 444 status.order(ByteOrder.LITTLE_ENDIAN); 445 SerialPortJNI.serialSetWriteBufferSize(m_port, size, status.asIntBuffer()); 446 HALUtil.checkStatus(status.asIntBuffer()); 447 } 448 449 /** 450 * Specify the flushing behavior of the output buffer. 451 * 452 * When set to kFlushOnAccess, data is synchronously written to the serial port 453 * after each call to either print() or write(). 454 * 455 * When set to kFlushWhenFull, data will only be written to the serial port when 456 * the buffer is full or when flush() is called. 457 * 458 * @param mode The write buffer mode. 459 */ 460 public void setWriteBufferMode(WriteBufferMode mode) { 461 ByteBuffer status = ByteBuffer.allocateDirect(4); 462 status.order(ByteOrder.LITTLE_ENDIAN); 463 SerialPortJNI.serialSetWriteMode(m_port, (byte)mode.value, status.asIntBuffer()); 464 HALUtil.checkStatus(status.asIntBuffer()); 465 } 466 467 /** 468 * Force the output buffer to be written to the port. 469 * 470 * This is used when setWriteBufferMode() is set to kFlushWhenFull to force a 471 * flush before the buffer is full. 472 */ 473 public void flush() { 474 ByteBuffer status = ByteBuffer.allocateDirect(4); 475 status.order(ByteOrder.LITTLE_ENDIAN); 476 SerialPortJNI.serialFlush(m_port, status.asIntBuffer()); 477 HALUtil.checkStatus(status.asIntBuffer()); 478 } 479 480 /** 481 * Reset the serial port driver to a known state. 482 * 483 * Empty the transmit and receive buffers in the device and formatted I/O. 484 */ 485 public void reset() { 486 ByteBuffer status = ByteBuffer.allocateDirect(4); 487 status.order(ByteOrder.LITTLE_ENDIAN); 488 SerialPortJNI.serialClear(m_port, status.asIntBuffer()); 489 HALUtil.checkStatus(status.asIntBuffer()); 490 } 491}