All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pi4j.io.serial.impl.SerialImpl Maven / Gradle / Ivy

Go to download

This bundle wraps Pi4j (http://pi4j.com) that wraps the native code Wiring Pi (http://wiringpi.com). It wraps these libraries to make them OSGi friendly and allow them to work together with the OSGi enRoute IoT circuit library (osgi.enroute.iot.circuit). The bundle will first use Pi4J to detect on what hardware it runs. If it runs on an appropriate type, it will register a component that can be configured with Metatype. The Metatype defines a full blown configuration template for all the Pi's functions. The GPIO's are registered as components for the circuit. Regardless of the success of the configuration, this bundle will also register a GpioController service, which is the main Pi4J class.

There is a newer version: 2.1.0
Show newest version
package com.pi4j.io.serial.impl;

/*
 * #%L
 * **********************************************************************
 * ORGANIZATION  :  Pi4J
 * PROJECT       :  Pi4J :: Java Library (Core)
 * FILENAME      :  SerialImpl.java  
 * 
 * This file is part of the Pi4J project. More information about 
 * this project can be found here:  http://www.pi4j.com/
 * **********************************************************************
 * %%
 * Copyright (C) 2012 - 2015 Pi4J
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */


import com.pi4j.io.serial.*;
import com.pi4j.io.serial.tasks.SerialDataEventDispatchTaskImpl;
import com.pi4j.jni.SerialInterrupt;
import com.pi4j.jni.SerialInterruptEvent;
import com.pi4j.jni.SerialInterruptListener;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;

/**
 * 

This implementation class implements the 'Serial' interface using the WiringPi Serial library.

* *

* Before using the Pi4J library, you need to ensure that the Java VM in configured with access to * the following system libraries: *

    *
  • pi4j
  • *
  • wiringPi
  • *
*
This library depends on the wiringPi native system library.
(developed by * Gordon Henderson @ http://wiringpi.com/) *
*

* * @see com.pi4j.io.serial.Serial * @see com.pi4j.io.serial.SerialDataEvent * @see com.pi4j.io.serial.SerialDataEventListener * @see com.pi4j.io.serial.SerialFactory * * @see http://www.pi4j.com/ * @author Robert Savage (http://www.savagehomeautomation.com) */ public class SerialImpl extends AbstractSerialDataReaderWriter implements Serial { protected int fileDescriptor = -1; protected final CopyOnWriteArrayList listeners; protected final ExecutorService executor; protected final SerialByteBuffer receiveBuffer; protected boolean bufferingDataReceived = true; /** * default constructor */ public SerialImpl(){ listeners = new CopyOnWriteArrayList<>(); executor = SerialFactory.getExecutorServiceFactory().newSingleThreadExecutorService(); receiveBuffer = new SerialByteBuffer(); // register shutdown callback hook class Runtime.getRuntime().addShutdownHook(new ShutdownHook()); } /** * This class is used to perform any configured shutdown actions * for the serial impl * * @author Robert Savage * */ private class ShutdownHook extends Thread { public void run() { // close serial port if(isOpen()){ try { close(); } catch (IOException e) { e.printStackTrace(); } } // remove serial port listener SerialInterrupt.removeListener(fileDescriptor); // perform shutdown of any monitoring threads SerialFactory.shutdown(); } } /** *

* This opens and initializes the serial port/device and sets the communication parameters. * It sets the port into raw mode (character at a time and no translations). *

* *

* (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) *

* * @see #DEFAULT_COM_PORT * * @param device * The device address of the serial port to access. You can use constant * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the * GPIO header. * @param baud * The baud rate to use with the serial port. (Custom baud rate are not supported) * @param dataBits * The data bits to use for serial communication. (5,6,7,8) * @param parity * The parity setting to use for serial communication. (None, Event, Odd, Mark, Space) * @param stopBits * The stop bits to use for serial communication. (1,2) * @param flowControl * The flow control option to use for serial communication. (none, hardware, software) * * @throws IOException thrown on any error. */ @Override public void open(String device, int baud, int dataBits, int parity, int stopBits, int flowControl) throws IOException{ // open serial port fileDescriptor = com.pi4j.jni.Serial.open(device, baud, dataBits, parity, stopBits, flowControl); // read in initial buffered data (if any) into the receive buffer int available = com.pi4j.jni.Serial.available(fileDescriptor); if(available > 0) { byte[] initial_data = com.pi4j.jni.Serial.read(fileDescriptor, available); if (initial_data.length > 0) { try { // write data to the receive buffer receiveBuffer.write(initial_data); } catch (IOException e) { e.printStackTrace(); } } } // create a serial data listener event for data receive events from the serial device SerialInterrupt.addListener(fileDescriptor, new SerialInterruptListener() { @Override public void onDataReceive(SerialInterruptEvent event) { try { SerialDataEvent sde = null; if(isBufferingDataReceived()) { // stuff event data payload into the receive buffer receiveBuffer.write(event.getData()); //System.out.println("BUFFER SIZE : " + receiveBuffer.capacity()); //System.out.println("BUFFER LEFT : " + receiveBuffer.remaining()); //System.out.println("BUFFER AVAIL: " + receiveBuffer.available()); // create the serial data event; since we are buffering data // it will be located in the receive buffer sde = new SerialDataEvent(SerialImpl.this); } else{ // create the serial data event; since we are NOT buffering data // we will pass the specific data payload directly into the event sde = new SerialDataEvent(SerialImpl.this, event.getData()); } // add a new serial data event notification to the thread pool for *immediate* execution // we notify the event listeners on a separate thread to prevent blocking the native monitoring thread executor.execute(new SerialDataEventDispatchTaskImpl(sde, listeners)); } catch (IOException e) { e.printStackTrace(); } } }); // ensure file descriptor is valid if (fileDescriptor == -1) { throw new IOException("Cannot open serial port"); } } /** *

* This opens and initializes the serial port/device and sets the communication parameters. * It sets the port into raw mode (character at a time and no translations). * * This method will use the following default serial configuration parameters: * - DATA BITS = 8 * - PARITY = NONE * - STOP BITS = 1 * - FLOW CONTROL = NONE * *

* *

* (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) *

* * @see #DEFAULT_COM_PORT * * @param device * The device address of the serial port to access. You can use constant * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the * GPIO header. * @param baud * The baud rate to use with the serial port. * * @throws IOException thrown on any error. */ @Override public void open(String device, int baud) throws IOException{ // open the serial port with config settings of "8N1" and no flow control open(device, baud, com.pi4j.jni.Serial.DATA_BITS_8, com.pi4j.jni.Serial.PARITY_NONE, com.pi4j.jni.Serial.STOP_BITS_1, com.pi4j.jni.Serial.FLOW_CONTROL_NONE); } /** *

* This opens and initializes the serial port/device and sets the communication parameters. * It sets the port into raw mode (character at a time and no translations). *

* *

* (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) *

* * @see #DEFAULT_COM_PORT * * @param device * The device address of the serial port to access. You can use constant * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the * GPIO header. * @param baud * The baud rate to use with the serial port. * @param dataBits * The data bits to use for serial communication. (5,6,7,8) * @param parity * The parity setting to use for serial communication. (None, Event, Odd, Mark, Space) * @param stopBits * The stop bits to use for serial communication. (1,2) * @param flowControl * The flow control option to use for serial communication. (none, hardware, software) * * @throws IOException thrown on any error. */ @Override public void open(String device, Baud baud, DataBits dataBits, Parity parity, StopBits stopBits, FlowControl flowControl) throws IOException{ // open the serial port with NO ECHO and NO (forced) BUFFER FLUSH open(device, baud.getValue(), dataBits.getValue(), parity.getIndex(), stopBits.getValue(), flowControl.getIndex()); } /** *

* This opens and initializes the serial port/device and sets the communication parameters. * It sets the port into raw mode (character at a time and no translations). *

* *

* (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) *

* * @see #DEFAULT_COM_PORT * * @param serialConfig * A serial configuration object that contains the device, baud rate, data bits, parity, * stop bits, and flow control settings. * * @throws IOException thrown on any error. */ @Override public void open(SerialConfig serialConfig) throws IOException{ // open the serial port with config settings open(serialConfig.device(), serialConfig.baud().getValue(), serialConfig.dataBits().getValue(), serialConfig.parity().getIndex(), serialConfig.stopBits().getValue(), serialConfig.flowControl().getIndex()); } /** * This method is called to determine if the serial port is already open. * * @see #open(String, int) * @return a value of 'true' is returned if the serial port is already open. */ @Override public boolean isOpen() { return (fileDescriptor >= 0); } /** * This method is called to determine if the serial port is already closed. * * @see #open(String, int) * @return a value of 'true' is returned if the serial port is already in the closed state. */ @Override public boolean isClosed(){ return !(isOpen()); } /** * This method is called to close a currently open open serial port. * * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void close() throws IllegalStateException, IOException { // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'close()'."); // remove serial port listener SerialInterrupt.removeListener(fileDescriptor); // close serial port now com.pi4j.jni.Serial.close(fileDescriptor); // reset file descriptor fileDescriptor = -1; } /** *

* Forces the transmission of any remaining data in the serial port transmit buffer. * Please note that this does not force the transmission of data, it discards it! *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void flush() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'flush()'."); // flush data to serial port immediately com.pi4j.jni.Serial.flush(fileDescriptor); } /** *

* Discards any data in the serial receive (input) buffer. * Please note that this does not force the transmission of data, it discards it! *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void discardInput() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'discardInput()'."); // flush data to serial port immediately com.pi4j.jni.Serial.discardInput(fileDescriptor); } /** *

* Discards any data in the serial transmit (output) buffer. * Please note that this does not force the transmission of data, it discards it! *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void discardOutput() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'discardOutput()'."); // flush data to serial port immediately com.pi4j.jni.Serial.discardOutput(fileDescriptor); } /** *

* Discards any data in both the serial receive and transmit buffers. * Please note that this does not force the transmission of data, it discards it! *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void discardAll() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'discardAll()'."); // flush data to serial port immediately com.pi4j.jni.Serial.discardAll(fileDescriptor); } /** *

* Send a BREAK signal to connected device. *

* * @param duration * The length of time (milliseconds) to send the BREAK signal * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void sendBreak(int duration) throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'sendBreak()'."); // send BREAK signal to serial port immediately com.pi4j.jni.Serial.sendBreak(fileDescriptor, duration); } /** *

* Send a BREAK signal to connected device for at least 0.25 seconds, and not more than 0.5 seconds *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void sendBreak() throws IllegalStateException, IOException{ sendBreak(0); } /** *

* Send a constant BREAK signal to connected device. (Turn break on/off) * When enabled this will send a steady stream of zero bits. * When enabled, no (other) data transmitting is possible. *

* * @param enabled * The enable or disable state to control the BREAK signal * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void setBreak(boolean enabled) throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'setBreak()'."); // control the constant state of the BREAK signal com.pi4j.jni.Serial.setBreak(fileDescriptor, enabled); } /** *

* Control the RTS (request-to-send) pin state. * When enabled this will set the RTS pin to the HIGH state. *

* * @param enabled * The enable or disable state to control the RTS pin state. * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void setRTS(boolean enabled) throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'setRTS()'."); // control the constant state of the RTS signal com.pi4j.jni.Serial.setRTS(fileDescriptor, enabled); } /** *

* Control the DTR (data-terminal-ready) pin state. * When enabled this will set the DTR pin to the HIGH state. *

* * @param enabled * The enable or disable state to control the RTS pin state. * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void setDTR(boolean enabled) throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'setDTR()'."); // control the constant state of the DTR signal com.pi4j.jni.Serial.setDTR(fileDescriptor, enabled); } /** *

* Get the RTS (request-to-send) pin state. *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ public boolean getRTS() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'getRTS()'."); // get pin state return com.pi4j.jni.Serial.getRTS(fileDescriptor); } /** *

* Get the DTR (data-terminal-ready) pin state. *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ public boolean getDTR() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'getDTR()'."); // get pin state return com.pi4j.jni.Serial.getDTR(fileDescriptor); } /** *

* Get the CTS (clean-to-send) pin state. *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ public boolean getCTS() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'getCTS()'."); // get pin state return com.pi4j.jni.Serial.getCTS(fileDescriptor); } /** *

* Get the DSR (data-set-ready) pin state. *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ public boolean getDSR() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'getDSR()'."); // get pin state return com.pi4j.jni.Serial.getDSR(fileDescriptor); } /** *

* Get the RI (ring-indicator) pin state. *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ public boolean getRI() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'getRI()'."); // get pin state return com.pi4j.jni.Serial.getRI(fileDescriptor); } /** *

* Get the CD (carrier-detect) pin state. *

* * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ public boolean getCD() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'getCD()'."); // get pin state return com.pi4j.jni.Serial.getCD(fileDescriptor); } // ---------------------------------------- // READ OPERATIONS // ---------------------------------------- /** * Gets the number of bytes available for reading, or -1 for any error condition. * * @return Returns the number of bytes available for reading, or -1 for any error * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public int available() throws IllegalStateException, IOException { // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'available()'."); // get the number of available bytes in the serial port's receive buffer //return com.pi4j.jni.Serial.available(fileDescriptor); return receiveBuffer.getInputStream().available(); } /** *

Reads all available bytes from the serial port/device.

* * @return Returns a byte array with the data read from the serial port. * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public byte[] read() throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'read()'."); // read serial data from receive buffer byte[] buffer = new byte[available()]; receiveBuffer.getInputStream().read(buffer); return buffer; } /** *

Reads a length of bytes from the port/serial device.

* * @param length * The number of bytes to get from the serial port/device. * This number must not be higher than the number of available bytes. * * @return Returns a byte array with the data read from the serial port. * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public byte[] read(int length) throws IllegalStateException, IOException{ // validate state if (isClosed()) throw new IllegalStateException("Serial connection is not open; cannot 'read()'."); // read serial data from receive buffer byte[] buffer = new byte[length]; receiveBuffer.getInputStream().read(buffer, 0 , length); return buffer; } // ---------------------------------------- // WRITE OPERATIONS // ---------------------------------------- /** *

Sends an array of bytes to the serial port/device identified by the given file descriptor.

* * @param data * A ByteBuffer of data to be transmitted. * @param offset * The starting index (inclusive) in the array to send from. * @param length * The number of bytes from the byte array to transmit to the serial port. * @throws IllegalStateException thrown if the serial port is not already open. * @throws IOException thrown on any error. */ @Override public void write(byte[] data, int offset, int length) throws IllegalStateException, IOException{ // validate state if (isClosed()) { throw new IllegalStateException("Serial connection is not open; cannot 'write()'."); } // write serial data to transmit buffer com.pi4j.jni.Serial.write(fileDescriptor, data, offset, length); } // ---------------------------------------- // EVENT OPERATIONS // ---------------------------------------- /** *

Add Serial Event Listener

* *

Java consumer code can call this method to register itself as a listener for serial data * events.

* * @see com.pi4j.io.serial.SerialDataEventListener * @see com.pi4j.io.serial.SerialDataEvent * * @param listener A class instance that implements the SerialListener interface. */ @Override public synchronized void addListener(SerialDataEventListener... listener) { // add the new listener to the list of listeners Collections.addAll(listeners, listener); } /** *

Remove Serial Event Listener

* *

Java consumer code can call this method to unregister itself as a listener for serial data * events.

* * @see com.pi4j.io.serial.SerialDataEventListener * @see com.pi4j.io.serial.SerialDataEvent * * @param listener A class instance that implements the SerialListener interface. */ @Override public synchronized void removeListener(SerialDataEventListener... listener) { // remove the listener from the list of listeners for (SerialDataEventListener lsnr : listener) { listeners.remove(lsnr); } } /** * This method returns the serial device file descriptor * @return fileDescriptor file descriptor */ @Override public int getFileDescriptor() { return fileDescriptor; } /** * This method returns the input data stream for the serial port's receive buffer * @return InputStream input stream */ @Override public InputStream getInputStream() { return receiveBuffer.getInputStream(); } /** * This method returns the output data stream for the serial port's transmit buffer * @return OutputStream output stream */ @Override public OutputStream getOutputStream() { return new SerialOutputStream(); } /** * This method returns the buffering state for data received from the serial device/port. * @return 'true' if buffering is enabled; else 'false' */ @Override public boolean isBufferingDataReceived(){ return bufferingDataReceived; } /** *

* This method controls the buffering state for data received from the serial device/port. *

*

* If the buffering state is enabled, then all data bytes received from the serial port will * get copied into a data receive buffer. You can use the 'getInputStream()' or and of the 'read()' * methods to access this data. The data will also be available via the 'SerialDataEvent' event. * It is important to know that if you are using data buffering, the data will continue to grow * in memory until your program consume it from the data reader/stream. *

*

* If the buffering state is disabled, then all data bytes received from the serial port will NOT * get copied into the data receive buffer, but will be included in the 'SerialDataEvent' event's * data payload. If you program does not care about or use data received from the serial port, * then you should disable the data buffering state to prevent memory waste/leak. *

* * @param enabled sets the buffering behavior state */ @Override public void setBufferingDataReceived(boolean enabled){ bufferingDataReceived = enabled; } private class SerialOutputStream extends OutputStream { @Override public void write(byte b[]) throws IOException { SerialImpl.this.write(b); } @Override public void write(int b) throws IOException { SerialImpl.this.write((byte)b); } public void write(byte b[], int offset, int length) throws IOException { SerialImpl.this.write(b, offset, length); } @Override public void flush() throws IOException { SerialImpl.this.flush(); } } private class SerialInputStream extends InputStream { @Override public int read() throws IOException { return 0; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy