Java tutorial
/******************************************************************************* * Project: Droplet - Toolkit for Liquid Art Photographers * Copyright (C) 2012 Stefan Brenner * * This file is part of Droplet. * * Droplet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Droplet 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 Public License for more details. * * You should have received a copy of the GNU General Public License * along with Droplet. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package com.stefanbrenner.droplet.service.impl; import gnu.io.CommPortIdentifier; import gnu.io.PortInUseException; import gnu.io.SerialPort; import gnu.io.SerialPortEvent; import gnu.io.SerialPortEventListener; import gnu.io.UnsupportedCommOperationException; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.TooManyListenersException; import org.apache.commons.lang3.StringUtils; import org.mangosdk.spi.ProviderFor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.stefanbrenner.droplet.model.IDropletContext; import com.stefanbrenner.droplet.service.ISerialCommunicationService; /** * <p> * Service for serial communication with an Arduino microcontroller. * <p> * For more information on Arduino see <a * href="http://arduino.cc">http://arduino.cc</a> * * @author Stefan Brenner */ @ProviderFor(ISerialCommunicationService.class) public class ArduinoService implements ISerialCommunicationService, SerialPortEventListener { private static final Logger LOGGER = LoggerFactory.getLogger(ArduinoService.class); private static final char NEWLINE = '\n'; /** Milliseconds to block while waiting for port open. */ private static final int TIME_OUT = 2000; /** Default bits per second for COM port. */ private static final int DATA_RATE = 9600; /** connected port information. **/ private static SerialPort connSerialPort = null; /** input stream for sending data. **/ private static DataInputStream input = null; /** output streams for receiving data. **/ private static DataOutputStream output = null; /** flag that indicates if the service is currently connected to a port. **/ private static boolean connected = false; private static IDropletContext dropletContext; @Override public String getName() { return "Arduino Service"; } @Override public CommPortIdentifier[] getPorts() { List<CommPortIdentifier> ports = new ArrayList<CommPortIdentifier>(); Enumeration<?> portEnum = CommPortIdentifier.getPortIdentifiers(); // iterate through, looking for the port while (portEnum.hasMoreElements()) { CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement(); // add only serial ports if (currPortId.getPortType() == CommPortIdentifier.PORT_SERIAL) { ports.add(currPortId); } } return ports.toArray(new CommPortIdentifier[] {}); } @Override public synchronized boolean connect(final CommPortIdentifier portId, final IDropletContext context) { try { ArduinoService.dropletContext = context; ArduinoService.LOGGER.debug("Connect to port: " + portId.getName()); // try to open a connection to the serial port ArduinoService.connSerialPort = (SerialPort) portId.open(this.getClass().getName(), ArduinoService.TIME_OUT); // set port parameters ArduinoService.connSerialPort.setSerialPortParams(ArduinoService.DATA_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); // open the streams ArduinoService.input = new DataInputStream(ArduinoService.connSerialPort.getInputStream()); ArduinoService.output = new DataOutputStream(ArduinoService.connSerialPort.getOutputStream()); // add event listeners ArduinoService.connSerialPort.addEventListener(this); ArduinoService.connSerialPort.notifyOnDataAvailable(true); ArduinoService.connSerialPort.notifyOnCarrierDetect(true); // set connected flag setConnected(true); ArduinoService.LOGGER.info("Connection to port " + portId.getName() + " successful established"); return true; } catch (PortInUseException e) { ArduinoService.LOGGER.error("Error connecting to port {}", portId.getName(), e); } catch (UnsupportedCommOperationException e) { ArduinoService.LOGGER.error("Error connecting to port {}", portId.getName(), e); } catch (IOException e) { ArduinoService.LOGGER.error("Error connecting to port {}", portId.getName(), e); } catch (TooManyListenersException e) { ArduinoService.LOGGER.error("Error connecting to port {}", portId.getName(), e); } setConnected(false); return false; } @Override public synchronized void close() { try { if (ArduinoService.connSerialPort != null) { ArduinoService.LOGGER.debug("close connection to port {}", ArduinoService.connSerialPort.getName()); ArduinoService.connSerialPort.removeEventListener(); ArduinoService.connSerialPort.close(); ArduinoService.input.close(); ArduinoService.output.close(); setConnected(false); } } catch (IOException e) { ArduinoService.LOGGER.error("Error closing connection to port {}", ArduinoService.connSerialPort.getName(), e); } } @Override public void serialEvent(final SerialPortEvent event) { if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { int data; byte[] buffer = new byte[1024]; try { int len = 0; while ((data = ArduinoService.input.read()) > -1) { if (data == ArduinoService.NEWLINE) { break; } buffer[len++] = (byte) data; } // TODO brenner: integrate message protocol to parse result // message // add message to model String message = new String(buffer, 0, len); ArduinoService.LOGGER.debug("Received message: {}", message); ArduinoService.dropletContext.addLoggingMessage(message); } catch (IOException e) { ArduinoService.LOGGER.error("Error receiving data", e); } } } // TODO brenner: implement a two way synchronous communication protocol @Override public void sendData(final String message) { try { if (ArduinoService.output != null) { // split message by newline for (String msg : StringUtils.split(message, ArduinoService.NEWLINE)) { ArduinoService.LOGGER.debug("send message: {}", msg); // send byte by byte // for (byte b : msg.getBytes()) { // output.write(b); // output.flush(); // } ArduinoService.output.write(msg.getBytes()); // finally send newline ArduinoService.output.write(ArduinoService.NEWLINE); ArduinoService.output.flush(); // wait for Arduino to process input before we send next // message try { Thread.sleep(100); } catch (InterruptedException e) { } } // remove control characters // message = StringUtils.trim(message); // output.write(message.getBytes()); // append newline // if (!message.endsWith("\n")) { // output.write('\n'); // } // output.flush(); } else { throw new RuntimeException("Not connected to a port!"); //$NON-NLS-1$ } } catch (IOException e) { ArduinoService.LOGGER.error("Error sending data", e); } } @Override public synchronized boolean isConnected() { return ArduinoService.connected; } private void setConnected(final boolean connected) { ArduinoService.connected = connected; } }