/** * ##library.name## * ##library.sentence## * ##library.url## * * Copyright ##copyright## ##author## * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA * * @author Quin Kennedy & Julio Terra from LAB at Rockwell Group * http://www.rockwellgroup.com * @credits Code for extracting arduino serial id was adapted from * work by Dave Vondle http://labs.ideo.com * @modified 12/01/20122 * @version ##library.prettyVersion## (##library.version##) */ package com.rockwellgroup.arduinoconnect; import processing.serial.*; import processing.core.*; import java.lang.ProcessBuilder; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Hashtable; /** * ArduinoConnect A class that connects to Arduinos by using unique Arduino serial ids. */ public class ArduinoConnect { public static final String SERIAL_NUMBER = "Serial Number: "; public static final String LOCATION = "Location ID: "; public static final String REGEX_ID = "^0x0*([1-9a-fA-F][0-9a-fA-F]*[1-9a-fA-F])0*"; public static final Pattern REGEX_PAT = Pattern.compile(REGEX_ID); public static final char CB_FLAG = '\n'; public static boolean DEBUG = false; public Serial connection; private PApplet app; private boolean connected; private boolean debug; /** * constructor method that initializes key variables * @param Link to applet that is linking to the arduino */ public ArduinoConnect(PApplet _app) { app = _app; debug = false; connected = false; } /** * constructor method that initializes key variables * @param Link to applet that is linking to the Arduino * @param Holds value for debug flag */ public ArduinoConnect(PApplet _app, boolean _debug) { app = _app; debug = _debug; connected = false; } /** * connect Attempts to connect to an arduino by looking up the arduino_name in a map that * is saved in the file sated in file_path. * @param file_path Holds the path to the file that holds the hashmap * @param device_name Holds the name of the arduino on the map * @param line_div Holds character that divides keys from values on the map * @param baud Holds the baud rate of the arduino serial connection * @return returns true if connection established */ public boolean connect(String file_path, String arduino_name, char line_div, int baud) { String device_name; String serial_list [] = Serial.list(); try { device_name = ArduinoConnect.getSerialPortName(arduino_name, file_path, line_div); for(int i = (serial_list.length - 1); i >= 0; i--){ if (serial_list[i].indexOf(device_name) == 0) { if (this.debug) PApplet.println("[connect] (device_name, baud) connecting to serial device at index: " + i); return connect(i, baud); } } } catch (Exception e) { PApplet.println("[connect] (file_path, arduino_name, baud) ERROR file not found or error reading file"); return false; } return false; } /** * connect Attempts to connect to the serial device at a specified port or by using a device id * @param device_name Holds the name of the serial port or device id to which we will connect * @param baud Holds the baud rate of the serial connection * @return returns true if connection established */ public boolean connect(String device_name, int baud) { return connect(device_name, baud, CB_FLAG); } /** * connect Attempts to connect to the serial device at a specified port or by using a device id * @param device_name Holds the name of the serial port or device id to which we will connect * @param baud Holds the baud rate of the serial connection * @param cb_flag Holds the char that triggers call of the serialEvent method * @return returns true if connection established */ public boolean connect(String device_name, int baud, char cb_flag) { if (this.debug) PApplet.println("[connect] (device_name, baud)"); // check if the device name is a device id, and if so, get the appropriate port name String regex_str = "([0-9a-fA-F])"; Pattern regex_pat = Pattern.compile(regex_str); Matcher idMatch = regex_pat.matcher(device_name); if (idMatch.groupCount() > 0 && idMatch.find()){ device_name = getSerialPortName(device_name); } if (device_name == null) { if (this.debug) PApplet.println("[connect] (device_name, baud) ERROR: arduino name not found"); return false; } else { if (this.debug) PApplet.println("[connect] (device_name, baud) arduino at port: " + device_name); String serial_list [] = Serial.list(); for(int i = (serial_list.length - 1); i >= 0; i--){ if (serial_list[i].indexOf(device_name) >= 0) { if (this.debug) PApplet.println("[connect] (device_name, baud) port is at index: " + i); return connect(i, baud, cb_flag); } } } return false; } /** * connect Attempts to connect to the arduino at the specified index location on the serial list * @param device_num Holds the index number in the serial device array of the device to which we will connect * @param baud Holds the baud rate of the serial connection * @return returns true if connection established */ public boolean connect(int device_num, int baud) { return connect(device_num, baud, CB_FLAG); } /** * connect Attempts to connect to the arduino at the specified index location on the serial list * @param device_num Holds the index number in the serial device array of the device to which we will connect * @param baud Holds the baud rate of the serial connection * @param cb_flag Holds the char that triggers call of the serialEvent method * @return returns true if connection established */ public boolean connect(int device_num, int baud, char cb_flag) { String serial_list [] = Serial.list(); if (this.debug) PApplet.println("[connect] (device_num, baud, cb_flag) "); // make sure that device num is valid and then attempt to connect to the arduino. if (device_num < serial_list.length) { try{ connection = new Serial(app, serial_list[device_num], baud); app.delay(100); connection.bufferUntil(cb_flag); connected = true; if (this.debug) PApplet.println("[connect] (device_num, baud, cb_flag) connected to serial port: " + serial_list[device_num]); } catch(Exception e) { // if connection failed print error message PApplet.println("[connect] ERROR: unable to connect to serial port"); connected = false; } } return connected; } /** * getConnection Returns the serial object for direct access. The connection object is public and can * also be accessed directly from the object instance as follows: instance_obj.connection. * @return Returns the active serial connection */ public Serial getConnection() { if (connected) return connection; return null; } /** * isConnected Returns true if the serial port is connect, false if the serial port is not connected. * @return Returns the active serial connection */ public boolean isConnected() { return connected; } /******************** ** STATIC METHODS ********************/ /** * getSerialPortName Returns the serial port name by searching a hashmap the arduino_key. The maps it * searches is stored in a file that is saved at file_path. All map key/value pair are stored on * separate lines. * @param arduino_key Holds the key/name of the arduino on the hash map * @param file_path Holds the path to the file where the hash map is stored * @param line_div Holds the character that divides the maps keys and map values * @return Name of port to which the arduino identified on the hash map is connected */ public static String getSerialPortName(String arduino_key, String file_path, char line_div) throws FileNotFoundException, IOException { String sID = null; BufferedReader reader = new BufferedReader(new FileReader(file_path)); for(String line = reader.readLine(); line != null; line = reader.readLine()){ String[] pieces = PApplet.split(line,line_div); if (pieces.length == 2 && pieces[0].equals(arduino_key)){ sID = pieces[1]; PApplet.println(arduino_key + " [getSerialName]: found arduino key in map " + pieces[0] + " : " + pieces[1]); break; } } reader.close(); //if it isn't in the map, return false (failed) if (sID == null){ return null; } return ArduinoConnect.getSerialPortName(sID); } /** * getSerialPortName Returns the serial port name by searching a hashmap the arduino_key. The maps it * searches is stored in a file that is saved at file_path. * @param arduino_key Holds the key/name of the arduino on the hash map * @param file_path Holds the path to the file where the hash map is stored * @return Name of port to which the arduino identified on the hash map is connected */ public static String getSerialPortName(String arduino_key, String file_path) throws FileNotFoundException, IOException { return getSerialPortName(arduino_key, file_path, ':'); } /** * getSerialPortName Returns the serial port name where the arduino with the serial id specified by * the arduino_serial_id parameter is connected. If the arduino is not it returns null. * @param arduino_serial_id Holds the unique serial id of the arduino that we want to connect to * @return Name of port to which the arduino identified on the hash map is connected */ public static String getSerialPortName(String arduino_serial_id){ //assuming OSX String getUsbArgs[] = new String[2]; getUsbArgs[0]="system_profiler"; getUsbArgs[1]="SPUSBDataType"; Matcher idMatch; try { Process process = new ProcessBuilder(getUsbArgs).start(); InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; String currSerial = null; String currLocation = null; boolean foundArduino = false; int serialNumPosition, locationPosition; while ( (line = br.readLine ()) != null) { serialNumPosition = line.indexOf(SERIAL_NUMBER); if (serialNumPosition >= 0) { currSerial = line.substring(serialNumPosition + SERIAL_NUMBER.length()); if (!currSerial.equals(arduino_serial_id)){ currSerial = null; } } else if (currSerial != null && (line.indexOf("Arduino") >= 0 || line.indexOf("FT232R") >= 0 || line.indexOf("Vendor ID: 0x20a0") >= 0)) { //Vendor ID: 0x20a0 is freetronics foundArduino = true; } else if (foundArduino && currSerial != null) { locationPosition = line.indexOf(LOCATION); if (locationPosition >= 0){ currLocation = line.substring(locationPosition + LOCATION.length()); idMatch = REGEX_PAT.matcher(currLocation); if (idMatch.groupCount() > 0 && idMatch.find()){ return "/dev/tty.usbmodem"+idMatch.group(1)+"1"; } currSerial = null; currLocation = null; foundArduino = false; } } } } catch(IOException e) { System.out.println(e.getMessage()); } return null; } /** * printArduinoSerialNumber Prints to the console the unique serial id number for all Arduinos that are currently * connected to the computer. This code will probably not work with a lot of Arduino clones, though it could * easily be modified to do so. * @return returns a Hashtable with the unique serial ids and port name of each arduino */ public static Hashtable <String, String> printArduinoSerialNumber() { //assuming OSX String getUsbArgs[] = new String[2]; getUsbArgs[0]="system_profiler"; getUsbArgs[1]="SPUSBDataType"; Hashtable <String, String> serialNums = new Hashtable <String, String> (); Matcher idMatch; String serialIn; try { Process process = new ProcessBuilder(getUsbArgs).start(); InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; String currSerial = null; String currLocation = null; boolean foundArduino = false; int serialNumPosition, locationPosition; int arduinoCount = 0; if (DEBUG) PApplet.println("[findSerialNumbers] reading USB device information"); while ( (line = br.readLine ()) != null) { if (DEBUG) PApplet.println(line); serialNumPosition = line.indexOf(SERIAL_NUMBER); if (serialNumPosition >= 0) { currSerial = line.substring(serialNumPosition + SERIAL_NUMBER.length()); } else if (line.indexOf("Arduino") >= 0 || line.indexOf("FT232R") >= 0 || line.indexOf("Vendor ID: 0x20a0") >= 0) { //Vendor ID: 0x20a0 is freetronics foundArduino = true; } else if (foundArduino && currSerial != null) { locationPosition = line.indexOf(LOCATION); if (locationPosition >= 0){ currLocation = line.substring(locationPosition + LOCATION.length()); idMatch = REGEX_PAT.matcher(currLocation); if (idMatch.groupCount() > 0 && idMatch.find()){ serialIn = "/dev/tty.usbmodem" + idMatch.group(1) + "1"; serialNums.put(currSerial, serialIn); PApplet.println("Arduino Device - " + arduinoCount++); PApplet.println(" - Arduino ID: " + currSerial); PApplet.println(" - Serial Port: " + serialIn); PApplet.println(); } else { PApplet.println("ERROR: "); PApplet.println(currSerial + " : " + currLocation); } currSerial = null; currLocation = null; foundArduino = false; } } } } catch(IOException e) { PApplet.println(e.getMessage()); System.out.println(e.getMessage()); } return serialNums; } }