001package com.pi4j.io.serial.impl; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : SerialImpl.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: http://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2013 Pi4J 015 * %% 016 * Licensed under the Apache License, Version 2.0 (the "License"); 017 * you may not use this file except in compliance with the License. 018 * You may obtain a copy of the License at 019 * 020 * http://www.apache.org/licenses/LICENSE-2.0 021 * 022 * Unless required by applicable law or agreed to in writing, software 023 * distributed under the License is distributed on an "AS IS" BASIS, 024 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 025 * See the License for the specific language governing permissions and 026 * limitations under the License. 027 * #L% 028 */ 029 030 031import java.util.concurrent.CopyOnWriteArrayList; 032 033import com.pi4j.io.serial.Serial; 034import com.pi4j.io.serial.SerialDataListener; 035import com.pi4j.io.serial.SerialPortException; 036 037/** 038 * <p> This implementation class implements the 'Serial' interface using the WiringPi Serial library.</p> 039 * 040 * <p> 041 * Before using the Pi4J library, you need to ensure that the Java VM in configured with access to 042 * the following system libraries: 043 * <ul> 044 * <li>pi4j</li> 045 * <li>wiringPi</li> 046 * </ul> 047 * <blockquote> This library depends on the wiringPi native system library.</br> (developed by 048 * Gordon Henderson @ <a href="https://projects.drogon.net/">https://projects.drogon.net/</a>) 049 * </blockquote> 050 * </p> 051 * 052 * @see #com.pi4j.io.serial.Serial 053 * @see #com.pi4j.io.serial.SerialDataEvent 054 * @see #com.pi4j.io.serial.SerialDataListener 055 * @see #com.pi4j.io.serial.SerialFactory 056 * 057 * @see <a href="http://www.pi4j.com/">http://www.pi4j.com/</a> 058 * @author Robert Savage (<a 059 * href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>) 060 */ 061public class SerialImpl implements Serial { 062 063 protected int fileDescriptor = -1; 064 protected final CopyOnWriteArrayList<SerialDataListener> listeners = new CopyOnWriteArrayList<SerialDataListener>(); 065 protected SerialDataMonitorThread monitor; 066 protected boolean isshutdown = false; 067 068 /** 069 * This method is call to open a serial port for communication. Throws SerialException on error. 070 * 071 * @see #DEFAULT_COM_PORT 072 * 073 * @param device The device address of the serial port to access. You can use constant 074 * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the 075 * GPIO header. 076 * @param baudRate The baud rate to use with the serial port. 077 * @return The return value is the file descriptor. 078 * @throws SerialPortException Exception thrown on any error. 079 */ 080 public void open(String device, int baudRate) throws SerialPortException { 081 fileDescriptor = com.pi4j.wiringpi.Serial.serialOpen(device, baudRate); 082 if (fileDescriptor == -1) { 083 throw new SerialPortException("Cannot open serial port"); 084 } 085 } 086 087 /** 088 * This method is called to determine if the serial port is already open. 089 * 090 * @see #open(String, int) 091 * @return a value of 'true' is returned if the serial port is already open. 092 */ 093 public boolean isOpen() { 094 return (fileDescriptor >= 0); 095 } 096 097 /** 098 * This method is called to determine if the serial port is already closed. 099 * 100 * @see #open(String, int) 101 * @return a value of 'true' is returned if the serial port is already in the closed state. 102 */ 103 public boolean isClosed(){ 104 return !(isOpen()); 105 } 106 107 108 /** 109 * This method is called to close a currently open open serial port. 110 */ 111 public void close() throws IllegalStateException { 112 113 // validate state 114 if (isClosed()) 115 throw new IllegalStateException("Serial connection is not open; cannot 'close()'."); 116 117 // close serial port now 118 com.pi4j.wiringpi.Serial.serialClose(fileDescriptor); 119 } 120 121 /** 122 * This method is called to immediately flush the serial data transmit buffer and force any 123 * pending data to be sent to the serial port immediately. 124 */ 125 public void flush() throws IllegalStateException { 126 127 // validate state 128 if (isClosed()) 129 throw new IllegalStateException("Serial connection is not open; cannot 'flush()'."); 130 131 // flush data to serial port immediately 132 com.pi4j.wiringpi.Serial.serialFlush(fileDescriptor); 133 } 134 135 /** 136 * <p> This method will read the next character available from the serial port receive buffer.</p> 137 * <p> 138 * <b>NOTE: If a serial data listener has been implemented and registered with this class, then 139 * this method should not be called directly. A background thread will be running to collect 140 * received data from the serial port receive buffer and the received data will be available on 141 * via the event.</b> 142 * </p> 143 * 144 * @return next available character in the serial data buffer 145 */ 146 public char read() throws IllegalStateException { 147 148 // validate state 149 if (isClosed()) 150 throw new IllegalStateException("Serial connection is not open; cannot 'read()'."); 151 152 // attempt read byte from serial port 153 return (char) com.pi4j.wiringpi.Serial.serialGetchar(fileDescriptor); 154 } 155 156 /** 157 * This method is called to submit a single character of data to the serial port transmit 158 * buffer. 159 * 160 * @param data A single character to be transmitted. 161 */ 162 public void write(char data) throws IllegalStateException { 163 164 // validate state 165 if (isClosed()) 166 throw new IllegalStateException("Serial connection is not open; cannot 'write(char)'."); 167 168 // write character to serial port 169 com.pi4j.wiringpi.Serial.serialPutchar(fileDescriptor, data); 170 } 171 172 /** 173 * This method is called to submit a character array of data to the serial port transmit buffer. 174 * 175 * @param data A character array of data to be transmitted. 176 */ 177 public void write(char data[]) throws IllegalStateException { 178 179 // validate state 180 if (isClosed()) 181 throw new IllegalStateException("Serial connection is not open; cannot 'write(char[])'."); 182 183 // write character array to serial port 184 write(new String(data)); 185 } 186 187 /** 188 * This method is called to submit a single byte of data to the serial port transmit buffer. 189 * 190 * @param data A single byte to be transmitted. 191 */ 192 public void write(byte data) throws IllegalStateException { 193 194 // validate state 195 if (isClosed()) 196 throw new IllegalStateException("Serial connection is not open; cannot 'write(byte)'."); 197 198 // write byte to serial port 199 com.pi4j.wiringpi.Serial.serialPutchar(fileDescriptor, (char) data); 200 } 201 202 /** 203 * This method is called to submit a byte array of data to the serial port transmit buffer. 204 * 205 * @param data A byte array of data to be transmitted. 206 */ 207 public void write(byte data[]) throws IllegalStateException { 208 209 // validate state 210 if (isClosed()) 211 throw new IllegalStateException("Serial connection is not open; cannot 'write(byte[])'."); 212 213 // write byte array to serial port 214 for(byte b : data){ 215 // write byte to serial port 216 com.pi4j.wiringpi.Serial.serialPutchar(fileDescriptor, (char)b); 217 } 218 } 219 220 /** 221 * This method is called to submit a string of data to the serial port transmit buffer. 222 * 223 * @param data A string of data to be transmitted. 224 */ 225 public void write(String data) throws IllegalStateException { 226 227 // validate state 228 if (isClosed()) 229 throw new IllegalStateException("Serial connection is not open; cannot 'write(String)'."); 230 231 // break data into packets of 1024 bytes 232 int position = 0; 233 while (position < data.length()) { 234 int length = 1024; 235 if (position + 1024 > data.length()) { 236 com.pi4j.wiringpi.Serial.serialPuts(fileDescriptor, data.substring(position)); 237 } else { 238 com.pi4j.wiringpi.Serial.serialPuts(fileDescriptor, 239 data.substring(position, (position + length))); 240 } 241 position += length; 242 } 243 } 244 245 /** 246 * This method is called to submit a string of data with trailing CR + LF characters to the 247 * serial port transmit buffer. 248 * 249 * @param data A string of data to be transmitted. 250 */ 251 public void writeln(String data) throws IllegalStateException { 252 253 // validate state 254 if (isClosed()) 255 throw new IllegalStateException("Serial connection is not open; cannot 'writeln(String)'."); 256 257 // write string with CR+LF to serial port 258 write(data + "\r\n"); 259 } 260 261 /** 262 * This method is called to submit a string of formatted data to the serial port transmit 263 * buffer. 264 * 265 * @param data A string of formatted data to be transmitted. 266 * @param args A series of arguments that can be included for the format string variable 267 * replacements. 268 */ 269 public void write(String data, String... args) throws IllegalStateException { 270 271 // validate state 272 if (isClosed()) 273 throw new IllegalStateException("Serial connection is not open; cannot 'write(String data, String...args)'."); 274 275 // write formatted string to serial port 276 write(String.format(data, (Object[]) args)); 277 } 278 279 /** 280 * This method is called to submit a string of formatted data with trailing CR + LF characters 281 * to the serial port transmit buffer. 282 * 283 * @param data A string of formatted data to be transmitted. 284 * @param args A series of arguments that can be included for the format string variable 285 * replacements. 286 */ 287 public void writeln(String data, String... args) throws IllegalStateException { 288 289 // validate state 290 if (isClosed()) 291 throw new IllegalStateException("Serial connection is not open; cannot 'writeln(String data, String...args)'."); 292 293 // write formatted string with CR+LF to serial port 294 write(data + "\r\n", args); 295 } 296 297 /** 298 * This method is called to determine if and how many bytes are available on the serial received 299 * data buffer. 300 * 301 * @return The number of available bytes pending in the serial received buffer is returned. 302 */ 303 public int availableBytes() throws IllegalStateException { 304 305 // validate state 306 if (isClosed()) 307 throw new IllegalStateException("Serial connection is not open; cannot 'availableBytes()'."); 308 309 // return the number of bytes available in the serial buffer 310 return com.pi4j.wiringpi.Serial.serialDataAvail(fileDescriptor); 311 } 312 313 /** 314 * <p>Add Serial Event Listener</p> 315 * 316 * <p> Java consumer code can call this method to register itself as a listener for serial data 317 * events. </p> 318 * 319 * @see #com.pi4j.io.serial.SerialDataListener 320 * @see #com.pi4j.io.serial.SerialDataEvent 321 * 322 * @param listener A class instance that implements the SerialListener interface. 323 */ 324 public synchronized void addListener(SerialDataListener... listener) { 325 // add the new listener to the list of listeners 326 for (SerialDataListener lsnr : listener) { 327 listeners.add(lsnr); 328 } 329 330 // if there is not a current listening monitor thread running, 331 // then lets start it now 332 if (monitor == null || monitor.isAlive() == false) { 333 monitor = new SerialDataMonitorThread(this, listeners); 334 monitor.start(); 335 } 336 } 337 338 /** 339 * <p>Remove Serial Event Listener</p> 340 * 341 * <p> Java consumer code can call this method to unregister itself as a listener for serial data 342 * events. </p> 343 * 344 * @see #com.pi4j.io.serial.SerialDataListener 345 * @see #com.pi4j.io.serial.SerialDataEvent 346 * 347 * @param listener A class instance that implements the SerialListener interface. 348 */ 349 public synchronized void removeListener(SerialDataListener... listener) { 350 // remove the listener from the list of listeners 351 for (SerialDataListener lsnr : listener) { 352 listeners.remove(lsnr); 353 } 354 355 // if there are not more listeners, then exit and destroy 356 // the monitor thread now 357 if (listeners.isEmpty() && monitor != null) { 358 monitor.shutdown(); 359 monitor = null; 360 } 361 } 362 363 /** 364 * This method returns TRUE if the serial interface has been shutdown. 365 * 366 * @return shutdown state 367 */ 368 @Override 369 public boolean isShutdown(){ 370 return isshutdown; 371 } 372 373 374 /** 375 * This method can be called to forcefully shutdown all 376 * serial data monitoring threads. 377 */ 378 @Override 379 public synchronized void shutdown() 380 { 381 // close serial port if still open 382 if(isOpen()) 383 close(); 384 385 // prevent reentrant invocation 386 if(isShutdown()) 387 return; 388 389 // shutdown monitoring thread 390 if(monitor != null) 391 monitor.shutdown(); 392 } 393}