diff options
| author | Ido Hadanny <ido.hadanny@gmail.com> | 2011-09-22 21:30:13 +0300 |
|---|---|---|
| committer | Ido Hadanny <ido.hadanny@gmail.com> | 2011-09-22 21:30:13 +0300 |
| commit | 53b947df484159f5934898ba6c0aa69a9c869007 (patch) | |
| tree | 189d5903a67765c390de8f1e3423e6b80cde57d1 /roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java | |
| parent | 654ca6d59e99b5e72b05dfb917bc5888c4ba6b1b (diff) | |
added stuff
Diffstat (limited to 'roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java')
| -rw-r--r-- | roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java new file mode 100644 index 0000000..c768baf --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java @@ -0,0 +1,505 @@ +/* + * RoombaComm Serial Interface + * + * Copyright (c) 2006 Tod E. Kurt, tod@todbot.com, ThingM + * + * 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 + * + */ + +package com.hackingroomba.roombacomm; + +import gnu.io.*; +import java.io.*; +import java.util.*; + + +/** + * The serial-port based implementation of RoombaComm. + * Handles both physical RS-232 ports, USB adapter ports like Keyspan + * USA-19HS, and Bluetooth serial port profiles. + * + * <p> Some code taken from processing.serial.Serial. Thanks guys! </p> + * + * The interaction model for setting the port and protocol and WaitForDSR parameters is as follows. + * <p> + * On creation, the class initializes the parameters, then tries to read .roomba_config. + * If it can read the config file and parse out the parameters, it sets the parameters to + * the values in the config file. Apps can read the current settings for display using methods + * on the class. Apps can override the settings by accepting user input and setting the + * parameters using methods on the class, or the connect() method. Parameters that are + * changed by the app are re-written in the config file, for use as defaults next run. + * Command-line apps can make these parameters optional, by using the defaults if the + * user doesn't specify them. + * + * SVN id value is $Id: RoombaCommSerial.java 136 2010-04-06 00:07:23Z black.123 $ + */ +public class RoombaCommSerial extends RoombaComm implements SerialPortEventListener +{ +static final int databits = 8; + static final int parity = SerialPort.PARITY_NONE; + static final int stopbits = SerialPort.STOPBITS_1; + /** + * The time to wait in milliseconds after sending sensors command before + * attempting to read + */ + public static int updateSensorsPause = 400; + + /** the serial input stream, normally you don't need access to this */ + public InputStream input; + /** the serial output stream, normally you don't need access to this */ + public OutputStream output; + + /** + * RXTX bombs when flushing output sometimes, so by default do not + * flush the output stream. If the output is too buffered to be + * useful, do: + * roombacomm.comm.flushOutput = true; + * before using it and see if it works. + */ + public boolean flushOutput = false; + byte buffer[] = new byte[32768]; + int bufferLast; + + //int bufferSize = 26; // how big before reset or event firing + //boolean bufferUntil; + //int bufferUntilByte; + + /** + * Let you check to see if a port is in use by another Rooomba + * before trying to use it. + */ + public static boolean isPortInUse( String pname ) { + if (ports != null && pname != null){ + Boolean inuse = (Boolean) ports.get( pname ); + if( inuse !=null ) { + return inuse.booleanValue(); + } + } + return false; + } + + // constructor + public RoombaCommSerial() { + super(); + makePorts(); + readConfigFile(); + } + public RoombaCommSerial(boolean autoupdate) { + super(autoupdate); + makePorts(); + readConfigFile(); + } + public RoombaCommSerial(boolean autoupdate, int updateTime) { + super(autoupdate, updateTime); + makePorts(); + readConfigFile(); + } + + void makePorts() { + if( ports == null ) + ports = Collections.synchronizedMap(new TreeMap()); + } + /** + * Connect to a serial port specified by portid + * doesn't guarantee connection to Roomba, just to serial port + * @param portid name of port, e.g. "/dev/cu.KeySerial1" or "COM3" + * @return true if connect was successful, false otherwise + */ + public boolean connect(String portid) { + logmsg("connecting to port '"+portid+"'"); + portname = portid; + writeConfigFile(portname, getProtocol(), waitForDSR?'Y':'N'); + + if( isPortInUse( portid ) ) { + logmsg("port is in use"); + return false; + } + + connected = open_port(); + + if( connected ) { + // log in the global ports hash if the port is in use now or not + ports.put( portname, new Boolean( connected ) ); + sensorsValid = false; + } + else { + disconnect(); + } + + return connected; + } + public boolean connect(String portid,String protocal) { + String setProt="OI"; + if (protocal.equalsIgnoreCase("SCI")){ + setProt="SCI"; + } + if (protocal.equalsIgnoreCase("OI")){ + setProt="OI"; + } + setProtocol(setProt); + logmsg("connecting to port '"+portid+"' using protocal '"+setProt+"'"); + return connect(portid); + } + + /** + * Disconnect from serial port + */ + public void disconnect() { + connected = false; + + // log in the global ports hash if the port is in use now or not + if (ports != null && portname != null ){ + ports.put( portname, new Boolean( connected ) ); + } + + try { + // do io streams need to be closed first? + if (input != null) input.close(); + if (output != null) output.close(); + } catch (Exception e) { + e.printStackTrace(); + } + input = null; + output = null; + + try { + if (serialPort != null) serialPort.close(); // close the port + } catch (Exception e) { + e.printStackTrace(); + } + serialPort = null; + } + + /** + * subclassed. FIXME: + */ + public boolean send(byte[] bytes) { + try { + output.write(bytes); + if( flushOutput ) output.flush(); // hmm, not sure if a good idea + } catch (Exception e) { // null pointer or serial port dead + e.printStackTrace(); + } + return true; + } + + /** + * This will handle both ints, bytes and chars transparently. + */ + public boolean send(int b) { // will also cover char or byte + try { + output.write(b & 0xff); // for good measure do the & + if( flushOutput ) output.flush(); // hmm, not sure if a good idea + } catch (Exception e) { // null pointer or serial port dead + //errorMessage("send", e); + e.printStackTrace(); + } + return true; + } + + /** + * toggles DD line via serial port DTR (if available) + */ + public void wakeup() { + serialPort.setDTR(false); + pause(500); + serialPort.setDTR(true); + } + + /** + * Update sensors. Block for up to 1000 ms waiting for update + * To use non-blocking, call sensors() and then poll sensorsValid() + */ + public boolean updateSensors() { + System.out.println("updateSensors-start"); + sensorsValid = false; + sensors(); // requests sensor group 0 + for(int i=0; i < 20; i++) { + if( sensorsValid ) { + if ((buffer[1] > 1) || (buffer[1] < 0)) { + sensorsValid = false; + logmsg("updateSensors: received invalid data while attempting to read Roomba sensors!"); + System.out.println("updateSensors: received invalid data while attempting to read Roomba sensors!"); + } else { + logmsg("updateSensors: sensorsValid!"); + System.out.println("updateSensors: sensorsValid!"); + } + + break; + } + logmsg("updateSensors: pausing..."); + System.out.println("updateSensors: pausing..."); + pause( 50 ); + } + if (!sensorsValid){ + logmsg("updateSensors: no data received."); + }; + System.out.println("updateSensors: end"); + return sensorsValid; + } + + /** + * Update sensors. Block for up to 1000 ms waiting for update + * To use non-blocking, call sensors() and then poll sensorsValid() + */ + public boolean updateSensors(int packetcode) { + sensorsValid = false; + sensors(packetcode); + for(int i=0; i < 20; i++) { + if( sensorsValid ) { + logmsg("updateSensors: sensorsValid!"); + break; + } + logmsg("updateSensors: pausing..."); + pause( 50 ); + } + + return sensorsValid; + } + + /** + * called by serialEvent when we have enough bytes to make sensors valid + */ + public void computeSensors() { + sensorsValid = true; + sensorsLastUpdateTime = System.currentTimeMillis(); + computeSafetyFault(); + } + /* + pause(updateSensorsPause); // take a breather to let data come back + sensorsValid = false; // assume the worst, we're gothy + int n = available(); + //logmsg("updateSensors:n="+n); + if( n >= 26) { // there are enough bytes to read + n = readBytes(sensor_bytes); + if( n==26 ) { // did we get enough? + sensorsValid = true; // then everything's good, otherwise bad + computeSafetyFault(); + } + } else { + logmsg("updateSensors:only "+n+" bytes available, not updating sensors"); + } + + //logmsg("buffer contains: "+ buffer ); + return sensorsValid; + */ + + /** + * If this just hangs and never completes on Windows, + * it may be because the DLL doesn't have its exec bit set. + * Why the hell that'd be the case, who knows. + * FIXME: deal more gracefully + * (from processing.serial.Serial) + */ + public String[] listPorts() { + Map ps = Collections.synchronizedMap(new LinkedHashMap()); + //Vector list = new Vector(); + try { + //System.err.println("trying"); + Enumeration portList=null; + try { + portList = CommPortIdentifier.getPortIdentifiers(); + } catch (Exception e) { + //System.err.println("2"); + errorMessage("listPorts1", e); + } + //System.err.println("got port list"); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement(); + logmsg("Found port: " + portId.getName()); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + String name = portId.getName(); + //list.addElement(name); + Boolean state = (Boolean) ports.get( name ); + if( state==null ) state = new Boolean(false); + ps.put( name, state ); + } + } + } catch (UnsatisfiedLinkError e) { + //System.err.println("1"); + errorMessage("listPorts", e); + } catch (Exception e) { + //System.err.println("2"); + errorMessage("listPorts", e); + } + //System.err.println("move out"); + /* + for( Enumeration e = list.elements(); e.hasMoreElements(); ) { + String p = (String) e.nextElement(); + if( ! ports.containsKey( p ) ) { + ports.put( p, new Boolean(false) ); + } + } + + // DEBUG + System.err.println("ports hashtable:"); + for( Enumeration e = ports.keys(); e.hasMoreElements(); ) { + String p = (String) e.nextElement(); + Boolean b = (Boolean) ports.get(p); + System.err.println("port:"+p+", inuse:"+b); + } + */ + ports = ps; + String outgoing[] = + (String[]) new TreeSet(ports.keySet()).toArray(new String[0]); + + return outgoing; + } + + + public boolean isWaitForDSR() { + return waitForDSR; + } + + public void setWaitForDSR(boolean waitForDSR) { + this.waitForDSR = waitForDSR; + writeConfigFile(portname, getProtocol(), waitForDSR?'Y':'N'); + } + + public String getPortname() { + return portname; + } + + public void setPortname(String p) { + portname = p; + logmsg("Port: " + portname); + writeConfigFile(portname, getProtocol(), waitForDSR?'Y':'N'); + + } + + // ------------------------------------------------------------- + + // below only used internally to this class + // ------------------------------------------------------------- + + /** + * internal method, used by connect() + * FIXME: make it faile more gracefully, recognize bad port + */ + private boolean open_port() { + boolean success = false; + try { + Enumeration portList = CommPortIdentifier.getPortIdentifiers(); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = + (CommPortIdentifier) portList.nextElement(); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + System.out.println("found " + portId.getName()); + if (portId.getName().equals(portname)) { + logmsg("open_port:"+ portId.getName()); + serialPort = (SerialPort)portId.open("roomba serial", 2000); + //port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); + input = serialPort.getInputStream(); + output = serialPort.getOutputStream(); + serialPort.setSerialPortParams(rate,databits,stopbits,parity); + serialPort.addEventListener(this); + serialPort.notifyOnDataAvailable(true); + logmsg("port "+portname+" opened successfully"); + + if( waitForDSR ) { + int i=40; + while( !serialPort.isDSR() && i-- != 0) { + logmsg("DSR not ready yet"); + pause(150); // 150*40 = 6 seconds + } + success = serialPort.isDSR(); + } else { + success = true; + } + } + } + } + + } catch (Exception e) { + logmsg("connect failed: "+e); + serialPort = null; + input = null; + output = null; + } + + return success; + } + + /** + * callback for SerialPortEventListener + * (from processing.serial.Serial) + */ + synchronized public void serialEvent(SerialPortEvent serialEvent) { + try { + logmsg("serialEvent:"+serialEvent+", Available:"+input.available()); + if (serialEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) { + while (input.available() > 0) { + logmsg("serialEvent: available="+input.available()); + buffer[bufferLast++] = (byte) input.read(); + if( bufferLast == 26 ) { + bufferLast = 0; + System.arraycopy(buffer, 0, sensor_bytes, 0, 26); + computeSensors(); + } + /* + synchronized (buffer) { + if (bufferLast == buffer.length) { + byte temp[] = new byte[bufferLast << 1]; + System.arraycopy(buffer, 0, temp, 0, bufferLast); + buffer = temp; + } + buffer[bufferLast++] = (byte) input.read(); + } + */ + } // while + } + } catch (IOException e) { + errorMessage("serialEvent", e); + } + } + + @SuppressWarnings("unchecked") + public void playSong(RoombaComm roombacomm, String rtttl){ + ArrayList notelist = RTTTLParser.parse( rtttl ); + int songsize = notelist.size(); + // if within the size of a roomba song, make the nsong, then play + if( songsize <= 16 ) { + updateDisplay("creating a song with createSong()", true); + int notearray[] = new int[songsize*2]; + int j=0; + for( int i=0; i< songsize; i++ ) { + Note note = (Note) notelist.get(i); + int sec64ths = note.duration * 64/1000; + notearray[j++] = note.notenum; + notearray[j++] = sec64ths; + } + roombacomm.createSong( 1, notearray ); + roombacomm.playSong( 1 ); + } + // otherwise, try to play it in realtime + else { + updateDisplay("playing song in realtime with playNote()",true); + int fudge = 20; + for( int i=0; i< songsize; i++ ) { + Note note = (Note) notelist.get(i); + int duration = note.duration; + int sec64ths = duration*64/1000; + if( sec64ths < 5 ) sec64ths = 5; + if( note.notenum != 0 ) + roombacomm.playNote( note.notenum, sec64ths ); + roombacomm.pause( duration + fudge ); + } + } + } +} |
