summaryrefslogtreecommitdiff
path: root/roombacomm-client/src/com
diff options
context:
space:
mode:
authorIdo Hadanny <ido.hadanny@gmail.com>2011-09-22 21:30:13 +0300
committerIdo Hadanny <ido.hadanny@gmail.com>2011-09-22 21:30:13 +0300
commit53b947df484159f5934898ba6c0aa69a9c869007 (patch)
tree189d5903a67765c390de8f1e3423e6b80cde57d1 /roombacomm-client/src/com
parent654ca6d59e99b5e72b05dfb917bc5888c4ba6b1b (diff)
added stuff
Diffstat (limited to 'roombacomm-client/src/com')
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java379
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java110
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java258
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/BumpTurn.java129
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java141
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java173
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java586
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/JImagePanel.java20
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/ListSerialPorts.java59
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/LogoA.java105
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Note.java21
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java67
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java112
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java144
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java945
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java240
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java910
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java364
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java75
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java1690
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java1571
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommFrame.java2239
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java125
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java1854
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java505
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java451
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java102
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java387
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java591
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorderPanel.java582
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java148
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Spiral.java127
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java139
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java144
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Spy.java132
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/SpyAuto.java147
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java136
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Test.java177
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Tribble.java111
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java115
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/XML.java173
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.pngbin0 -> 1058 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.pngbin0 -> 518 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.pngbin0 -> 400 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.pngbin0 -> 354 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.pngbin0 -> 448 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.pngbin0 -> 1061 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.pngbin0 -> 345 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.pngbin0 -> 1339 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.pngbin0 -> 1367 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.pngbin0 -> 423 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.pngbin0 -> 990 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.pngbin0 -> 2795 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.pngbin0 -> 2860 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.pngbin0 -> 2549 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.pngbin0 -> 1182 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.pngbin0 -> 1184 bytes
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java70
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/net/RoombaCommTCPServer.java94
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/net/SimpleTest.java131
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/net/TextHttpServer.java109
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java255
62 files changed, 17143 insertions, 0 deletions
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java
new file mode 100644
index 0000000..d029413
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java
@@ -0,0 +1,379 @@
+/*
+ * roombacomm.ArduinoClient -- test out the Arduino subsystem without robot motion
+ *
+ * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net
+ *
+ * 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 com.hackingroomba.roombacomm.RobotConnection.ReadTerminator;
+import com.hackingroomba.roombacomm.RobotType.robotTypes;
+
+import java.io.*;
+
+/**
+ * The extensions to roombacomm for ArduinoBot operations.
+ *
+ * <h2> Overview </h2>
+ * This class contains the operations available from an ArduinoBot
+**/
+
+public class ArduinoBot extends RoombaComm {
+
+ // connection variables
+ private Integer compassHeading;
+ private String arduinoString;
+ private RobotConnection robotConnection;
+ private RobotType robotType;
+
+ // compass variables
+ private int compassOffset = 0;
+ private boolean compassOffsetInitialized = false;
+
+ // encoder variables
+ private int encoder;
+
+ private long startTime;
+ public long elapsedTime;
+
+ public ArduinoBot() {
+ super();
+ }
+
+ public ArduinoBot(RobotConnection robotConnection) {
+ super(robotConnection);
+ this.robotConnection = robotConnection;
+ }
+
+ /**
+ * Initialize an Arduino robot controller
+ */
+ public boolean initArduinoBot(RobotType rt)
+ {
+ byte[] robotTypeMsg = "robot m\r".getBytes(); // message to Arduino to tell it the robot type
+
+ robotType = rt; // record robotType for later use
+ System.out.println("checking for ArduinoBot... \n");
+ try {
+ Thread.sleep(2000);
+ } catch (Exception e) {System.out.println("Exception in Thread.sleep()"); }
+
+ // send the robot type message to Arduino, so it knows how to read the encoders, and any other robot-specific things
+ if (robotType.robotType == RobotType.robotTypes.frankenRoomba) {
+ System.out.println("Sending robot r to Arduino");
+ robotTypeMsg = "robot r\r".getBytes();
+ } else if (robotType.robotType == robotTypes.tankbot) {
+ System.out.println("Sending robot t to Arduino");
+ robotTypeMsg = "robot t\r".getBytes();
+ }
+ send(robotTypeMsg);
+
+ printCompass();
+ printEncoders();
+ return true;
+ }
+
+
+ /**
+ * Send a single byte to the ArduinoBot
+ * (defined as int because of stupid java signed bytes). SendToArduino method of RobotConnection
+ * is used because this is an ardunobot, which requires a leading m and trailing <LF> to make
+ * the parser work right
+ * @param b byte of an Arduino command to send
+ * @return true on successful send
+ */
+ public boolean send(int b) {
+ return(robotConnection.sendToArduino(b));
+ }
+
+ /**
+ * Send a byte array to the ArduinoBot. If the first byte is 128 or higher, preface the byte string with m<space>
+ * so it's treated by Arduino as a roomba command
+ */
+ public boolean send(byte[] bytes) {
+ if (bytes[0] < 0) // must be a roomba command
+ return(robotConnection.sendToArduino(bytes));
+ else
+ return(robotConnection.send(bytes));
+ }
+
+ /**
+ * Read a string from Arduino up to a <LF>
+ * @return array of bytes read from ArduinoBot
+ */
+ public String readArduinoBot() throws Exception {
+ return(robotConnection.readBotToTerminator(ReadTerminator.LF));
+ }
+
+ public void printCompass() {
+ int compassReading;
+ startTime = System.currentTimeMillis();
+ compassReading = getCompass();
+ if (compassReading < 0) return;
+ elapsedTime = System.currentTimeMillis() - startTime;
+ System.out.println("Heading: " + compassReading + " in " + elapsedTime + " ms");
+ }
+
+ public int getCompass() {
+ // read the uncorrected heading
+ try {
+ compassHeading = getUncorrectedCompass();
+ } catch (Exception e) {
+ System.out.println("Exception reading uncorrected compass\n" + e.getMessage() + "\n" );
+ e.printStackTrace();
+ return -1;
+ }
+ // if we haven't initialized the compass offset yet, try to read an offset from a config file
+ // compassOffsetInitialized ensures we only try this once
+ try {
+ if (!compassOffsetInitialized) { // get the compass offset from file if it exists
+ compassOffset = readConfigInt(new String("compass_offset"));
+ compassOffsetInitialized = true;
+ }
+ } catch (Exception e) {
+ System.out.println("No compass_offset file could be opened\n" + e.getMessage());
+ compassOffsetInitialized = true;
+ }
+
+ // offset the uncorrected heading by the compassOffset, which depends on mounting position
+ compassHeading -= compassOffset;
+ compassHeading %= 360; // compensate for offset which can make it exceed 360
+ if (compassHeading < 0) compassHeading += 360;
+ return compassHeading;
+ }
+
+ private int getUncorrectedCompass() throws Exception {
+ byte [] compassCmd = {'c', '\r'};
+ int heading;
+
+ //System.out.println("getUncorrectedCompass");
+ startTime = System.currentTimeMillis();
+ if (!send(compassCmd)) {
+ System.out.println("Error sending c command");
+ }
+ try {
+ arduinoString = robotConnection.readBotToTerminator(ReadTerminator.LF);
+ if (arduinoString == null) {
+ System.out.println("Error: getUncorrectedCompass failed to read heading - null string");
+ }
+ } catch (Exception e) {
+ elapsedTime = System.currentTimeMillis() - startTime;
+ System.err.println("Exception reading compass in readBotToTerminator in " + elapsedTime + " ms");
+ e.printStackTrace();
+ throw e;
+ }
+ elapsedTime = System.currentTimeMillis() - startTime;
+
+ //System.out.println("received string: " + arduinoString);
+ String headingString = arduinoString.split("\\s")[0];
+ heading = new Integer(headingString);
+ return heading;
+ }
+
+ public boolean setCompassOffset(int currentMagHeading)
+ {
+ int heading;
+
+ if ((currentMagHeading < 0) || (currentMagHeading > 360)) {
+ System.out.println("currentMagHeading must be between 0 and 360");
+ return false;
+ }
+ try {
+ heading = getUncorrectedCompass();
+ if (heading < 0) return false;
+ compassOffset = heading - currentMagHeading;
+ if (compassOffset < 0) compassOffset += 360;
+ writeConfigInt(new String("compass_offset"), compassOffset);
+ } catch (Exception e) {
+ System.err.println("Error getting compass or writing compass offset");
+ e.printStackTrace();
+ return false;
+ }
+ compassOffsetInitialized = true;
+ return true;
+ }
+
+ /*
+ * Print, Get the current wheel encoders reading
+ */
+ public void printEncoders()
+ {
+ double readEncDistance = 0;
+
+ startTime = System.currentTimeMillis();
+ try {
+ readEncDistance = getEncoders();
+ } catch (Exception e) {
+ System.err.println("Exception reading robot encoders: " + e.getMessage());
+ }
+ long elapsedTime = System.currentTimeMillis() - startTime;
+ System.out.println("Wheel encoders reading: " + readEncDistance + " in " + elapsedTime + " ms");
+
+ }
+
+ public double getEncoders() throws Exception {
+ byte [] cmd = {'e', '\r'};
+
+ //System.out.println("read encoders");
+ startTime = System.currentTimeMillis();
+ if (!send(cmd)) {
+ System.out.println("Error sending e command");
+ }
+ try {
+ arduinoString = robotConnection.readBotToTerminator(ReadTerminator.LF);
+ elapsedTime = System.currentTimeMillis() - startTime;
+ if (arduinoString == null) {
+ System.err.println("Error: getEncoders failed to read encoders, null returned in " + elapsedTime + " ms");
+ }
+ } catch (Exception e) {
+ elapsedTime = System.currentTimeMillis() - startTime;
+ System.err.println("Exception reading encoders from readBotToTerminator in " + elapsedTime + " ms");
+ e.printStackTrace();
+ throw e;
+ }
+
+ //System.out.println("received string: " + arduinoString);
+ String [] encoderStrings = arduinoString.split("\\s");
+ if (encoderStrings.length == 1) {
+ encoder = new Integer(encoderStrings[0]);
+ } else {
+ System.out.println("Error reading encoders - expected 1 string, found:" + encoderStrings.length
+ + " in: " + arduinoString + " in " + elapsedTime + " ms");
+ return -1;
+ }
+
+ double encoderDistance = encoder /robotType.countsPerInch;
+ return encoderDistance;
+ }
+
+
+ /**
+ * Read roomba 26-byte sensor record using robotConnection. Tries once to read valid data, allowing 100ms
+ * timeout on each attempt.
+ * @return true if read 26 bytes of valid data. Data has been stored in sensor_bytes. False otherwise
+ */
+ public boolean updateSensors()
+ {
+ return updateSensors(SENSORS_ALL);
+ }
+
+ public boolean updateSensors(int sensorGroup)
+ {
+ int sensorGroupSize;
+
+ if (robotConnection == null) {
+ System.out.println("Error at ArduinoBot.updateSensors(): no connection object for robot");
+ return false;
+ }
+
+ switch(sensorGroup) {
+ case SENSORS_ALL: sensorGroupSize = 26; break;
+ case 100: sensorGroupSize = 80; break;
+ default:
+ System.err.println("Invalid sensor group in updateSensors(): " + sensorGroup);
+ return false;
+ }
+ sensors(sensorGroup);
+ return getSensorData(sensorGroupSize);
+ }
+
+ /**
+ * Writes an int to the named file
+ * @throws FileNotFoundException If it can't open the file
+ * @throws IOException If it can't write to the file
+ */
+ public void writeConfigInt(String filename, int value) throws FileNotFoundException, IOException {
+ DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File(filename)));
+ dos.writeInt(value);
+ }
+
+ /**
+ * Read a configuration file containing an int.
+ * @param filename The file to open & read an int from
+ * @return Int read from file
+ * @throws Exception related to attempting to open & read int from file
+ */
+ public int readConfigInt(String filename) throws Exception {
+ int configInt = -1;
+
+ try {
+ DataInputStream dis = new DataInputStream(new FileInputStream(new File(filename)));
+ configInt = dis.readInt();
+
+ } catch (Exception e) {
+ System.out.println("No config file could be read\n" + e.getMessage());
+ }
+ return configInt;
+ }
+
+
+ /**
+ * Writes a double to the named file
+ * @throws FileNotFoundException If it can't open the file
+ * @throws IOException If it can't write to the file
+ */
+ public void writeConfigDouble(String filename, double value) throws FileNotFoundException, IOException {
+ DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File(filename)));
+ dos.writeDouble(value);
+ }
+
+ /**
+ * Read a configuration file containing a double.
+ * @param filename The file to open & read a double from
+ * @return double read from file
+ * @throws Exception related to attempting to open & read double from file
+ */
+ public double readConfigDouble(String filename) throws Exception {
+ double configDouble = -1;
+
+ try {
+ DataInputStream dis = new DataInputStream(new FileInputStream(new File(filename)));
+ configDouble = dis.readDouble();
+
+ } catch (Exception e) {
+ System.out.println("No config file could be read\n" + e.getMessage());
+ throw(e);
+ }
+ return configDouble;
+ }
+
+ public int getEncoder() {
+ return encoder;
+ }
+
+ /**
+ * Bogus methods which should never be called on ArduinoClient
+ */
+ public String [] listPorts()
+ {
+ System.out.println("Error at ArduinoBot.listPorts(): should never get here");
+ return (new String[] {"Error: listports calledon ArdunoBot; not supported"});
+ }
+ public boolean connect(String s)
+ {
+ System.out.println("Error at ArduinoBot.connect(): should never get here");
+ return false;
+ }
+ public void disconnect()
+ {
+ System.out.println("Error at ArduinoBot.disconnect(): should never get here");
+ return;
+ }
+
+} \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java
new file mode 100644
index 0000000..6ea2843
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java
@@ -0,0 +1,110 @@
+/*
+ * roombacomm.ArduinoClient -- test out the Arduino subsystem without robot motion
+ *
+ * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net
+ *
+ * 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 jargs.gnu.CmdLineParser;
+import java.io.*;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+
+/**
+ * A test class for Arduino communications.
+ *
+ * <h2> Overview </h2>
+ * This class contains the communications layer-independent parts of
+ * how to communicate with an Arduino. It does assume a very serial port-like
+ * interaction.
+**/
+
+public class ArduinoClient {
+ private static final long serialVersionUID = 1L;
+ private static final int lumBufHeight = 32;
+
+ String usage =
+ "Usage: \n"+
+ " ArduinoClient {--arduinoServer|-s} <IP> [{--arduinoPortNum|-p} <port>] [options]\n" +
+ "where [options] can be one or more of:\n"+
+ " --debug|-X -- turn on debug output\n";
+
+ private RobotConnection arduinoConnection;
+ private String arduinoServer;
+ private Boolean debug;
+
+ public ArduinoClient() {
+ // constructor, mustn't throw exceptions. Do nothing for now
+ }
+
+ // main - it all starts here; when run as main class, ask Arduino for its compass reading
+ public static void main(String[] args) {
+ ArduinoClient l = new ArduinoClient();
+ l.testClient(args);
+ }
+
+ public void testClient(String[] args) {
+ parseCmd(args);
+ arduinoConnection = new RobotConnection(arduinoServer);
+ arduinoConnection.connect();
+ ArduinoBot arduinobot = new ArduinoBot(arduinoConnection);
+ arduinobot.printCompass();
+ arduinoConnection.disconnect();
+ }
+
+
+ public void parseCmd(String[] args){
+ //System.out.println("*** running ArduinoClient standalone WparseCmd");
+
+ CmdLineParser parser = new CmdLineParser();
+ CmdLineParser.Option debugOption = parser.addBooleanOption('X', "debug");
+ CmdLineParser.Option arduinoServerOption = parser.addStringOption('s', "arduinoServer");
+
+ try {
+ parser.parse(args);
+ }
+ catch ( CmdLineParser.OptionException e ) {
+ System.err.println(e.getMessage());
+ System.out.println("parseCmd had an error\n"+ usage );
+ System.exit(2);
+ }
+
+ // String portname = args[0]; // e.g. "/dev/cu.KeySerial1", or "COM5" or "192.168.1.1:5002"
+ arduinoServer = (((String)parser.getOptionValue(arduinoServerOption)));
+ Boolean debugBool = (Boolean)parser.getOptionValue(debugOption,new Boolean(false));
+ setDebug(debugBool.booleanValue());
+ //System.out.println("debug is ("+isDebug()+")");
+ //System.out.println("*** end of parseCmd");
+ }
+
+
+ public boolean isDebug() {
+ return debug;
+ }
+ public void setDebug(boolean debug_) {
+ debug = debug_;
+ }
+ /**
+ * @param arduuinoServer the localizerServer to set
+ */
+
+ }
+
+ \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java b/roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java
new file mode 100644
index 0000000..cb99e6a
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java
@@ -0,0 +1,258 @@
+/*
+ * roombacomm.AudioLocalizerClient -- test out the audio localizer subsystem without robot motion
+ *
+ * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net
+ *
+ * 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 jargs.gnu.CmdLineParser;
+import java.io.*;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+
+/**
+ Run it with something like: <pre>
+ java roombacomm.AudioLocalizerClient --localizerServer 192.168.0.150 --localizerPortNum 5005 --debug<br>
+ Usage:
+ roombacomm.AudioLocalizerClient --localizerServer <IP> --localizerPortNum <port> [options]<br>
+ where
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ </pre>
+*/
+ public class AudioLocalizerClient {
+ private static final long serialVersionUID = 1L;
+ private static final int lumBufHeight = 32;
+
+ String usage =
+ "Usage: \n"+
+ " AudioLocalizerClient --localizerServer <IP> --localizerPortNum <port> [options]\n" +
+ "where [options] can be one or more of:\n"+
+ " --debug -- turn on debug output\n";
+ boolean debug = false;
+ private int waittime;
+ private String localizerServer = "";
+ private int localizerPortNum = 5010 ;
+ private int locationStringSize = 200;
+ private byte[] locationBytes = new byte[locationStringSize];
+ private byte[] locationStringBuf = new byte[locationStringSize];
+ private double x, y;
+
+ // socket variables
+ Socket localizerSocket;
+ BufferedInputStream in;
+ BufferedOutputStream out;
+
+ public AudioLocalizerClient() {
+ // constructor, mustn't throw exceptions. Do nothing for now
+ }
+
+ // main - it all starts here
+ public static void main(String[] args) {
+ AudioLocalizerClient l = new AudioLocalizerClient();
+ l.parseCmd(args);
+ l.connectLocalizer();
+ l.printLocation();
+ }
+
+ public void connectLocalizer()
+ {
+ if (getLocalizerServer() == null || getLocalizerServer().length() == 0) {
+ System.err.println(" you must supply a --localizerServer value to use the command");
+ if (getLocalizerPortNum() <= 0) {
+ System.err.println(" you must supply a --localizerPortNum value to use the command");
+ }
+ System.exit(6);
+ }
+ if (getLocalizerPortNum() <= 0) {
+ System.err.println(" you must supply a --localizerPortNum value to use the command \"getVideo\"");
+ System.exit(7);
+ }
+
+ connect();
+ }
+
+ public void printLocation()
+ {
+ //while (true) {
+ int size = readLocation();
+ String locationString = new String(locationBytes, 0, size);
+ System.out.println("received string: " + locationString);
+ String [] splitLocationStrings = locationString.split("\\s");
+ if (splitLocationStrings[0].compareTo("Invalid") == 0) {
+ System.out.println("Location is invalid");
+ } else {
+ x = new Double(splitLocationStrings[1]);
+ y = new Double(splitLocationStrings[2]);
+ System.out.println("X: " + x + " Y: " + y);
+ }
+ //}
+ }
+ public int readLocation()
+ {
+ int readLength = 0;
+ int maxReadSize = locationStringSize;
+ locationBytes[0] = 'L'; // send the "localize" command
+ int locStringIx = 0;
+
+ try {
+ out.write(locationBytes, 0, 1);
+ out.flush();
+
+ // ACHTUNG - locationStringBuf gets overwritten at the beginning by multiple read buffers - use locationString
+ for (int i=0; i<5; i++) {
+ readLength = in.read(locationStringBuf, 0, maxReadSize);
+ maxReadSize -= readLength;
+ for (int j=0; j<readLength; j++, locStringIx++) {
+ locationBytes[locStringIx] = locationStringBuf[j];
+ }
+ if ((locationBytes[locStringIx-1] == '\0') || (locStringIx == locationStringSize))
+ break;
+
+ try {
+ Thread.sleep(10);
+ System.out.println("Have " + locStringIx + ", trying again");
+ } catch (Exception e) {
+ System.out.print(e);
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("I/O exception");
+ e.printStackTrace();
+ System.exit(-1);
+ }
+
+ System.out.println("readLocation read " + locStringIx + " bytes");
+
+ return (locStringIx);
+ }
+
+ public void connect()
+ {
+ try {
+ localizerSocket = new Socket(localizerServer, localizerPortNum);
+ in = new BufferedInputStream(localizerSocket.getInputStream());
+ out = new BufferedOutputStream(localizerSocket.getOutputStream());
+ } catch (UnknownHostException e) {
+ System.out.println("Unknown host: " + localizerServer + ":" + localizerPortNum);
+ System.exit(-1);
+ } catch(IOException e) {
+ System.out.println("I/O exception");
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ System.out.println("connected to localizer");
+ }
+
+ public void disconnect()
+ {
+ try {
+ // do io streams need to be closed first?
+ if (in != null) in.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ in = null;
+
+ try {
+ if (localizerSocket != null) localizerSocket.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ localizerSocket = null;
+ System.out.println("disconnected from Video");
+ }
+
+ public void parseCmd(String[] args){
+ //System.out.println("*** start of AudioLocalizerClient WparseCmd");
+
+ CmdLineParser parser = new CmdLineParser();
+ CmdLineParser.Option debugOption = parser.addBooleanOption('X', "debug");
+ CmdLineParser.Option localizerServerOption = parser.addStringOption('l', "localizerServer");
+ CmdLineParser.Option localizerPortNumOption = parser.addIntegerOption('p', "localizerPortNum");
+
+ try {
+ parser.parse(args);
+ }
+ catch ( CmdLineParser.OptionException e ) {
+ System.err.println(e.getMessage());
+ System.out.println("parseCmd had an error\n"+ usage );
+ System.exit(2);
+ }
+
+ // String portname = args[0]; // e.g. "/dev/cu.KeySerial1", or "COM5" or "192.168.1.1"
+ setLocalizerServer(((String)parser.getOptionValue(localizerServerOption)));
+ setLocalizerPortNum(((Integer)parser.getOptionValue(localizerPortNumOption, getLocalizerPortNum())).intValue());
+ // String cmd = args[1+argOffset];
+
+ Boolean debugBool = (Boolean)parser.getOptionValue(debugOption,new Boolean(false));
+ setDebug(debugBool.booleanValue());
+ System.out.println("debug is ("+isDebug()+")");
+ //System.out.println("*** end of parseCmd");
+ }
+
+
+ public boolean isDebug() {
+ return debug;
+ }
+ public void setDebug(boolean debug_) {
+ debug = debug_;
+ }
+ public int getWaittime() {
+ return waittime;
+ }
+ public void setWaittime(int waittime_) {
+ waittime = waittime_;
+ }
+ /**
+ * @return the localizerServer
+ */
+ protected String getLocalizerServer() {
+ return localizerServer;
+ }
+ /**
+ * @param localizerServer the localizerServer to set
+ */
+ public void setLocalizerServer(String localizerServer) {
+ this.localizerServer = localizerServer;
+ }
+ /**
+ * @return the localizerPortNum
+ */
+ protected int getLocalizerPortNum() {
+ return localizerPortNum;
+ }
+ /**
+ * @param localizerPortNum the localizerPortNum to set
+ */
+ public void setLocalizerPortNum(int localizerPortNum) {
+ this.localizerPortNum = localizerPortNum;
+ }
+ public double getX() {
+ return x;
+ }
+
+
+ public double getY() {
+ return y;
+ }
+ }
+
+ \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/BumpTurn.java b/roombacomm-client/src/com/hackingroomba/roombacomm/BumpTurn.java
new file mode 100644
index 0000000..3618d0a
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/BumpTurn.java
@@ -0,0 +1,129 @@
+/*
+ * roombacomm.BumpTurn -- turn away from bumps
+ *
+ * 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 java.io.*;
+
+/**
+ Read sensors to detect bumps and turn away from them while driving
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.BumpTurn /dev/cu.KeySerial1<br>
+ Usage:
+ roombacomm.Drive serialportname [protocol] [options]
+ where:
+ protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ </pre>
+
+*/
+public class BumpTurn {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Drive <serialportname> [protocol]" +
+ "" +
+ " [options]\n" +
+ "where protocol (optional) is SCI or OI and [options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ "nohwhandshake -- don't use hardware-handshaking" +
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+
+ public static void main(String[] args) {
+ if( args.length < 1 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ for( int i=1; i < args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ }
+
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(100);
+
+ roombacomm.updateSensors();
+
+ System.out.println("Press return to exit.");
+ boolean done = false;
+ while( !done ) {
+ if( roombacomm.bumpLeft() ) {
+ roombacomm.spinRight(90);
+ }
+ else if( roombacomm.bumpRight() ) {
+ roombacomm.spinLeft(90);
+ }
+ else if( roombacomm.wall() ) {
+ roombacomm.playNote( 72,10 ); // beep!
+ }
+ roombacomm.goForward();
+ roombacomm.updateSensors();
+
+ done = keyIsPressed();
+ }
+
+ roombacomm.stop();
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+ /** check for keypress, return true if so */
+ public static boolean keyIsPressed() {
+ boolean press = false;
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ press = true;
+ }
+ } catch( IOException ioe ) { }
+ return press;
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java
new file mode 100644
index 0000000..57685bc
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java
@@ -0,0 +1,141 @@
+/*
+ * roombacomm.Drive -- test out the DRIVE command
+ *
+ * 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 java.io.*;
+
+/**
+ * A program for driving Roomba.
+ * <p>
+ * Run it with something like: <pre>
+ * java roombacomm.Drive /dev/cu.KeySerial1 byte1 byte2 byte3 byte4<br>
+ * Usage:
+ * roombacomm.Drive serialportname [protocol] velocity radius waittime [options]<br>
+ * where protocol (optional) is SCI or OI
+ * velocity is in mm/sec
+ * radius is mm from the centerpoint
+ * waittime is in milliseconds
+ * [options] can be one or more of:
+ * -debug -- turn on debug output
+ * -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ * -nohwhandshake -- don't use hardware-handshaking
+ * </pre>
+ *
+ */
+public class Drive {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Drive <serialportname> [protocol] <velocity> <radius> <waittime> [options]\n" +
+ "where protocol (optional) is SCI or OI\n" +
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+
+ public static void main(String[] args) {
+ new Drive(args);
+ }
+
+ Drive(String[] args)
+ {
+ int argIndx;
+ String portname, protocol;
+ if( args.length < 4 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ /*
+ * Parse port & protocol
+ */
+ portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ int argOffset = 0;
+ if (args[1].equals("SCI") || (args[1].equals("OI"))) {
+ roombacomm.setProtocol(args[1]);
+ argOffset = 1;
+ }
+
+ /*
+ * Parse command arguments
+ */
+ int velocity=0, radius=0, waittime=0;
+ try {
+ velocity = Integer.parseInt( args[1+argOffset] ); // velocity would be the 1st numeric
+ radius = Integer.parseInt( args[2+argOffset] );
+ waittime = Integer.parseInt( args[3+argOffset] );
+ } catch( Exception e ) {
+ System.err.println("Couldn't parse velocity, radius, or waittime");
+ System.exit(1);
+ }
+
+
+ /*
+ * Parse options
+ */
+ for( int i=4+argOffset; i < args.length; i++ ) {
+ if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ }
+
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( roombacomm.getPortname())) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+ System.out.println("Using port: " + roombacomm.getPortname() + " protocol: "
+ + roombacomm.getProtocol() + " velocity: " + velocity + " radius: "
+ + radius + " waittime: " + waittime + "\n");
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.pause(100);
+ roombacomm.control();
+ roombacomm.playNote( 72, 10 ); // C , test note
+ roombacomm.pause( 200 );
+ if( roombacomm.updateSensors() )
+ System.out.println("Roomba found!\n");
+ else
+ System.out.println("No Roomba. :( Is it turned on?\n");
+
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java b/roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java
new file mode 100644
index 0000000..b52fcf8
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java
@@ -0,0 +1,173 @@
+/*
+ * roombacomm.Drive -- test out the DRIVE command
+ *
+ * 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 javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+
+
+/**
+ Drive the Roomba with the arrow keys in real-time
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.DriveRealTime /dev/cu.KeySerial1<br>
+ Usage:
+ roombacomm.DriveRealTime serialportname [protocol] [options]<br>
+ where:
+ protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ </pre>
+*/
+public class DriveRealTime extends JFrame implements KeyListener {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.DriveRealTime <serialportname> [protocol] [options] \n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ "-nohwhandshake -- don't use hardware-handshaking\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+
+ RoombaCommSerial roombacomm;
+ JTextArea displayText;
+
+ public static void main(String[] args) {
+ if( args.length < 1 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+ new DriveRealTime(args);
+ }
+
+ public DriveRealTime(String[] args) {
+ super("DriveRealTime");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ String portname = args[0];
+ roombacomm = new RoombaCommSerial();
+ for( int i=1; i < args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ }
+
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(50);
+
+ setupWindow();
+
+ updateDisplay("click on this window\nthen use arrow keys to drive Roomba around.\n");
+ }
+
+ /** Handle the key pressed event from the text field. */
+ public void keyPressed(KeyEvent e) {
+ int keyCode = e.getKeyCode();
+ if( keyCode == KeyEvent.VK_SPACE ) {
+ updateDisplay("stop");
+ roombacomm.stop();
+ }
+ else if( keyCode == KeyEvent.VK_UP ) {
+ updateDisplay("forward");
+ roombacomm.goForward();
+ }
+ else if( keyCode == KeyEvent.VK_DOWN ) {
+ updateDisplay("backward");
+ roombacomm.goBackward();
+ }
+ else if( keyCode == KeyEvent.VK_LEFT ) {
+ updateDisplay("spinleft");
+ roombacomm.spinLeft();
+ }
+ else if( keyCode == KeyEvent.VK_RIGHT ) {
+ updateDisplay("spinright");
+ roombacomm.spinRight();
+ }
+ else if( keyCode == KeyEvent.VK_COMMA ) {
+ updateDisplay("speed down");
+ roombacomm.setSpeed( roombacomm.getSpeed() - 50 );
+ }
+ else if( keyCode == KeyEvent.VK_PERIOD ) {
+ updateDisplay("speed up");
+ roombacomm.setSpeed( roombacomm.getSpeed() + 50 );
+ }
+ else if( keyCode == KeyEvent.VK_R ) {
+ updateDisplay("reset");
+ roombacomm.reset();
+ roombacomm.control();
+ }
+ }
+
+ /** Handle the key released event from the text field. */
+ public void keyReleased(KeyEvent e) {
+ }
+
+ /** Handle the key typed event from the text field. */
+ public void keyTyped(KeyEvent e) {
+ }
+
+ /** a sort of gui equivalent to system.out.println */
+ public void updateDisplay( String s ) {
+ displayText.append( s+"\n" );
+ displayText.setCaretPosition(displayText.getDocument().getLength());
+ }
+
+ /**
+ */
+ public void setupWindow() {
+ displayText = new JTextArea(20,30);
+ displayText.setLineWrap(true);
+ displayText.setEditable(false);
+ displayText.addKeyListener(this);
+ JScrollPane scrollPane = new JScrollPane(displayText,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
+ Container content = getContentPane();
+ content.add( scrollPane, BorderLayout.CENTER );
+ addKeyListener(this);
+ pack();
+ setResizable(false);
+ setVisible(true);
+ }
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java b/roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java
new file mode 100644
index 0000000..85d7b60
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java
@@ -0,0 +1,586 @@
+package com.hackingroomba.roombacomm;
+import java.io.*;
+import java.net.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.lang.Thread;
+import java.util.*;
+import java.lang.Math;
+
+
+public class FrameProcessor {
+ private static final long serialVersionUID = 1L;
+ private static final int lumBufHeight = 32;
+ // socket variables
+ int portNum = 5005;
+ Socket videoSocket;
+ BufferedInputStream in;
+ BufferedOutputStream out;
+ RoboRealmAPI rr;
+
+ // video variables
+ byte[] readBuf; // where we read raw network data into
+ byte[] vidBuf; // raw pixel bytes
+ int[] vidDispBuf, sliceBufInt;
+ int[] lumBuf = new int[256*lumBufHeight];
+ int[] qDisplayBuf, q; // the quantized array, qDisplayBuf is video data, q is 0 or 1 in each int
+ int[] ltBuf; // the graphic markers for current middle & bundaries of tracking
+ String server = "";
+ int frameSize;
+ int imgWidth;
+ int imgHeight;
+ Container contentPane;
+ Image img, lumImg, quantizedImg, sliceImg, trackingImg;
+ JFrame frame;
+ Insets insets;
+ int qStartRow, qEndRow;
+ int thresholdOverride = 0;
+ int frameCount = 0;
+
+ // line variables. Units are pixels distance from center of image. Negative value is left of center
+ class LineBoundary {
+ int start;
+ int middle;
+ int end;
+ };
+ ArrayList<LineBoundary> lineList;
+ LineBoundary currentLine;
+ int first;
+ int[] smoothSlice; // a quantized slice across the image, averaged to remove noise
+ int minLineThickness, maxLineThickness;
+
+ public FrameProcessor(String server,int portnum, int width, int height, int minLine, int maxline, int thresholdOv)
+ {
+ imgWidth = width;
+ imgHeight = height;
+ minLineThickness = minLine;
+ maxLineThickness = maxline;
+ lineList = new ArrayList<LineBoundary>();
+ currentLine = new LineBoundary();
+ first = 1;
+ smoothSlice = new int[width];
+ ltBuf = new int[4*imgWidth];
+ thresholdOverride = thresholdOv;
+ this.server = server;
+ this.portNum = portnum;
+ }
+
+ public void makeImagePane()
+ {
+ javax.swing.SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ createAndShowGUI();
+ }
+ });
+ }
+
+ void createAndShowGUI() {
+ //Create and set up the window.
+ frame = new JFrame("Raw Image");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ contentPane = frame.getContentPane();
+ contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20));
+
+ //Display the window.
+ frame.pack();
+ frame.setVisible(true);
+ insets = frame.getInsets();
+ frame.setMinimumSize(new Dimension(400,350));
+
+ }
+
+ public void displayFrame()
+ {
+ Graphics g = frame.getGraphics();
+
+ // display the raw image
+ img = frame.createImage(new MemoryImageSource(imgWidth,imgHeight,vidDispBuf, 0, imgWidth));
+ g.drawImage(img, insets.left+2, insets.top+2, null);
+
+ // display the slice
+ sliceImg = frame.createImage(new MemoryImageSource(imgWidth, qEndRow-qStartRow, sliceBufInt, 0, imgWidth));
+ g.drawImage(sliceImg, insets.left, insets.top + 20 + imgHeight, null);
+
+ // display the quantized image
+ quantizedImg = frame.createImage(new MemoryImageSource(imgWidth, qEndRow-qStartRow, qDisplayBuf, 0, imgWidth));
+ g.drawImage(quantizedImg, insets.left, insets.top + 40 + imgHeight, null);
+
+ // display the line tracking markers
+ trackingImg = frame.createImage(new MemoryImageSource(imgWidth, 4, ltBuf, 0, imgWidth));
+ g.drawImage(trackingImg, insets.left, insets.top + 60 + imgHeight, null);
+
+ //lumImg = frame.createImage(new MemoryImageSource(256,lumBufHeight,lumBuf, 0, 256));
+ //g.drawImage(lumImg, insets.left, insets.top + 20 + imgHeight, null);
+ }
+
+ public Boolean frame2Roborealm()
+ {
+ return rr.setImage(vidBuf, imgWidth, imgHeight);
+ }
+
+ public String getShapeData()
+ {
+ String rrString = rr.getVariable("SHAPES");
+ //System.out.println(rrString);
+ return rrString;
+ }
+
+ public void connect(boolean connectRoborealm)
+ {
+ try {
+ videoSocket = new Socket(server, portNum);
+ in = new BufferedInputStream(videoSocket.getInputStream());
+ out = new BufferedOutputStream(videoSocket.getOutputStream());
+ } catch (UnknownHostException e) {
+ System.out.println("Unknown host: " + server + ":" + portNum);
+ System.exit(-1);
+ } catch(IOException e) {
+ System.out.println("I/O exception");
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ System.out.println("connected to video");
+
+ // connect to RoboRealm if requested
+ if (connectRoborealm) {
+ rr = new RoboRealmAPI();
+ if (!rr.connect("localhost"))
+ {
+ System.out.println("Could not connect to RoboRealm on localhost! Exiting...");
+ System.exit(-1);
+ }
+ }
+ }
+
+ public void disconnect()
+ {
+ try {
+ // do io streams need to be closed first?
+ if (in != null) in.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ in = null;
+
+ try {
+ if (videoSocket != null) videoSocket.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ videoSocket = null;
+ System.out.println("disconnected from Video");
+ }
+
+ /**
+ * Read a video frame from the network device
+ * @param width in pixels
+ * @param height in pixels
+ * @param captureType 0 = monochrome, 1 = rgb color
+ * @return number of pixels read or negative for error during read (e.g. size doesn't match frame size)
+ */
+
+ public int readFrame(int width, int height, int captureType)
+ {
+ int bytesPerPixel = 1;
+ int readLength = 0;
+ int rxImageSize;
+ int rgbShiftSize = 16;
+ int pixel = 255 << 24;
+
+ if (captureType == 1) // if rgb color, 3 bytes/pixel, otherwise 1 for grayscale
+ bytesPerPixel = 3;
+ frameSize = width * height * bytesPerPixel;
+
+ int maxReadSize = frameSize;
+ readBuf = new byte[frameSize]; // analysis form - temp buffer for raw received data
+ vidBuf = new byte[frameSize]; // raw image bytes
+ vidDispBuf = new int[frameSize]; // display version (alpha set, bytes replicated if needed)
+ int vidDispBufIx = 0;
+ int vidBufIx = 0;
+
+ readBuf[0] = (byte)(200 + captureType); // send the "capture" command
+ byte[] t = new byte[4];
+
+ try {
+ out.write(readBuf, 0, 1);
+ out.flush();
+
+ readLength = in.read(t, 0, 4); // read imgWidth
+ if (readLength != 4)
+ return -4;
+ //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]);
+ imgWidth = ((t[2] << 8) & 0xff00) | t[3] & 0xff; // I think this way of getting byte to int is broken
+ if (imgWidth != width)
+ return -1;
+
+ readLength = in.read(t, 0, 4); // read imgHeight
+ //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]);
+ if (readLength != 4)
+ return -5;
+ imgHeight = (int)(t[2] & 0xff) << 8 | t[3] & 0xff;
+ //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]);
+ if (imgHeight != height)
+ return -2;
+
+ readLength = in.read(t, 0, 4); // read imgLength
+ if (readLength != 4)
+ return -6;
+ rxImageSize = ((t[1] << 16) & 0xff0000) | ((t[2] << 8) & 0xff00) | (int)t[3] & 0xff;
+ //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]);
+ if (rxImageSize != frameSize)
+ return -3;
+
+ //System.out.println("Chumby reports image size " + imgWidth + "x" + imgHeight + " length " + rxImageSize + " for frameSize " + frameSize);
+
+
+ // ACHTUNG - readBuf gets overwritten at the beginning by multiple read buffers - use vidDispBuf or vidBuf for image
+
+ for (int i=0; i<50; i++) {
+ readLength = in.read(readBuf, 0, maxReadSize);
+ maxReadSize -= readLength;
+ for (int j=0; j<readLength; j++) {
+ vidBuf[vidBufIx++] = readBuf[j]; // copy video bytes into the video buffer
+ if (captureType == 0) {
+ // copy pixel luminance into r, g, b bytes in vidDispBuf & set alpha to opaque
+ vidDispBuf[vidDispBufIx++] = (255 << 24) | (int)(readBuf[j] & 0xff) << 16
+ | (int)(readBuf[j] & 0xff) << 8 | (readBuf[j] & 0xff);
+ vidBuf[vidBufIx++] = readBuf[j];
+ } else {
+ switch (rgbShiftSize) {
+ case 16:
+ pixel |= (readBuf[j] << rgbShiftSize) & 0xff0000;
+ rgbShiftSize = 8;
+ break;
+ case 8:
+ pixel |= (readBuf[j] << rgbShiftSize) & 0xff00;
+ rgbShiftSize = 0;
+ break;
+ case 0:
+ pixel |= (readBuf[j]) & 0xff;
+ vidDispBuf[vidDispBufIx++] = pixel;
+ pixel = 255 << 24;
+ rgbShiftSize = 16;
+ break;
+ default:
+ System.err.println("Illegal value for rgbShiftSize " + rgbShiftSize);
+ return -100;
+ }
+ }
+ }
+ if (maxReadSize == 0)
+ break;
+ try {
+ Thread.sleep(20);
+ //System.out.println("Have " + vidBufIx + ", trying again");
+ } catch (Exception e) {
+ System.out.print(e);
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("I/O exception");
+ e.printStackTrace();
+ System.exit(-1);
+ }
+
+ //System.out.println("readFrame read " + frameSize + " bytes");
+ System.out.print(".");
+ frameCount++;
+ if (frameCount%128 == 0)
+ System.out.println();
+ return (vidDispBufIx);
+ }
+
+ /*
+ * Develop a normalization array that compensates for illumination variations in each voxel.
+ * This method returns an array of doubles, one per voxel, containing the factor by which each
+ * voxel must be multiplied to normalize its value to 0xc0. getFrame must have been called with the
+ * camera looking at white before this is called. It processes vidDispBuf at the requested rows.
+ */
+ public double [] normalizeRows(int startRow, int endRow)
+ {
+ int start, end;
+
+ assert (endRow <= startRow);
+ start = startRow * imgWidth;
+ end = endRow * imgWidth;
+ double [] normalizeArray = new double[end - start];
+ for (int i=start; i<end; i++) {
+ normalizeArray[i] = 192.0 / (double)(vidDispBuf[start + i]);
+ }
+ return (normalizeArray);
+ }
+
+ /*
+ * quantize the start row through end row-1 (looking down the image)
+ * See UTD Prof Schweitzer's notes on "Thresholding by Quantization"
+ */
+ public int quantizeRows (int startRow, int endRow)
+ {
+ assert (endRow <= startRow);
+ qStartRow = startRow;
+ qEndRow = endRow;
+ return(quantize(startRow*imgWidth, endRow*imgWidth));
+ }
+ // take a start & end offset into the readBuf array
+ private int quantize(int start, int end) {
+ int[] h, xh; // the histogram of the slice, and x * histogram
+ int[] q1, q2, e; // array of possible quantization values & Error for each t
+ int t, tMin, tMax;
+ int eMin, threshold;
+ int sigmaXhQ1, sigmaHQ1, sigmaXhQ2, sigmaHQ2;
+ int sigmaXQ1H, sigmaXQ2H;
+ int x;
+ int qtmp;
+
+ if (end <= start) {
+ System.out.println("error: quantize end < start");
+ System.exit(0);
+ }
+ // array values zeroed on create
+ h = new int[256];
+ xh = new int[256];
+ q1 = new int[256];
+ q2 = new int[256];
+ e = new int[256]; // E term (error) at t value
+ qDisplayBuf = new int[(end-start)];
+ q = new int[(end-start)];
+ sliceBufInt = new int[(end-start)];
+
+ // mark the live image with start/end
+ for (int i=(start-imgWidth); i<start; i++) {
+ if (i < 0) break;
+ vidDispBuf[i] = 0xff<<24 | 0xff;
+ }
+ for (int i=end; i<(end+imgWidth); i++) {
+ vidDispBuf[i] = 0xff<<24 | 0xff;
+ }
+ // build the histogram
+ for (int i=(start), j=0; i<(end); i++, j++) {
+ x = (int)(vidDispBuf[i])& 0xff;
+ h[x]++; // increment the appropriate histogram bucket for this image value
+ sliceBufInt[j] = x | ((x<<8)&0xff00) | ((x<<16)& 0xff0000) | (0xff<<24);
+ }
+ // calculate x * h(x) & store in xh, and print histogram values for testing
+ //System.out.println("Histogram: x, h, xh");
+ tMin = 0;
+ tMax = 0;
+ for (int j=0; j<h.length; j++) {
+ xh[j] = j * h[j];
+ if (tMin == 0) { // initialize tMin to the next t value after the first non-zero histogram bucket (avoid divide-by-zero)
+ if (h[j] != 0)
+ tMin = j+1;
+ } else {
+ if (h[j] != 0) {
+ tMax = j-1;
+ }
+ }
+ //if (h[j] != 0) System.out.println(j + "\t" + h[j] + "\t" + xh[j]);
+ }
+ if ((tMax - tMin) < 3) {
+ System.out.println("Error: image is too uniform in value - abandoning quantization");
+ return(-1);
+ }
+
+ // build arrays of q1, q2. Start summation at the first non-zero histogram index
+ t = tMin;
+ sigmaXhQ1 = xh[tMin-1];
+ sigmaHQ1 = h[tMin-1];
+
+ // initialize the q2 summations
+ sigmaXhQ2 = 0;
+ sigmaHQ2 = 0;
+ for (x=tMin; x<256; x++) {
+ sigmaXhQ2 += xh[x];
+ sigmaHQ2 += h[x];
+ }
+
+ // calculate q1 & q2 arrays for t = 1 to t = 254
+ do {
+ q1[t] = sigmaXhQ1 / sigmaHQ1;
+ q2[t] = sigmaXhQ2 / sigmaHQ2;
+ sigmaXhQ1 += xh[t]; // incrementing t means sigma**Q1 gets one more histogram value, and
+ sigmaHQ1 += h[t]; // sigma**Q2 loses that same one
+ sigmaXhQ2-= xh[t];
+ sigmaHQ2 -= h[t];
+ if (sigmaXhQ2 == 0) { // if we reach the highest luminance value, set tMax & bail
+ tMax = t;
+ break;
+ }
+ t++;
+ } while (t<255);
+
+ // calculate e array for t=1 to t=254
+ //System.out.println("\nt\tq1\tq2\tsgmQ1H\tsgmQ2H\te"); // print the header for diagnostic prints
+ for (t=tMin; t<tMax; t++) {
+ for (x=tMin-1, qtmp = q1[t], sigmaXQ1H=0; x<t; x++) {
+ sigmaXQ1H += (Math.pow((x - qtmp),2)) * h[x];
+ }
+ for (x=t, qtmp = q2[t], sigmaXQ2H=0; x<tMax; x++) {
+ sigmaXQ2H += (java.lang.Math.pow((x - qtmp),2)) * h[x];
+ }
+ e[t] = sigmaXQ1H + sigmaXQ2H;
+ //System.out.println(t + "\t" + q1[t] + "\t" + q2[t] + "\t" + sigmaXQ1H + "\t" + sigmaXQ2H + "\t" + e[t]);
+ }
+
+ // find minimum e & corresponding t
+ eMin = (int)2E9; // close to max positive number
+ threshold = 1;
+ for (t=tMin; t<tMax; t++) {
+ if (e[t] < eMin) {
+ eMin = e[t];
+ threshold = t;
+ }
+ }
+ //System.out.println("Threshold = " + threshold + " q1 = " + q1[t] + " q2 = " + q2[t] + "\n");
+
+ // Create the quantized image
+ x = 0;
+ if (thresholdOverride != 0)
+ threshold = thresholdOverride;
+ for (int srcIx=start; srcIx<end; srcIx++, x++) {
+ qDisplayBuf[x] = ((vidDispBuf[srcIx] & 0xff) < threshold) ? 0xff<<24 : -1; // assign quantized values to each pixel
+ q[x] = ((vidDispBuf[srcIx] & 0xff) < threshold) ? 0 : 1; // assign quantized values to each pixel
+ }
+ return(0);
+ }
+
+ /*
+ * segment the image into lines by doing an initial smoothing & averaging which ignores regions of black with fewer than 3
+ * black pixels in a 4-pixel vertical line. This produces a 1-line array of ints representing black or white at that
+ * portion of the image (called a smoothSlice). Then scan the smoothSlice and extract white-black-white transitions
+ * into an array container of found line boundaries.
+ * Note: this is designed to track black on white, but this is where tracking white on black would be supported
+ */
+ public int segmentImage()
+ {
+ int off1, off2, off3; // offsets into quantized array
+ boolean inBlack = false; // initially assume we're in white (virtual white at beginning of slice)
+ int blackStart, blackEnd;
+
+ lineList.clear(); // clear out previous lines
+ // average 4 vertical pixels to decide whether this point of the slice is white or black
+ off1 = imgWidth;
+ off2 = imgWidth * 2;
+ off3 = imgWidth * 3;
+ for (int i=0; i<imgWidth; i++) {
+ int whiteCnt = q[i] + q[i+off1] + q[i+off2] + q[i+off3];
+ if (whiteCnt < 3)
+ smoothSlice[i] = 0;
+ else
+ smoothSlice[i] = 1;
+ }
+
+ // scan the slice & pick out the black regions > minLineThickness & create a LineBoundary for each
+ blackStart = blackEnd = 0;
+ for (int i=0; i<imgWidth; i++) {
+ if (inBlack) { // we're in a black part of the image (can never happen on element 0)
+ if ((smoothSlice[i] == 1) || (i == imgWidth-1)) { // were in black, just transitioned to white, or end of array
+ blackEnd = i-1;
+ inBlack = false;
+ if (((blackEnd - blackStart) > minLineThickness) && ((blackEnd - blackStart) < maxLineThickness)) {
+ LineBoundary lb = new LineBoundary();
+ lb.start = blackStart - (imgWidth/2);
+ lb.end = blackEnd - (imgWidth/2);
+ lb.middle = ((lb.start + lb.end)/2);
+ lineList.add(lb);
+ } // else ignore this as a false line (noise) - we're in white now
+ }
+ } else { // inWhite
+ if (smoothSlice[i] == 0) {
+ inBlack = true; // were in white, just transitioned to black (can happen on element 0)
+ blackStart = i;
+ }
+ }
+ }
+
+ // print found lines
+// System.out.print("Found " + lineList.size() + " lines: ");
+// for (LineBoundary l : lineList) {
+// System.out.print(l.start + " " + l.middle + " " + l.end + " ");
+// }
+// System.out.println();
+
+ return(0);
+ }
+
+ /*
+ * Find the line we should be following. The very first time this runs, or if it loses the line & backs up
+ * it will pick the line closest to center. Thereafter it chooses the first found line who's center is within
+ * the boundaries of the last line it chose. If it can't find one, it returns an error. Therefore it will always take
+ * a left fork. It returns the middle value of the chosen line, or a large value if error.
+ */
+ public int trackLine()
+ {
+ int m;
+ LineBoundary lTmp = new LineBoundary();
+ // clear out current markers in the tracking display
+ m = currentLine.middle + imgWidth/2;
+ ltBuf[m + 3*imgWidth] = ltBuf[m + 2*imgWidth] = ltBuf[m + imgWidth] = ltBuf[m] = 0;
+
+ if (lineList.size() == 0) {
+ if (currentLine.middle >= 0) {
+ System.out.println("Error: line disappeared to right, last seen at " + currentLine.middle);
+ return(100);
+ } else {
+ System.out.println("Error: line disappeared to left, last seen at " + currentLine.middle);
+ return (-100);
+ }
+ }
+ if (first == 1) {
+ lTmp.middle = 100; // any found line will be closer than this
+ for (LineBoundary lb : lineList) {
+ if (Math.abs(lb.middle) < Math.abs(lTmp.middle)) {
+ lTmp = lb; // save the new lineBoundary with the lowest absolute value of middle
+ }
+ }
+ currentLine = lTmp;
+ first = 0;
+ System.out.print("Picked line center at " + lTmp.middle);
+ return(lTmp.middle);
+ } else {
+ if (lineList.size() == 1) {
+ System.out.println("tracking line center at " + lineList.get(0).middle + " width: " + (lineList.get(0).end-lineList.get(0).start));
+ currentLine = lineList.get(0);
+ return (lineList.get(0).middle);
+ }
+ for (LineBoundary lb : lineList) {
+ if ((lb.middle > currentLine.start-20) && (lb.middle < currentLine.end + 20)) {
+ System.out.println("tracking line center at " + lb.middle + " width: " + (lb.end-lb.start));
+ currentLine = lb;
+ // write the image of the new currentLine middle
+ m = lb.middle+(imgWidth/2);
+ ltBuf[m] = 0xff<<24 | 0xff<<16;
+ ltBuf[m + 3*imgWidth] = ltBuf[m + 2*imgWidth] = ltBuf[m + imgWidth] = ltBuf[m];
+ return (lb.middle);
+ }
+ }
+ }
+ System.out.println("Error: Lost the line I was tracking");
+ first = 1;
+ return(2001);
+ }
+ public void testQuantization()
+ {
+ byte[] testArray1 =
+ {6, 6, 6, 10,
+ 6, 6, 6, 10,
+ 17, 17, 17, 17,
+ 17, 17, 17, 88};
+ for (int i=0; i<testArray1.length; i++) {
+ readBuf[i] = testArray1[i];
+ }
+ quantize(0, testArray1.length);
+ }
+ void dumpVideo(int row)
+ {
+ System.out.print("row " + row + " ");
+
+ for (int col=0; col<160; col++) {
+ System.out.print((int)readBuf[row*160 + col] + " ");
+ }
+ System.out.println();
+ }
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/JImagePanel.java b/roombacomm-client/src/com/hackingroomba/roombacomm/JImagePanel.java
new file mode 100644
index 0000000..410c49d
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/JImagePanel.java
@@ -0,0 +1,20 @@
+package com.hackingroomba.roombacomm;
+import java.awt.*;
+import java.awt.image.*;
+import javax.swing.*;
+
+public class JImagePanel extends JPanel{
+ private BufferedImage image;
+ int x, y;
+ public JImagePanel(BufferedImage image, int x, int y) {
+ super();
+ this.image = image;
+ this.x = x;
+ this.y = y;
+ }
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ g.drawImage(image, x, y, null);
+ }
+} \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/ListSerialPorts.java b/roombacomm-client/src/com/hackingroomba/roombacomm/ListSerialPorts.java
new file mode 100644
index 0000000..ed713d8
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/ListSerialPorts.java
@@ -0,0 +1,59 @@
+/*
+ * roombacomm.ListSerialPorts
+ *
+ * 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;
+
+/**
+ * A simple test of RoombaComm and RoombaCommSerial functionality.
+ * <p>
+ * Run it with something like: <pre>
+ * java roombacomm.ListSerialPorts
+ * </pre>
+ *
+ */
+public class ListSerialPorts {
+
+ public static void main(String[] args) {
+
+ RoombaComm roombacomm = new RoombaCommSerial();
+ String portlist[];
+ //roombacomm.debug = true;
+
+ portlist = roombacomm.listPorts();
+ System.err.println("Available ports:");
+ for(int i=0;i<portlist.length;i++) {
+ System.err.println(" "+i+": "+portlist[i]);
+ }
+
+ System.err.println("Sleeping for 5 seconds so you can (un)plug a USB serial device in and watch the port list change...\n");
+ try { Thread.sleep(5000); } catch( Exception e ) {}
+
+ portlist = roombacomm.listPorts();
+ System.err.println("Available ports (again):");
+ for(int i=0;i<portlist.length;i++) {
+ System.err.println(" "+i+": "+portlist[i]);
+ }
+
+
+ }
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/LogoA.java b/roombacomm-client/src/com/hackingroomba/roombacomm/LogoA.java
new file mode 100644
index 0000000..cbb9ddf
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/LogoA.java
@@ -0,0 +1,105 @@
+/*
+ * roombacomm.LogoA
+ *
+ * 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;
+
+/**
+ Some example Logo-like things to do
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.LogoA /dev/cu.KeySerial1<br>
+ Usage:
+ roombacomm.LogoA serialportname [protocol] [options]<br>
+ where:
+ protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ </pre>
+
+*/
+public class LogoA {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.LogoA <serialportname> [options]\n" +
+ "where protocol (optional) is SCI or OI\n"+
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ "\n";
+ static boolean debug = false;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ for( int i=1; i < args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ }
+
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(30);
+
+ for( int i=0; i<8; i++ ) {
+ roombacomm.spinRight( 45 );
+ square( roombacomm, 100 );
+ }
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+ /**
+ * Make a square with a Roomba.
+ * Leaves Roomba in same place it began (theoretically)
+ * @param rc RoombaComm object connected to a Roomba
+ * @param size size of square in mm
+ */
+ public static void square(RoombaComm rc, int size) {
+ rc.goForward( size );
+ rc.spinLeft( 90 );
+ rc.goForward( size );
+ rc.spinLeft( 90 );
+ rc.goForward( size );
+ rc.spinLeft( 90 );
+ rc.goForward( size );
+ rc.spinLeft( 90 );
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Note.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Note.java
new file mode 100644
index 0000000..d0bbc25
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Note.java
@@ -0,0 +1,21 @@
+
+package com.hackingroomba.roombacomm;
+
+/**
+ * Simple wrapper for musical notes
+ */
+public class Note {
+ public int notenum; // midi note number
+ public int duration; // in milliseconds
+
+ Note( int anotenum, int aduration ) {
+ notenum = anotenum;
+ duration = aduration;
+ }
+ public String toString() {
+ return "("+notenum+","+duration+")";
+ }
+ public int toSec64ths() {
+ return duration * 64/1000;
+ }
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java
new file mode 100644
index 0000000..97c5297
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java
@@ -0,0 +1,67 @@
+package com.hackingroomba.roombacomm;
+
+/*
+ * This class implements the following PID algorithm
+ * previous_error = 0
+ * start:
+ * error = setpoint - actual_position
+ * P = Kp * error
+ * I = Ki * sum(error)
+ * D = Kd * (error - previous_error)
+ * output = P + I + D
+ * previous_error = error
+ * wait(dt)
+ * goto start
+ */
+public class Pid {
+ double k_p, k_i, k_d, i_state_max; // the PID constants
+ double d_state, i_state; // the PID states
+ boolean disableD = true;
+ int lastError;
+
+ Pid(double p, double i, double d)
+ {
+ k_p = p;
+ k_i = i;
+ k_d = d;
+ d_state = 0.0;
+ i_state = 0.0;
+ i_state_max = 200.0;
+ }
+
+ public double computePid( double target, double value )
+ {
+ double error;
+ double p, i, d;
+ double ret;
+
+ error = target - value;
+ p = k_p * error;
+ d = k_d * (error - d_state );
+ if (disableD) { // prevent an initial kick on the first iteration, before d_state is set.
+ disableD = false;
+ d = 0;
+ }
+ d_state = error;
+ i_state += error;
+
+ // cap I term windup
+ if( i_state > i_state_max )
+ i_state = i_state_max;
+ if( i_state < -i_state_max )
+ i_state = -i_state_max;
+ // clear I term & diable D if we overshoot
+ if (((error > 0) && (lastError < 0)) || ((error < 0) && (lastError > 0))) {
+ i_state = 0;
+ disableD = true;
+ }
+
+ i = k_i * i_state;
+
+ ret = p + i + d;
+ System.out.printf("error %4.1f p: %4.1f i: %4.1f d: %4.1f\n", error, p, i, d);
+
+ return ret;
+ }
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java
new file mode 100644
index 0000000..9759b93
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java
@@ -0,0 +1,112 @@
+
+
+
+package com.hackingroomba.roombacomm;
+
+import java.util.*;
+import java.util.regex.*;
+
+/**
+ *
+ */
+public class RTTTLParser {
+
+ public static HashMap noteToNum;
+ static {
+ noteToNum = new HashMap();
+ noteToNum.put("c", new Integer(0) );
+ noteToNum.put("c#", new Integer(1) );
+ noteToNum.put("d", new Integer(2) );
+ noteToNum.put("d#", new Integer(3) );
+ noteToNum.put("e", new Integer(4) );
+ noteToNum.put("f", new Integer(5) );
+ noteToNum.put("f#", new Integer(6) );
+ noteToNum.put("g", new Integer(7) );
+ noteToNum.put("g#", new Integer(8) );
+ noteToNum.put("a", new Integer(9) );
+ noteToNum.put("a#", new Integer(10) );
+ noteToNum.put("b", new Integer(11) );
+ noteToNum.put("h", new Integer(7) );
+ }
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( "usage: roombacomm.RTTTLParser <rttlstring>");
+ System.exit(0);
+ }
+ String rtttl = args[0];
+ ArrayList notelist = parse( rtttl );
+ for( int i=0; i< notelist.size(); i++ ) {
+ System.out.println("notelist["+i+"]="+notelist.get(i));
+ }
+ }
+
+ public static ArrayList parse(String rtttl) {
+ System.out.println("parsing: "+rtttl);
+ String rtttl_working = rtttl.toLowerCase();
+ String parts[] = rtttl_working.split(":");
+ String name = parts[0];
+ String defaults[] = parts[1].split("[,=]");
+ String notes[] = parts[2].split(",");
+
+ // global defaults
+ int bpm = 63;
+ int octave = 6;
+ int duration = 4;
+
+ ArrayList notelist = new ArrayList();
+
+ for( int i=0; i < defaults.length; i++ ) {
+ //System.out.println("defaults["+i+"]="+defaults[i]);
+ if( defaults[i].equals("b") )
+ try { bpm = Integer.parseInt(defaults[i+1]); }
+ catch(Exception e) {}
+ else if( defaults[i].equals("o") )
+ try { octave = Integer.parseInt(defaults[i+1]); }
+ catch(Exception e) {}
+ else if( defaults[i].equals("d") )
+ try { duration = Integer.parseInt(defaults[i+1]); }
+ catch(Exception e) {}
+ }
+ System.out.println("bpm:"+bpm+",octave:"+octave+",duration:"+duration);
+
+ for( int i=0; i < notes.length; i++ ) {
+ Matcher m =Pattern.compile("(\\d+)*(.+?)(\\d)*(\\.)*").matcher(notes[i]);
+ m.find();
+ // group(1) == duration (optional)
+ // group(2) == note (required)
+ // group(3) == scale (optional)
+ // group(4) == triplet (optional)
+ int dur = duration;
+ int oct = octave;
+ if( m.group(1) != null )
+ try { dur = Integer.parseInt( m.group(1) ); }
+ catch(Exception e) {}
+ if( m.group(4) != null && m.group(4).equals(".") )
+ dur += dur/2;
+ if( m.group(3) != null )
+ try { oct = Integer.parseInt( m.group(3) ); }
+ catch(Exception e) {}
+ if( m.group(2) != null ) {
+ int notenum;
+ if( m.group(2).equals("p") ) {
+ notenum = 0;
+ }
+ else {
+ Integer nn = (Integer) noteToNum.get( m.group(2) );
+ notenum = nn.intValue();
+ notenum = notenum + 12*oct;
+ }
+ dur = bpmToMillis(bpm) / dur;
+ notelist.add( new Note( notenum, dur ) );
+ }
+ }
+ return notelist;
+ }
+
+ public static int bpmToMillis( int bpm ) {
+ return (60 * 1000 ) / bpm;
+ }
+}
+
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java
new file mode 100644
index 0000000..7184022
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java
@@ -0,0 +1,144 @@
+/*
+ * roombacomm.RTTTLPlay
+ *
+ * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com
+ *
+ * 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 java.util.*;
+
+/*
+ Play RTTL formatted ringtones on the Roomba.
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.RTTTLPlay /dev/cu.KeySerial1 'tron:d=4,o=5,b=200:8f6,8c6,8g,e,8p,8f6,8c6,8g,8f6,8c6,8g,e,8p,8f6,8c6,8g,e.,2d'<br>
+ Usage:
+ roombacomm.RTTTLPlay serialportname [protocol] rttl_string [options]<br>
+ where:
+ protocol (optional) is SCI or OI
+ rttl_string is a string of notes
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ -flush -- flush on sends(), normally not needed
+ </pre>
+ */
+public class RTTTLPlay {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.RTTTLPlay <serialportname> [protocol] <rttl string> [options]\n" +
+ "where:\n"+
+ "protocol (optional) is SCI or OI\n"+
+ "rttl string is a string of notes\n"+
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ " -flush -- flush on sends(), normally not needed\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+ static boolean flush = false;
+
+ public static void main(String[] args) {
+ if( args.length < 2 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ int argOffset = 0;
+ if (args[1].equals("SCI") || (args[1].equals("OI"))) {
+ roombacomm.setProtocol(args[1]);
+ argOffset = 1;
+ }
+ String rtttl = args[1+argOffset];
+
+ for( int i=2+argOffset; i < args.length; i++ ) {
+ if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ else if( args[i].endsWith("flush") )
+ flush = true;
+ }
+
+
+ roombacomm.debug = debug;
+ roombacomm.flushOutput = flush;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup on port"+portname);
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(30);
+
+ System.out.println("Checking for Roomba... ");
+ if( roombacomm.updateSensors() )
+ System.out.println("Roomba found!");
+ else
+ System.out.println("No Roomba. :( Is it turned on?");
+
+ 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 ) {
+ System.out.println("creating a song with createSong()");
+ 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 {
+ System.out.println("playing song in realtime with playNote()");
+ 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 );
+ }
+ }
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java
new file mode 100644
index 0000000..65816f9
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java
@@ -0,0 +1,945 @@
+package com.hackingroomba.roombacomm;
+
+import java.io.*;
+import java.net.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.util.*;
+import java.net.URL;
+
+class RoboRealmAPI
+{
+ // default read and write socket timeout
+ public final static int DEFAULT_TIMEOUT = 60000;
+
+ // the port number to listen on ... needs to match that used in RR interface
+ public final static int SERVER_PORTNUM = 6060;
+
+ // indicates that the application is connected to RoboRealm Server
+ boolean connected = false;
+
+ // holds the previously read data size
+ int lastDataTop = 0;
+
+ // holds the previously read data buffer
+ int lastDataSize = 0;
+
+ // contains the read/write socket timeouts
+ int timeout = DEFAULT_TIMEOUT;
+
+ // general buffer for data manipulation and socket reading
+ byte buffer[] = new byte[4096];
+
+ // our instance of our primitive XML parser
+ XML xml = new XML();
+
+ // socket based reader and writer objects
+ BufferedInputStream bufferedReader;
+ BufferedOutputStream bufferedWriter;
+
+ // out main socket handle
+ Socket handle;
+
+ public int width=0, height=0;
+
+ /******************************************************************************/
+ /* Text string manipulation routines */
+ /******************************************************************************/
+
+ public RoboRealmAPI()
+ {
+ }
+
+ /*
+ Generalized string replace routine used in escaping strings
+ to the appropriate XML string. Java only has the character
+ replace routine as apposed to string replace.
+ */
+ private String replace(String txt, String src, String dest)
+ {
+ if (txt==null) return new String("");
+ int i,j;
+ int len=src.length();
+ StringBuffer sb=new StringBuffer(txt.length());
+
+ j=0;
+ while ((i=txt.indexOf(src,j))>=0)
+ {
+ sb.append(txt.substring(j,i));
+ sb.append(dest);
+ i+=len;
+ j=i;
+ }
+ sb.append(txt.substring(j));
+
+ return sb.toString();
+ }
+
+ /*
+ Escapes strings to be included in XML message. This can be accomplished by a
+ sequence of replace statements.
+ & -> &amp;
+ " -> &quote;
+ < -> &lt;
+ > -> &gt;
+ */
+ private String escape(String txt)
+ {
+ txt = replace(txt, "&", "&amp;");
+ txt = replace(txt, "\"", "&quote;");
+ txt = replace(txt, "<", "&lt;");
+ txt = replace(txt, ">", "&gt;");
+ return txt;
+ }
+
+ /******************************************************************************/
+ /* Socket Routines */
+ /******************************************************************************/
+
+ /* Initiates a socket connection to the RoboRealm server */
+ public boolean connect(String hostname)
+ {
+ connected=false;
+
+ try
+ {
+ handle = new Socket(hostname, SERVER_PORTNUM);
+
+ handle.setSoTimeout(timeout);
+
+ bufferedReader = new BufferedInputStream(handle.getInputStream());
+ bufferedWriter = new BufferedOutputStream(handle.getOutputStream());
+ }
+ catch (IOException e2)
+ {
+ //Unable to open connection to RoboRealm port 6060
+ return false;
+ }
+
+ connected=true;
+
+ return true;
+ }
+
+ /* close the socket handle */
+ public void disconnect()
+ {
+ try
+ {
+ if (connected)
+ handle.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ // cause the roborealm application to close
+ public boolean close()
+ {
+ if (!connected) return false;
+
+ if (send("<request><close/></request>"))
+ {
+ // read in variable length
+ String buffer;
+ if ((buffer = readMessage())!=null)
+ {
+ return buffer.equals("<response>ok</response>");
+ }
+ }
+
+ return false;
+ }
+
+ // sends a String over the socket port to RoboRealm
+ private boolean send(String txt)
+ {
+ try
+ {
+ bufferedWriter.write(txt.getBytes(), 0, txt.length());
+ bufferedWriter.flush();
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ Buffered socket image read. Since we don't know how much data was read from a
+ previous socket operation we have to add in any previously read information
+ that may still be in our buffer. We detect the end of XML messages by the
+ </response> tag but this may require reading in part of the image data that
+ follows a message. Thus when reading the image data we have to move previously
+ read data to the front of the buffer and continuing reading in the
+ complete image size from that point.
+ */
+
+ public int readImageData(byte pixels[], int len)
+ {
+ int num;
+
+ // check if we have any information left from the previous read
+ num = lastDataSize-lastDataTop;
+ if (num>len)
+ {
+ System.arraycopy(pixels, lastDataTop, buffer, 0, len);
+ lastDataTop+=num;
+ return num;
+ }
+ System.arraycopy(pixels, lastDataTop, buffer, 0, num);
+ len-=num;
+ lastDataSize=lastDataTop=0;
+
+ // then keep reading until we're read in the entire image length
+ do
+ {
+ int res;
+ try
+ {
+ res = bufferedReader.read(pixels, num, len);
+ }
+ catch (IOException e)
+ {
+ return 0;
+ }
+
+ if (res<0)
+ {
+ lastDataSize=lastDataTop=0;
+ return -1;
+ }
+ num+=res;
+ len-=res;
+ }
+ while (len>0);
+
+ return num;
+ }
+
+ /* If an image is too large for the provided buffer the rest of the data needs
+ to be skipped so we can continue to interact with the XML API. This routine
+ will remove that additional data from the socket*/
+ public int skipData(int len)
+ {
+ int num;
+
+ // check if we have any information left from the previous read
+ num = lastDataSize-lastDataTop;
+ if (num>len)
+ {
+ lastDataTop+=num;
+ return num;
+ }
+ len-=num;
+ lastDataSize=lastDataTop=0;
+
+ try
+ {
+ bufferedReader.skip(len);
+ }
+ catch (IOException e)
+ {
+ return 0;
+ }
+
+ return num+len;
+ }
+
+ /* Read's in an XML message from the RoboRealm Server. The message is always
+ delimited by a </response> tag. We need to keep reading in information until
+ this tag is seen. Sometimes this will accidentally read more than needed
+ into the buffer such as when the message is followed by image data. We
+ need to keep this information for the next readImage call.*/
+ private String readMessage()
+ {
+ int num=0;
+ byte delimiter[] = "</response>".getBytes();
+ int top=0;
+ int i;
+
+ // read in blocks of data looking for the </response> delimiter
+ while (true)
+ {
+ int res;
+ try
+ {
+ res = bufferedReader.read(buffer, num, 4096-num);
+ }
+ catch (IOException e)
+ {
+ System.out.println(e.getMessage());
+ return null;
+ }
+
+ if (res<0)
+ {
+ lastDataSize=lastDataTop=0;
+ return null;
+ }
+
+ lastDataSize=num+res;
+ for (i=num;i<num+res;i++)
+ {
+ if (buffer[i]==delimiter[top])
+ {
+ top++;
+ if (top>=delimiter.length)
+ {
+ num=i+1;
+ buffer[num]=0;
+ lastDataTop=num;
+ return new String(buffer, 0, num);
+ }
+ }
+ else
+ top=0;
+ }
+ num+=res;
+ }
+ }
+
+ /******************************************************************************/
+ /* API Routines */
+ /******************************************************************************/
+
+ /* Returns the current image dimension */
+ public Dimension getDimension()
+ {
+ if (!connected) return null;
+
+ if (send("<request><get_dimension/></request>"))
+ {
+ // read in variable length
+ String buffer;
+ if ((buffer = readMessage())!=null)
+ {
+ if (xml.parse(buffer))
+ {
+ return new Dimension(xml.getInt("response.width"), xml.getInt("response.height"));
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ Returns the current processed image as a Java image.
+ */
+
+ public int[] getImage(String name)
+ {
+ if (!connected) return null;
+ if (name==null) name="";
+
+ // create the message request
+ if (send("<request><get_image>"+escape(name)+"</get_image></request>"))
+ {
+ String buffer;
+ // read in response which contains image information
+ if ((buffer=readMessage())!=null)
+ {
+ // parse image width and height
+ xml.parse(buffer);
+ int len = xml.getInt("response.length");
+ width = xml.getInt("response.width");
+ height = xml.getInt("response.height");
+ // ensure that we have enough room in pixels
+ byte pixels[] = new byte[len];
+ // actual image data follows the message
+ if (readImageData(pixels, len)==len)
+ {
+ //DataBuffer db = new DataBufferByte(pixels, width*height*3, 0);
+ //WritableRaster raster = Raster.createWritableRaster(BufferedImage.TYPE_3BYTE_BGR, db, null);
+ //return new BufferedImage(ColorModel.getRGBdefault(), raster, false, null);
+ int pixelInts[] = new int[width*height];
+ int l = width*height*3;
+ int i,j;
+ for (j=i=0;i<l;i+=3,j++)
+ pixelInts[j]=((pixels[i]&255)<<16)|((pixels[i+1]&255)<<8)|(pixels[i+2]&255);
+
+ return pixelInts;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ Returns the current processed image.
+ pixels - output - contains RGB 8 bit byte.
+ width - output - contains grabbed image width
+ height - output - contains image height
+ len - input - maximum size of pixels to read
+ */
+
+ public Dimension getImage(byte pixels[], int len)
+ {
+ return getImage((String)"processed", pixels, len);
+ }
+
+ /*
+ Returns the named image.
+ name - input - name of image to grab. Can be source, processed, or marker name.
+ pixels - output - contains RGB 8 bit byte.
+ width - output - contains grabbed image width
+ height - output - contains image height
+ len - input - maximum size of pixels to read
+ */
+
+ public Dimension getImage(String name, byte pixels[], int max)
+ {
+ if (!connected) return null;
+ if (name==null) name="";
+
+ // create the message request
+ if (send("<request><get_image>"+escape(name)+"</get_image></request>"))
+ {
+ String buffer;
+ // read in response which contains image information
+ if ((buffer=readMessage())!=null)
+ {
+ // parse image width and height
+ xml.parse(buffer);
+ int len = xml.getInt("response.length");
+ int width = xml.getInt("response.width");
+ int height = xml.getInt("response.height");
+ // ensure that we have enough room in pixels
+ if (len>max)
+ {
+ skipData(len);
+ return null;
+ }
+
+ // actual image data follows the message
+ if (readImageData(pixels, len)==len)
+ return new Dimension(width, height);
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ Sets the current source image.
+ pixels - input - contains RGB 8 bit byte.
+ width - input - contains grabbed image width
+ height - input - contains image height
+ */
+
+ public boolean setImage(byte pixels[], int width, int height)
+ {
+ return setImage(null, pixels, width, height);
+ }
+
+ /*
+ Sets the current source image.
+ name - input - the name of the image to set. Can be source or marker name
+ pixels - input - contains RGB 8 bit byte.
+ width - input - contains grabbed image width
+ height - input - contains image height
+ */
+
+ public boolean setImage(String name, byte pixels[], int width, int height)
+ {
+ if (!connected) return false;
+ if (name==null) name="";
+
+ // setup the message request
+ if (send("<request><set_image><source>"+escape(name)+"</source><width>"+width+"</width><height>"
+ +height+"</height><format>RGB</format><wait>1</wait></set_image></request>"))
+ {
+ // send the RGB triplet pixels after message
+ try
+ {
+ bufferedWriter.write(pixels, 0, width*height*3);
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+
+ // read message response
+ String buffer;
+ if ((buffer = readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ Returns the value of the specified variable.
+ name - input - the name of the variable to query
+ result - output - contains the current value of the variable
+ max - input - the maximum size of what the result can hold
+ */
+
+ public String getVariable(String name)
+ {
+ if (!connected) return null;
+ if ((name==null)||(name.length()==0)) return null;
+
+ if (send("<request><get_variable>"+escape(name)+"</get_variable></request>"))
+ {
+ // read in variable length
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (xml.parse(buffer))
+ {
+ return xml.getFirst();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ Returns the value of the specified variables.
+ name - input - the names of the variable to query
+ result - output - contains the current values of the variables
+ max - input - the maximum size of what the result can hold
+ */
+
+ public Vector getVariables(String names)
+ {
+ if (!connected) return null;
+ if ((names==null)||(names.length()==0)) return null;
+
+ if (send("<request><get_variables>"+escape(names)+"</get_variables></request>"))
+ {
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ return xml.parseVector(buffer);
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ Sets the value of the specified variable.
+ name - input - the name of the variable to set
+ value - input - contains the current value of the variable to be set
+ */
+
+ public boolean setVariable(String name, String value)
+ {
+ if (!connected) return false;
+ if ((name==null)||(name.length()==0)) return false;
+
+ if (send("<request><set_variable><name>"+escape(name)+"</name><value>"+escape(value)+"</value></set_variable></request>"))
+ {
+ // read in confirmation
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ Sets the value of the specified variables.
+ names - input - the name of the variable to set
+ values - input - contains the current value of the variable to be set
+ */
+
+ public boolean setVariables(String names[], String values[], int num)
+ {
+ if (!connected) return false;
+ if ((names==null)||(values==null)||(names[0].length()==0)) return false;
+
+ int j=0;
+ int i;
+
+ StringBuffer sb = new StringBuffer();
+
+ // create request message
+ sb.append("<request><set_variables>");
+ for (i=0;(i<num);i++)
+ {
+ sb.append("<variable><name>");
+ sb.append(escape(names[i]));
+ sb.append("</name><value>");
+ sb.append(escape(values[i]));
+ sb.append("</value></variable>");
+ }
+ sb.append("</set_variables></request>");
+
+ // send that message to RR Server
+ if (send(sb.toString()))
+ {
+ // read in confirmation
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ Deletes the specified variable
+ name - input - the name of the variable to delete
+ */
+
+ public boolean deleteVariable(String name)
+ {
+ if (!connected) return false;
+ if ((name==null)||(name.length()==0)) return false;
+
+ if (send("<request><delete_variable>"+escape(name)+"</delete_variable></request>"))
+ {
+ // read in variable length
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ Executes the provided image processing pipeline
+ source - the XML .robo file string
+ */
+
+ public boolean execute(String source)
+ {
+ if (!connected) return false;
+ if ((source==null)||(source.length()==0)) return false;
+
+ //send the string
+ if (send("<request><execute>"+escape(source)+"</execute></request>"))
+ {
+ // read in result
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ Executes the provided .robo file. Note that the file needs to be on the machine
+ running RoboRealm. This is similar to pressing the 'open program' button in the
+ main RoboRealm dialog.
+ filename - the XML .robo file to run
+ */
+ public boolean loadProgram(String filename)
+ {
+ if (!connected) return false;
+ if ((filename==null)||(filename.length()==0)) return false;
+
+ if (send("<request><load_program>"+escape(filename)+"</load_program></request>"))
+ {
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ Loads an image into RoboRealm. Note that the image needs to exist
+ on the machine running RoboRealm. The image format must be one that
+ RoboRealm using the freeimage.dll component supports. This includes
+ gif, pgm, ppm, jpg, png, bmp, and tiff. This is
+ similar to pressing the 'load image' button in the main RoboRealm
+ dialog.
+ name - name of the image. Can be "source" or a marker name,
+ filename - the filename of the image to load
+ */
+ public boolean loadImage(String name, String filename)
+ {
+ if (!connected) return false;
+
+ if ((filename==null)||(filename.length()==0)) return false;
+ if ((name==null)||(name.length()==0)) name="source";
+
+ if (send("<request><load_image><filename>"+escape(filename)+"</filename><name>"+escape(name)+"</name></load_image></request>"))
+ {
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ Saves the specified image in RoboRealm to disk. Note that the filename is relative
+ to the machine that is running RoboRealm. The image format must be one that
+ RoboRealm using the freeimage.dll component supports. This includes
+ gif, pgm, ppm, jpg, png, bmp, and tiff. This is
+ similar to pressing the 'save image' button in the main RoboRealm
+ dialog.
+ name - name of the image. Can be "source","processed", or a marker name,
+ filename - the filename of the image to save
+ */
+ public boolean saveImage(String source, String filename)
+ {
+ if (!connected) return false;
+
+ if ((filename==null)||(filename.length()==0)) return false;
+ if ((source==null)||(source.length()==0)) source="processed";
+
+ // create the save image message
+ if (send("<request><save_image><filename>"+escape(filename)+"</filename><source>"+escape(source)+"</source></save_image></request>"))
+ {
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ Sets the current camera driver. This can be used to change the current viewing camera
+ to another camera installed on the same machine. Note that this is a small delay
+ when switching between cameras. The specified name needs only to partially match
+ the camera driver name seen in the dropdown picklist in the RoboRealm options dialog.
+ For example, specifying "Logitech" will select any installed Logitech camera including
+ "Logitech QuickCam PTZ".
+ */
+ public boolean setCamera(String name)
+ {
+ if (!connected) return false;
+ if ((name==null)||(name.length()==0)) return false;
+
+ // create the save image message
+ if (send("<request><set_camera>"+escape(name)+"</set_camera></request>"))
+ {
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ This routine provides a way to stop processing incoming video. Some image processing
+ tasks can be very CPU intensive and you may only want to enable processing when
+ required but otherwise not process any incoming images to release the CPU for other
+ tasks. The run mode can also be used to processing individual frames or only run
+ the image processing pipeline for a short period. This is similar to pressing the
+ "run" button in the main RoboRealm dialog.
+ mode - can be toggle, on, off, once, or a number of frames to process
+ */
+ public boolean run(String mode)
+ {
+ if (!connected) return false;
+ if ((mode==null)||(mode.length()==0)) return false;
+
+ // create the save image message
+ if (send("<request><run>"+escape(mode)+"</run></request>"))
+ {
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ There is often a need to pause your own Robot Controller program to wait for
+ RoboRealm to complete its task. The eaisest way to accomplish this is to wait
+ on a specific variable that is set to a specific value by RoboRealm. Using the
+ waitVariable routine you can pause processing and then continue when a variable
+ changes within RoboRealm.
+ name - name of the variable to wait for
+ value - the value of that variable which will cancel the wait
+ timeout - the maximum time to wait for the variable value to be set
+ */
+
+ public boolean waitVariable(String name, String value, int timeout)
+ {
+ if (timeout==0) timeout=100000000;
+
+ if (!connected) return false;
+ if ((name==null)||(name.length()==0)) return false;
+
+ if (send("<request><wait_variable><name>"+escape(name)+"</name><value>"+escape(value)+"</value><timeout>"+timeout+"</timeout></wait_variable></request>"))
+ {
+ try
+ {
+ handle.setSoTimeout(timeout);
+ }
+ catch (SocketException e)
+ {
+ return false;
+ }
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ try
+ {
+ handle.setSoTimeout(DEFAULT_TIMEOUT);
+ }
+ catch (SocketException e)
+ {
+ return false;
+ }
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ try
+ {
+ handle.setSoTimeout(DEFAULT_TIMEOUT);
+ }
+ catch (SocketException e)
+ {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ If you are rapdily grabbing images you will need to wait inbetween each
+ get_image for a new image to be grabbed from the video camera. The wait_image
+ request ensures that a new image is available to grab. Without this routine
+ you may be grabbing the same image more than once.
+ */
+
+ public boolean waitImage(int timeout)
+ {
+ if (!connected) return false;
+
+ if (send("<request><wait_image><timeout>"+timeout+"</timeout></wait_image></request>"))
+ {
+ String buffer;
+ if ((buffer=readMessage())!=null)
+ {
+ if (buffer.equals("<response>ok</response>"))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /* If you are running RoboRealm on the same machine as your API program you can use
+ this routine to start RoboRealm if it is not already running.
+ filename - the path to RoboRealm on your machine
+ */
+
+ //////////////////////////////////// Basic Image Load/Save routines ////////////////////////
+ // Utility routine to save a basic PPM
+ public boolean savePPM(String filename, byte buffer[], int width, int height)
+ {
+ try
+ {
+ FileOutputStream fos = new FileOutputStream(new File(filename));
+ String header = "P6\n"+width+" "+height+"\n255\n";
+ fos.write(header.getBytes(), 0, header.length());
+ fos.write(buffer, 0, width*height*3);
+ fos.close();
+ }
+ catch (Exception e)
+ {
+ return false;
+ };
+
+ return true;
+ }
+
+ private String readLine(FileInputStream fis)
+ {
+ StringBuffer sb = new StringBuffer();
+ while (true)
+ {
+ try
+ {
+ int c = fis.read();
+ if (c=='\n')
+ {
+ if (sb.charAt(0)!='#')
+ return sb.toString();
+
+ sb.setLength(0);
+ }
+ sb.append((char)c);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ }
+
+ // Utility routine to load a basic PPM. Note that this routine does NOT handle
+ // comments and is only included as a quick example.
+ public Dimension loadPPM(String filename, byte buffer[], int max)
+ {
+ int width=0, height=0;
+
+ try
+ {
+ FileInputStream fis = new FileInputStream(new File(filename));
+
+ // read in P6 header skipping comments
+ String header = readLine(fis);
+ if (!header.equals("P6")) return null;
+
+ // read in width height header skipping comments
+ String size = readLine(fis);
+ int ind = size.indexOf(' ');
+ if (ind<0) return null;
+ width = Integer.parseInt(size.substring(0, ind));
+ height = Integer.parseInt(size.substring(ind+1));
+
+ if ((width*height*3)>max) return null;
+ fis.read(buffer, 0, width*height*3);
+ fis.close();
+ }
+ catch (Exception e)
+ {
+ return null;
+ };
+
+ return new Dimension(width, height);
+ }
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java
new file mode 100644
index 0000000..f38e736
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java
@@ -0,0 +1,240 @@
+package com.hackingroomba.roombacomm;
+
+import java.awt.*;
+import java.net.*;
+import java.util.*;
+import java.awt.image.*;
+import java.io.*;
+import java.net.URL;
+
+public class RoboRealmTest
+{
+ public static void sendImage(RoboRealmAPI rr, Image img)
+ {
+ int width = img.getWidth(null);
+ int height = img.getHeight(null);
+
+ // create your own copy of the data in case the parent goes away ...
+ int pixelInt[] = new int[width*height];
+ byte pixelByte[] = new byte[width*height*3];
+
+ // Get the image data
+ PixelGrabber grabber = new PixelGrabber(img, 0, 0, width, height, pixelInt, 0, width);
+
+ try
+ {
+ grabber.grabPixels();
+
+ int i,j;
+
+ for (j=i=0;i<width*height;)
+ {
+ int num = pixelInt[i++];
+ pixelByte[j++] = (byte)(num&255);
+ pixelByte[j++] = (byte)((num>>8)&255);
+ pixelByte[j++] = (byte)((num>>16)&255);
+ }
+
+ // send image to RR
+ rr.setImage(pixelByte, width, height);
+ }
+ catch (Exception e)
+ {
+ };
+ }
+
+
+ // This is where the program first starts
+ public static void main(String[] args)
+ {
+ byte image[] = new byte[1280*960];
+ RoboRealmAPI rr = new RoboRealmAPI();
+
+ if (!rr.connect("localhost"))
+ {
+ System.out.println("Could not connect to RoboRealm on localhost!");
+ return;
+ }
+
+ // load an image using Java awt
+
+ Dimension d = rr.getDimension();
+ if (d!=null)
+ {
+ System.out.println("Dimension "+d.width+"x"+d.height);
+ }
+
+ // VARIABLES
+
+ // set a custom variable to test
+ rr.setVariable("custom_var", "test");
+
+ // read back our custom variable ... should be equal to 'test'
+ String res = rr.getVariable("custom_var");
+ if (!res.equals("test"))
+ {
+ System.out.println("Error in custom_var");
+ return;
+ }
+
+ // delete our custom variable
+ if (!rr.deleteVariable("custom_var"))
+ {
+ System.out.println("Error in delete variable");
+ return;
+ }
+
+ // try to get it back again ... should be empty
+ res = rr.getVariable("custom_var");
+ if (res!=null)
+ {
+ System.out.println("Error in delete custom_var");
+ return;
+ }
+
+ // set multiple variables
+ String names[] = new String[2];
+ String values[] = new String[2];
+ names[0]="custom_var_1";
+ names[1]="custom_var_2";
+ values[0] = "test1";
+ values[1] = "test2";
+ rr.setVariables(names, values, 2);
+
+ // get multiple variables
+ Vector v = rr.getVariables("custom_var_1, custom_var_2");
+ if (v==null)
+ {
+ System.out.println("Error in GetVariables, did not return any results");
+ return;
+ }
+ else
+ {
+ if (!((String)v.elementAt(0)).equals("test1"))
+ {
+ System.out.println("Error in get/set multiple variables. Got "+(String)v.elementAt(0));
+ return;
+ }
+ if (!((String)v.elementAt(1)).equals("test2"))
+ {
+ System.out.println("Error in get/set multiple variables. Got "+(String)v.elementAt(1));
+ return;
+ }
+ }
+
+ // IMAGES
+
+ // ensure that the camera is on and processing images
+ rr.setCamera("on");
+ rr.run("on");
+
+ // execute a RGB filter on the loaded image
+ rr.execute("<head><version>1.50</version></head><RGB_Filter><min_value>40</min_value><channel>3</channel></RGB_Filter>");
+
+ // get the current processed image from RoboRealm and save as a PPM
+ d = rr.getImage(image, 1280*960);
+ if (d!=null)
+ {
+ rr.savePPM("c:\\temp\\test.ppm", image, d.width, d.height);
+ }
+
+ // get the current source image from RoboRealm and save as a PPM
+ d = rr.getImage("source", image, 1280*960);
+ if (d!=null)
+ {
+ rr.savePPM("c:\\temp\\test2.ppm", image, d.width, d.height);
+ }
+
+ // turn off live camera
+ rr.setCamera("off");
+
+ // load an image for experimentation
+ d = rr.loadPPM("c:\\Program Files\\RoboRealm\\remo.ppm", image, 320*240*3);
+
+ // change the current image
+ rr.setImage(image, d.width, d.height);
+
+ // load an image using Java awt
+ try
+ {
+ Image img = Toolkit.getDefaultToolkit().getImage(new URL("http://www.google.com/intl/en_ALL/images/logo.gif"));
+ sendImage(rr, img);
+ }
+ catch (Exception e)
+ {
+ };
+
+ // add a marker image called my_new_image
+ rr.setImage("my_new_image", image, d.width, d.height);
+
+ // run a .robo program
+ //rr.loadProgram("c:\\Program Files\\RoboRealm\\scripts\\red.robo");
+
+ // load an image from disk
+ rr.loadImage(null, "c:\\Program Files\\RoboRealm\\remo.gif");
+
+ // save that image back to disk .. note that we can switch extensions
+ rr.saveImage(null, "c:\\temp\\remo.jpg");
+
+ rr.setCamera("on");
+ // change the camera to another one
+ //rr.setCamera("CompUSA PC Camera");
+ try
+ {
+ Thread.sleep(2000);
+ }
+ catch (Exception e)
+ {
+ };
+ // now set it back
+ //rr.setCamera("Logitech");
+
+ // turn off processing
+ rr.run("off");
+ try
+ {
+ Thread.sleep(2000);
+ }
+ catch (Exception e)
+ {
+ };
+ // run once
+ rr.run("once");
+ try
+ {
+ Thread.sleep(2000);
+ }
+ catch (Exception e)
+ {
+ };
+ // run for 100 frames (~3.3 seconds) .. note that if your frame rate is different this
+ // may be longer than 4 seconds
+ System.out.println("running for 100 frames");
+ rr.run("100");
+ try
+ {
+ Thread.sleep(4000);
+ }
+ catch (Exception e)
+ {
+ };
+ // turn processing back on
+ rr.run("on");
+
+ // wait for the image count to exceed 1000 (assuming a 30 fps here)
+ System.out.println("waiting for image count to exceed 1000");
+ rr.waitVariable("image_count", "500", 100000);
+
+ // wait for a new image
+ System.out.println("waiting for a new image");
+ rr.waitImage(5000);
+
+ // close the RoboRealm application .. if you want too ... otherwise leave it running
+ //rr.close();
+
+ // disconnect from API Server
+ rr.disconnect();
+
+ System.out.println("Finished RoboRealmTest");
+ }
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java
new file mode 100644
index 0000000..5fd4a40
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java
@@ -0,0 +1,910 @@
+/*
+ * roombacomm.Bsquare -- test out the Bsquare command
+ *
+ * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net
+ *
+ * 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 jargs.gnu.CmdLineParser;
+import com.hackingroomba.roombacomm.RoombaCommTCPClient;
+import java.io.*;
+
+
+/**
+ Drive the Roomba forward, back, or CW or CCW square<br>
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.Bsquare /dev/cu.KeySerial1 [protocol] command velocity distance<br>
+ Usage:
+ roombacomm.Bsquare <serialportname> [protocol] <command> <velocity> <distance> [options]<br>
+ where
+ protocol (optional) is SCI or OI
+ command is one of:
+ f -- forward or back; direction controlled by +/- speed
+ bcw or bccw -- clockwise or counter-clockwise b-square
+ onb -- out and back
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ velocity and distance are in inches\n";
+ </pre>
+*/
+ public class Roborama {
+
+ private int maxLineWidth = 30;
+ private int minLineWidth = 5;
+ private int height = 120;
+ private int width = 160;
+ String usage =
+ "Usage: \n"+
+ " roombacomm.Roborama <serialportname> [protocol] <command> <velocity> <distance> [options]\n" +
+ "where <command> is one of:\n" +
+ " f: go forward or back based on velocity for the specified distance\n" +
+ " bcw: do a clockwise Borenstein square with leg size specified\n" +
+ " bccw: do a counter-clockwise Borenstein square with leg size specified\n" +
+ " onb: do out'n'back for distance specified\n" +
+ " fig8: do figure 8 with length specified (width is hard-coded to 3 feet)\n" +
+ " followline: Follow a line using camera" +
+ " followLine takes arguments like: -v 100 -p 192.168.15.150:5001 -c followLine --videoServer 192.168.15.150 --videoPortNum 5005 -x 320 -y 240 --debug" +
+ " to match roombasrvr string ./roombasrvr -v -x320 -y240 -m " +
+ "and where [options] can be one or more of:\n"+
+ " --debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ "velocity and distance are in mm/s and mm\n";
+ boolean debug = false;
+ boolean hwhandshake = false;
+ byte [] sensorBytes; // The last bytes read by roombacomm library
+ byte bumpSensor;
+ short odoDistance;
+ int leftEncoder, rightEncoder;
+ int leftMm, rightMm;
+ int beginLeft, beginRight;
+ private final static double countsPerMm = 2.57;
+ private int minVelocity = 50;
+ RoombaComm roombacomm;
+ private String portname;
+ private String cmd;
+ private int velocity;
+ private int waittime;
+ private String protocol;
+ private int distance = 0 ;
+ private int angle;
+ private int radius;
+ private int thresholdOverride = 0;
+ private String videoServer = "";
+ private int videoPortNum = 0 ;
+ private int videoRowStart = 50;
+ private int videoRowEnd = 54;
+
+ public Roborama() {
+ // constructor, mustn't throw exceptions. Do nothing for now
+ }
+
+ // main - it all starts here
+ public static void main(String[] args) {
+ Roborama b = new Roborama();
+ b.doCommand(args);
+ }
+
+ public void f()
+ {
+ /*
+ // get sensor values before starting
+ roombacomm.queryList(sensorList, 6);
+ boolean sensorStatus = roombacomm.wait4Sensors();
+ if (sensorStatus == false) {
+ System.out.println("Failed to read sensors"); // wait until sensorsValid true, when 6 bytes ready
+ System.exit(-1);
+ }
+ sensorBytes = roombacomm.getSensor_bytes();
+ odoDistance = roombacomm.toShort(sensorBytes[0], sensorBytes[1]);
+ leftEncoder = roombacomm.toShort(sensorBytes[2], sensorBytes[3]);
+ rightEncoder = roombacomm.toShort(sensorBytes[4], sensorBytes[5]);
+ System.out.println("Odometry sensors: distance: " + odoDistance + " left encoder: "
+ + leftEncoder + " rightEncoder " + rightEncoder);
+*/
+ // now drive
+ roombacomm.drive( getVelocity(), 0x8000 );
+ roombacomm.pause(getWaittime());
+ roombacomm.stop();
+ roombacomm.pause(250); // wait for robot to stop moving
+/*
+ // get sensor values after the run
+ roombacomm.queryList(sensorList, 6);
+ sensorStatus = roombacomm.wait4Sensors();
+ if (sensorStatus == false) {
+ System.out.println("Failed to read sensors"); // wait until sensorsValid true, when 6 bytes ready
+ System.exit(-1);
+ }
+ sensorBytes = roombacomm.getSensor_bytes();
+ odoDistance = roombacomm.toShort(sensorBytes[0], sensorBytes[1]);
+ leftEncoder = roombacomm.toShort(sensorBytes[2], sensorBytes[3]);
+ rightEncoder = roombacomm.toShort(sensorBytes[4], sensorBytes[5]);
+ System.out.println("Odometry sensors: distance: " + odoDistance + " left encoder: "
+ + leftEncoder + " rightEncoder " + rightEncoder);
+*/
+ }
+
+ void initOdometry()
+ {
+ beginLeft = beginRight = 0;
+ // get sensor values before starting
+ getOdometry(); // get 5 bytes of return data
+ beginLeft = leftEncoder;
+ beginRight = rightEncoder;
+ getOdometry(); // update leftMm & rightMm (should be 0)
+ printOdometry();
+ }
+ boolean getOdometry()
+ {
+ byte[] sensorList = {7, 43, 44}; // bump/wheeldrop, and encoders
+ int returnLength = 5;
+
+ roombacomm.queryList(sensorList, returnLength);
+ roombacomm.logmsg("updateSensors: pausing.");
+
+ for(int i=0; i < 20; i++) {
+ if( roombacomm.sensorsValid() ) {
+ break;
+ }
+ roombacomm.pause( 50 );
+ }
+ if (!roombacomm.sensorsValid()) {
+ System.out.println("ERROR: unable to read sensors");
+ return(false);
+ }
+ sensorBytes = roombacomm.getSensor_bytes();
+ bumpSensor = sensorBytes[0];
+ leftEncoder = RoombaComm.toUnsignedShort(sensorBytes[1], sensorBytes[2]);
+ leftMm = (int)((leftEncoder - beginLeft) / countsPerMm);
+ rightEncoder = RoombaComm.toUnsignedShort(sensorBytes[3], sensorBytes[4]);
+ rightMm = (int)((rightEncoder - beginRight) / countsPerMm);
+ return(true);
+ }
+ void printOdometry()
+ {
+ System.out.println("Odometry sensors: left encoder: "
+ + leftEncoder + " (" + leftMm + "mm), rightEncoder " + rightEncoder + " (" + rightMm + "mm) bump: " + bumpSensor);
+
+ }
+ public void fod() // forward using odometry
+ {
+ int rampDownDistance;
+ int currentVelocity = minVelocity; // starting velocity
+ long nextUpdateTime;
+ boolean rampUp, rampDown;
+ int currentRadius = radius;
+ int initialDifference;
+ int distanceTravelled;
+
+ initOdometry();
+ initialDifference = leftEncoder - rightEncoder;
+ rampDownDistance = (distance - 150); // - (int)(Math.pow(velocity, 2)/ 200);
+ System.out.println("target: " + distance + " rampdown Distance: " + rampDownDistance);
+ rampUp = true;
+ rampDown = false;
+
+ // every 100ms calculate new speed & correct course
+ roombacomm.logmsg("velocity: " + velocity + " rampUp: " + rampUp + " rampDown: " + rampDown);
+ do {
+ nextUpdateTime = System.currentTimeMillis() + 100;
+ if (rampUp == true) {
+ currentVelocity += 20;
+ if (currentVelocity >= velocity) {
+ currentVelocity = velocity;
+ rampUp = false;
+ }
+ }
+ if (rampDown == true) {
+ currentVelocity -= 20;
+ if (currentVelocity < minVelocity) {
+ currentVelocity = minVelocity;
+ }
+ }
+ getOdometry();
+ printOdometry();
+
+ // check whether we hit anything
+ if (bumpSensor != 0) {
+ System.out.println("Hit bumper - stopping");
+ roombacomm.stop();
+ roombacomm.powerOff();
+ System.exit(-1);
+ }
+
+ // Check which wheel to track for distance forward, or track average if not spinning
+ if (radius == 1) {
+ distanceTravelled = rightMm;
+ } else if (radius == -1) {
+ distanceTravelled = leftMm;
+ } else {
+ distanceTravelled = leftMm;
+ }
+
+ if (distanceTravelled > rampDownDistance) {
+ rampUp = false;
+ rampDown = true;
+ }
+
+ if (distanceTravelled > distance)
+ break;
+
+ System.out.println("encoder difference: " + (leftEncoder - rightEncoder - initialDifference));
+ //System.out.println("vel " + currentVelocity + " rampUp " + rampUp + " rampDown " + rampDown);
+ roombacomm.logmsg("driving at: " + currentVelocity + " radius: " + radius + " leftMm: " + leftMm);
+ roombacomm.drive( currentVelocity, radius );
+
+ // Sleep till next tick
+ if (System.currentTimeMillis() < nextUpdateTime) {
+ try {
+ Thread.sleep(nextUpdateTime - System.currentTimeMillis());
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ System.out.println("WARNING: missed frame by " + (System.currentTimeMillis() - nextUpdateTime) + "ms");
+ }
+ } while (distanceTravelled < distance);
+
+ roombacomm.stop();
+ roombacomm.pause(500); // wait for roomba to settle
+
+ getOdometry(); // get 5 bytes of return data
+ printOdometry();
+
+ System.out.println("Ldistance: " + (leftEncoder - beginLeft) + " Rdistance: " + (rightEncoder - beginRight));
+
+
+ }
+
+ public void bcwod()
+ {
+ int tempDistance = distance;
+ int tempRadius = radius;
+ int turnDistance = angle; // use --angle to set turnDistance
+
+ // left leg
+ distance = tempDistance;
+ radius = tempRadius;
+ fod();
+
+ radius = -1;
+ distance = turnDistance;
+ fod();
+
+ // top leg
+ distance = tempDistance;
+ radius = tempRadius;
+ fod();
+
+ radius = -1;
+ distance = turnDistance;
+ fod();
+
+ // right leg
+ distance = tempDistance;
+ radius = tempRadius;
+ fod();
+
+ radius = -1;
+ distance = turnDistance;
+ fod();
+
+ // bottom leg
+ distance = tempDistance;
+ radius = tempRadius;
+ fod();
+
+ radius = -1;
+ distance = turnDistance;
+ fod();
+
+ }
+ public void bcw()
+ {
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(-angle);
+ roombacomm.pause(1000);
+
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(-angle);
+ roombacomm.pause(1000);
+
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(-angle);
+ roombacomm.pause(1000);
+
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(-angle);
+ }
+
+ public void bccw()
+ {
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(angle);
+ roombacomm.pause(1000);
+
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(angle);
+ roombacomm.pause(1000);
+
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(angle);
+ roombacomm.pause(1000);
+
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.spin(angle);
+ }
+
+ public void onbod()
+ {
+ fod();
+ roombacomm.drive(-200, radius);
+ roombacomm.pause(500);
+ roombacomm.drive(-400, radius);
+ roombacomm.pause(500);
+ roombacomm.drive( 0-velocity, radius );
+ roombacomm.pause(waittime+5000);
+ roombacomm.stop();
+ roombacomm.pause(1500);
+
+ }
+ public void onb()
+ {
+ roombacomm.drive(200, radius);
+ roombacomm.pause(500);
+ roombacomm.drive(400, radius);
+ roombacomm.pause(500);
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(500);
+ roombacomm.drive(-200, radius);
+ roombacomm.pause(500);
+ roombacomm.drive(-400, radius);
+ roombacomm.pause(500);
+ roombacomm.drive( 0-velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ //roombacomm.spin(360);
+ }
+
+ public void fig8()
+ {
+ // calculate delay when crossing from one side of figure 8 to the other
+ int crosstime = Math.abs((1000 / velocity) * 900); // waittime in ms (correction .9) - 3' cross
+
+ // lower left leg
+ System.out.println("lower left leg");
+ fod();
+ roombacomm.spin(-angle);
+ roombacomm.pause(500);
+
+ // 1st middle crossing
+ System.out.println("1st middle crossing");
+ fod();
+ roombacomm.spin(angle);
+ roombacomm.pause(500);
+
+ // upper right leg
+ System.out.println("upper right leg");
+ fod();
+ roombacomm.spin(angle);
+ roombacomm.pause(500);
+
+ // upper crossing
+ System.out.println("upper crossing");
+ fod();
+ roombacomm.spin(angle);
+ roombacomm.pause(500);
+
+ // upper left leg
+ System.out.println("upper left leg");
+ fod();
+ roombacomm.spin(angle);
+ roombacomm.pause(500);
+
+ // 2nd middle crossing
+ System.out.println("2nd middle crossing");
+ fod();
+ roombacomm.spin(-angle);
+ roombacomm.pause(500);
+
+ // lower right leg
+ System.out.println("lower right leg");
+ fod();
+ roombacomm.spin(-angle);
+ roombacomm.pause(500);
+
+ // bottom crossing
+ System.out.println("bottom crossing");
+ fod();
+ roombacomm.spin(-angle);
+ roombacomm.pause(500);
+ }
+
+ public void followLine()
+ {
+ int frameSize;
+ int trackError, trackErrorPrev1, trackErrorPrev2;
+ double loopGain = 5;
+
+ if (getVideoServer() == null || getVideoServer().length() == 0) {
+ System.err.println(" you must supply a --videoServer value to use the command \"getVideo\"");
+ if (getVideoPortNum() <= 0) {
+ System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\"");
+ }
+ System.exit(6);
+ }
+ if (getVideoPortNum() <= 0) {
+ System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\"");
+ System.exit(7);
+ }
+ trackErrorPrev1 = 0;
+ trackErrorPrev2 = 0;
+
+ FrameProcessor fp = new FrameProcessor(getVideoServer(),getVideoPortNum(), getWidth(), getHeight(), getMinLineWidth(), getMaxLineWidth(), getThresholdOverride());
+ //fp.testQuantization();
+ fp.createAndShowGUI();
+ fp.connect(false);
+
+ while (true) {
+ frameSize = fp.readFrame(getWidth(), getHeight(), 0);
+ if (frameSize != (getWidth() * getHeight())) {
+ System.out.println("getVideo read " + frameSize + " bytes - abandoning frame");
+ continue;
+ }
+ if (fp.quantizeRows(videoRowStart, videoRowEnd) == 0)
+ fp.segmentImage();
+ fp.displayFrame();
+
+ trackError = 0 - fp.trackLine(); // change error sign to correspond to desired turn direction
+
+ // calculate drive radius - lots of magic numbers here
+ if (Math.abs(trackError)<5){
+ radius = 0x8000;
+ } else if ((trackError >= 5) && (trackError < 74)){
+ radius = (int)(-loopGain * trackError + 321.0);
+ if (radius < 1)
+ radius = 1;
+ } else if ((trackError <= -5) && trackError > -74) {
+ radius = (int)(-loopGain * trackError - 321);
+ if (radius > -1)
+ radius = -1;
+ } else if ((trackError == -100) || (trackError == 100)) {
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.drive(50, 0x8000);
+ roombacomm.pause(3240); // go forward 1/2 a roomba length
+ roombacomm.stop();
+ roombacomm.pause(200);
+ roombacomm.spin((trackError < 0) ? -angle : angle); // spin 90 degrees
+ roombacomm.stop();
+ roombacomm.pause(200);
+ roombacomm.drive(-50, 0x8000); // back up 1/2 a roomba length
+ roombacomm.pause(3240);
+ roombacomm.stop(); // camera should be on same point we lost the line
+ continue;
+ } else if (trackError == 2001) {
+ roombacomm.stop();
+ roombacomm.pause(1000);
+ roombacomm.drive(-50, 0x8000);
+ roombacomm.pause(500); // go forward 1/2 a roomba length
+ roombacomm.stop();
+ roombacomm.pause(200);
+ continue;
+ } else {
+ System.out.println("HELP: TRACKERROR OUT OF BOUNDS: " + trackError);
+ roombacomm.drive(0, 8000); // stop
+ continue;
+ }
+ roombacomm.logmsg(" Driving radius: " + radius);
+ roombacomm.drive(velocity, radius);
+
+ }
+ //frameSize = fp.readFrame();
+ //System.out.println("getVideo read " + frameSize + " bytes");
+ //fp.disconnect();
+ }
+
+ public void normalize()
+ {
+ int frameSize;
+ FileOutputStream fos;
+ DataOutputStream dos;
+
+ if (getVideoServer() == null || getVideoServer().length() == 0) {
+ System.err.println(" you must supply a --videoServer value to use the command \"getVideo\"");
+ if (getVideoPortNum() <= 0) {
+ System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\"");
+ }
+ System.exit(6);
+ }
+ if (getVideoPortNum() <= 0) {
+ System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\"");
+ System.exit(7);
+ }
+
+ FrameProcessor fp = new FrameProcessor(getVideoServer(),getVideoPortNum(), getWidth(), getHeight(), getMinLineWidth(), getMaxLineWidth(), getThresholdOverride());
+ fp.connect(false);
+
+ frameSize = fp.readFrame(getWidth(), getHeight(), 0);
+ System.out.println("getVideo read " + frameSize + " bytes");
+ double [] normalizeArray = fp.normalizeRows(videoRowStart, videoRowEnd);
+
+ try {
+ File file= new File("normalizeArray");
+ fos = new FileOutputStream(file);
+ dos=new DataOutputStream(fos);
+ for (int i=0; i<normalizeArray.length; i++) {
+ System.out.print(normalizeArray[i] + " ");
+ if ((i % getWidth()) == 0) {
+ System.out.println();
+ }
+ dos.writeDouble(normalizeArray[i]);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ void doCommand(String[] args)
+ {
+ // parse the arguments
+ if( args.length < 4 ) {
+ System.out.println("args length was ("+args.length+") and it need to be >=4\n");
+ System.out.println( usage );
+ System.exit(0);
+ }
+ parseCmd(args);
+
+ // open a connection to Roomba (net or serial)
+ char portNameChar1 = getPortname().charAt(0);
+ if (portNameChar1 >= '0' && portNameChar1 <='9') { // portname begins with number, assume it's an IP
+ System.out.println("Using network IP " + getPortname());
+ RoombaCommTCPClient roombacommTCPClient = new RoombaCommTCPClient();
+// roombacommTCPClient.setProtocol(args[1]);
+ roombacommTCPClient.setProtocol(getProtocol());
+ if( ! roombacommTCPClient.connect( getPortname() ) ) {
+ System.out.println("Couldn't connect to "+getPortname());
+ System.exit(1);
+ }
+ roombacomm = roombacommTCPClient;
+ } else {
+ System.out.println("using serial port " + getPortname());
+ RoombaCommSerial roombacommSerial = new RoombaCommSerial();
+ roombacommSerial.setProtocol(getProtocol());
+ roombacommSerial.setWaitForDSR(isHwhandshake());
+ if( ! roombacommSerial.connect( getPortname() ) ) {
+ System.out.println("Couldn't connect to "+getPortname());
+ System.exit(1);
+ }
+ roombacomm = roombacommSerial;
+ }
+
+ // set up ^C handling
+ MyShutdown sh = new MyShutdown(roombacomm);
+ Runtime.getRuntime().addShutdownHook(sh);
+ roombacomm.debug = isDebug();
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+//over ride
+ // roombacomm.full();
+ roombacomm.pause(100);
+
+ // run the requested command
+ System.out.println("running command " + getCmd() + "\n");
+ if (cmd.equals("f") ) {
+ f();
+ } else if (cmd.equals("bccw")) {
+ bccw();
+ } else if (cmd.equals("bcw")){
+ bcw();
+ } else if (cmd.equals("onb")) {
+ onb();
+ } else if (cmd.equals("onbod")) {
+ onbod();
+ } else if (cmd.equals("spin")) {
+ for (int i=0; i<4; i++) {
+ roombacomm.spin(-angle);
+ roombacomm.pause(1000);
+ }
+ } else if (cmd.equals("fig8")) {
+ fig8();
+ } else if (cmd.equals("followLine")) {
+ followLine();
+ } else if (cmd.equals("fod")){ // forward using odometry
+ fod();
+ } else if (cmd.equals("bcwod")) {
+ bcwod();
+ } else {
+ System.out.println("Invalid Command");
+ }
+ roombacomm.stop();
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+ public void parseCmd(String[] args){
+ System.out.println("*** start of parseCmd");
+
+ CmdLineParser parser = new CmdLineParser();
+ CmdLineParser.Option debugOption = parser.addBooleanOption('X', "debug");
+// CmdLineParser.Option verboseOption = parser.addBooleanOption('W', "Verbose");
+ CmdLineParser.Option portNameOption = parser.addStringOption('p', "portname");
+ CmdLineParser.Option protocalOption = parser.addStringOption('a', "api");
+ CmdLineParser.Option angleOption = parser.addIntegerOption("angle");
+ CmdLineParser.Option velocityOption = parser.addIntegerOption('v', "velocity");
+ CmdLineParser.Option distanceOption = parser.addIntegerOption('d', "distance");
+ CmdLineParser.Option commandOption = parser.addStringOption('c', "command");
+ CmdLineParser.Option radiusOption = parser.addIntegerOption('r', "radius");
+ CmdLineParser.Option widthOption = parser.addIntegerOption('x', "width");
+ CmdLineParser.Option heightOption = parser.addIntegerOption('y', "height");
+ CmdLineParser.Option minlineOption = parser.addIntegerOption("min");
+ CmdLineParser.Option maxlineOption = parser.addIntegerOption("max");
+ CmdLineParser.Option videoServerOption = parser.addStringOption("videoServer");
+ CmdLineParser.Option videoPortNumOption = parser.addIntegerOption("videoPortNum");
+ CmdLineParser.Option thresholdOption = parser.addIntegerOption('t', "threshold");
+ CmdLineParser.Option hwHandShakeOption = parser.addBooleanOption("nohwhandshake");
+ try {
+ parser.parse(args);
+ }
+ catch ( CmdLineParser.OptionException e ) {
+ System.err.println(e.getMessage());
+ System.out.println("parseCmd had an error\n"+ usage );
+ System.exit(2);
+ }
+
+ // String portname = args[0]; // e.g. "/dev/cu.KeySerial1", or "COM5" or "192.168.1.1"
+ setPortname((String)parser.getOptionValue(portNameOption));
+ System.out.println("portname is ("+getPortname()+")");
+
+ setProtocol((String)parser.getOptionValue(protocalOption,"SCI"));
+ System.out.println("protocal is ("+getProtocol()+")");
+ setAngle((Integer)parser.getOptionValue(angleOption,new Integer(83)));
+ System.out.println("angle is ("+getAngle()+")");
+ setRadius((Integer)parser.getOptionValue(radiusOption,new Integer(0x8000)));
+ System.out.println("radius is ("+getRadius()+")");
+ if (args[1].equals("SCI") || (args[1].equals("OI"))) {
+ } else {
+ }
+ setThresholdOverride(((Integer)parser.getOptionValue(thresholdOption, getThresholdOverride())).intValue());
+ setWidth(((Integer)parser.getOptionValue(widthOption,getWidth())).intValue());
+ setHeight(((Integer)parser.getOptionValue(heightOption,getHeight())).intValue());
+ setMinLineWidth(((Integer)parser.getOptionValue(minlineOption,getMinLineWidth())).intValue());
+ setMaxLineWidth(((Integer)parser.getOptionValue(maxlineOption,getMaxLineWidth())).intValue());
+ setVideoServer(((String)parser.getOptionValue(videoServerOption)));
+ setVideoPortNum(((Integer)parser.getOptionValue(videoPortNumOption, getVideoPortNum())).intValue());
+ // String cmd = args[1+argOffset];
+ setCmd((String)parser.getOptionValue(commandOption,"fig8"));
+ if (getCmd().equalsIgnoreCase("followLine")){
+ System.out.println("videoServer is ("+getVideoServer()+")");
+ System.out.println("videoPortNum is ("+getVideoPortNum()+")");
+ }
+ Integer velocityInt = (Integer)parser.getOptionValue(velocityOption,new Integer(0));
+ Integer distanceInt = (Integer)parser.getOptionValue(distanceOption,new Integer(0));
+ try {
+// velocity = (int)(Integer.parseInt( args[2+argOffset]));
+// distance = (int)(Integer.parseInt( args[3+argOffset] ));
+ setVelocity(velocityInt.intValue());
+ System.out.println("velocity is ("+getVelocity()+")");
+ setDistance(distanceInt.intValue());
+ System.out.println("distance is ("+getDistance()+")");
+ setWaittime(Math.abs((getDistance()/getVelocity()) * 900)); // waittime in ms (correction .9)
+ System.out.println("waittime is ("+getWaittime()+")");
+ if (getWaittime() == 0) {
+ System.out.println("Invalid waittime "+getWaittime());
+ }
+ if (getMinLineWidth() == 0) {
+ System.out.println("Invalid MinLineWidth "+getMinLineWidth());
+ } else {
+ System.out.println("MinLineWidth is ("+getMinLineWidth()+")");
+ }
+ if (getMaxLineWidth() == 0) {
+ System.out.println("Invalid MaxLineWidth "+getMaxLineWidth());
+ } else {
+ System.out.println("MaxLineWidth is ("+getMaxLineWidth()+")");
+ }
+ System.out.println("velocity: " + getVelocity() + " distance: " + getDistance() + " waittime: " + getWaittime());
+ System.out.println("width: " + getWidth() + "height: " + getHeight() + "\n");
+ } catch( Exception e ) {
+ System.err.println(e.getMessage());
+ System.err.println("Couldn't parse velocity or distance2");
+ System.exit(1);
+ }
+// for( int i=4+argOffset; i < args.length; i++ ) {
+// if( args[i].endsWith("debug") )
+// debug = true;
+// }
+ Boolean debugBool = (Boolean)parser.getOptionValue(debugOption,new Boolean(false));
+ setDebug(debugBool.booleanValue());
+ System.out.println("debug is ("+isDebug()+")");
+ Boolean hwHandShakeBool = (Boolean)parser.getOptionValue(hwHandShakeOption, new Boolean(false));
+ setHwhandshake(hwHandShakeBool.booleanValue());
+ setThresholdOverride((Integer)parser.getOptionValue(thresholdOption, new Integer(0)));
+ System.out.println("thresholdOverride is " + getThresholdOverride());
+ System.out.println("hwHandShake is ("+isHwhandshake()+")");
+ System.out.println("*** end of parseCmd");
+ }
+ public int getRadius() {
+ return radius;
+ }
+ public void setRadius(int rad) {
+ radius = rad;
+ }
+ public int getDistance() {
+ return distance;
+ }
+ public void setDistance(int dist) {
+ distance = dist;
+ }
+ public boolean isHwhandshake() {
+ return hwhandshake;
+ }
+ public void setHwhandshake(boolean hwhandshake_) {
+ hwhandshake = hwhandshake_;
+ }
+ public int getAngle() {
+ return angle;
+ }
+ public void setAngle(int ang) {
+ angle = ang;
+ }
+ public boolean isDebug() {
+ return debug;
+ }
+ public void setDebug(boolean debug_) {
+ debug = debug_;
+ }
+ public String getPortname() {
+ return portname;
+ }
+ public void setPortname(String portname_) {
+ portname = portname_;
+ }
+ public String getCmd() {
+ return cmd;
+ }
+ public void setCmd(String cmd_) {
+ cmd = cmd_;
+ }
+ public int getVelocity() {
+ return velocity;
+ }
+ public void setVelocity(int velocity_) {
+ velocity = velocity_;
+ }
+ public int getWaittime() {
+ return waittime;
+ }
+ public void setWaittime(int waittime_) {
+ waittime = waittime_;
+ }
+ public String getProtocol() {
+ return protocol;
+ }
+ public void setProtocol(String protocol_) {
+ protocol = protocol_;
+ }
+ public class MyShutdown extends Thread {
+ RoombaComm roomba = null;
+ public MyShutdown(RoombaComm roomba){
+ this.roomba = roomba;
+ }
+ public void run() {
+ System.out.println("MyShutdown hook called");
+ if (roomba != null && roomba.isConnected()){
+ System.out.println("roomba not null trying to stop and disconenct");
+ roomba.stop();
+ System.out.println("roomba stop issued");
+ roomba.disconnect();
+ System.out.println("roomba disconenct issued");
+ }else{
+ System.out.println("roomba was null or not connected");
+ }
+ }
+ }
+ public int getThresholdOverride() {
+ return thresholdOverride;
+ }
+ public void setThresholdOverride(int thresholdOverride) {
+ this.thresholdOverride = thresholdOverride;
+ }
+ protected int getMaxLineWidth() {
+ return maxLineWidth;
+ }
+ protected void setMaxLineWidth(int maxLineWidth) {
+ this.maxLineWidth = maxLineWidth;
+ }
+ protected int getMinLineWidth() {
+ return minLineWidth;
+ }
+ protected void setMinLineWidth(int minLineWidth) {
+ this.minLineWidth = minLineWidth;
+ }
+ protected int getHeight() {
+ return height;
+ }
+ protected void setHeight(int height) {
+ this.height = height;
+ }
+ protected int getWidth() {
+ return width;
+ }
+ protected void setWidth(int width) {
+ this.width = width;
+ }
+ /**
+ * @return the videoServer
+ */
+ protected String getVideoServer() {
+ return videoServer;
+ }
+ /**
+ * @param videoServer the videoServer to set
+ */
+ protected void setVideoServer(String videoServer) {
+ this.videoServer = videoServer;
+ }
+ /**
+ * @return the videoPortNum
+ */
+ protected int getVideoPortNum() {
+ return videoPortNum;
+ }
+ /**
+ * @param videoPortNum the videoPortNum to set
+ */
+ protected void setVideoPortNum(int videoPortNum) {
+ this.videoPortNum = videoPortNum;
+ }
+
+ }
+
+ \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java
new file mode 100644
index 0000000..504d19a
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java
@@ -0,0 +1,364 @@
+/*
+ * roombacomm.ArduinoClient -- test out the Arduino subsystem without robot motion
+ *
+ * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net
+ *
+ * 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 java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public class RobotConnection implements Runnable {
+ private int waittime;
+ private String robotServer;
+ private int robotPortNum = 0;
+ private Socket socket;
+ public InputStream in;
+ public OutputStream out;
+ public enum ConnectionType {SERIAL, NET};
+ private ConnectionType connectionType;
+ private int maxStringSize = 200;
+ byte[] readBytes = new byte[maxStringSize];
+ byte[] readBytesBuf = new byte[maxStringSize];
+
+ // variables for the read thread
+ Thread thread;
+ private int readRequestLength = 0;
+ private int numBytesRead;
+ private boolean readComplete;
+ public enum ReadTerminator {LF, COUNT, NULL};
+ private ReadTerminator readTerminator;
+ private int readTimeout = 1000; // 1000ms read timeout
+
+ public RobotConnection(String robotServer, int preferredPortNum) {
+ connectionType = ConnectionType.NET;
+ robotPortNum = preferredPortNum;
+ this.robotServer = robotServer;
+ }
+ /**
+ * Initialize the connection with a server name and optional ':' delimited port number
+ * @param robotServer port name (if serial) or server IP. Server IP may have a trailing ":port"
+ */
+ public RobotConnection(String robotServer) {
+ this.robotServer = robotServer;
+ }
+
+ private void parseServerString(String robotServer)
+ {
+ // open a connection to robot (net or serial)
+ char portNameChar1 = robotServer.charAt(0);
+ if (portNameChar1 >= '0' && portNameChar1 <='9') { // portname begins with number, assume it's an IP
+ connectionType = ConnectionType.NET;
+ String s[] = robotServer.split(":");
+ if (s.length == 2){ // if length = 1, leave robotServer alone - portNum was specified another way
+ this.robotServer = s[0];
+ try {
+ robotPortNum = Integer.parseInt(s[1]);
+ } catch( Exception e ) {
+ robotPortNum = 0;
+ }
+ System.err.println("Using network server '" + this.robotServer + ":" + robotPortNum + "'" );
+ }
+
+ } else {
+ connectionType = ConnectionType.SERIAL;
+ this.robotServer = robotServer; // serial port name
+ System.out.println("Using serial port " + robotServer);
+ System.err.println("Error: Serial port not supported");
+ }
+ }
+
+ /**
+ * Create a network or serial connection and associated input & output streams. After creating
+ * the connection, the caller should get the input & output streams to use for reading & writing
+ * the connection. Alternatively, the caller can use the in and out objects.
+ */
+ public boolean connect()
+ {
+ parseServerString(robotServer); // the server string can override the preferred port number & connectionType
+ if (connectionType == ConnectionType.NET) {
+ try {
+ if ((robotServer == null) || (robotPortNum == 0)) {
+ System.out.println("Error: server or port not set");
+ return false;
+ }
+ // open a socket to the robot
+ socket = new Socket(robotServer, robotPortNum);
+ socket.setKeepAlive(true);
+ socket.setTcpNoDelay(true);
+ socket.setSoTimeout(0);
+ //socket.setSoTimeout(30000);// timeout in milliseconds - 30 sec
+ in = socket.getInputStream();
+ out = socket.getOutputStream();
+
+ // hang a read thread on the socket
+ readComplete = true; // initialize the "go" variables
+ readRequestLength = 0;
+ thread = new Thread(this);
+ thread.setPriority(Thread.MAX_PRIORITY);
+ thread.start();
+ } catch( Exception e ) {
+ System.out.println("connect: "+e); //e.printStackTrace();
+ return false;
+ }
+ } else if (connectionType == ConnectionType.SERIAL) {
+ System.out.println("ERROR: SERIAL NOT IMPLEMENTED YET");
+ return false;
+ }
+ return true;
+ }
+
+ public void disconnect() {
+ try {
+ // do io streams need to be closed first?
+ if (in != null) in.close();
+ if (out != null) out.close();
+ } catch (Exception e) {
+ System.out.print("exception in disconnect");
+ e.printStackTrace();
+ }
+ in = null;
+ out = null;
+
+ try {
+ if (socket != null) socket.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ socket = null;
+ }
+ public boolean send(int b) { // will also cover char
+ try {
+ //System.out.println("Send_( "+b+" & 0xff)");
+ out.write(b & 0xff); // for good measure do the &
+ out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+ public boolean send(byte[] bytes) {
+ try {
+ //logmsg("Send_byte( "+bytes+")");
+ out.write(bytes);
+ out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+ public boolean sendToArduino(int b) { // will also cover char
+ byte [] arduinoHeader = {'m', ' '}; // roomba commands to arduino are prefixed with m<space> & end with LF
+ byte arduinoTrailer = '\r';
+
+ try {
+ //System.out.println("Send_( "+b+" & 0xff)");
+ out.write(arduinoHeader);
+ out.write(b & 0xff); // for good measure do the &
+ out.write(arduinoTrailer);
+ out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+ public boolean sendToArduino(byte[] bytes) {
+ byte [] arduinoHeader = {'m', ' '}; // roomba commands to arduino are prefixed with m<space> & end with LF
+ byte arduinoTrailer = '\r';
+
+ try {
+ //logmsg("Send_byte( "+bytes+")");
+ out.write(arduinoHeader);
+ out.write(bytes);
+ out.write(arduinoTrailer);
+ out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ public byte [] readBot(int count) throws Exception
+ {
+ byte [] returnData = new byte[count];
+ readComplete = false;
+ readTerminator = ReadTerminator.COUNT;
+ readRequestLength = count;
+
+ // wait max 1000ms for read to complete - most anything should be done by then
+ //System.out.println("Trying to read " + readRequestLength + " bytes");
+ for (int i=0; i<200; i++) {
+ Thread.sleep(5); // wait 5ms for answer to come back
+ if (readComplete == true)
+ break;
+ }
+ if (readComplete) {
+ System.arraycopy(readBytes, 0, returnData, 0, numBytesRead);
+ return (returnData);
+ } else {
+ return (null);
+ }
+ }
+
+ public void flushInput()
+ {
+ while(true) {
+ String junk;
+ System.out.print("Flushing... ");
+ try {
+ junk = readBotToTerminator(ReadTerminator.LF);
+ if (junk != null)
+ System.out.println("Flushed: " + junk);
+ else
+ return;
+ } catch (Exception e) {
+ System.err.println("Exception while flushing" + e.getMessage());
+ return;
+ }
+ }
+ }
+
+ /**
+ * Read bytes from ArduinoBot up to \n, \r, or other terminator
+ * @return String read from robot
+ */
+ public String readBotToTerminator(ReadTerminator terminator) throws Exception
+ {
+ readComplete = false;
+ readTerminator = terminator;
+ readRequestLength = maxStringSize;
+ numBytesRead = 0;
+
+ // wait readTimout ms for read to complete - most anything should be done by then
+ Thread.sleep(10);
+ for (int i=0; i<readTimeout/5; i++) {
+ Thread.sleep(5); // wait 5ms for answer to come back
+ if (readComplete == true) {
+ //System.out.println("readComplete with: " + numBytesRead);
+ break;
+ }
+ }
+ if (readComplete == false) {
+ System.out.println("Error in readBotToTerminator: timeout");
+ return null;
+ }
+ // skip over leading white space
+ int readBytesIx = 0;
+ while (readBytes[readBytesIx] < ' ')
+ readBytesIx++;
+ String readString = new String(readBytes, readBytesIx, numBytesRead);
+ //System.out.println("readBotToTerminator read: " + numBytesRead + " string length: " + readString.length() + " string: "+ readString);
+ return (readString);
+
+ }
+
+ /**
+ * Runs in a separate thread waiting for input. Sets input complete when the desired termination
+ * read terminator is found (\n or count to read
+ */
+ public void run() {
+ readByBlock();
+ }
+
+
+ public void readByBlock()
+ {
+ int readLength = 0;
+ int bytesLeftToRead;
+ byte buffer[] = new byte[maxStringSize];
+ int bufferIndex = 0;
+
+
+ //System.out.println("ReadByBlock thread started");
+ while ((Thread.currentThread() == thread) && (in != null)) {
+ try {
+ if (readRequestLength == 0) {
+ bufferIndex = 0;
+ readLength = in.available();
+ if (readLength > 0) {
+ readLength = in.read(buffer); // read & discard data following the terminator (handles extra \n)
+ //System.out.println("readByBlock discarded " + readLength + " bytes");
+ }
+ Thread.sleep(5);
+ continue;
+ }
+ bytesLeftToRead = readRequestLength;
+ readBytes[0] = 0; // initialize data to empty string
+ //System.out.println("readByBlock starting to read");
+ // ACHTUNG - buffer gets overwritten at the beginning by multiple read buffers
+ for (int i=0; i<5; i++) {
+ readLength = in.read(buffer, 0, (readRequestLength - bufferIndex)); // read as much as there is, even if its more than was requested
+ if ((readLength == -1) || (in == null)) {
+ System.out.println("Error in run: in.read() returned -1 or null; application exiting in 30s: " + readLength);
+ Thread.sleep(30000);
+ System.exit(-1);;
+ }
+ bytesLeftToRead -= readLength;
+ for (int j=0; j<readLength; j++, bufferIndex++) {
+ readBytes[bufferIndex] = buffer[j];
+ }
+ //System.out.print("readByBlock read " + readLength + " bytes from byte[0]: "); System.out.printf("0x%x", buffer[0]); System.out.print(" to byte[" + (readLength - 1) + "]: "); System.out.printf("0x%x\n", buffer[readLength-1]);
+
+ // check to see if it's the end of input
+ if ((bufferIndex > 0) && (((readTerminator == ReadTerminator.COUNT) && (bufferIndex >= readRequestLength)) ||
+ ((readTerminator == ReadTerminator.LF) && (readBytes[bufferIndex-1] == '\n') && (bufferIndex > 1)) || // guard against a single \n from previous read causing premature termination
+ ((readTerminator == ReadTerminator.NULL) && (readBytes[bufferIndex-1] == '\0')) ||
+ (bufferIndex == maxStringSize)))
+ {
+ numBytesRead = bufferIndex;
+ readRequestLength = 0; // flag this request as complete (so ditch all following data until next request
+ readComplete = true;
+ bufferIndex = 0;
+ //System.out.println("readByBlock completed read with bufferIndex: " + numBytesRead + " readLength: " + readLength);
+ break;
+ } else {
+ Thread.sleep(10);
+ if (bufferIndex > 0) {
+ //System.out.println("readByBlock has " + bufferIndex + ", trying again");
+ } else {
+ System.out.print(".");
+ }
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("Exception: readByBlock thread exiting" + e.getMessage());
+ //e.printStackTrace();
+ return; // causes thread to exit
+ }
+ //System.out.println("readByBlock read " + readLength + " bytes");
+ }
+ }
+
+ public InputStream getIn() {
+ return in;
+ }
+ public OutputStream getOut() {
+ return out;
+ }
+ public void setReadTimeout(int readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+} \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java
new file mode 100644
index 0000000..17d5e0f
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java
@@ -0,0 +1,75 @@
+package com.hackingroomba.roombacomm;
+
+public class RobotType {
+ // encoder constants
+ private final double ROOMBA_COUNTS_PER_INCH = 57;
+ private final double MOBOT_COUNTS_PER_INCH = 250/80; // 205 counts in 80 inches
+ private final double TANKBOT_COUNTS_PER_INCH = 1800;
+ public double countsPerInch = ROOMBA_COUNTS_PER_INCH;
+
+ // spin tolerance constants
+ private final int ROOMBA_TOLERANCE = 2;
+ private final int MOBOT_TOLERANCE = 7;
+ private final int DEFAULT_TOLERANCE = 10;
+ public int tolerance = DEFAULT_TOLERANCE;
+ private final int FAST_ROOMBA_SPINSPEED = 100;
+ private final int SLOW_ROOMBA_SPINSPEED = 20;
+ private final int FAST_MOBOT_SPINSPEED = 120;
+ private final int SLOW_MOBOT_SPINSPEED = 80;
+ public int fastSpinSpeed;
+ public int slowSpinSpeed;
+
+ // PID constants
+ double ROOMBA_KP = 2.0;
+ double ROOMBA_KI = 1.0;
+ double ROOMBA_KD = 0.0;
+ double MOBOT_KP = 0.1;
+ double MOBOT_KI = 0.05;
+ double MOBOT_KD = 0.1;
+ double KP;
+ double KI;
+ double KD;
+
+
+ // robot types
+ public enum robotTypes {roomba, frankenRoomba, tankbot, mobot};
+ robotTypes robotType;
+
+ RobotType (robotTypes rt)
+ {
+ robotType = rt;
+ if ((rt == robotTypes.roomba) || (rt == robotTypes.frankenRoomba)) {
+ countsPerInch = ROOMBA_COUNTS_PER_INCH;
+ tolerance = ROOMBA_TOLERANCE; // tolerance of angle for spinToHeading
+ KP = ROOMBA_KP; // roomba PID constants
+ KI = ROOMBA_KI;
+ KD = ROOMBA_KD;
+ fastSpinSpeed = FAST_ROOMBA_SPINSPEED;
+ slowSpinSpeed = SLOW_ROOMBA_SPINSPEED;
+ } else if (rt == robotTypes.tankbot) {
+ countsPerInch = TANKBOT_COUNTS_PER_INCH;
+ tolerance = DEFAULT_TOLERANCE; // tolerance of angle for spinToHeading
+ KP = ROOMBA_KP; // tankbot PID constants
+ KI = ROOMBA_KI;
+ KD = ROOMBA_KD;
+ fastSpinSpeed = FAST_ROOMBA_SPINSPEED;
+ slowSpinSpeed = SLOW_ROOMBA_SPINSPEED;
+ } else if (rt == robotTypes.mobot) {
+ countsPerInch = MOBOT_COUNTS_PER_INCH;
+ tolerance = MOBOT_TOLERANCE; // tolerance of angle for spinToHeading
+ KP = MOBOT_KP; // Mo'bot PID constants
+ KI = MOBOT_KI;
+ KD = MOBOT_KD;
+ fastSpinSpeed = FAST_MOBOT_SPINSPEED;
+ slowSpinSpeed = SLOW_MOBOT_SPINSPEED;
+ }
+ }
+ public robotTypes getRobotType() {
+ return robotType;
+ }
+
+ public void setRobotType(robotTypes robotType) {
+ this.robotType = robotType;
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java
new file mode 100644
index 0000000..733ae53
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java
@@ -0,0 +1,1690 @@
+/*
+ * RoombaComm 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.SerialPort;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Map;
+
+/**
+ * The abstract base for all Roomba communications.
+ *
+ * <h2> Overview </h2>
+ * This class contains the communications layer-independent parts of
+ * how to communicate with a Roomba. It does assume a very serial port-like
+ * interaction.
+ *
+ * Standard lifecyle of this object (and its subclasses) <pre>
+ * RoombaComm roomba = new RoombaCommSubClass(); // (e.g. RoombaCommSerial)
+ * roomba.listports(); // if implemented
+ * roomba.connect("someportid");
+ * roomba.startup();
+ * roomba.updateSensors();
+ * while( ... ) {
+ * roomba.sensors();
+ * roomba.playNote( 53, 12 );
+ * roomba.goForward( 400 );
+ * roomba.spinRight( 45 );
+ * if( roomba.bump() ) roomba.goBackward( 100 );
+ * }
+ * roomba.disconnect();
+ * </pre>
+ *
+ * <h2> API levels </h2>
+ * Describe different API levels
+ *
+ * <h2> Sensor Functions </h2>
+ * Describe sensor functions
+ *
+ * <h2> Sublass behavior </h2>
+ * Describe subclassing strategries
+ *
+ *
+ * @author Tod E. Kurt
+ * SVN id value is $Id: RoombaComm.java 182 2010-11-02 03:49:10Z bouchier $
+ */
+public abstract class RoombaComm
+{
+ /** version of the library */
+ static public final String VERSION = "0.96.3";
+
+ /**
+ * contains a list of all the ports
+ * keys are port names (e.g. "/dev/usbserial1")
+ * values are Boolean in-use indicator
+ */
+ protected static Map ports = null;
+
+ /** turns on/off various debugging messages */
+ public boolean debug = false;
+
+ public boolean isDebug() {
+ return debug;
+ }
+
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /** distance between wheels on the roomba, in millimeters */
+ public static final int wheelbase = 258;
+ /** mm/deg is circumference distance divided by 360 degrees */
+ public static final float
+ millimetersPerDegree = (float)(wheelbase * Math.PI / 360.0);
+ /** mm/rad is a circumference distance divied by two pi */
+ public static final float
+ millimetersPerRadian = (float)(wheelbase/2);
+
+ /** default speed for movement operations if speed isn't specified */
+ public static final int defaultSpeed = 200;
+
+ /** default update time in ms for auto sensors update */
+ public static final int defaultSensorsUpdateTime = 200;
+
+ /** current mode, if known */
+ int mode;
+
+ /** current speed for movement operations that don't take a speed */
+ public int speed = defaultSpeed;
+
+ /** computed boolean for when Roomba is errored out of safe mode */
+ boolean safetyFault = false;
+ /** if sensor variables have been updated successfully */
+ protected boolean sensorsValid = false;
+ /** Set to true to make sensors auto-update (at expense of serial b/w) */
+ boolean sensorsAutoUpdate = false;
+ /** Time in milliseconds between sensor updates */
+ int sensorsUpdateTime = 200;
+ /** last time (System.currentTimeMillis) that the sensors were updated */
+ protected long sensorsLastUpdateTime;
+ /** how many bytes we expect to read from the sensor command */
+ protected int readRequestLength;
+
+ /** internal storage for all roomba sensor data */
+ protected byte[] sensor_bytes = new byte[1024];
+
+ /** connected to a serial port or not, not necessarily to roomba */
+ boolean connected = false;
+
+ public boolean isConnected() {
+ String str = this.getSensorsAsString();
+ if (str != null && str.length() >=1){
+ if (debug){
+ logmsg("isConnected found sensorString as ("+str+")");
+ }
+ this.setConnected(true);
+ return true;
+ }else{
+ if (debug){
+ if (str != null){
+ logmsg("isConnected found sensorString ("+str+")");
+ }else{
+ logmsg("isConnected found sensorString (null)");
+ }
+ }
+ }
+ return connected;
+ }
+
+ public void setConnected(boolean connected) {
+ this.connected = connected;
+ }
+
+ /** set of flgs for the current state of the LEDs */
+ /** note this is a superset of all protocol's supported */
+ private boolean redOn = false;
+ private boolean greenOn = false;
+ private boolean toggleSpot = false;
+ private boolean toggleClean = false;
+ private boolean toggleMax = false;
+ private boolean toggleDirt = false;
+ private boolean toggleDock = false;
+ private boolean toggleCheckRobot = false;
+ private int power_color = 0;
+ private int power_int = 0;
+
+ /** default RoombaComm protocol to identify classes of API calls to be made */
+ private String protocol = "SCI";
+ /** default baud rate for the default protocol */
+ protected int rate = 57600;
+
+ protected String portname = null;
+ /** connection object to use when appropriate */
+ RobotConnection robotConnection;
+
+
+ /**
+ * Some "virtual" serial ports like Bluetooth serial on Windows
+ * return weird errors deep inside RXTX if an opened port is used
+ * before the virtual COM port is ready. One way to check that it
+ * is ready is to look for the DSR line going high.
+ * However, most simple, real serial ports do not do hardware handshaking
+ * so never set DSR high.
+ * Thus, if using Bluetooth serial on Windows, do:
+ * roombacomm.waitForDSR = true;
+ * before using it and see if it works.
+ */
+ public boolean waitForDSR = false;
+
+ /** The RXTX port object, normally you don't need access to this */
+ public SerialPort serialPort = null;
+
+ public RoombaComm() {
+ connected = false;
+ mode = MODE_UNKNOWN;
+ }
+
+ public RoombaComm(boolean autoUpdate) {
+ this();
+ if( autoUpdate )
+ startAutoUpdate();
+ }
+
+ public RoombaComm(boolean autoUpdate, int updateTime) {
+ this(autoUpdate);
+ sensorsUpdateTime = updateTime;
+ }
+
+ public RoombaComm(RobotConnection rc) {
+ robotConnection = rc;
+ }
+ public void startAutoUpdate() {
+ new Thread( new Runnable() {
+ public void run() {
+ try {
+ while( sensorsUpdateTime > 0 ) {
+ if( connected() ) sensors();
+ Thread.sleep( sensorsUpdateTime );
+ }
+ } catch(InterruptedException ex) {}
+ }
+ }).start();
+ }
+
+ /**
+ * List available ports
+ * @return a list available portids, if applicable
+ * or empty set if no ports,
+ * or return null if list is not enumerable
+ */
+ public abstract String[] listPorts();
+
+ /**
+ * Connect to a port
+ * (for serial, portid is serial port name, for net, portid is url?)
+ * @return true on successful connect, false otherwise
+ */
+ public abstract boolean connect(String portid);
+ /**
+ * Disconnect from a port, clean up any memory in use
+ */
+ public abstract void disconnect();
+
+
+ /**
+ * Send given byte array to Roomba.
+ * @param bytes byte array of ROI commands to send
+ * @return true on successful send
+ */
+ public abstract boolean send(byte[] bytes);
+
+ /**
+ * Send a single byte to the Roomba
+ * (defined as int because of stupid java signed bytes)
+ * @param b byte of an ROI command to send
+ * @return true on successful send
+ */
+ public abstract boolean send(int b);
+
+ /**
+ * Query Roomba for sensor status and sync its state with this object's
+ * Subclasses should query Roomba and fill up 'sensor_bytes' with the full
+ * sensor data set
+ * If a RooombaComm object is constructed with 'autoUpdate' true,
+ * calling this method is not required because a separate thread is created
+ * to do sensor updating.
+ *
+ * @return true on successful sensor update, false otherwise
+ */
+ //public abstract boolean updateSensors();
+
+ /**
+ * Wake's Roomba up, if possible, thus optional
+ * To wake up the Roomba requires twiddling its DD line, often
+ * hooked up to the RS-232 DTR line, which may not be available in some
+ * implementations
+ */
+ public void wakeup() {
+ logmsg("subclass has not implemented");
+// byte cmd[] = { (byte)POWER, (byte)v, (byte)power_color, (byte)power_intensity };
+// send(cmd);
+// MSComm1.Output = "+++" & Chr(13)
+// MSComm1.Output = "ATSW22,6,1,1" & Chr(13)
+// MSComm1.Output = "ATSW23,6,0,1" & Chr(13)
+// MSComm1.Output = "ATSW23,6,1,1" & Chr(13)
+// MSComm1.Output = "ATMD" & Chr(13)
+ String str="+++\nATSW22,6,1,1\n,ATSW23,6,0,1\nATSW23,6,1,1\nATMD\n";
+ send(str.getBytes());
+// byte bytes[] = str.getBytes();
+// for (int i = 0; i < bytes.length; i++) {
+//
+// }
+ }
+
+ /**
+ * Put Roomba in safe mode.
+ * As opposed to full mode. Safe mode is the preferred working state
+ * when playing with the Roomba as it provides some measure of
+ * autonomous self-preservation if it encounters a cliff or is picked up
+ * If that happens it goes into passive mode and must be 'reset()'.
+ * @see #reset()
+ */
+ public void startup() {
+ logmsg("startup");
+ speed = defaultSpeed;
+ start();
+ }
+
+ /**
+ * Reset Roomba after a fault. This takes it out of whatever mode it was
+ * in and puts it into safe mode.
+ * This command also syncs the object's sensor state with the Roomba's
+ * by calling updateSensors()
+ * @see #startup()
+ * @see #updateSensors()
+ */
+ public void reset() {
+ logmsg("reset");
+ stop();
+ startup();
+ control();
+ updateSensors();
+ }
+
+ /** Send START command */
+ public void start() {
+ logmsg("start");
+ mode = MODE_PASSIVE;
+ send( START );
+ }
+ /** Send CONTROL command */
+ public void control() {
+ logmsg("control");
+ mode = MODE_SAFE;
+ send( CONTROL );
+ // set blue dirt LED on so we know roomba is powered on & under control
+ // (and we don't forget to turn it off, and run it's batteries flat)
+ // FIXME: first time after a poweron, the lights flash then turn off
+ setLEDs(false, false, false, false, false, true, 128, 255);
+ }
+ /** Send SAFE command */
+ public void safe() {
+ logmsg("safe");
+ mode = MODE_SAFE;
+ send( SAFE );
+ }
+ /** Send FULL command */
+ public void full() {
+ logmsg("full");
+ mode = MODE_FULL;
+ send( FULL );
+ }
+
+ /**
+ * Power off the Roomba. Once powered off, the only way to wake it
+ * is via wakeup() (if implemented) or via a physically pressing
+ * the Power button
+ * @see #wakeup()
+ */
+ public void powerOff() {
+ logmsg("powerOff");
+ mode = MODE_UNKNOWN;
+ send( POWER );
+ }
+
+ /** Send the SPOT command */
+ public void spot() {
+ logmsg("spot");
+ mode = MODE_PASSIVE;
+ send( SPOT );
+ }
+ /** Send the CLEAN command */
+ public void clean() {
+ logmsg("clean");
+ mode = MODE_PASSIVE;
+ send( CLEAN );
+ }
+ /** Send the max command */
+ public void max() {
+ logmsg("max");
+ mode = MODE_PASSIVE;
+ send( MAX );
+ }
+ /** Send the max command */
+ public void dock() {
+ logmsg("dock");
+ mode = MODE_PASSIVE;
+// send( CLEAN );
+ send( DOCK );
+ }
+ /**
+ * Send the SENSORS command
+ * with one of the SENSORS_ arguments
+ * Typically, one does "sensors(SENSORS_ALL)" to get all sensor data
+ * @param packetcode one of SENSORS_ALL, SENSORS_PHYSICAL,
+ * SENSORS_INTERNAL, or SENSORS_POWER, or for roomba 5xx, it
+ * is the sensor packet number (from the spec)
+ */
+ public void sensors(int packetcode ) {
+ sensorsValid = false;
+ logmsg("sensors:"+packetcode);
+ switch (packetcode) {
+ case 0: readRequestLength = 26; break;
+ case 1: readRequestLength = 10; break;
+ case 2: readRequestLength = 6; break;
+ case 3: readRequestLength = 10; break;
+ case 4: readRequestLength = 14; break;
+ case 5: readRequestLength = 12; break;
+ case 6: readRequestLength = 52; break;
+ case 100: readRequestLength = 80; break;
+ case 101: readRequestLength = 28; break;
+ case 106: readRequestLength = 12; break;
+ case 107: readRequestLength = 9; break;
+ case 19:
+ case 20:
+ case 22:
+ case 23:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 39:
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ case 54:
+ case 55:
+ case 56:
+ case 57: readRequestLength = 2; break;
+ default: readRequestLength = 1; break;
+ }
+
+ byte cmd[] = { (byte)SENSORS, (byte)packetcode};
+ send(cmd);
+ }
+
+ /**
+ * get all sensor data
+ */
+ public void sensors() {
+ readRequestLength = 26;
+ sensors( SENSORS_ALL );
+ }
+ /**
+ * Read roomba 26-byte sensor record using robotConnection. Tries once to read valid data, allowing 100ms
+ * timeout on each attempt.
+ * @return true if read 26 bytes of valid data. Data has been stored in sensor_bytes. False otherwise
+ */
+ public boolean updateSensors()
+ {
+ return updateSensors(SENSORS_ALL);
+ }
+
+ public boolean updateSensors(int sensorGroup)
+ {
+ int sensorGroupSize;
+
+ if (robotConnection == null) {
+ System.out.println("Error at ArduinoBot.updateSensors(): no connection object for robot");
+ return false;
+ }
+
+ switch(sensorGroup) {
+ case SENSORS_ALL: sensorGroupSize = 26; break;
+ case 100: sensorGroupSize = 80; break;
+ default:
+ System.err.println("Invalid sensor group in updateSensors(): " + sensorGroup);
+ return false;
+ }
+ sensors(sensorGroup);
+ return getSensorData(sensorGroupSize);
+ }
+
+ /**
+ * Query a list of sensors. This is a roomba 5xx only command.
+ * @param sensorList A byte array containing the sensor groups requested to be read
+ * @param returnLen The number of bytes of data expected to be returned from roomba
+ */
+ public void queryList(byte[] sensorList, int returnLen)
+ {
+ int i = 0;
+ int j;
+
+ sensorsValid = false;
+ readRequestLength = returnLen;
+ byte cmd[] = new byte[2+sensorList.length];
+ cmd[i++] = (byte) QUERYLIST;
+ cmd[i++] = (byte)sensorList.length;
+ for (j=0; j<sensorList.length; j++)
+ cmd[i++] = sensorList[j];
+ send(cmd);
+ }
+
+ /**
+ * @param sensorGroupSize
+ */
+ public boolean getSensorData(int sensorGroupSize) {
+ byte [] readData;
+
+ // try once to read valid sensor data before giving up
+ //startTime = System.currentTimeMillis();
+ try {
+ readData = robotConnection.readBot(sensorGroupSize);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ // readBot either read requested # of bytes or returned a null (indicating timeout.
+ // If null or invalid data and group 0 (26 bytes expected), try again
+ if( readData != null ) {
+ if (((readData[1] > 1) || (readData[1] < 0)) && (sensorGroupSize == 26)) {
+ sensorsValid = false;
+ logmsg("updateSensors: received invalid data while attempting to read Roomba sensors!");
+ } else {
+ sensorsValid = true;
+ System.arraycopy(readData, 0, sensor_bytes, 0, sensorGroupSize);
+ logmsg("updateSensors: sensorsValid!");
+ //elapsedTime = System.currentTimeMillis() - startTime;
+ return true;
+ }
+ }
+ System.out.println("Error: timeout on sensor read");
+ return false;
+ }
+ //
+ // basic functions
+ //
+
+ /**
+ * Alias to pause
+ * @see #pause(int)
+ */
+ public void delay( int millis ) { pause( millis ); }
+
+ /**
+ * Just a simple pause function.
+ * Makes the thread block with Thread.sleep()
+ * @param millis number of milliseconds to wait
+ */
+ public void pause( int millis ) {
+ try { Thread.sleep(millis); } catch(Exception e) { }
+ }
+
+
+ //
+ // higher-level functions
+ //
+
+ /**
+ * Stop Rooomba's motion.
+ * Sends drive(0,0)
+ */
+ public void stop() {
+ logmsg("stop");
+ drive( 0, 0 );
+ }
+
+ /** Set speed for movement commands */
+ public void setSpeed( int s ) { speed = Math.abs(s); }
+ /** Get speed for movement commands */
+ public int getSpeed() { return speed; }
+
+ /**
+ * Go straight at the current speed for a specified distance.
+ * Positive distance moves forward, negative distance moves backward.
+ * This method blocks until the action is finished.
+ * @param distance distance in millimeters, positive or negative
+ */
+ public void goStraight( int distance ) {
+ float pausetime = Math.abs(distance / speed); // mm/(mm/sec) = sec
+ if (distance > 0)
+ goStraightAt( speed );
+ else
+ goStraightAt( -speed);
+ pause( (int)(pausetime*1000) );
+ stop();
+ }
+
+ /**
+ * @param distance distance in millimeters, positive
+ */
+ public void goForward( int distance ) {
+ if( distance < 0 ) return;
+ goStraight( distance );
+ }
+
+ /**
+ * @param distance distance in millimeters, positive
+ */
+ public void goBackward( int distance ) {
+ if( distance < 0 ) return;
+ goStraight( -distance );
+ }
+
+ /**
+ *
+ */
+ public void turnLeft() {
+ turn(129);
+ }
+ public void turnRight() {
+ turn(-129);
+ }
+ public void turn( int radius ) {
+ drive( speed, radius );
+ }
+
+ /**
+ * Spin right or spin left a particular number of degrees
+ * @param angle angle in degrees,
+ * positive to spin left, negative to spin right
+ */
+ public void spin( int angle ) {
+ if( angle > 0 ) spinLeft( angle );
+ else if( angle < 0 ) spinRight( -angle );
+ }
+
+ /**
+ * Spin right the current speed for a specified angle
+ * @param angle angle in degrees, positive
+ */
+ public void spinRight( int angle ) {
+ if( angle < 0 ) return;
+ float pausetime = Math.abs( millimetersPerDegree * angle / speed );
+ spinRightAt( Math.abs(speed) );
+ pause( (int)(pausetime*1000) );
+ stop();
+ }
+
+ /**
+ * Spin left a specified angle at a specified speed
+ * @param angle angle in degrees, positive
+ */
+ public void spinLeft( int angle ) {
+ if( angle<0 ) return;
+ //float pausetime =
+ float pausetime = Math.abs( millimetersPerDegree * angle / speed );
+ spinLeftAt( Math.abs(speed) );
+ pause( (int)(pausetime*1000) );
+ stop();
+ }
+
+ /**
+ * Spin in place anti-clockwise, at the current speed
+ */
+ public void spinLeft() {
+ spinLeftAt( speed );
+ }
+ /**
+ * Spin in place clockwise, at the current speed
+ */
+ public void spinRight() {
+ spinRightAt( speed );
+ }
+
+ /**
+ * Spin in place anti-clockwise, at the current speed.
+ * @param aspeed speed to spin at
+ */
+ public void spinLeftAt(int aspeed) {
+ drive( aspeed, 1 );
+ }
+
+ /**
+ * Spin in place clockwise, at the current speed.
+ * @param aspeed speed to spin at, positive
+ */
+ public void spinRightAt(int aspeed) {
+ drive( aspeed, -1 );
+ }
+
+ //
+ // mid-level movement, no blocking, parameterized by speed, not distance
+ //
+
+ /**
+ * Go straight at a specified speed.
+ * Positive is forward, negative is backward
+ * @param velocity velocity of motion in mm/sec
+ */
+ public void goStraightAt( int velocity ) {
+ //System.out.println("goStraightAt: velocity:"+velocity);
+ if( velocity > 500 ) velocity = 500;
+ if( velocity < -500 ) velocity = -500;
+ drive( velocity, 0x8000 );
+ }
+
+ /**
+ * Go forward the current (positive) speed
+ */
+ public void goForward() {
+ goStraightAt( Math.abs(speed) );
+ }
+
+ /**
+ * Go backward at the current (negative) speed
+ */
+ public void goBackward() {
+ goStraightAt( - Math.abs(speed) );
+ }
+
+ /**
+ * Go forward at a specified speed
+ */
+ public void goForwardAt( int aspeed ) {
+ if( aspeed < 0 ) return;
+ goStraightAt( aspeed );
+ }
+
+ /**
+ * Go backward at a specified speed
+ */
+ public void goBackwardAt( int aspeed ) {
+ if( aspeed < 0 ) return;
+ goStraightAt( -aspeed );
+ }
+
+
+ //
+ // low-level movement and action
+ //
+
+ /**
+ * Move the Roomba via the low-level velocity + radius method.
+ * See the 'Drive' section of the Roomba ROI spec for more details.
+ * Low-level command.
+ * @param velocity speed in millimeters/second,
+ * positive forward, negative backward
+ * @param radius radius of turn in millimeters
+ */
+ public void drive( int velocity, int radius ) {
+ byte cmd[] = { (byte)DRIVE,(byte)(velocity>>>8),(byte)(velocity&0xff),
+ (byte)(radius >>> 8), (byte)(radius & 0xff) };
+ logmsg("drive: "+hex(cmd[0])+","+hex(cmd[1])+","+hex(cmd[2])+","+
+ hex(cmd[3])+","+hex(cmd[4]));
+ send( cmd );
+ }
+
+ /**
+ * Play a musical note
+ * Does it via the hacky method of defining a one-note song & playing it
+ * Uses up song slot 15.
+ * If another note is played before one is finished, the new note cuts off
+ * the old one.
+ * @param note a note number from 31 (G0) to 127 (G8)
+ * @param duration duration of note in 1/64ths of a second
+ */
+ public void playNote( int note, int duration ) {
+ logmsg("playnote: "+note+":"+duration);
+ byte cmd[] = {
+ (byte)SONG, 3, 1, (byte)note, (byte)duration, // define song
+ (byte)PLAY, 3 }; // play it back
+ send( cmd );
+ }
+
+ public void playSong( int songnum ) {
+ byte cmd[] = { (byte)PLAY, (byte)songnum };
+ send(cmd);
+ }
+
+ /**
+ * Make a song
+ * @param songnum number of song to define
+ * @param song array of songnotes,
+ * even entries are notenums, odd are duration of 1/6ths
+ */
+ public void createSong( int songnum, int song[] ) {
+ int len = song.length;
+ int songlen = len/2;
+ logmsg("createSong: songnum:"+songnum+", songlen:"+songlen);
+ byte cmd[] = new byte[len+3];
+ cmd[0] = (byte) SONG;
+ cmd[1] = (byte) songnum;
+ cmd[2] = (byte) songlen;
+ for( int i=0; i < len; i++ ) {
+ cmd[3+i] = (byte)song[i];
+ }
+ send(cmd);
+ }
+ /**
+ * Make a song
+ * @param songnum number of song to define
+ * @param song array of Notes
+ */
+ public void createSong( int songnum, Note song[] ) {
+ int songlen = song.length;
+ logmsg("createSong: songnum:"+songnum+", songlen:"+songlen);
+ byte cmd[] = new byte[songlen+3];
+ cmd[0] = (byte) SONG;
+ cmd[1] = (byte) songnum;
+ cmd[2] = (byte) songlen;
+ int j=3;
+ for( int i=0; i < songlen; i++ ) {
+ cmd[j++] = (byte)song[i].notenum;
+ cmd[j++] = (byte)song[i].toSec64ths();
+ }
+ send(cmd);
+ }
+
+
+
+ /**
+ * Turns on/off the non-drive motors (main brush, vacuum, sidebrush).
+ * Sort of low-level.
+ * @param mainbrush mainbrush motor on/off state
+ * @param vacuum vacuum motor on/off state
+ * @param sidebrush sidebrush motor on/off state
+ */
+ public void setMotors(boolean mainbrush,boolean vacuum,boolean sidebrush) {
+ byte cmd[] = {
+ (byte)MOTORS,
+ (byte)((mainbrush?0x04:0) | (vacuum?0x02:0) | (sidebrush?0x01:0))};
+ send( cmd );
+ }
+
+ /**
+ * Turns on/off the various LEDs.
+ * Low-level command.
+ * FIXME: this is too complex
+ */
+ public void setLEDs( boolean status_green, boolean status_red,
+ boolean spot,boolean clean,boolean max,boolean dirt,
+ int power_color, int power_intensity ) {
+ int v = (status_green?0x20:0) | (status_red?0x10:0) |
+ (spot?0x08:0) | (clean?0x04:0) | (max?0x02:0) | (dirt?0x01:0);
+ logmsg("setLEDS: "+binary(v));
+ byte cmd[] = { (byte)LEDS, (byte)v,
+ (byte)power_color, (byte)power_intensity };
+ send(cmd);
+ }
+
+ //500 series
+ public void setLEDsOI( boolean checkRobot, boolean spot,boolean dock,boolean dirt,
+ int power_color, int power_intensity ) {
+ updateDisplay("setLEDsOI ("+checkRobot+")("+spot+")("+dock+")("+dirt+")("+power_color+")("+power_intensity+")", this.debug);
+ int v = (checkRobot?0x08:0) | (dock?0x04:0) | (spot?0x02:0) | (dirt?0x01:0);
+ logmsg("setLEDS: "+binary(v));
+ byte cmd[] = { (byte)LEDS, (byte)v,
+ (byte)power_color, (byte)power_intensity };
+ // TODO: find a way to do an updateDisplay with a byte array
+ send(cmd);
+}
+
+ /**
+ * Turn all vacuum motors on or off according to state
+ * @param state true to turn on vacuum function, false to turn it off
+ */
+ public void vacuum(boolean state) {
+ logmsg("vacuum: "+state);
+ setMotors(state,state,state);
+ }
+
+
+ //
+ // sensor functions
+ //
+
+
+ /**
+ * Compute possible safety fault.
+ * Called on every successful updateSensors().
+ * In normal use, call updateSensors() then check safetyFault().
+ * @return true if indicates we had an event that took the Roomba out of
+ * safe mode
+ * @see #updateSensors()
+ */
+ public boolean computeSafetyFault() {
+ safetyFault = (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROP_MASK) != 0 ||
+ sensor_bytes[CLIFFLEFT]==1 || sensor_bytes[CLIFFFRONTLEFT]==1 ||
+ sensor_bytes[CLIFFRIGHT]==1 || sensor_bytes[CLIFFFRONTRIGHT]==1;
+
+ if( safetyFault && (mode == MODE_SAFE) ) mode = MODE_PASSIVE;
+
+ return safetyFault;
+ }
+
+ /**
+ * Returns current connected state.
+ * It's up to subclasses to ensure this variable is correct.
+ * @return current connected state
+ */
+ public boolean connected() { return connected; }
+
+ /** current ROI mode RoombaComm thinks the Roomba is in */
+ public int mode() { return mode; }
+ /** mode as String */
+ public String modeAsString() {
+ String s=null;
+ switch(mode) {
+ case MODE_UNKNOWN: s = "unknown"; break;
+ case MODE_PASSIVE: s = "passive"; break;
+ case MODE_SAFE: s = "safe"; break;
+ case MODE_FULL: s = "full"; break;
+ }
+ return s;
+ }
+
+ /** */
+ public boolean sensorsAutoUpdate() { return sensorsAutoUpdate; }
+ /** */
+ public void setSensorsAutoUpdate(boolean b) { sensorsAutoUpdate=b; }
+ /** */
+ public int sensorsUpdateTime() { return sensorsUpdateTime; }
+ /** */
+ public void setSensorsUpdateTime(int i) { sensorsUpdateTime=i; }
+
+ /**
+ *
+ */
+ public boolean safetyFault() { return safetyFault; }
+
+ /**
+ *
+ */
+ public boolean sensorsValid() {
+ // FIXME:
+// if( sensorsValid ) { // may be valid but stale
+// long difftime = System.currentTimeMillis() - sensorsLastUpdateTime;
+// if( difftime > 2*sensorsUpdateTime ) { // give it some space
+// return false;
+// }
+// else return true;
+// }
+ return sensorsValid;
+ }
+ public String getSensorsAsString() {
+ return sensorsAsString();
+ }
+ public String convertByteArrayToString(byte[] byteArray) {
+ String value = new String(byteArray);
+ return value;
+ }
+
+
+ /**
+ * @return all sensor data as a string
+ */
+ //* this likely needs to know about protocal to know how to read the sensors */
+ public String sensorsAsString() {
+ String sd="";
+
+ if( debug ) {
+ sd = "\n";
+ for( int i=0; i<26; i++ )
+ sd += " "+hex(sensor_bytes[i]);
+ }
+ return
+ "*****\n" +
+ "bump:" +
+ (bumpLeft()?"l":"_") +
+ (bumpRight()?"r":"_") +
+ " wheel:" +
+ (wheelDropLeft() ?"l":"_") +
+ (wheelDropCenter()?"c":"_") +
+ (wheelDropRight() ?"r":"_") +
+ " wall:" + (wall() ?"Y":"n") +
+ " cliff:" +
+ (cliffLeft() ?"l":"_") +
+ (cliffFrontLeft() ?"L":"_") +
+ (cliffFrontRight() ?"R":"_") +
+ (cliffRight() ?"r":"_") +
+ " dirtL:"+dirtLeft()+
+ " dirtR:"+dirtRight()+ "\n" +
+ "vwal:" + virtual_wall() +
+ " motr:" + motor_overcurrents() +
+ " dirt:" + dirt_left() + "," + dirt_right() +
+ " remo:" + hex(remote_opcode()) +
+ " butt:" + hex(buttons()) +
+ " dist:" + distance() +
+ " angl:" + angle() + "\n" +
+ "chst:" + charging_state() +
+ " volt:" + voltage() +
+ " curr:" + current() +
+ " temp:" + temperatureF() + "F" +
+ " chrg:" + charge() +
+ " capa:" + capacity() +
+ sd ;
+ }
+ public String chargeDataAsString() {
+ String sd="";
+ if( debug ) {
+ sd = "\n";
+ for( int i=0; i<26; i++ )
+ sd += " "+hex(sensor_bytes[i]);
+ }
+ return
+ "Charging State: " + charging_state() +
+ " Temperature: " + temperatureF() + "F\n" +
+ "Voltage: " + voltage() +
+ " Current: " + current() + "\n" +
+ "Capacity: " + capacity() +
+ " Charge: " + charge() +
+ sd;
+ }
+ /** Did we bump into anything */
+ public boolean bump() {
+ return (sensor_bytes[BUMPSWHEELDROPS] & BUMP_MASK) !=0;
+ }
+ /** Left bump sensor */
+ public boolean bumpLeft() {
+ return (sensor_bytes[BUMPSWHEELDROPS] & BUMPLEFT_MASK) !=0;
+ }
+ /** Right bump sensor */
+ public boolean bumpRight() {
+ return (sensor_bytes[BUMPSWHEELDROPS] & BUMPRIGHT_MASK) !=0;
+ }
+ /** Left wheeldrop sensor */
+ public boolean wheelDropLeft() {
+ return (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROPLEFT_MASK) !=0;
+ }
+ /** Right wheeldrop sensor */
+ public boolean wheelDropRight() {
+ return (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROPRIGHT_MASK) !=0;
+ }
+ /** Center wheeldrop sensor */
+ public boolean wheelDropCenter() {
+ return (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROPCENT_MASK) !=0;
+ }
+ /** Can we see a wall? */
+ public boolean wall() {
+ return sensor_bytes[WALL] != 0;
+ }
+
+ /**
+ * @return true if dirt present
+ */
+ public boolean dirt() {
+ int dl = sensor_bytes[DIRTLEFT] & 0xff;
+ int dr = sensor_bytes[DIRTRIGHT] & 0xff;
+ //if(debug) println("Roomba:dirt: dl,dr="+dl+","+dr);
+ return (dl > 100) || (dr > 100);
+ }
+ /**
+ * amount of dirt seen by left dirt sensor
+ */
+ public int dirtLeft() {
+ return dirt_left(); // yeah yeah
+ }
+ /**
+ * amount of dirt seen by right dirt sensor
+ */
+ public int dirtRight() {
+ return dirt_right();
+ }
+
+ /** left cliff sensor */
+ public boolean cliffLeft() {
+ return (sensor_bytes[CLIFFLEFT] != 0);
+ }
+ /** front left cliff sensor */
+ public boolean cliffFrontLeft() {
+ return (sensor_bytes[CLIFFFRONTLEFT] != 0);
+ }
+ /** front right cliff sensor */
+ public boolean cliffFrontRight() {
+ return (sensor_bytes[CLIFFFRONTRIGHT] != 0);
+ }
+ /** right cliff sensor */
+ public boolean cliffRight() {
+ return sensor_bytes[CLIFFRIGHT] != 0;
+ }
+
+ /** overcurrent on left drive wheel */
+ public boolean motorOvercurrentDriveLeft() {
+ return (sensor_bytes[MOTOROVERCURRENTS] & MOVERDRIVELEFT_MASK) != 0;
+ }
+ /** overcurrent on right drive wheel */
+ public boolean motorOvercurrentDriveRight() {
+ return (sensor_bytes[MOTOROVERCURRENTS] & MOVERDRIVERIGHT_MASK) != 0;
+ }
+ /** overcurrent on main brush */
+ public boolean motorOvercurrentMainBrush() {
+ return (sensor_bytes[MOTOROVERCURRENTS] & MOVERMAINBRUSH_MASK) != 0;
+ }
+ /** overcurrent on vacuum */
+ public boolean motorOvercurrentVacuum() {
+ return (sensor_bytes[MOTOROVERCURRENTS] & MOVERVACUUM_MASK) != 0;
+ }
+ /** overcurrent on side brush */
+ public boolean motorOvercurrentSideBrush() {
+ return (sensor_bytes[MOTOROVERCURRENTS] & MOVERSIDEBRUSH_MASK) !=0;
+ }
+
+ /** 'Power' button pressed state */
+ public boolean powerButton() {
+ return (sensor_bytes[BUTTONS] & POWERBUTTON_MASK) != 0;
+ }
+ /** 'Spot' button pressed state */
+ public boolean spotButton() {
+ return (sensor_bytes[BUTTONS] & SPOTBUTTON_MASK) != 0;
+ }
+ /** 'Clean' button pressed state */
+ public boolean cleanButton() {
+ return (sensor_bytes[BUTTONS] & CLEANBUTTON_MASK) != 0;
+ }
+ /** 'Max' button pressed state */
+ public boolean maxButton() {
+ return (sensor_bytes[BUTTONS] & MAXBUTTON_MASK) != 0;
+ }
+
+
+ //
+ // lower-level sensor access
+ //
+ /** lower-level func, returns raw byte */
+ public int bumps_wheeldrops() {
+ return sensor_bytes[BUMPSWHEELDROPS];
+ }
+ /** lower-level func, returns raw byte */
+ public int cliff_left() {
+ return sensor_bytes[CLIFFLEFT];
+ }
+ /** lower-level func, returns raw byte */
+ public int cliff_frontleft() {
+ return sensor_bytes[CLIFFFRONTLEFT];
+ }
+ /** lower-level func, returns raw byte */
+ public int cliff_frontright() {
+ return sensor_bytes[CLIFFFRONTRIGHT];
+ }
+ /** lower-level func, returns raw byte */
+ public int cliff_right() {
+ return sensor_bytes[CLIFFRIGHT];
+ }
+ /** lower-level func, returns raw byte */
+ public int virtual_wall() {
+ return sensor_bytes[VIRTUALWALL];
+ }
+ /** lower-level func, returns raw byte */
+ public int motor_overcurrents() {
+ return sensor_bytes[MOTOROVERCURRENTS];
+ }
+ /** */
+ public int dirt_left() {
+ return sensor_bytes[DIRTLEFT] & 0xff;
+ }
+ /** */
+ public int dirt_right() {
+ return sensor_bytes[DIRTRIGHT] & 0xff;
+ }
+ /** lower-level func, returns raw byte */
+ public int remote_opcode() {
+ return sensor_bytes[REMOTEOPCODE];
+ }
+ /** lower-level func, returns raw byte */
+ public int buttons() {
+ return sensor_bytes[BUTTONS];
+ }
+
+ /**
+ * Distance traveled since last requested
+ * units: mm
+ * range: -32768 - 32767
+ */
+ public short distance() {
+ return toShort(sensor_bytes[DISTANCE_HI],
+ sensor_bytes[DISTANCE_LO]);
+ }
+ /**
+ * Angle traveled since last requested
+ * units: mm, diff in distance traveled by two drive wheels
+ * range: -32768 - 32767
+ */
+ public short angle() {
+ return toShort(sensor_bytes[ANGLE_HI],
+ sensor_bytes[ANGLE_LO]);
+ }
+ /**
+ * angle since last read, but in degrees
+ */
+ // FIXME I think this should be (360 * angle())/(258 * PI)
+ public float angleInDegrees() {
+ return (float) angle() / millimetersPerDegree;
+ }
+ /**
+ * angle since last read, but in radians
+ */
+ // FIXME I think this should be (2 * angle())/258
+ public float angleInRadians() {
+ return (float) angle() / millimetersPerRadian;
+ }
+
+ /**
+ * Charging state
+ * units: enumeration
+ * range:
+ */
+ public int charging_state() {
+ return sensor_bytes[CHARGINGSTATE] & 0xff;
+ }
+ /**
+ * Voltage of battery
+ * units: mV
+ * range: 0 - 65535
+ */
+ public int voltage() {
+ return toUnsignedShort(sensor_bytes[VOLTAGE_HI],
+ sensor_bytes[VOLTAGE_LO]);
+ }
+ /**
+ * Current flowing in or out of battery
+ * units: mA
+ * range: -332768 - 32767
+ */
+ public short current() {
+ return toShort(sensor_bytes[CURRENT_HI],
+ sensor_bytes[CURRENT_LO]);
+ }
+ /**
+ * temperature of battery
+ * units: degrees Celcius
+ * range: -128 - 127
+ */
+ public byte temperature() {
+ return sensor_bytes[TEMPERATURE];
+ }
+ public byte temperatureF() {
+ int c = sensor_bytes[TEMPERATURE];
+ return (byte) ((9.0/5.0)*c + 32);
+ }
+ /**
+ * Current charge of battery
+ * units: mAh
+ * range: 0-65535
+ */
+ public int charge() {
+ return toUnsignedShort(sensor_bytes[CHARGE_HI],
+ sensor_bytes[CHARGE_LO]);
+ }
+ /**
+ * Estimated charge capacity of battery
+ * units: mAh
+ * range: 0-65535
+ */
+ public int capacity() {
+ return toUnsignedShort(sensor_bytes[CAPACITY_HI],
+ sensor_bytes[CAPACITY_LO]);
+ }
+
+ // possible modes
+ public static final int MODE_UNKNOWN = 0;
+ public static final int MODE_PASSIVE = 1;
+ public static final int MODE_SAFE = 2;
+ public static final int MODE_FULL = 3;
+
+ // Roomba ROI opcodes
+ // these should all be bytes, but Java bytes are signed, sucka
+ public static final int START = 128; // 0
+ public static final int BAUD = 129; // 1
+ public static final int CONTROL = 130; // 0
+ public static final int SAFE = 131; // 0
+ public static final int FULL = 132; // 0
+ public static final int POWER = 133; // 0
+ public static final int SPOT = 134; // 0
+ public static final int CLEAN = 135; // 0
+ public static final int MAX = 136; // 0
+ public static final int DRIVE = 137; // 4
+ public static final int MOTORS = 138; // 1
+ public static final int LEDS = 139; // 3
+ public static final int SONG = 140; // 2N+2
+ public static final int PLAY = 141; // 1
+ public static final int SENSORS = 142; // 1
+ public static final int DOCK = 143; // 0
+ public static final int PWMMOTORS = 144; // 3
+ public static final int DRIVEWHEELS = 145; // 4
+ public static final int DRIVEPWM = 146; // 4
+ public static final int STREAM = 148; // N+1
+ public static final int QUERYLIST = 149; // N+1
+ public static final int STOPSTARTSTREAM = 150; // 1
+ public static final int SCHEDULINGLEDS = 162; // 2
+ public static final int DIGITLEDSRAW = 163; // 4
+ public static final int DIGITLEDSASCII = 164; // 4
+ public static final int BUTTONSCMD = 165; // 1
+ public static final int SCHEDULE = 167; // n
+ public static final int SETDAYTIME = 168; // 3
+
+ // offsets into sensor_bytes data
+ public static final int BUMPSWHEELDROPS = 0;
+ public static final int WALL = 1;
+ public static final int CLIFFLEFT = 2;
+ public static final int CLIFFFRONTLEFT = 3;
+ public static final int CLIFFFRONTRIGHT = 4;
+ public static final int CLIFFRIGHT = 5;
+ public static final int VIRTUALWALL = 6;
+ public static final int MOTOROVERCURRENTS = 7;
+ public static final int DIRTLEFT = 8;
+ public static final int DIRTRIGHT = 9;
+ public static final int REMOTEOPCODE = 10;
+ public static final int BUTTONS = 11;
+ public static final int DISTANCE_HI = 12;
+ public static final int DISTANCE_LO = 13;
+ public static final int ANGLE_HI = 14;
+ public static final int ANGLE_LO = 15;
+ public static final int CHARGINGSTATE = 16;
+ public static final int VOLTAGE_HI = 17;
+ public static final int VOLTAGE_LO = 18;
+ public static final int CURRENT_HI = 19;
+ public static final int CURRENT_LO = 20;
+ public static final int TEMPERATURE = 21;
+ public static final int CHARGE_HI = 22;
+ public static final int CHARGE_LO = 23;
+ public static final int CAPACITY_HI = 24;
+ public static final int CAPACITY_LO = 25;
+
+ // bitmasks for various thingems
+ public static final int WHEELDROP_MASK = 0x1C;
+ public static final int BUMP_MASK = 0x03;
+ public static final int BUMPRIGHT_MASK = 0x01;
+ public static final int BUMPLEFT_MASK = 0x02;
+ public static final int WHEELDROPRIGHT_MASK = 0x04;
+ public static final int WHEELDROPLEFT_MASK = 0x08;
+ public static final int WHEELDROPCENT_MASK = 0x10;
+
+ public static final int MOVERDRIVELEFT_MASK = 0x10;
+ public static final int MOVERDRIVERIGHT_MASK= 0x08;
+ public static final int MOVERMAINBRUSH_MASK = 0x04;
+ public static final int MOVERVACUUM_MASK = 0x02;
+ public static final int MOVERSIDEBRUSH_MASK = 0x01;
+
+ public static final int POWERBUTTON_MASK = 0x08;
+ public static final int SPOTBUTTON_MASK = 0x04;
+ public static final int CLEANBUTTON_MASK = 0x02;
+ public static final int MAXBUTTON_MASK = 0x01;
+
+ // which sensor packet, argument for sensors(int)
+ public static final int SENSORS_ALL = 0;
+ public static final int SENSORS_PHYSICAL = 1;
+ public static final int SENSORS_INTERNAL = 2;
+ public static final int SENSORS_POWER = 3;
+
+ public static final int REMOTE_NONE = 0xff;
+ public static final int REMOTE_POWER = 0x8a;
+ public static final int REMOTE_PAUSE = 0x89;
+ public static final int REMOTE_CLEAN = 0x88;
+ public static final int REMOTE_MAX = 0x85;
+ public static final int REMOTE_SPOT = 0x84;
+ public static final int REMOTE_SPINLEFT = 0x83;
+ public static final int REMOTE_FORWARD = 0x82;
+ public static final int REMOTE_SPINRIGHT = 0x81;
+
+ /*
+no button = -1
+power = -118 8a
+pause = -119 89
+clean = -120 88
+max = -123 85
+spot = -124 84
+spinleft = -125 81 (8d keyup?)
+forward = -126 82 (8c?)
+spinright = -127 83
+ */
+
+ //
+ // utility methods
+ //
+
+ /**
+ *
+ */
+ static public final short toShort(byte hi, byte lo) {
+ return (short)((hi << 8) | (lo & 0xff));
+ }
+ /**
+ *
+ */
+ static public final int toUnsignedShort(byte hi, byte lo) {
+ return (int)(hi & 0xff) << 8 | lo & 0xff;
+ }
+
+ public void println(String s) {
+ System.out.println(s);
+ }
+
+ public String hex(byte b) {
+ return Integer.toHexString(b&0xff);
+ }
+
+ public String hex(int i) {
+ return Integer.toHexString(i);
+ }
+
+
+ public String binary(int i) {
+ return Integer.toBinaryString(i);
+ }
+
+ /**
+ * just a little debug
+ */
+ public void logmsg(String msg) {
+ if(debug)
+ {
+ System.err.println("RoombaComm ("+System.currentTimeMillis()+"):"+msg);
+ System.err.flush();
+ }
+ }
+
+ /**
+ * General error reporting, all corraled here just in case
+ * I think of something slightly more intelligent to do.
+ */
+ public void errorMessage(String where, Throwable e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error inside Serial." + where + "()");
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ if (protocol.equals("SCI")) {
+ rate = 57600;
+ } else if (protocol.equals("OI")) {
+ rate = 115200;
+ }
+ this.protocol = protocol;
+ logmsg("Protocol: " + protocol +" , rate: "+rate);
+ writeConfigFile(portname, protocol, waitForDSR?'Y':'N');
+ }
+
+ /**
+ * Write a config file with current settings
+ */
+ protected void writeConfigFile(String port, String protocol, char waitForDSR) {
+ try {
+ FileWriter f = new FileWriter(".roomba_config", false);
+ BufferedWriter w = new BufferedWriter(f); // create file
+ if (port != null){
+ w.write(port);
+ }else{
+ w.newLine();
+ }
+ w.newLine();
+ if (protocol != null){
+ w.write(protocol);
+ }else{
+ w.newLine();
+ }
+ w.newLine();
+ w.write(waitForDSR);
+ w.newLine();
+ w.close();
+ f.close();
+ } catch (IOException e) {
+ logmsg("unable to write .roomba_config " + e);
+ }
+ }
+
+ protected void readConfigFile() {
+ try {
+ FileReader f = new FileReader(".roomba_config");
+ BufferedReader r = new BufferedReader(f);
+ portname = r.readLine();
+ setProtocol(r.readLine());
+ if (getProtocol().equals("SCI")) {
+ rate = 57600;
+ }else if (getProtocol().equals("OI")) {
+ rate = 115200;
+ }
+ waitForDSR = r.readLine().equals("Y")?true:false;
+ logmsg("read config port: " + serialPort + " protocol: " + getProtocol() + " waitDSR: " + waitForDSR);
+ } catch (IOException e) {
+ logmsg("unable to read .roomba_config " + e);
+ }
+ }
+
+ public void setLEDs(RoombaComm roombacomm) {
+ if( !roombacomm.connected() ){
+ updateDisplay("not-connected", this.debug);
+ return;
+ }
+ updateDisplay("setLEDs protocal is ("+this.protocol+")", this.debug);
+ if (this.protocol.equalsIgnoreCase("SCI")){
+ roombacomm.setLEDs(this.greenOn, this.redOn, this.toggleSpot, this.toggleClean, this.toggleMax, this.toggleDirt,
+ this.power_color, this.power_int);
+ }
+ if (this.protocol.equalsIgnoreCase("OI")){
+ roombacomm.setLEDsOI(this.toggleCheckRobot, this.toggleSpot, this.toggleDock, this.toggleDirt, this.power_color, this.power_int);
+ updateDisplay("Checkrobot("+this.toggleCheckRobot +"),Spot("+ this.toggleSpot +"),Dock("+ this.toggleDock +"),Dirt("+ this.toggleDirt +"),Pcolor("+ this.power_color +"),Pint("+ this.power_int+")",true);
+ }
+ }
+
+ public void setChgGreenLED(RoombaComm roombacomm, boolean green) {
+ this.greenOn=green;
+ updateDisplay("setChgGreenLED", true);
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgRedLED(RoombaComm roombacomm, boolean red) {
+ this.redOn=red;
+ updateDisplay("setChgRedLED", true);
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgSpotLED(RoombaComm roombacomm, boolean spot) {
+ this.toggleSpot=spot;
+ updateDisplay("setChgSpotLED value("+spot+")", true);
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgCleanLED(RoombaComm roombacomm, boolean clean) {
+ updateDisplay("setChgCleanLED",true);
+ this.toggleClean=clean;
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgMaxLED(RoombaComm roombacomm, boolean max) {
+ updateDisplay("setChgMaxLED",true);
+ this.toggleMax=max;
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgDirtLED(RoombaComm roombacomm, boolean dirt) {
+ updateDisplay("setChgDirtLED",true);
+ this.toggleDirt=dirt;
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgPowerColorLED(RoombaComm roombacomm, int power_color) {
+ updateDisplay("setChgPowerColorLED",true);
+ this.power_color=power_color;
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgPowerIntensityLED(RoombaComm roombacomm, int power_intensity) {
+ updateDisplay("setChgPowerIntensityLED",true);
+ this.power_int=power_intensity;
+ this.setLEDs(roombacomm);
+ }
+ public void setChgCheckRobotLED(RoombaComm roombacomm, boolean toggleCheckRobot) {
+ this.toggleCheckRobot = toggleCheckRobot;
+ this.setLEDs(roombacomm);
+ }
+
+ public void setChgDockLED(RoombaComm roombacomm, boolean toggleDock) {
+ this.toggleDock = toggleDock;
+ this.setLEDs(roombacomm);
+ }
+ protected void updateDisplay(String s, boolean onlyDebug) {
+ if (onlyDebug && debug){
+ updateDisplay(s);
+ System.out.println(s);
+ }
+ }
+
+ protected void updateDisplay(String s) {
+ // displayText.append( s );
+ // displayText.setCaretPosition(displayText.getDocument().getLength());
+ }
+
+ public boolean isRedOn() {
+ return redOn;
+ }
+
+ public boolean isGreenOn() {
+ return greenOn;
+ }
+
+ public boolean isToggleSpot() {
+ return toggleSpot;
+ }
+
+ public boolean isToggleClean() {
+ return toggleClean;
+ }
+
+ public boolean isToggleMax() {
+ return toggleMax;
+ }
+
+ public boolean isToggleDirt() {
+ return toggleDirt;
+ }
+
+ public boolean isToggleCheckRobot() {
+ return toggleCheckRobot;
+ }
+
+
+ public boolean isToggleDock() {
+ return toggleDock;
+ }
+
+
+ /**
+ * Returns the number of bytes that have been read from serial
+ * and are waiting to be dealt with by the user.
+ * (from processing.serial.Serial)
+ *
+ private int available() {
+ return (bufferLast - bufferIndex);
+ }
+
+ /**
+ * Return a byte array of anything that's in the serial buffer.
+ * Not particularly memory/speed efficient, because it creates
+ * a byte array on each read, but it's easier to use than
+ * readBytes(byte b[]) (see below).
+ * (from processing.serial.Serial)
+ *
+ private byte[] readBytes() {
+ if (bufferIndex == bufferLast) return null;
+
+ synchronized (buffer) {
+ int length = bufferLast - bufferIndex;
+ byte outgoing[] = new byte[length];
+ System.arraycopy(buffer, bufferIndex, outgoing, 0, length);
+
+ bufferIndex = 0; // rewind
+ bufferLast = 0;
+ return outgoing;
+ }
+ }
+
+ /**
+ * Grab whatever is in the serial buffer, and stuff it into a
+ * byte buffer passed in by the user. This is more memory/time
+ * efficient than readBytes() returning a byte[] array.
+ *
+ * Returns an int for how many bytes were read. If more bytes
+ * are available than can fit into the byte array, only those
+ * that will fit are read.
+ * (from processing.serial.Serial)
+ *
+ public int readBytes(byte outgoing[]) {
+ if (bufferIndex == bufferLast) return 0;
+
+ synchronized (buffer) {
+ int length = bufferLast - bufferIndex;
+ if (length > outgoing.length) length = outgoing.length;
+ System.arraycopy(buffer, bufferIndex, outgoing, 0, length);
+
+ bufferIndex += length;
+ if (bufferIndex == bufferLast) {
+ bufferIndex = 0; // rewind
+ bufferLast = 0;
+ }
+ return length;
+ }
+ }
+ */
+ public void powerOn() {
+ logmsg("powerOn");
+ mode = MODE_PASSIVE;
+ // MSComm1.Output = "+++" & Chr(13)
+ // MSComm1.Output = "ATSW22,6,1,1" & Chr(13)
+ // MSComm1.Output = "ATSW23,6,0,1" & Chr(13)
+ // MSComm1.Output = "ATSW23,6,1,1" & Chr(13)
+ // MSComm1.Output = "ATMD" & Chr(13
+ send( ("+++"+(char)13).getBytes());
+ send( ("ATSW22,6,1,1"+(char)13).getBytes());
+ send( ("ATSW23,6,0,1"+(char)13).getBytes());
+ send( ("ATSW23,6,1,1"+(char)13).getBytes());
+ send( ("ATMD"+(char)13).getBytes());
+ //: TYPE : ATSW22,6,0,1<cr> ; First change it to high
+ //REPLY: <cr_lf>OK<cr_lf>
+ //TYPE : ATSW22,6,0,0<cr> ; Change it to low
+ //REPLY: <cr_lf>OK<cr_lf>
+ //TYPE : ATSW22,6,0,1<cr>
+ //REPLY: <cr_lf>OK<cr_lf> ; Change it to high
+ // send( "+++\n".getBytes());
+ // send( "ATSW22,6,0,1\n".getBytes());
+ // send( "ATSW22,6,0,0\n".getBytes());
+ // send( "ATSW22,6,0,1\n".getBytes());
+ // send( "ATMD\n".getBytes());
+
+ }
+
+ public byte[] getSensor_bytes() {
+ return sensor_bytes;
+ }
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java
new file mode 100644
index 0000000..8e121ac
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java
@@ -0,0 +1,1571 @@
+/*
+ * roombacomm.RoombaCommCLI -- test out RoombaComm library by issuing commands to it from the command line
+ *
+ * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net
+ *
+ * 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 java.io.*;
+import com.hackingroomba.roombacomm.RobotConnection.ReadTerminator;
+
+public class RoombaCommCLI {
+ // robot communications variables
+ private String curLine = ""; // Line read from standard in
+ private String [] args;
+ private String protocol;
+ private String portName;
+ private RobotConnection robotConnection = null;
+ private RobotConnection arduinoConnection = null;
+ private RobotConnection localizerConnection = null;
+ private RoombaComm robot; // use ArduinoBot as the robot, because it's the highest class
+ private ArduinoBot arduino;
+ private RobotType robotType;
+
+ // encoder variables
+ int speed = 50;
+ int angle;
+ int distance = 0;
+ int lEncoder = 0;
+ int rEncoder = 0;
+ int lastLEncoder = 0;
+ int lastREncoder = 0;
+ int lEncoderOverflow = 0;
+ int rEncoderOverflow = 0;
+
+ // navigation variables
+ private double x = 0;
+ private double y = 0;
+ private double targetX, targetY;
+ private boolean targetInitialized = false;
+ private int localizerCompassOffset = 0; // angle between localizer Y axis and compass North, add to vectorToStart angle to get compass to target
+ private boolean localizerOffsetInitialized = false;
+ private double distanceToTarget;
+ private int courseToTarget;
+
+ //RoboRealm variables
+ private watchVideo wv;
+ private FrameProcessor fp;
+ private int rrShapeX; // X location in image frame of detected shape from RoboRealm
+ private int rrConfidence;
+ private int rrSize;
+ private int rrRelativeHeading;
+ private boolean roborealmConnected = false;
+
+ // I/O variables
+ InputStreamReader isr = new InputStreamReader(System.in);
+ BufferedReader in = new BufferedReader(isr);
+
+ private long startTime;
+ private long elapsedTime;
+
+ public RoombaCommCLI()
+ {
+ // do nothing
+ }
+ // main - it all starts here
+ public static void main(String[] args) {
+ System.out.println("Enter a command (type 'help' for command listing or 'quit' to exit): ");
+ RoombaCommCLI cli = new RoombaCommCLI();
+ cli.runCommands();
+ }
+
+ private void usage() {
+ System.out.print(
+ "Available Commands\n------------------\n" +
+ "help\n" +
+ "quit\n" +
+ "connect {ip:portnum | serialPort} protocol; protocol is OI or SCI or AR (tankbot) or FR (FrankenRoomba) or MO (Mo'bot)\n" +
+ "\tUse AR or MO for Arduino connection - skips roombaInit and does arduinoInit\n" +
+ "disconnect Disconnect robot\n" +
+ "safe Enter safe mode\n" +
+ "full Enter full mode\n" +
+ "speed {defaultSpeed} Set the default speed\n" +
+ "spin {angleToSpin} Spins based on timer - YMMV\n" +
+ "spinTo (compass heading}\n" +
+ "drive {distance in inches} Drives based on timer - YMMV - literally\n" +
+ "driveFor {distance in inches} [compass heading] Drives based on wheel encoders and compass\n" +
+ "driveToTarget Drive from current position to target\n" +
+ "rectangle {length, width} [heading] Drive a rectangle, length is 1st, 3rd leg, width is 2nd, 4th leg, heading is initial direction\n" +
+ "squaredance {length, heading} Drive square with side 'length', initial heading 'heading', use localizer to seek to target which should be set before run with setTarget" +
+ "squaredance2 {length, heading} Drive square with side 'length', initial heading 'heading'" +
+ "stop - Stop robot\n" +
+ "compass Print the current compass reading\n" +
+ "encoders Print the current wheel encoders reading\n" +
+ "currentHeading {heading} Set the offset between compass-reported heading and actual heading\n" +
+ "sensors\n" +
+ "localizer {IP:Port} Connect to localizer\n" +
+ "disconnectLocalizer\n" +
+ "localize - get and print localizer fix\n" +
+ "setTarget {x y} - save away the target location ((x,y) coordinate in feet) in the target variables\n" +
+ "vectorToTarget - compute distance, direction to target\n" +
+ "printTarget - Print the current target" +
+ "vectorToTarget - compute the angle (in localizer coordinate system) and distance to start point\n" +
+ "localizerXBearing {bearing of +X axis} - bearing represents compass bearing of +X axis , i.e. +ve clockwise from north to X axis\n" +
+ "roborealm - connect to RoboRealm\n" +
+ "rrsnap - snap a frame, send it to RoboRealm, and get SHAPES data from RoboRealm\n" +
+ "pointToVisualTarget - rotate to find and point at a visual target\n" +
+ "seekToVisualTarget - rotate then travel incrementally to a visual target\n" +
+ "test Run tests\n" +
+ "Connect notes: Frankenroomba: connect 192.168.11.2 FR; should have sdr.sh and sdar1.sh running on chumby\n" +
+ "Mobot: with USB serial cable to Serial3, and arduino on USB1, run sdar1.sh on chumby and connect 192.168.11.2:5002 MO"
+ );
+ }
+
+ private void runCommands()
+ {
+ while (true){
+ // get the command line
+ try {
+ curLine = in.readLine();
+ } catch (IOException e) {
+ // Print out the exception that occurred
+ System.out.println("Error reading line: " + e.getMessage());
+ System.exit(0);
+ }
+
+ // split the command line
+ args = curLine.split("\\s+");
+/* System.out.println("You typed: " + curLine);
+ System.out.println("Args found: " + args.length);
+ for (String s: args) {
+ System.out.println(s);
+ }
+*/
+ // parse the command & execute it
+ if (args[0].equalsIgnoreCase("quit")) {
+ robotConnection.disconnect();
+ if (arduinoConnection != null)
+ arduinoConnection.disconnect();
+ System.exit(0);
+ } else if (args[0].equalsIgnoreCase("help")) {
+ usage();
+/*
+ * Connect Commands
+ */
+ } else if (args[0].equalsIgnoreCase("connect")) {
+ connect();
+ } else if (args[0].equalsIgnoreCase("disconnect")) {
+ robotConnection.disconnect();
+ robot = null;
+ if (arduinoConnection != null)
+ arduinoConnection.disconnect();
+ arduino = null;
+/*
+ * Roborama commands
+ */
+ } else if (args[0].equalsIgnoreCase("squareDance")) {
+ squareDance();
+ } else if (args[0].equalsIgnoreCase("squareDance2")) {
+ squareDance2();
+ } else if (args[0].equalsIgnoreCase("squareDance3")) {
+ squareDance3();
+ } else if (args[0].equalsIgnoreCase("tabletrip")) {
+ tabletrip();
+ } else if (args[0].equalsIgnoreCase("robocolumbus")) {
+ robocolumbus();
+/*
+ * Mode Commands
+ */
+ } else if (args[0].equalsIgnoreCase("safe")) {
+ robot.safe();
+ } else if (args[0].equalsIgnoreCase("full")) {
+ robot.full();
+ } else if (args[0].equalsIgnoreCase("speed")) {
+ if (args.length != 2)
+ System.out.println("Error: must specify speed");
+ else {
+ speed = Integer.parseInt(args[1]);
+ }
+/*
+ * Spin Commands
+ */
+ } else if (args[0].equalsIgnoreCase("spin")) {
+ if (args.length != 2)
+ System.out.println("Error: must specify angle to spin");
+ else {
+ // get heading
+// if (protocol.equalsIgnoreCase("FR")) {
+// System.out.print("Heading before turn: ");
+// arduino.printCompass();
+// }
+ angle = Integer.parseInt(args[1]);
+ int rv = spinByCompass(angle);
+ if (rv < 0)
+ System.out.println("Error spinning");
+ // get heading
+ if (protocol.equalsIgnoreCase("FR")) {
+ System.out.printf("\nHeading after turn: ");
+ arduino.printCompass();
+ }
+
+ }
+ } else if (args[0].equalsIgnoreCase("spinto")) {
+ if (args.length != 2)
+ System.out.println("Error: must specify angle to spin");
+ else {
+ // get heading
+ if (protocol.equalsIgnoreCase("FR")) {
+ System.out.print("Heading before turn: ");
+ arduino.printCompass();
+ }
+ angle = Integer.parseInt(args[1]);
+ spinToHeading(angle);
+ // getheading
+ if (protocol.equalsIgnoreCase("FR")) {
+ System.out.print(" Heading after turn: ");
+ arduino.printCompass();
+ }
+
+ }
+ } else if (args[0].equalsIgnoreCase("randomSpin")) {
+ while (true) {
+ angle = (int)(Math.random() * 358.0);
+ System.out.println("spinning to " + angle);
+ spinToHeading(angle);
+ try {
+ if (System.in.available() != 0){
+ robot.stop();
+ break;
+ }
+ } catch (Exception e) {
+ System.out.println("Exception: in System.in.available");
+ System.exit(-1);
+ }
+ }
+/*
+ * Drive Commands
+ */
+ } else if (args[0].equalsIgnoreCase("stop")) {
+ System.out.println("Stopping robot");
+ robot.stop();
+ } else if (args[0].equalsIgnoreCase("drive")) {
+ if (args.length == 2) {
+ distance = Integer.parseInt(args[1]);
+ robot.setSpeed(speed);
+ robot.goStraight(distance * 25); // 25mm per inch, goStraight takes mm
+ } else if (args.length == 3) {
+ distance = Integer.parseInt(args[1]);
+ angle = Integer.parseInt(args[2]);
+ float pausetime = Math.abs((distance * 25) / speed); // mm/(mm/sec) = sec
+ System.out.println("driving speed " + speed + " angle " + angle + " pausetime " + pausetime);
+ robot.drive( speed, angle );
+ robot.pause( (int)(pausetime*1000) );
+ robot.stop();
+ }
+ } else if (args[0].equalsIgnoreCase("drivefor")) {
+ if (args.length > 1) {
+ distance = Integer.parseInt(args[1]);
+ if (args.length > 2)
+ angle = Integer.parseInt(args[2]);
+ else
+ angle = -1; // drive in current direction
+ try {
+ driveByCompass(distance, angle);
+ } catch (Exception e) {
+ robot.stop();
+ System.out.println("Error: driveFor took exception, robot stopped");
+ e.printStackTrace();
+ }
+ }
+ // drive in a rectangle, by compass
+ } else if (args[0].equalsIgnoreCase("rectangle")) {
+ rectangle();
+ } else if (args[0].equalsIgnoreCase("driveToTarget")) {
+ seekToTarget();
+/*
+ * Sensor Commands
+ */
+ } else if (args[0].equalsIgnoreCase("compass")) {
+ arduino.printCompass();
+ } else if (args[0].equalsIgnoreCase("currentHeading")) {
+ if (args.length != 2) {
+ System.out.println("Error - must specify current heading");
+ continue;
+ }
+ int currentMagHeading = Integer.parseInt(args[1]);
+ arduino.setCompassOffset(currentMagHeading);
+ } else if (args[0].equalsIgnoreCase("sensors")) {
+ if (!robot.updateSensors())
+ System.err.println("Error attempting to read valid data from robot.UpdateSensors()");
+ else
+ System.out.println(robot.getSensorsAsString());
+ } else if (args[0].equalsIgnoreCase("initEncoders")) {
+ try {
+ readEncoderDistance(true);
+ } catch (Exception e) {
+ System.err.println("Exception in initEncoders" + e.getMessage());
+ }
+ } else if (args[0].equalsIgnoreCase("encoders")) {
+ double readEncDistance = 0;
+
+ startTime = System.currentTimeMillis();
+ try {
+ readEncDistance = readEncoderDistance(false);
+ } catch (Exception e) {
+ System.err.println("Exception reading robot encoders: " + e.getMessage());
+ }
+ long elapsedTime = System.currentTimeMillis() - startTime;
+ System.out.println("Distance: " + readEncDistance + " in " + elapsedTime + " ms");
+
+/*
+ * Localizer commands
+ */
+ } else if (args[0].equalsIgnoreCase("localizer")) {
+ connectLocalizer();
+ } else if (args[0].equalsIgnoreCase("disconnectLocalizer")) {
+ localizerConnection.disconnect();
+ } else if (args[0].equalsIgnoreCase("localize")) {
+ if (!localize()) {
+ System.out.println("Error reading localizer");
+ continue;
+ }
+ } else if (args[0].equalsIgnoreCase("setTarget")) {
+ if (args.length < 3) {
+ System.out.println("Must provide x & y location of target (in feet)");
+ continue;
+ }
+ saveTarget(args[1], args[2]);
+ } else if (args[0].equalsIgnoreCase("vectorToTarget")) {
+ if (!localize()) {
+ System.out.println("Error reading localizer");
+ continue;
+ }
+ try {
+ vectorToTarget();
+ } catch (Exception e) {
+ System.err.println("Exception computing vector to target - is target initialized?");
+ continue;
+ }
+ } else if (args[0].equalsIgnoreCase("printTarget")) {
+ System.out.println("targetX: " + targetX + " targetY: " + targetY);
+ } else if (args[0].equalsIgnoreCase("localizerXBearing")) {
+ if (args.length != 2) {
+ System.out.println("Error - must specify bearing of +X axis");
+ continue;
+ }
+ try {
+ // Subtract 90 from localizer X axis (measured by hand-compass)
+ localizerCompassOffset = (Integer.parseInt(args[1]) + 270) % 360;
+ arduino.writeConfigInt("localizerCompassOffset", localizerCompassOffset);
+ System.out.println("Wrote localizerCompassOffset to " + localizerCompassOffset);
+ localizerOffsetInitialized = true;
+ } catch (Exception e) {
+ System.err.println("Error saving localizerCompassOffset\n" + e.getMessage());
+ }
+/*
+ * RoboRealm commands
+ */
+ } else if (args[0].equalsIgnoreCase("roborealm")) {
+ connectRoborealm();
+ } else if (args[0].equalsIgnoreCase("rrsnap")) {
+ int rv;
+ try {
+ rv = rrsnap();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ continue;
+ }
+ System.out.print("Found shape confidence " + rrConfidence + " at X:" + rrShapeX + " ("
+ + rrRelativeHeading + " degrees), size " + rrSize);
+ } else if (args[0].equalsIgnoreCase("seekToVisualTarget")) {
+ seekToVisualTarget(0);
+ } else if (args[0].equalsIgnoreCase("pointToVisualTarget")) {
+ pointToVisualTarget();
+/*
+ * Uncategorized commands
+ */
+ } else if (args[0].equalsIgnoreCase("test")) {
+ testHeadingChange();
+ } else {
+ System.out.println("Invalid command");
+ }
+ }
+
+ }
+ /**
+ * Connect to robot using args
+ */
+ private void connect() {
+ if (args.length > 1) {
+ portName = args[1];
+ }
+ if (args.length > 2) {
+ protocol = args[2];
+ }
+ if ((protocol == null) || (portName == null)) {
+ System.out.println("port name and protocol must be specified\n");
+ return;
+ }
+
+ try {
+ if (!connect(portName)) {
+ System.out.println("Error connecting to service");
+ return;
+ }
+ if (protocol.equalsIgnoreCase("OI") || protocol.equalsIgnoreCase("SCI") ||
+ protocol.equalsIgnoreCase("FR")) {
+ robotType = new RobotType(RobotType.robotTypes.roomba);
+ initRoomba();
+ } else if (protocol.equalsIgnoreCase("FR")) { // FrankenRoomba
+ robotType = new RobotType(RobotType.robotTypes.frankenRoomba);
+ arduino.initArduinoBot(robotType);
+ } else if (protocol.equalsIgnoreCase("AR")) { // Arduino
+ robotType = new RobotType(RobotType.robotTypes.tankbot);
+ arduino.initArduinoBot(robotType);
+ } else if (protocol.equalsIgnoreCase("MO")) {
+ robotType = new RobotType(RobotType.robotTypes.mobot);
+ arduino.initArduinoBot(robotType);
+ } else {
+ System.out.println("Invalid protocol");
+ return;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ /**
+ * Connect to a TCP or serial service, which can have roomba
+ * behind it.
+ * @param portName A string containing a serial port name (e.g. /dev/ttyUSB0, or COM12:) or an IP address
+ * with optional port number after it (e.g. 192.168.1.33:5001).
+ */
+ public boolean connect(String portName)
+ {
+ int preferredPortNum = 5001;
+
+ robotConnection = new RobotConnection(portName, preferredPortNum);
+ if (!robotConnection.connect()) {
+ System.out.println("Couldn't connect to " + portName);
+ return false;
+ }
+ // init robot, which is used for the standard roombacomm commands
+ if ((protocol.equalsIgnoreCase("AR")) || (protocol.equalsIgnoreCase("MO"))) {
+ robot = new ArduinoBot(robotConnection); // superclass of roombacomm
+ } else {
+ robot = new RoombaCommTCPClient(robotConnection); // superclass of roombacomm
+ }
+ robot.setConnected(true);
+ // if FrankenRoomba, set up a 2nd robot and connection
+ if (protocol.equalsIgnoreCase("FR")) {
+ arduinoConnection = new RobotConnection(portName, 5002);
+ if (!arduinoConnection.connect()) {
+ System.out.println("Couldn't connect to port 5002 on " + portName);
+ return false;
+ }
+ arduino = new ArduinoBot(arduinoConnection); // it's actually a superclass of roombacomm
+ arduino.setConnected(true);
+ System.out.println("Connected to arduino socket");
+ } else {
+ // this is an uggly hack to cover up that lower level methods use 2 robots.
+ // it should be done by creating a new robot type: Frankenroomba, which sends the right commands to
+ // the right place
+ arduino = (ArduinoBot)robot;
+ }
+ return true;
+ }
+
+ /**
+ * Initialize a Roomba
+ */
+ public boolean initRoomba()
+ {
+ System.out.println("Roomba startup");
+ robot.startup();
+ robot.control();
+ robot.pause(1000);
+
+ System.out.println("Checking for Roomba... \n");
+ // roombaCommTCPClient.setDebug(true);
+ //robot.updateSensors(); // do it once, which doesn't seem to return anything
+ //robot.pause(1000);
+ if (robot.updateSensors()) {
+ System.out.println("Roomba found!\n");
+ System.out.println(robot.getSensorsAsString());
+ } else {
+ System.out.println("No Roomba. :( Is it turned on?\n");
+ return false;
+ }
+ // roombacomm.updateSensors();
+ System.out.println("connected (" + robot.connected() + ")\n");
+ if (robot.connected()) {
+ System.out.println("Playing some notes\n");
+ robot.playNote(72, 10); // C
+ robot.pause(200);
+ robot.playNote(79, 10); // G
+ robot.pause(200);
+ robot.playNote(76, 10); // E
+ robot.pause(200);
+ }
+ robot.setSpeed(speed);
+
+ return true;
+ }
+ /**
+ * Connect to robot using args
+ */
+ private boolean connectLocalizer() {
+ String localizerPort = null;
+
+ if (args.length > 1) {
+ localizerPort = args[1];
+ }
+ localizerConnection = new RobotConnection(localizerPort, 5010);
+ if (!localizerConnection.connect()) {
+ System.out.println("Couldn't connect to localizer at: " + portName);
+ return false;
+ }
+ localizerConnection.setReadTimeout(30000);
+ System.out.println("Connected to localizer");
+ return true;
+ }
+
+ /*
+ * Spin an angle
+ * @param angle Angle to spin, positive is clockwise (like a compass)
+ */
+ public int spinByCompass(int angle)
+ {
+ int rv;
+ int targetHeading;
+
+ targetHeading = arduino.getCompass();
+ if (targetHeading < 0)
+ return targetHeading;
+ targetHeading += angle;
+ targetHeading %= 360;
+ rv = spinToHeading(targetHeading);
+ return rv;
+ }
+
+ /*
+ * Spin to a heading using the simple algorithm for roomba, tankbot, and PID for Mobot. Mo'bot has
+ * lots of inertia, so needs derivative to prevent overshoot, and operates in high-load environment
+ * like grass, so needs integral to get it moving.
+ * @param heading Desired direction
+ * @return 0 = success
+ */
+ public int spinToHeading(int heading)
+ {
+ if (robotType.robotType == RobotType.robotTypes.mobot) {
+ return(spinToHeadingPID(heading));
+ } else {
+ return (spinToHeadingSimple(heading));
+ }
+ }
+ /*
+ * Spin to a heading
+ * @param target Compass direction to turn to
+ */
+ public int spinToHeadingSimple(int target)
+ {
+ int currentHeading;
+ int angleToSpin, absAngleToSpin;
+ int spinSpeed;
+
+ spinSpeed = robotType.fastSpinSpeed;
+
+ if ((target > 359) || (target < 0)) {
+ System.out.println("Invalid heading: " + target);
+ return -1;
+ }
+ System.out.print("Heading: ");
+ while (true) {
+ try{
+ currentHeading = arduino.getCompass();
+ System.out.print(currentHeading + " ");
+ angleToSpin = headingChange(target, currentHeading);
+ absAngleToSpin = Math.abs(angleToSpin);
+
+ if (currentHeading < 0) {
+ System.out.println("Read invalid heading: " + currentHeading);
+ robot.stop();
+ return -2;
+ }
+
+ // spin slow when we get close
+ if (absAngleToSpin < 10) {
+ spinSpeed = robotType.slowSpinSpeed;
+ } else {
+ spinSpeed = robotType.fastSpinSpeed;
+ }
+
+ if ((absAngleToSpin < robotType.tolerance) || (System.in.available() > 0)) {
+ robot.stop();
+ System.out.println();
+ robot.pause(500); // pause to let motion stop, then recheck heading
+ currentHeading = arduino.getCompass();
+ angleToSpin = headingChange(target, currentHeading);
+ absAngleToSpin = Math.abs(angleToSpin);
+ if (absAngleToSpin < robotType.tolerance) {
+ return 0;
+ }
+ }
+ if (angleToSpin > 0)
+ robot.spinRightAt(spinSpeed);
+ else
+ robot.spinLeftAt(spinSpeed);
+
+ if (System.in.available() != 0){
+ robot.stop();
+ return -4;
+ }
+ Thread.sleep(100); // spin for 100ms
+ } catch (Exception e) {
+ e.printStackTrace();
+ return -3;
+ }
+ }
+ }
+
+ /*
+ * Spin to a heading with PID control
+ * @param heading Desired direction at end of spin
+ * @return 0 = success, negative for error
+ */
+ public int spinToHeadingPID(int target)
+ {
+ int currentHeading;
+ int headingError, absHeadingError;
+ int spinSpeed;
+ double radiusPidOutput = 0;
+
+ if ((target > 359) || (target < 0)) {
+ System.out.println("Invalid heading: " + target);
+ return -1;
+ }
+
+ System.out.println("Spinning to heading " + target + " using PID");
+ // initialize the direction control system
+ Pid radiusPid = new Pid(robotType.KP, robotType.KI, robotType.KD);
+
+ // spin checking compass every 100ms
+ while (true) {
+ try{
+ startTime = System.currentTimeMillis();
+
+ currentHeading = arduino.getCompass();
+ System.out.print(currentHeading + " ");
+ headingError = headingChange(target, currentHeading);
+ absHeadingError = Math.abs(headingError);
+
+ if (currentHeading < 0) {
+ System.out.println("Read invalid heading: " + currentHeading);
+ robot.stop();
+ return -2;
+ }
+
+ // stop and re-check if angle to spin < tolerance or user interrupt
+ if ((absHeadingError < robotType.tolerance) || (System.in.available() > 0)) {
+ robot.stop();
+ robot.pause(500); // pause to let motion stop, then recheck heading
+ currentHeading = arduino.getCompass();
+ System.out.println(currentHeading);
+ headingError = headingChange(target, currentHeading);
+ absHeadingError = Math.abs(headingError);
+ if (absHeadingError < robotType.tolerance) {
+ return 0;
+ }
+ }
+
+ // compute the PID output given current headingError
+ radiusPidOutput = radiusPid.computePid(0, headingError);
+ spinSpeed = (int)radiusPidOutput;
+ if (spinSpeed > 50) spinSpeed = 50; // cap spinSpeed
+ if (spinSpeed < -50) spinSpeed = -50; // cap spinSpeed
+ System.out.print(" rPidOut " + radiusPidOutput );
+
+ // start spinning
+ robot.spin(spinSpeed);
+
+ // check for user stop input
+ if (System.in.available() != 0){
+ robot.stop();
+ return -4;
+ }
+ elapsedTime = System.currentTimeMillis() - startTime;
+ System.out.println(" in " + elapsedTime + "ms");
+
+ // spin for 100ms
+ // Thread.sleep(100);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return -3;
+ }
+ }
+ }
+
+ public int driveByCompass(double distance) throws Exception
+ {
+ int rv;
+ int direction;
+
+ direction = arduino.getCompass();
+ if (direction < 0) {
+ System.out.println("Read invalid heading: " + direction);
+ robot.stop();
+ return -3;
+ }
+ rv = driveByCompass(distance, direction);
+ return rv;
+ }
+
+ public int driveByCompass(double distance, int direction) throws Exception
+ {
+ // distance control variables
+ double distanceTravelled = 0;
+ double startEncDistance = 0;
+ double readEncDistance = 0;
+ double distanceToGo = 0;
+ int heading = 0;
+ int headingError = 0;
+ // speed control variables: speedRamp is a table of {speed, brakingDistance}
+ final int [][] speedRamp = {{20,2},{60,2},{100,2},{140,3},{180,4},{220,9},{260,12},{300,15},{340,19},{380,23},{420,27},{460,33},{500,38}};
+ final int maxRamp = 12; // number of steps in speed ramp
+ int rampIndex = 0;
+ boolean rampUp = true; // start by ramping up
+ int currentSpeed;
+ // direction control variables
+ int [] radiusTable;
+ int radiusTableMiddle; // define the midpoint around which PID swings us
+ int radiusTableIndx, radius;
+ double radiusPidOutput = 0;
+
+ readEncoderDistance(true); // initialize the encoder reader
+ while (direction < 0) {
+ System.out.println("getting direction to head in");
+ direction = arduino.getCompass();
+ if (direction < 0) {
+ System.err.println("Error reading compass");
+ }
+ }
+ System.out.println("Driving " + distance + " inches on heading " + direction);
+ // initialize the distance control system
+ if (distance <= 0) return -1;
+ distanceToGo = distance;
+ readEncDistance = startEncDistance = readEncoderDistance(false);
+ if (startEncDistance < 0) {
+ System.out.println("FIXME Error reading encoders in driveByCompass()");
+ readEncDistance = startEncDistance = readEncoderDistance(false); // read it a 2nd time
+ if (startEncDistance < 0) {
+ System.out.println("Second error reading encoders in driveByCompass()");
+ return -2;
+ }
+ }
+ currentSpeed = speedRamp[rampIndex][0];
+ //System.out.println("Encoders (L, R): " + lEncoder +" " + rEncoder);
+
+ // initialize the direction control system
+ radiusTable = initRadiusTable();
+ radiusTableMiddle = (radiusTable.length/2); // table is always an odd # of elements, point to middle of table
+ radiusTableIndx = radiusTableMiddle; // initial radius is middle of table (straight)
+ radius = radiusTable[radiusTableIndx];
+ Pid radiusPid = new Pid(robotType.KP, robotType.KI, robotType.KD);
+ System.out.printf("Distance: %4.2f to go: %4.2f Speed: %d, ReadEncDist: %4.2f (%d %d) radiusPidOutput: %4.2f Radius %d\n",
+ distanceTravelled, distanceToGo, currentSpeed, readEncDistance, lEncoder, rEncoder, radiusPidOutput, radius);
+
+ // turn to point in the right direction
+ spinToHeading(direction);
+ robot.pause(500);
+
+ // drive checking sensors every 100ms
+ while (true) {
+ robot.drive(currentSpeed, radius);
+ Thread.sleep(100); // drive for 100ms
+
+ // get encoders & compute distance remaining
+ readEncDistance = readEncoderDistance(false);
+ if (readEncDistance < 0) {
+ System.err.println("FIXME Error reading encoders in driveByCompass()");
+ readEncDistance = readEncoderDistance(false); // read it a 2nd time
+ if (startEncDistance < 0) {
+ System.out.println("Second error reading encoders in driveByCompass()");
+ continue;
+ }
+ }
+ distanceTravelled = readEncDistance - startEncDistance;
+ distanceToGo = distance - distanceTravelled;
+ heading = arduino.getCompass();
+ if (heading < 0) {
+ System.err.println("Error reading compass");
+ continue;
+ }
+ //System.out.println("Encoders (L, R, distance): " + lEncoder +" " + rEncoder + " " + currentDistance);
+
+ // ramp speed up then down
+ if (distanceToGo < speedRamp[rampIndex][1]) { // if closer than braking distance, decelerate to Vmin
+ rampUp = false;
+ if (rampIndex > 0)
+ rampIndex--;
+ } else if (distanceToGo > speedRamp[rampIndex][1]) { // if further away than braking distance, accelerate to Vmax
+ if ((rampIndex < maxRamp) && (rampUp == true))
+ rampIndex++;
+ }
+ currentSpeed = speedRamp[rampIndex][0];
+
+ heading = arduino.getCompass();
+ headingError = headingChange(direction, heading);
+ radiusPidOutput = radiusPid.computePid(0, headingError);
+ radiusTableIndx = (int)radiusPidOutput + radiusTableMiddle; // offset PID output into table
+ if (radiusTableIndx < 0) radiusTableIndx = 0;
+ else if (radiusTableIndx >= radiusTable.length) radiusTableIndx = radiusTable.length -1;
+ radius = radiusTable[radiusTableIndx];
+
+ //System.out.printf("Distance: %4.2f to go: %4.2f Heading: %d Speed: %d, ReadEncDist: %4.2f (%d %d) radiusPidOutput: %4.2f Radius %d\n",
+ // distanceTravelled, distanceToGo, heading, currentSpeed, readEncDistance, lEncoder, rEncoder, radiusPidOutput, radius);
+
+ // stop if we've arrived
+ if ((distance < distanceTravelled) || (System.in.available() > 0) || (readEncDistance < 0)) {
+ robot.stop();
+ System.out.println("Finished trip, travelled: " + distanceTravelled + " in, readEncDistance: " + readEncDistance);
+ robot.pause(500); // pause to let motion stop, then recheck distance
+ break;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Drive to a target that has been previously set using the settarget command.
+ * Use the localizer to decide how far & which direction to drive
+ */
+ private void seekToTarget() {
+ int currentHeading;
+ while ((currentHeading = arduino.getCompass()) < 0)
+ ;
+ seekToTarget(currentHeading, 0);
+
+ }
+ private void seekToTarget(int startHeading, long timeLimit) {
+ // measure/compute initial distance & direction to target
+ try {
+ while (!localize())
+ robot.goForward(12 * 25); // go forward a foot
+ vectorToTarget(); // compute the distance and course to target
+ } catch (Exception e) {
+ System.err.println("Exception getting vector to target\n" + e.getMessage());
+ return;
+ }
+
+ // seek to target while further away than tolerance, limited by timeLimit if non-zero
+ while ((distanceToTarget > 2.0) && ((timeLimit == 0) || (timeLimit > System.currentTimeMillis()))) {
+ int rv = spinToHeading(courseToTarget);
+ if (rv < 0) {
+ System.out.println("Error spinning to target: " + rv);
+ return;
+ }
+ try {
+ rv = driveByCompass(distanceToTarget, courseToTarget);
+ if (rv < 0) {
+ System.out.println("Error driving to target: " + rv);
+ }
+ } catch (Exception e) {
+ System.out.println("Error driving to target: " + e.getMessage());
+ e.printStackTrace();
+ return;
+ }
+ // measure/compute current distance & direction to target
+ try {
+ while (!localize())
+ robot.goForward(12 * 25); // go forward a foot
+ vectorToTarget(); // compute the distance and course to target
+ } catch (Exception e) {
+ System.err.println("Exception getting vector to target\n" + e.getMessage());
+ return;
+ }
+ }
+ spinToHeading(startHeading);
+ if (timeLimit > System.currentTimeMillis()) {
+ System.out.println("**** Timed out while seeking target ****");
+ } else {
+ System.out.println("**** Arrived at target ****");
+ }
+ return;
+ }
+
+ /**
+ * Drive in a rectangle specified by length (1st & 3rd legs) & width (2nd & 4th legs) with optional heading
+ */
+ private void rectangle() {
+ int length, width;
+
+ if (args.length < 3) {
+ System.out.println("Error: must specify length, width");
+ return;
+ }
+ length = Integer.parseInt(args[1]);
+ width = Integer.parseInt(args[2]);
+ if (args.length == 4)
+ angle = Integer.parseInt(args[3]);
+ else
+ angle = arduino.getCompass(); // if initial heading not specified, use current heading
+ if (angle < 0) {
+ System.out.println("initial heading must be > 0, was: " + angle);
+ return;
+ }
+ System.out.println("Driving rectangle length: " + length + " width: " + width + "initial heading: " + angle);
+ try {
+ for (int i=0;i<2;i++) {
+ driveByCompass(length, angle);
+ angle += 90;
+ angle %= 360;
+ driveByCompass(width, angle);
+ angle += 90;
+ angle %= 360;
+ }
+ spinToHeading(angle);
+ } catch (Exception e) {
+ System.out.println("Exception driving");
+ e.printStackTrace();
+ }
+ return;
+ }
+
+ /**
+ * Drive in a square specified by length with optional heading
+ */
+ private void squareDance() {
+ int length;
+ int startHeading;
+
+ if (args.length < 2) {
+ System.out.println("Error: must specify length of side");
+ return;
+ }
+ length = Integer.parseInt(args[1]);
+ startHeading = arduino.getCompass(); // record what direction to turn to at finish
+
+ if (args.length == 3)
+ startHeading = angle = Integer.parseInt(args[2]);
+ else
+ angle = startHeading; // if initial heading not specified, use current heading
+ if (angle < 0) {
+ System.out.println("initial heading must be > 0, was: " + angle);
+ return;
+ }
+ System.out.println("Driving square dance length: " + length + "initial heading: " + angle);
+ startTime = System.currentTimeMillis();
+ long timeLimit = System.currentTimeMillis() + (2500 * 60); // limit square dance to 2.5 minutes to avoid running overtime
+
+ try {
+ // drive 4 sides
+ for (int i=0;i<4;i++) {
+ driveByCompass(length, angle);
+ angle += 90;
+ angle %= 360;
+ }
+
+ seekToTarget(startHeading, timeLimit);
+
+ } catch (Exception e) {
+ System.out.println("Exception driving");
+ e.printStackTrace();
+ }
+ spinToHeading(startHeading);
+ return;
+ }
+
+ /*
+ * Drive squaredance just based on time, without seek to target at end.
+ * Don't use compass to control direction, no timeLimit
+ */
+ private void squareDance2() {
+ int length;
+ int startHeading;
+
+ if (args.length < 2) {
+ System.out.println("Error: must specify length of side");
+ return;
+ }
+ length = Integer.parseInt(args[1]);
+ startHeading = arduino.getCompass(); // record what direction to turn to at finish
+
+ if (args.length == 3)
+ startHeading = angle = Integer.parseInt(args[2]);
+ else
+ angle = startHeading; // if initial heading not specified, use current heading
+ if (angle < 0) {
+ System.out.println("initial heading must be > 0, was: " + angle);
+ return;
+ }
+ System.out.println("Driving square dance length: " + length + "initial heading: " + angle);
+ startTime = System.currentTimeMillis();
+
+ try {
+ // drive 4 sides
+ robot.speed =200;
+ for (int i=0;i<4;i++) {
+ robot.goStraight(length * 25);
+ angle += 90;
+ angle %= 360;
+ spinToHeading(angle);
+ }
+ } catch (Exception e) {
+ System.out.println("Exception driving");
+ e.printStackTrace();
+ }
+ spinToHeading(startHeading);
+ return;
+
+ }
+ /*
+ * Drive squaredance just based on time, without seek to target at end.
+ * Don't use compass to control direction, no timeLimit
+ */
+ private void squareDance3() {
+ int length;
+ int startHeading;
+
+ if (args.length < 2) {
+ System.out.println("Error: must specify length of side");
+ return;
+ }
+ length = Integer.parseInt(args[1]);
+ startHeading = arduino.getCompass(); // record what direction to turn to at finish
+
+ if (args.length == 3)
+ startHeading = angle = Integer.parseInt(args[2]);
+ else
+ angle = startHeading; // if initial heading not specified, use current heading
+ if (angle < 0) {
+ System.out.println("initial heading must be > 0, was: " + angle);
+ return;
+ }
+ System.out.println("Driving square dance length: " + length + "initial heading: " + angle);
+ startTime = System.currentTimeMillis();
+ long timeLimit = System.currentTimeMillis() + (2500 * 60); // limit square dance to 2.5 minutes to avoid running overtime
+
+ try {
+ // drive 4 sides
+ robot.speed =200;
+ for (int i=0;i<4;i++) {
+ robot.goStraight(length * 25);
+ angle = arduino.getCompass();
+ angle += 90;
+ angle %= 360;
+ spinToHeading(angle);
+ }
+
+ } catch (Exception e) {
+ System.out.println("Exception driving");
+ e.printStackTrace();
+ }
+ seekToTarget(startHeading, timeLimit);
+
+
+ spinToHeading(startHeading);
+ return;
+
+ }
+
+ /**
+ * Drive to edge of table, back up, turn around, & do it to the other end, but don't fall off
+ */
+ private void tabletrip()
+ {
+ robot.goForwardAt(100);
+ robot.pause(15000);
+ robot.full();
+ robot.pause(500);
+ robot.goBackward(50);
+ robot.safe();
+ robot.pause(500);
+ System.out.println("safe, spinning");
+ spinByCompass(180);
+ robot.goForwardAt(100);
+ robot.pause(15000);
+ robot.full();
+ robot.pause(500);
+ robot.goBackward(50);
+ robot.safe();
+ System.out.println("safe, spinning");
+ robot.pause(500);
+ spinByCompass(180);
+ }
+
+ /**
+ * RoboColumbus: drive a distance, turn on video & snap pic, find target in image using roborealm, & incrementally seek
+ * to target, re-acquiring image every so often
+ */
+ private void robocolumbus()
+ {
+ int initialDriveDistance = 12;
+ int startHeading;
+ boolean rv;
+
+ // set up parameters
+ if (args.length < 1) {
+ System.out.println("Error: must specify drive distance (feet) before seeking target");
+ return;
+ }
+ //initialDriveDistance = Integer.parseInt(args[1]) * 12;
+ startHeading = arduino.getCompass(); // record what direction to face when searching for target
+
+ angle = 83;
+ //angle = ;
+ initialDriveDistance = 95;
+ if (args.length == 2) {
+ initialDriveDistance = 12 * Integer.parseInt(args[1]);
+ }
+ if (args.length == 3)
+ startHeading = angle = Integer.parseInt(args[2]);
+ else
+ angle = startHeading; // if initial heading not specified, use current heading
+ if (angle < 0) {
+ System.out.println("initial heading must be > 0, was: " + angle);
+ return;
+ }
+
+
+ System.out.println("Driving RoboColumbus length: " + initialDriveDistance + "initial heading: " + angle);
+ startTime = System.currentTimeMillis();
+ long timeLimit = System.currentTimeMillis() + (4500 * 60); // limit square dance to 2.5 minutes to avoid running overtime
+
+ // start driving
+ try {
+ // drive to image search point
+ spinToHeading(angle);
+ robot.speed = 200;
+ driveByCompass(initialDriveDistance*12, angle);
+
+ // point to the start direction for the image search
+ spinToHeading(angle);
+
+ rv = seekToVisualTarget(timeLimit);
+ if (!rv) {
+ System.out.println("FAILED: unable to visually acquire target - stopping");
+ System.exit(-1);
+ }
+ } catch (Exception e) {
+ System.out.println("Exception driving");
+ e.printStackTrace();
+ }
+ return;
+
+ }
+
+ private boolean seekToVisualTarget(long timeLimit)
+ {
+ double incrementalDistance = 12.0;
+ boolean arrived = false;
+ int currentSeekHeading;
+
+ currentSeekHeading = arduino.getCompass(); // starting direction for looking for targe
+ // measure/compute initial distance & direction to target
+
+ while (!arrived) {
+ currentSeekHeading = pointToVisualTarget(); // update current heading to target
+ try {
+ if (System.in.available() != 0){
+ robot.stop();
+ return false;
+ }
+ } catch (Exception e) {
+ System.out.println("Exception: in System.in.available");
+ System.exit(-1);
+ }
+
+ int rv = spinToHeading(currentSeekHeading);
+ if (rv < 0) {
+ System.out.println("Error spinning to target: " + rv);
+ return false;
+ }
+ try {
+ rv = driveByCompass(incrementalDistance, currentSeekHeading);
+ if (rv < 0) {
+ System.out.println("Error driving to target: " + rv);
+ }
+ } catch (Exception e) {
+ System.out.println("Error driving to target: " + e.getMessage());
+ e.printStackTrace();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * Point robot at visual target. On exit, provide compass heading to target, and robot is pointed
+ * approximately at target
+ * @param currentHeading The starting direction we're approximately facing in, and the direction
+ * around which the algorithm searches for the target
+ */
+ public int pointToVisualTarget()
+ {
+ int targetHeading;
+ int currentSeekHeading, startHeading;
+ int currentSeekHeadingIncrement = 5;
+ int seekIteration;
+ final int seekLimit = 24; // how many seeks until we give up
+ boolean gotDataFlag;
+
+ currentSeekHeading = arduino.getCompass();
+ startHeading = currentSeekHeading;
+
+ // seek back and forth until we acquire image with good confidence
+ for (seekIteration=1; seekIteration<seekLimit; seekIteration++) {
+ try {
+ if (System.in.available() != 0){
+ robot.stop();
+ System.out.println("Exception: aborted by user keystroke");
+ System.exit(-1);
+ }
+ } catch (Exception e) {
+ System.out.println("Exception: in System.in.available");
+ System.exit(-1);
+ }
+ try {
+ rrsnap();
+ gotDataFlag = true;
+ } catch (Exception e) {
+ System.out.print(e.getMessage());
+ gotDataFlag = false;
+ }
+ if ((gotDataFlag == false) || (rrConfidence < 50)) {
+ System.out.println("; failed to find target on iteration " + seekIteration + "in direction " + currentSeekHeading);
+ //currentSeekHeadingIncrement = 0 - (seekIteration * currentSeekHeadingIncrement); // next direction to look in
+ if (seekIteration == 12) {
+ currentSeekHeading = startHeading; //Go back to initial direction & search the other way
+ currentSeekHeadingIncrement = 0 - currentSeekHeadingIncrement;
+ }
+ currentSeekHeading += currentSeekHeadingIncrement; // failed to find target
+ currentSeekHeading = normalizeCompassHeading(currentSeekHeading); // fixup wraparound
+ spinToHeading(currentSeekHeading); // point in the next direction to search
+ } else {
+ System.out.println("Found target with robot pointed in direction " + currentSeekHeading);
+ break;
+ }
+ }
+
+ if (seekIteration == seekLimit) {
+ System.out.println("Couldn't find target: abandoning search");
+ return -1;
+ }
+
+ // return the target heading
+ targetHeading = arduino.getCompass() + rrRelativeHeading;
+ targetHeading = normalizeCompassHeading(targetHeading);
+ System.out.println("pointToVisualTarget returning target at " + targetHeading + " robot direction " + arduino.getCompass());
+ return targetHeading;
+ }
+
+ private int normalizeCompassHeading(int heading)
+ {
+ if (heading > 359)
+ return (heading % 360);
+ else if (heading < 0)
+ return (heading + 360);
+ return heading;
+ }
+
+ /*
+ * Ask the localizer for robot location. Results returned in x, y class variables
+ * @return boolean which is false if localization failed, true otherwise
+ */
+ private boolean localize()
+ {
+ String locationString = "";
+
+
+ if (localizerConnection == null) {
+ // if no localizer, prompt for a location
+ System.out.println("Enter location in feet (decimals ok), x and y, in form like x 1.2 2.9<enter>");
+ try {
+ locationString = in.readLine();
+ } catch (IOException e) {
+ // Print out the exception that occurred
+ System.out.println("Error reading line: " + e.getMessage());
+ return false;
+ }
+ } else {
+ // we are connected to localizer, ask it for location
+ localizerConnection.send('L'); // send localize command to localizer
+ try {
+ locationString = localizerConnection.readBotToTerminator(ReadTerminator.NULL);
+ } catch (Exception e) {
+ System.out.println("Error: exception reading localizer");
+ return false;
+ }
+ }
+
+ // split the string apart & look for the Invalid keyword
+ System.out.println("received string: " + locationString);
+ String [] splitLocationStrings = locationString.split("\\s");
+ if (splitLocationStrings[0].compareTo("bogus") == 0) {
+ System.out.println("Location is invalid");
+ return false;
+ }
+
+ // parse the location string into x & y and print
+ if (splitLocationStrings.length < 3) {
+ System.out.println("Error: must have at least 3 elements in location string");
+ return false;
+ }
+ try {
+ x = new Double(splitLocationStrings[1]) * 12;
+ y = new Double(splitLocationStrings[2]) * 12;
+ } catch (Exception e) {
+ System.err.println("Error parsing location strings\n" + e.getMessage());
+ return false;
+ }
+ System.out.println("x: " + x + " y: " + y);
+ return true;
+ }
+ private void saveTarget(String x, String y)
+ {
+ // target is entered in decimal feet, usually from localizer reading
+ targetX = Double.parseDouble(x) * 12;
+ targetY = Double.parseDouble(y) * 12;
+ try {
+ arduino.writeConfigDouble(new String("targetX"), targetX);
+ arduino.writeConfigDouble(new String("targetY"), targetY);
+ targetInitialized = true;
+ } catch (Exception e) {
+ System.err.println("Error writing target location");
+ }
+ }
+ public void vectorToTarget () throws Exception
+ {
+ double deltaX, deltaY;
+ double localizerDirection = 0;
+ int angle2t;
+
+ // if we haven't initialized the target location yet, try to read from a config file
+ // targetInitialized ensures we only try this once
+ try {
+ if (!targetInitialized) { // get the compass offset from file if it exists
+ targetX = arduino.readConfigDouble(new String("targetX"));
+ targetY = arduino.readConfigDouble(new String("targetY"));
+ System.out.println("target set by file read to x: " + targetX + " y: " + targetY);
+ targetInitialized = true;
+ }
+ if (!localizerOffsetInitialized) { // get the localizer Y axis offset from North if it exists
+ localizerCompassOffset = arduino.readConfigInt("localizerCompassOffset");
+ localizerOffsetInitialized = true;
+ System.out.println("localizerCompassOffset set by file to " + localizerCompassOffset);
+ }
+ } catch (Exception e) {
+ System.out.println("No targetX or targetY file or localizer compass offset file could be opened\n" + e.getMessage());
+ throw (e);
+ }
+
+ // compute distance from current location to target
+ deltaX = targetX - x;
+ deltaY = targetY - y;
+ distanceToTarget = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
+ if (deltaX == 0) { // make sure we don't try to divide by zero
+ if (deltaY > 0)
+ localizerDirection = Math.PI/2;
+ else
+ localizerDirection = -Math.PI/2;
+ } else { // deltaX is non-zero, compute arctan to find angle to target
+ localizerDirection = Math.atan(deltaY/deltaX); // in radians, with incrementing positive values going counterclockwise
+ if ((deltaX < 0) && (deltaY <=0)) localizerDirection -= Math.PI;
+ if ((deltaX < 0) && (deltaY > 0)) localizerDirection += Math.PI;
+ }
+ angle2t = (int)Math.toDegrees(localizerDirection); // angle to start in localizer frame of reference, +180 to -180
+ courseToTarget = 90 - angle2t + localizerCompassOffset; // convert to +ve Y axis = 0 degrees, increasing angle clockwise
+ courseToTarget %= 360; // make sure course < -360
+ if (courseToTarget < 0) courseToTarget += 360; // turn -90 into 270
+ System.out.println("** Current location: x: " + x + " y: " + y + " Target location x: " + targetX + " y: " + targetY);
+ System.out.println("** Distance to target: " + distanceToTarget + " heading: " + courseToTarget);
+ System.out.println("localizerCompassOffset: " + localizerCompassOffset);
+ }
+
+ private int [] initRadiusTable()
+ {
+ // speed table goes in steps of 100 from -500 to -2000, with 0x8000 instead of 0
+ // then 2000 to 500
+ int stepSize = 100;
+ int minRadius = 500;
+ int maxRadius = 2000;
+ int numEntries = (((maxRadius-minRadius) * 2)/stepSize) + 3; // +1 for straight, +1 on each side for inclusive entries
+ int [] radiusTable = new int[numEntries];
+ int indx = 0;
+ int tableVal = -500;
+
+ do {
+ radiusTable[indx++] = tableVal;
+ tableVal -= stepSize; // -500, -600 ... -2000
+ } while (tableVal >= -maxRadius);
+ tableVal = 0x8000; // mid-value is 0x8000
+ radiusTable[indx++] = tableVal;
+ tableVal = maxRadius;
+ do {
+ radiusTable[indx++] = tableVal;
+ tableVal -= stepSize; // 2000, 1900, ... 500
+ } while (tableVal >= 500);
+
+ return radiusTable;
+ }
+ /**
+ *
+ * @param init If true, init initializes encoder variables, otherwise regular read encoder distance
+ * @return Average of encoders distance, in inches
+ * @throws Exception
+ */
+ public double readEncoderDistance(boolean init) throws Exception
+ {
+ byte [] encoders = {43, 44};
+ byte [] sensor_bytes;
+ double encoderDistance;
+
+ if ((protocol.equalsIgnoreCase("AR")) || (protocol.equalsIgnoreCase("MO"))) {
+ // FIXME need to handle overflow
+ encoderDistance = arduino.getEncoders();
+ lEncoder = rEncoder = arduino.getEncoder();
+ } else {
+ // WARNING - THIS DOESN'T SEEM TO HANDLE OVERFLOW WELL - WON'T WORK WITH TANKBOT
+ // read encoders from roomba
+ robot.queryList(encoders, 4);
+ if (robot.getSensorData(4) == false) // read failed
+ return -1;
+ sensor_bytes = robot.getSensor_bytes();
+
+ // handle overflow in encoders
+ lastLEncoder = lEncoder; // first time through will be 0, then will be last reading;
+ lEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[0], sensor_bytes[1]);
+ lEncoder += lEncoderOverflow;
+ lastREncoder = rEncoder;
+ rEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[2], sensor_bytes[3]);
+ rEncoder += rEncoderOverflow;
+
+ if (init) {
+ lEncoderOverflow = 0;
+ rEncoderOverflow = 0;
+ lEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[0], sensor_bytes[1]);
+ rEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[2], sensor_bytes[3]);
+ lastLEncoder = lEncoder; // initialize last value to current reading
+ lastREncoder = rEncoder;
+ } else {
+ if ((lEncoder - lastLEncoder) < -1000) {
+ lEncoderOverflow += 1<<16; // add 65536 to overflow adjuster if encoder rolled over
+ lEncoder += 1<<16;
+ }
+ if ((rEncoder - lastREncoder) < -1000) {
+ rEncoderOverflow += 1<<16; // add 65536 to overflow adjuster if encoder rolled over
+ rEncoder += 1<<16;
+ }
+ }
+ // average the encoders & turn into inches
+ }
+
+ encoderDistance = ((lEncoder + rEncoder)/2) /robotType.countsPerInch;
+ //System.out.println("encoderDistance: " + encoderDistance + " lEncoder: " + lEncoder + " rEncoder: " + rEncoder + " lastLEncoder: " + lastLEncoder + " lEncoderOverflow: " + lEncoderOverflow
+ // + " lastREncoder: " + lastREncoder + " rEncoderOverflow: " + rEncoderOverflow);
+ return encoderDistance;
+ }
+
+ /**
+ * Calculate the shortest heading change which will move the bot from current heading to target.
+ * See http://www.dreamincode.net/forums/topic/163469-calculating-the-difference-between-two-angles/
+ * for some concepts.
+ *
+ * @param target Desired compass heading: 0 - 359 degrees
+ * @param heading Current compass heading: 0 - 359 degrees
+ * @return direction and angle to turn. Positive is clockwise (like a compass)
+ */
+ public int headingChange(int target, int heading)
+ {
+ int diff = target - heading;
+ if (diff > 180) diff -= 360;
+ else if (diff < -180) diff += 360;
+ return diff;
+ }
+ private void testHeadingChange()
+ {
+ int heading[] = {35, 45, 315, 45, 0, 180, 45};
+ int target[] = {45, 35, 45, 315, 180, 0, 45};
+ int diff;
+ int testCaseCount = 7;
+ for (int i=0; i<testCaseCount; i++) {
+ diff = headingChange(target[i], heading[i]);
+ System.out.println("Target: " + target[i] + " Heading: " + heading[i] + " Heading Change: " + diff);
+ }
+
+ // first test of vectorToStart: x,y = 0,0, ring then sit on top of x,y
+ int xTest[] = {0, 10, 10, 10, 0, -10, -10, -10, 0};
+ int yTest[] = {10, 10, 0, -10, -10, -10, 0, 10, 0};
+ x = 0; y = 0;
+ saveTarget("0", "0");
+ for (int i=0; i<xTest.length; i++) {
+ x = xTest[i]; y = yTest[i];
+ try {
+ vectorToTarget();
+ } catch (Exception e) {
+ System.err.println("Exception in vectorToTarget");
+ System.exit(0);
+ }
+ System.out.println("x: " + x + " y: " + y + " distanceToTarget: " + distanceToTarget + " courseToTarget: " +
+ courseToTarget);
+
+ }
+ }
+ /**
+ * Initialize connection to Roborealm
+ */
+ private void connectRoborealm() {
+ wv = new watchVideo();
+ wv.setHeight(240);
+ wv.setWidth(320);
+ String s[] = portName.split(":"); // in case portnumber was specified, get the IP by itself
+ wv.setVideoServer(s[0]);
+ wv.setColor(true);
+ wv.setRoborealm(true);
+ fp = wv.initVideo();
+ System.out.println("Connected to video source and RoboRealm");
+ roborealmConnected = true;
+ }
+
+ /**
+ * Snap a webcam image and send it to roborealm for analysis and print the results
+ */
+ public int rrsnap() throws Exception {
+ double conf;
+
+ if (!roborealmConnected)
+ connectRoborealm();
+
+ wv.getShowFrame();
+ Boolean rv = fp.frame2Roborealm();
+ if (rv == false) {
+ System.out.println("error sending image to Roborealm");
+ throw new Exception("Error sending image to Roborealm");
+ }
+ String rrString = fp.getShapeData();
+ if (rrString != null) {
+ String s[] = rrString.split(",");
+ int xL, xR;
+ xL = Integer.parseInt(s[3]);
+ xR = Integer.parseInt(s[4]);
+ rrShapeX = (xL + xR)/2;
+ rrRelativeHeading = (rrShapeX - (wv.getWidth()/2))/16; // distance from image center
+ conf = Double.parseDouble("0." + s[0]);
+ conf *= 100; // convert to %
+ rrConfidence = (int)conf;
+ rrSize = Integer.parseInt(s[2]);
+
+ //System.out.print("Found shape confidence " + s[0] + " at " + rrShapeX);
+ return rrRelativeHeading;
+ } else {
+ //System.out.println("No RobotRealm data returned");
+ throw new Exception("Error: no Roborealm data returned");
+ }
+ }
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommFrame.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommFrame.java
new file mode 100644
index 0000000..7df55ad
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommFrame.java
@@ -0,0 +1,2239 @@
+/*
+ * RoombaCommFrame -
+ *
+ * Based heavily on RoombaCommPanel, but very altered.
+ *
+ * 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 java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.JTextPane;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.DefaultCaret;
+
+/**
+* This code was edited or generated using CloudGarden's Jigloo
+* SWT/Swing GUI Builder, which is free for non-commercial
+* use. If Jigloo is being used commercially (ie, by a corporation,
+* company or business for any purpose whatever) then you
+* should purchase a license for each developer using Jigloo.
+* Please visit www.cloudgarden.com for details.
+* Use of Jigloo implies acceptance of these licensing terms.
+* A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR
+* THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED
+* LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE.
+*/
+/**
+ * A JFrame containing controls for testing RoombaComm.
+ *
+ * It is hoped that this UI will implement ways to
+ * manually test RoombaComm and maybe even support more automated
+ * testing in the future.
+ *
+ * SVN id value is $Id: RoombaCommFrame.java 139 2010-05-26 22:30:41Z black.123 $
+ */
+public class RoombaCommFrame extends JFrame implements ActionListener,
+ ChangeListener {
+
+ /** The led panel. */
+ JPanel ctrlPanel, selectPanel, buttonPanel, displayPanel, ledPanel;
+
+ /** The j panel4. */
+ private JPanel jPanel4;
+
+ /** The led panel shared. */
+ private JPanel ledPanelShared;
+
+ /** The led panel oi only. */
+ private JPanel ledPanelOIOnly;
+
+ /** The j text pane1. */
+ private JTextPane jTextPane1;
+
+ /** The j panel power. */
+ private JPanel jPanelPower;
+
+ /** The j panel vacuum. */
+ private JPanel jPanelVacuum;
+
+ /** The but_play rttl. */
+ private JButton but_playRTTL;
+
+ /** The j panel sensors. */
+ private JPanel jPanelSensors;
+
+ /** The j panel test programs. */
+ private JPanel jPanelTestPrograms;
+
+ /** The port choices. */
+ JComboBox portChoices;
+
+ /** The protocol choices. */
+ JComboBox protocolChoices;
+
+ /** The handshake button. */
+ JCheckBox handshakeButton;
+
+ /** The display text. */
+ JTextArea displayText;
+
+ /** The connect button. */
+ JButton connectButton;
+
+ /** The net button. */
+ JButton netButton;
+
+ /** The power color intensity. */
+ JSlider speedSlider, powerColorSlider, powerColorIntensity;
+
+ /** The debug. */
+ private boolean debug = false;
+
+ /** The tribble on. */
+ boolean tribbleOn = false;
+ // default values for flags for LEDs
+ /** The red on. */
+ boolean redOn = false;
+
+ /** The green on. */
+ boolean greenOn = false;
+
+ /** The toggle spot. */
+ boolean toggleSpot = false;
+
+ /** The toggle clean. */
+ boolean toggleClean = false;
+
+ /** The toggle max. */
+ boolean toggleMax = false;
+
+ /** The toggle dirt. */
+ boolean toggleDirt = false;
+
+ /** The toggle check robot. */
+ boolean toggleCheckRobot = false;
+
+ /** The toggle dock. */
+ boolean toggleDock = false;
+
+ /** The j panel sounds. */
+ private JPanel jPanelSounds;
+
+ /** The j panel modes. */
+ private JPanel jPanelModes;
+
+ /** The j panel3. */
+ private JPanel jPanel3;
+
+ /** The j panel2. */
+ private JPanel jPanel2;
+
+ /** The j label comm. */
+ private JLabel jLabelCOMM;
+
+ /** The j text field port. */
+ private JTextField jTextFieldPort;
+
+ /** The j label port. */
+ private JLabel jLabelPort;
+
+ /** The j panel1. */
+ private JPanel jPanel1;
+
+ /** The j text field host. */
+ private JTextField jTextFieldHost;
+
+ /** The j label host. */
+ private JLabel jLabelHost;
+
+ /** The j panel config serial. */
+ private JPanel jPanelConfigSerial;
+
+ /** The j panel config net. */
+ private JPanel jPanelConfigNet;
+
+ /** The j tabbed panel config. */
+ private JTabbedPane jTabbedPanelConfig;
+
+ /** The j button4. */
+ private JButton jButton4;
+
+ /** The j button3. */
+ private JButton jButton3;
+
+ /** The j button2. */
+ private JButton jButton2;
+
+ /** The j button1. */
+ private JButton jButton1;
+
+ /** The power_color. */
+ int power_color = 0;
+
+ /** The power_int. */
+ int power_int = 0;
+
+ /** The roomba comm serial. */
+ RoombaCommSerial roombaCommSerial;
+
+ /** The roomba comm abstract class to allow the actions to work with serial or net connections. */
+ RoombaComm roombaComm;
+
+ /** The roomba comm tcp client. */
+ RoombaCommTCPClient roombaCommTCPClient;
+
+ /** The formatter. */
+ SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss");
+
+ /** The led panel sci only. */
+ private JPanel ledPanelSCIOnly;
+
+ /** The protocols. */
+ private String[] protocols = new String[] { "Roomba 1xx-4xx (SCI)", "Roomba 5xx (OI)" };
+
+ /**
+ * Instantiates a new roomba comm frame without any params
+ */
+ public RoombaCommFrame() {
+ this(false);
+ }
+
+ /**
+ * Instantiates a new roomba comm frame.
+ *
+ * @param debug boolean will increase STDOUT to show details about the process at runtime.
+ */
+ public RoombaCommFrame(boolean debug) {
+ super();
+ debugPrintln(debug,"RoombaCommFrame-start");
+ debugPrintln(debug," debug is ("+debug+")");
+ initialize();
+ roombaCommSerial = new RoombaCommSerial();
+ roombaCommTCPClient = new RoombaCommTCPClient();
+ this.debug = debug;
+ roombaCommSerial.debug = debug;
+ roombaCommTCPClient.debug = debug;
+ debugPrintln(debug,"RoombaCommFrame-makePanels-start");
+ makePanels();
+ debugPrintln(debug,"RoombaCommFrame-makePanels-end");
+ // Set Look & Feel
+ try {
+ javax.swing.UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ debugPrintln(debug,"RoombaCommFrame-end");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ }
+
+ /**
+ * The main method.
+ *
+ * @param args the arguments are currently ignored
+ */
+ public static void main(String[] args) {
+ // by default (for now) turn on debug output if the class is called directly.
+ RoombaCommFrame me = new RoombaCommFrame(true);
+ me.pack();
+ me.setVisible(true);
+ }
+
+ /**
+ * This method initializes this class to initial default conditions.
+ * In the future this method may also take args to control/style the UI to multiple styles.
+ */
+ private void initialize() {
+ Dimension defaultSize = new Dimension(826,651);
+ this.addComponentListener(new ComponentAdapter() {
+ public void componentResized(java.awt.event.ComponentEvent e) {
+ debugPrintln(debug,"147-componentResized("+e.getComponent().getWidth()+","+e.getComponent().getHeight()+")"); // TODO Auto-generated Event stub componentResized()
+ }
+ public void componentMoved(java.awt.event.ComponentEvent e) {
+ }
+ public void componentShown(ComponentEvent e) {
+ }
+ public void componentHidden(java.awt.event.ComponentEvent e) {
+ }
+ });
+ debugPrintln(debug,"initialize : setting size to (" +defaultSize.getWidth()+","+defaultSize.getHeight()+")");
+ GridBagLayout thisLayout = new GridBagLayout();
+ this.setSize(defaultSize);
+ thisLayout.rowWeights = new double[] {0.0};
+ thisLayout.rowHeights = new int[] {137};
+ thisLayout.columnWeights = new double[] {0.0};
+ thisLayout.columnWidths = new int[] {538};
+ getContentPane().setLayout(thisLayout);
+ this.setPreferredSize(new java.awt.Dimension(defaultSize)); //638
+ this.setFocusTraversalKeysEnabled(false);
+ this.setTitle("RoombaComm");
+ this.addWindowListener(new WindowAdapter() {
+ public void windowClosed(WindowEvent evt) {
+ thisWindowClosed(evt);
+ }
+ });
+ }
+
+ /**
+ * Set to 'false' to hide the "h/w handshake" button, which seems to be only
+ * needed on Windows.
+ *
+ * @param b the new show hardware handhake
+ */
+ public void setShowHardwareHandhake(boolean b) {
+ handshakeButton.setVisible(b);
+ }
+
+ /**
+ * Connect to a Roomba using a serial connection.<br>
+ * This method uses the value of protocol to set the connection to the correct Roomba API version. <br>
+ * This method uses the value of handshakeButton and portChoices to make the serial connection. <br>
+ * This method also "chirps" the Roomba when it connects to provide an audio feedback/confirmation of the connection.
+ *
+ * @return true, if successful
+ */
+ public boolean connect() {
+ debugPrintln(debug,"connect-start");
+ String portname = (String) portChoices.getSelectedItem();
+ roombaCommSerial.setWaitForDSR(handshakeButton.isSelected());
+ int i = protocolChoices.getSelectedIndex();
+ roombaCommSerial.setProtocol((i == 0) ? "SCI" : "OI");
+ if (portname == null){
+ // we do not yet have ports so refresh the list
+ setCommPorts();
+ }
+ updateDisplay("connecting to " + portname + "\n");
+ connectButton.setText("connecting");
+ if (portname != null){
+ if (!roombaCommSerial.connect(portname)) {
+ updateDisplay("Couldn't connect to " + portname + "\n");
+ connectButton.setText(" connect ");
+ debugPrintln(debug,"connect-end (could not connect)");
+ return false;
+ }else{
+ updateDisplay("connected to " + portname + "\n");
+ }
+ }else{
+ updateDisplay("you must first select a COMM port to use before you can attempt to connect");
+ jTabbedPanelConfig.setSelectedIndex(1);
+ return false;
+ }
+ updateDisplay("Roomba startup\n");
+
+ roombaCommSerial.startup();
+ roombaCommSerial.control();
+ roombaCommSerial.playNote(72, 10); // C , test note
+ roombaCommSerial.pause(200);
+
+ connectButton.setText("disconnect");
+ connectButton.setActionCommand("disconnect");
+ roombaComm = roombaCommSerial;
+ updateDisplay("Checking for Roomba... ");
+ if (roombaCommSerial.updateSensors()) {
+ updateDisplay("Roomba found!\n");
+ debugPrintln(debug,"connect-end");
+ return true;
+ }else{
+ updateDisplay("No Roomba. :( Is it turned on?\n");
+ debugPrintln(debug,"connect-end");
+ return true ;
+ }
+ }
+
+ /**
+ * Connect to a Roomba using a network connection.<br>
+ * This method uses the value of protocol to set the connection to the correct Roomba API version. <br>
+ * This method uses the value of host and port to make the network connection. <br>
+ * This method also "chirps" the Roomba when it connects to provide an audio feedback/confirmation of the connection.
+ *
+ * @param portname the portname
+ * @return true, if successful
+ */
+ public boolean connectNet(String portname) {
+ debugPrintln(debug,"connectNet called");
+ int i = protocolChoices.getSelectedIndex();
+ if (roombaCommTCPClient == null){
+ roombaCommTCPClient = new RoombaCommTCPClient();
+ roombaCommTCPClient.setConnected(false);
+ }
+ roombaCommTCPClient.setProtocol((i == 0) ? "SCI" : "OI");
+ if (!roombaCommTCPClient.connect(portname)) {
+ updateDisplay("Couldn't connect to " + portname);
+ return false;
+ }
+ updateDisplay("Roomba startup on port " + portname);
+ roombaCommTCPClient.startup();
+ roombaCommTCPClient.control();
+ // roombacomm.setSensorsAutoUpdate(true);
+ roombaCommTCPClient.pause(30);
+ //TODO: I am not sure this works yet... need to test to confirm. (2010.03.03)
+ //TODO: The commented block below did not work.. so I commented it and hardcoded a "true" for the connect
+ updateDisplay("Checking for Roomba... \n");
+ roombaCommTCPClient.setConnected(true);
+// if (roombaCommTCPClient.updateSensors()) {
+// updateDisplay("Roomba found!\n");
+// updateDisplay(roombaCommTCPClient.getSensorsAsString());
+// } else {
+// updateDisplay("No Roomba. :( Is it turned on?\n");
+// }
+ updateDisplay("buffer is(" + roombaCommTCPClient.getBuffer() + ")",
+ this.debug);
+ updateDisplay("connected (" + roombaCommTCPClient.connected() + ")\n");
+ if (roombaCommTCPClient.connected()) {
+ updateDisplay("Playing some notes\n");
+ roombaCommTCPClient.playNote(72, 10); // C
+ roombaCommTCPClient.pause(200);
+ roombaCommTCPClient.playNote(79, 10); // G
+ roombaCommTCPClient.pause(200);
+ roombaCommTCPClient.playNote(76, 10); // E
+ roombaCommTCPClient.pause(200);
+ netButton.setText("disconnect-net");
+ netButton.setActionCommand("disconnect-net");
+ roombaComm = roombaCommTCPClient; // set the pointer so that the
+ // action stuff can work against
+ // any class
+ }
+ return true;
+ }
+
+ /**
+ * Disconnect from the roomba when it is connected via a serial connection.
+ */
+ public void disconnect() {
+ roombaCommSerial.disconnect();
+ connectButton.setText(" connect ");
+ connectButton.setActionCommand("connect");
+ }
+
+ /**
+ * Disconnect from the roomba when it is connected via a network connection.
+ */
+ public void disconnectNet() {
+ roombaCommTCPClient.disconnect();
+ netButton.setText(" net ");
+ netButton.setActionCommand("net");
+ }
+
+ /**
+ * Play a (MIDI) note, that is, make the Roomba a musical instrument<br>
+ * notenums 32-127:<br> notenum == corresponding note played thru beeper<br>
+ * velocity == duration in number of 1/64s of a second (e.g. 64==1 second)<br>
+ * notenum 24: notenum == main vacuum velocity == non-zero turns on, zero
+ * turns off<br> notenum 25: blink LEDs, velcoity is color of Power<br> notenum 28 & 29: spin left & spin right, velocity is speed
+ *
+ * @param notenum 32-127<br>corresponding note played thru beeper<br>
+ * @param velocity duration in number of 1/64s of a second (e.g. 64==1 second)
+ */
+ public void playMidiNote(int notenum, int velocity) {
+ updateDisplay("play note: " + notenum + "," + velocity + "\n");
+ if (!roombaCommSerial.connected())
+ return;
+
+ if (notenum >= 31) { // G and above
+ if (velocity == 0)
+ return;
+ if (velocity < 4)
+ velocity = 4; // has problems at lower durations
+ else
+ velocity = velocity / 2;
+ roombaCommSerial.playNote(notenum, velocity);
+ } else if (notenum == 24) { // C
+ roombaCommSerial.vacuum(!(velocity == 0));
+ } else if (notenum == 25) { // C#
+ boolean lon = (velocity != 0);
+ int inten = (lon) ? 255 : 128; // either full bright or half bright
+ roombaCommSerial.setLEDs(lon, lon, lon, lon, lon, lon,
+ velocity * 2, inten);
+ } else if (notenum == 28) { // E
+ if (velocity != 0)
+ roombaCommSerial.spinLeftAt(velocity * 2);
+ else
+ roombaCommSerial.stop();
+ } else if (notenum == 29) { // F
+ if (velocity != 0)
+ roombaCommSerial.spinRightAt(velocity * 2);
+ else
+ roombaCommSerial.stop();
+ }
+ }
+
+ /**
+ * implement actionlistener.
+ *
+ * @param event the event
+ */
+ public void actionPerformed(ActionEvent event) {
+ String action = event.getActionCommand();
+ updateDisplay(formatter.format(new Date()) + ": action (" + action
+ + ") happened\n", this.debug);
+ if ("comboBoxChanged".equals(action)) {
+ int i = protocolChoices.getSelectedIndex();
+ if (roombaComm != null) {
+ roombaComm.setProtocol((i == 0) ? "SCI" : "OI");
+ } else {
+ updateDisplay(
+ formatter.format(new Date())
+ + ": null roombaComm object found in actionPerformed\n",
+ this.debug);
+ }
+ return;
+ }
+ if ("net".equals(action)) {
+ if (jTextFieldHost != null && jTextFieldHost.getText() != null && jTextFieldPort != null && jTextFieldPort.getText() != null){
+ if(connectNet(jTextFieldHost.getText()+":"+jTextFieldPort.getText())){
+ // TODO: find a way to hide/disable the other connect tab until the session is disconnected
+// getJTabbedPanelConfig().getComponentAt(1).setVisible(false); // hide the Serial tab while we are connected with a Net port
+ }else{
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(0).setVisible(true); // make sure the net tab is not hidden when we fail to connect
+ }
+ }else{
+ updateDisplay("connect (via net) pressed with missing values:");
+ if (jTextFieldHost != null && jTextFieldHost.getText() != null){
+ updateDisplay(" host :"+jTextFieldHost.getText());
+ }else{
+ updateDisplay(" host : MISSING VALUE");
+ }
+ if (jTextFieldPort != null && jTextFieldPort.getText() != null){
+ updateDisplay(" port :"+jTextFieldPort.getText());
+ }else{
+ updateDisplay(" port : MISSING VALUE");
+ }
+ }
+ return;
+ } else if ("disconnect-net".equals(action)) {
+ disconnectNet();
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(1).setVisible(true); // show the serial tab while we disconnect from the net
+ return;
+ }
+ if ("connect".equals(action)) {
+ if (connect()){
+ // TODO: find a way to hide/disable the other connect tab until the session is disconnected
+ }else{
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // make sure the net tab is not hidden when we fail to connect
+// getJTabbedPanelConfig().repaint(100);
+ }
+ return;
+ } else if ("disconnect".equals(action)) {
+ disconnect();
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // show the net tab while we disconnect from the serial
+ return;
+ }
+ // stop right here if we're not connected
+ if (roombaComm == null || !roombaComm.connected()) {
+ updateDisplay("not connected!\n");
+ return;
+ }
+
+ if ("stop".equals(action)) {
+ roombaComm.stop();
+ } else if ("forward".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.goForward();
+ } else if ("backward".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.goBackward();
+ } else if ("spinleft".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.spinLeft();
+ } else if ("spinright".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.spinRight();
+ } else if ("turnleft".equals(action)) {
+ roombaComm.turnLeft();
+ } else if ("turnright".equals(action)) {
+ roombaComm.turnRight();
+ } else if ("max".equals(action)) {
+ roombaComm.max();
+ } else if ("dock".equals(action)) {
+ roombaComm.dock();
+ } else if ("test".equals(action)) {
+ LogoA.square(roombaComm, 300);
+ } else if ("OSU".equals(action)) {
+ updateDisplay("Going to play OSU\n");
+ roombaComm.stop();
+ roombaComm.pause(500);
+ playSong(
+ roombaComm,
+ "OSU:d=4,o=5,b=125:a,g,a#,a,8g#,a,8g#,2a,8p,8f,8g,8g#,a,8g#,a,8g#,a,g,f,a,g,8a,g,d,8f,8p,8f,8p,8f,8p,8f,8p,c6,a,g,f,8a#,a,8g,f,p,c6,a,g,a,8a#,a,8a#,c6,p,2d6,d,8c6,a#,g,f,8f,8f,8g,a#,8g,a#,a,2a#");
+ // playSong(roombacomm,"Baa Baa Black Sheep:d=4,o=5,b=125:c,c,g,g,8a,8b,8c6,8a,g,p,f,f,e,e,d,d,c");
+ roombaComm.stop();
+ } else if ("Play RTTL".equals(action)){
+ updateDisplay("Going to prompt for rttl string...\n");
+ String rttl ="";
+ updateDisplay("Going to play rttl string ("+rttl+")...\n");
+ } else if ("Tribble On".equals(action)) {
+ tribbleOn = true;
+ tribbleStart(roombaComm, displayText, tribbleOn);
+ } else if ("reset".equals(action)) {
+ roombaComm.stop();
+ roombaComm.startup();
+ roombaComm.control();
+ } else if ("passive".equals(action)) {
+ //passive / start command
+ roombaComm.start();
+ } else if ("safe".equals(action)) {
+ roombaComm.safe();
+ } else if ("full".equals(action)) {
+ roombaComm.full();
+ } else if ("power-off".equals(action)) {
+ roombaComm.powerOff();
+ } else if ("power-on".equals(action)) {
+ roombaComm.powerOn();
+ } else if ("wakeup".equals(action)) {
+ roombaComm.wakeup();
+ } else if ("beep-lo".equals(action)) {
+ roombaComm.playNote(50, 32); // C1
+ roombaComm.pause(200);
+ } else if ("beep-hi".equals(action)) {
+ roombaComm.playNote(90, 32); // C7
+ roombaComm.pause(200);
+ } else if ("clean".equals(action)) {
+ roombaComm.clean();
+ } else if ("spot".equals(action)) {
+ roombaComm.spot();
+ } else if ("vacuum-on".equals(action)) {
+ roombaComm.vacuum(true);
+ } else if ("vacuum-off".equals(action)) {
+ roombaComm.vacuum(false);
+ } else if ("blink-leds".equals(action)) {
+ roombaComm.setLEDs(true, true, true, true, true, true, 255, 255);
+ roombaComm.pause(300);
+ roombaComm.setLEDs(false, false, false, false, false, false, 0, 128);
+ } else if ("sensors".equals(action)) {
+ if (roombaComm.updateSensors())
+ updateDisplay(roombaComm.sensorsAsString() + "\n");
+ else
+ updateDisplay("couldn't read Roomba. Is it connected?\n");
+ } else if ("chargedata".equals(action)) {
+ if (roombaComm.updateSensors())
+ updateDisplay("*****\n" + roombaComm.chargeDataAsString()+ "\n");
+ else
+ updateDisplay("couldn't read Roomba. Is it connected?\n");
+ } else if ("toggleGreen".equals(action)) {
+ setChgGreenLED(roombaComm, !greenOn);
+ } else if ("toggleRed".equals(action)) {
+ setChgRedLED(roombaComm, !redOn);
+ } else if ("toggleSpot".equals(action)) {
+ setChgSpotLED(roombaComm, !toggleSpot);
+ } else if ("toggleClean".equals(action)) {
+ setChgCleanLED(roombaComm, !toggleClean);
+ } else if ("toggleMax".equals(action)) {
+ setChgMaxLED(roombaComm, !toggleMax);
+ } else if ("toggleDirt".equals(action)) {
+ setChgDirtLED(roombaComm, !toggleDirt);
+ } else if ("toggleCheckRobot".equals(action)) {
+ setToggleCheckRobot(roombaComm, !toggleCheckRobot);
+ } else if ("toggleDock".equals(action)) {
+ setToggleDock(roombaComm, !toggleDock);
+ }
+ }
+
+ /**
+ * implement ChangeListener, for the slider.
+ *
+ * @param e the e
+ */
+ public void stateChanged(ChangeEvent e) {
+ // System.err.println("stateChanged:"+e);
+ JSlider src = (JSlider) e.getSource();
+ if (!src.getValueIsAdjusting()) {
+ int speed = (int) src.getValue();
+ speed = (speed < 1) ? 1 : speed; // don't allow zero speed
+ if (roombaComm != null){
+ updateDisplay("setting speed = " + speed + "\n");
+ roombaComm.setSpeed(speed);
+ }
+ }
+ }
+
+ /**
+ * TODO: comment needed for makePanels.
+ */
+ void makePanels() {
+ debugPrintln(debug,"makePanels-start");
+ getContentPane().add(getJPanel4xx(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2));
+ makeDisplayPanel();
+ updateDisplay("RoombaComm, version " + RoombaComm.VERSION + "\n");
+ debugPrintln(debug,"makePanels-finish");
+ }
+
+ /**
+ * TODO: need comment for makeDisplayPanel.
+ */
+ void makeDisplayPanel() {
+ JTextArea displayText2 = new JTextArea(25, 30);
+ displayText2.setLineWrap(true);
+ DefaultCaret dc = new DefaultCaret();
+ dc.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+ }
+
+ /**
+ * Update display.
+ *
+ * @param s the s
+ */
+ protected void updateDisplay(String s) {
+ debugPrintln(debug,"updateDisplay(string): start");
+ displayText.append(s);
+ if (s != null && !(s.endsWith("\n"))) {
+ displayText.append("\n");
+ }
+ debugPrintln(debug,"updateDisplay(string): reposition to "+displayText.getDocument().getLength());
+ displayText.getParent().validate();
+ displayText.getParent().repaint(100);
+ displayText.setText(displayText.getText() );
+ debugPrintln(debug,"updateDisplay(string): end");
+ }
+
+ /**
+ * Update display.
+ *
+ * @param s the s
+ * @param onlyDebug the only debug
+ */
+ protected void updateDisplay(String s, boolean onlyDebug) {
+ if (onlyDebug && (roombaCommSerial.debug || roombaCommTCPClient.debug)) {
+ updateDisplay(s);
+ debugPrintln(true,s);
+ }
+ }
+
+ /**
+ * Update display.
+ *
+ * @param roombacomm the roombacomm
+ * @param jtextarea the jtextarea
+ * @param s the s
+ * @param onlyDebug the only debug
+ */
+ protected static void updateDisplay(RoombaComm roombacomm,
+ JTextArea jtextarea, String s, boolean onlyDebug) {
+ if (onlyDebug && roombacomm.debug) {
+ jtextarea.append(s);
+ jtextarea.setCaretPosition(jtextarea.getDocument().getLength());
+ jtextarea.getParent().validate();
+ jtextarea.getParent().repaint(100);
+ System.out.println(s);
+ }
+ }
+
+ /**
+ * Returns an ImageIcon, or null if the path was invalid.
+ *
+ * @param path the path
+ * @param description the description
+ * @return the image icon
+ */
+ protected static ImageIcon createImageIcon(String path, String description) {
+ java.net.URL imgURL = RoombaCommFrame.class.getResource(path);
+ if (imgURL != null) {
+ return new ImageIcon(imgURL, description);
+ } else {
+ System.err.println("Couldn't find file: " + path);
+ return null;
+ }
+ }
+
+ // uh... we should try to avoid @SuppressWarnings ...
+ /**
+ * Play song.
+ *
+ * @param roombacomm the roombacomm
+ * @param rtttl the rtttl
+ */
+ @SuppressWarnings("unchecked")
+ protected 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()", this.debug);
+ 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()\n",
+ this.debug);
+ 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);
+ }
+ }
+ }
+
+ /**
+ * Tribble start.
+ *
+ * @param roombacomm the roombacomm
+ * @param jtextarea the jtextarea
+ * @param tribbleOn the tribble on
+ */
+ protected static void tribbleStart(RoombaComm roombacomm,
+ JTextArea jtextarea, boolean tribbleOn) {
+ createTribblePurrSong(roombacomm);
+
+ updateDisplay(roombacomm, jtextarea, "Press return to exit.", roombacomm.debug);
+
+ while (tribbleOn) {
+ purr(roombacomm, jtextarea);
+ if (Math.random() < 0.1)
+ bark(roombacomm, jtextarea);
+ roombacomm.pause(1500 + (int) (Math.random() * 500));
+ // tribbleOn = keyIsPressed();
+ roombacomm.updateSensors();
+ boolean b = roombacomm.maxButton();
+ updateDisplay(roombacomm, jtextarea, "max button is (" + b + ")",roombacomm.debug);
+ tribbleOn = (!b);
+ }
+ }
+
+ /**
+ * Purr.
+ *
+ * @param roombacomm the roombacomm
+ * @param jtextarea the jtextarea
+ */
+ protected static void purr(RoombaComm roombacomm, JTextArea jtextarea) {
+ updateDisplay(roombacomm, jtextarea, "purr", roombacomm.debug);
+ roombacomm.playSong(5);
+ for (int i = 0; i < 5; i++) {
+ roombacomm.spinLeftAt(75);
+ roombacomm.pause(100);
+ roombacomm.spinRightAt(75);
+ roombacomm.pause(100);
+ roombacomm.stop();
+ }
+ }
+
+ /**
+ * Creates the tribble purr song.
+ *
+ * @param roombacomm the roombacomm
+ */
+ protected static void createTribblePurrSong(RoombaComm roombacomm) {
+ int song[] = { 68, 4, 67, 4, 66, 4, 65, 4, 64, 4, 63, 4, 62, 4, 61, 4,
+ 60, 4, 59, 4, 60, 4, 61, 4, };
+ roombacomm.createSong(5, song);
+ }
+
+ /**
+ * Bark.
+ *
+ * @param roombacomm the roombacomm
+ * @param jtextarea the jtextarea
+ */
+ protected static void bark(RoombaComm roombacomm, JTextArea jtextarea) {
+ updateDisplay(roombacomm, jtextarea, "bark", roombacomm.debug);
+ roombacomm.vacuum(true);
+ roombacomm.playNote(50, 5);
+ roombacomm.pause(150);
+ roombacomm.vacuum(false);
+ }
+
+ /**
+ * Gets the power_color.
+ *
+ * @return the power_color
+ */
+ protected int getPower_color() {
+ return power_color;
+ }
+
+ /**
+ * Sets the power_color.
+ *
+ * @param power_color the new power_color
+ */
+ protected void setPower_color(int power_color) {
+ if (power_color >= 0 && power_color <= 255) {
+ this.power_color = power_color;
+ } else {
+ this.power_color = 0;
+ updateDisplay("invalid power color attempted (" + power_color + ")");
+ }
+ }
+
+ /**
+ * Gets the power_int.
+ *
+ * @return the power_int
+ */
+ protected int getPower_int() {
+ return power_int;
+ }
+
+ /**
+ * Sets the power_int.
+ *
+ * @param power_int the new power_int
+ */
+ protected void setPower_int(int power_int) {
+
+ if (power_color >= 0 && power_color <= 255) {
+ this.power_int = power_int;
+ } else {
+ this.power_int = 0;
+ updateDisplay("invalid power intensity attempted (" + power_color
+ + ")");
+ }
+ }
+
+ /**
+ * Sets the lE ds.
+ *
+ * @param roombacomm the new lE ds
+ */
+ protected void setLEDs(RoombaComm roombacomm) {
+ if (!roombacomm.connected())
+ return;
+ if (roombacomm.getProtocol().equalsIgnoreCase("SCI")) {
+ roombacomm.setLEDs(this.greenOn, this.redOn, this.toggleSpot,
+ this.toggleClean, this.toggleMax, this.toggleDirt,
+ this.power_color, this.power_int);
+ }
+ if (roombacomm.getProtocol().equalsIgnoreCase("OI")) {
+ roombacomm.setLEDsOI(this.toggleCheckRobot, this.toggleSpot,
+ this.toggleDock, this.toggleDirt, this.power_color,
+ this.power_int);
+ }
+ return;
+ }
+
+ /**
+ * Sets the chg green led.
+ *
+ * @param roombacomm the roombacomm
+ * @param green the green
+ */
+ protected void setChgGreenLED(RoombaComm roombacomm, boolean green) {
+ this.greenOn = green;
+ updateDisplay("setChgGreenLED", this.debug);
+ this.setLEDs(roombacomm);
+ }
+
+ /**
+ * Sets the chg red led.
+ *
+ * @param roombacomm the roombacomm
+ * @param red the red
+ */
+ protected void setChgRedLED(RoombaComm roombacomm, boolean red) {
+ this.redOn = red;
+ updateDisplay("setChgRedLED", this.debug);
+ this.setLEDs(roombacomm);
+ }
+
+ /**
+ * Sets the chg spot led.
+ *
+ * @param roombacomm the roombacomm
+ * @param spot the spot
+ */
+ protected void setChgSpotLED(RoombaComm roombacomm, boolean spot) {
+ updateDisplay("setChgSpotLED value(" + spot + ")", this.debug);
+ roombacomm.setChgSpotLED(roombacomm, spot);
+ this.toggleSpot = roombacomm.isToggleSpot();
+ }
+
+ /**
+ * Sets the chg clean led.
+ *
+ * @param roombacomm the roombacomm
+ * @param clean the clean
+ */
+ protected void setChgCleanLED(RoombaComm roombacomm, boolean clean) {
+ updateDisplay("setChgCleanLED value(" + clean + ")", this.debug);
+ roombacomm.setChgCleanLED(roombacomm, clean);
+ this.toggleClean = roombacomm.isToggleClean();
+ }
+
+ /**
+ * Sets the chg max led.
+ *
+ * @param roombacomm the roombacomm
+ * @param max the max
+ */
+ protected void setChgMaxLED(RoombaComm roombacomm, boolean max) {
+ updateDisplay("setChgMaxLED value(" + max + ")", this.debug);
+ roombacomm.setChgMaxLED(roombacomm, max);
+ this.toggleMax = roombacomm.isToggleMax();
+ }
+
+ /**
+ * Sets the chg dirt led.
+ *
+ * @param roombacomm the roombacomm
+ * @param dirt the dirt
+ */
+ protected void setChgDirtLED(RoombaComm roombacomm, boolean dirt) {
+ updateDisplay("setChgDirtLED value(" + dirt + ")", this.debug);
+ roombacomm.setChgDirtLED(roombacomm, dirt);
+ this.toggleDirt = roombacomm.isToggleDirt();
+ }
+
+ /**
+ * Sets the toggle check robot.
+ *
+ * @param roombacomm the roombacomm
+ * @param CheckRobot the check robot
+ */
+ public void setToggleCheckRobot(RoombaComm roombacomm, boolean CheckRobot) {
+ updateDisplay("setChgCheckRobotLED value(" + CheckRobot + ")",this.debug);
+ roombacomm.setChgCheckRobotLED(roombacomm, CheckRobot);
+ this.toggleCheckRobot = roombacomm.isToggleCheckRobot();
+ }
+
+ /**
+ * Sets the toggle dock.
+ *
+ * @param roombacomm the roombacomm
+ * @param dock the dock
+ */
+ public void setToggleDock(RoombaComm roombacomm, boolean dock) {
+ updateDisplay("setChgDockLED value(" + dock + ")", this.debug);
+ roombacomm.setChgDockLED(roombacomm, dock);
+ this.toggleDock = roombacomm.isToggleDock();
+ }
+
+ /**
+ * Sets the chg power color led.
+ *
+ * @param roombacomm the roombacomm
+ * @param power_color the power_color
+ */
+ protected void setChgPowerColorLED(RoombaComm roombacomm, int power_color) {
+ updateDisplay("setChgPowerColorLED value(" + power_color + ")",this.debug);
+ roombacomm.setChgPowerColorLED(roombacomm, power_color);
+ // this.power_color
+ // TODO: Keep track of power color
+ }
+
+ /**
+ * Sets the chg power intensity led.
+ *
+ * @param roombacomm the roombacomm
+ * @param power_intensity the power_intensity
+ */
+ protected void setChgPowerIntensityLED(RoombaComm roombacomm, int power_intensity) {
+ updateDisplay("setChgPowerIntensityLED value(" + power_intensity + ")",this.debug);
+ roombacomm.setChgPowerIntensityLED(roombacomm, power_intensity);
+ // TODO: Keep track of Power Intensity
+ }
+
+ /**
+ * Gets the toggle check robot.
+ *
+ * @return the toggle check robot
+ */
+ public boolean getToggleCheckRobot() {
+ return toggleCheckRobot;
+ }
+
+ /**
+ * Gets the toggle dock.
+ *
+ * @return the toggle dock
+ */
+ public boolean getToggleDock() {
+ return toggleDock;
+ }
+
+ /**
+ * Gets the j tabbed panel config.
+ *
+ * @return the j tabbed panel config
+ */
+ private JTabbedPane getJTabbedPanelConfig() {
+ if(jTabbedPanelConfig == null) {
+ jTabbedPanelConfig = new JTabbedPane();
+ jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(139, 28));
+ jTabbedPanelConfig.addTab("Net", null, getJPanelConfigNet(), "set the TCP network settings here");
+ jTabbedPanelConfig.addTab("Serial", null, getJPanelConfigSerial(), "set the serial settings here");
+ }
+ jTabbedPanelConfig.setMinimumSize(new Dimension(200,100));
+ jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(254, 100));
+ jTabbedPanelConfig.setToolTipText("Use one of the two connection methods to communicate with the Roomba");
+ // Register a change listener
+ jTabbedPanelConfig.addChangeListener(new ChangeListener() {
+ // This method is called whenever the selected tab changes
+ public void stateChanged(ChangeEvent evt) {
+ debugPrintln(debug,"jTabbedPanelConfig - state changed");
+ JTabbedPane pane = (JTabbedPane)evt.getSource();
+ // Get current tab
+ int sel = pane.getSelectedIndex();
+ debugPrintln(debug,"jTabbedPanelConfig - tab "+sel+" selected");
+ debugPrintln(debug,"jTabbedPanelConfig - roombaCommSerial.isConnected() "+roombaCommSerial.isConnected());
+ debugPrintln(debug,"jTabbedPanelConfig - roombaCommTCPClient.isConnected() "+roombaCommTCPClient.isConnected());
+ if (roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){
+ debugPrintln(debug,"***** I have no idea why both should ever be connected at the same time... THIS IS STRANGE *****");
+ }
+ if (sel == 1){
+ setCommPorts();
+ if (roombaCommTCPClient.isConnected() && !roombaCommSerial.isConnected()){
+ pane.setSelectedIndex(0);
+ debugPrintln(debug,"active net connection found setting focus to the correct tab");
+ }
+ }
+ if (sel == 0){
+ if (!roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){
+ pane.setSelectedIndex(1);
+ debugPrintln(debug,"active Serial connection found setting focus to the correct tab");
+ }
+ }
+ }
+ });
+ return jTabbedPanelConfig;
+ }
+
+ /**
+ * Gets the j panel config net.
+ *
+ * @return the j panel config net
+ */
+ private JPanel getJPanelConfigNet() {
+ if(jPanelConfigNet == null) {
+ jPanelConfigNet = new JPanel();
+ GridBagLayout jPanelConfigNetLayout = new GridBagLayout();
+ jPanelConfigNetLayout.rowWeights = new double[] {0.0, 0.1};
+ jPanelConfigNetLayout.rowHeights = new int[] {26, 7};
+ jPanelConfigNetLayout.columnWeights = new double[] {0.1, 0.1, 0.1};
+ jPanelConfigNetLayout.columnWidths = new int[] {7, 7, 7};
+ jPanelConfigNet.setLayout(jPanelConfigNetLayout);
+ jPanelConfigNet.setPreferredSize(new java.awt.Dimension(318, 72));
+ jPanelConfigNet.add(getJLabelHost(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigNet.add(getJTextFieldHost(), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigNet.add(getJLabelPort(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigNet.add(getJTextFieldPort(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ {
+ netButton = new JButton();
+ jPanelConfigNet.add(netButton, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ netButton.setText("connect");
+ netButton.setActionCommand("net");
+ netButton.addActionListener(this);
+ }
+ }
+ return jPanelConfigNet;
+ }
+
+ /**
+ * Gets the j panel config serial.
+ *
+ * @return the j panel config serial
+ */
+ private JPanel getJPanelConfigSerial() {
+ if(jPanelConfigSerial == null) {
+ jPanelConfigSerial = new JPanel();
+ GridBagLayout jPanelConfigSerialLayout = new GridBagLayout();
+ jPanelConfigSerial.setPreferredSize(new java.awt.Dimension(318, 72));
+ jPanelConfigSerialLayout.rowWeights = new double[] {0.1};
+ jPanelConfigSerialLayout.rowHeights = new int[] {7};
+ jPanelConfigSerialLayout.columnWeights = new double[] {0.0, 0.1, 0.1};
+ jPanelConfigSerialLayout.columnWidths = new int[] {77, 7, 7};
+ jPanelConfigSerial.setLayout(jPanelConfigSerialLayout);
+ jPanelConfigSerial.addFocusListener(new FocusAdapter() {
+ public void focusGained(FocusEvent evt) {
+ jPanelConfigSerialFocusGained(evt);
+ }
+ });
+ {
+ portChoices = new JComboBox();
+ jPanelConfigSerial.add(portChoices, new GridBagConstraints(1, -1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ portChoices.validate();
+ portChoices.repaint();
+ portChoices.addActionListener(this);
+ }
+ {
+ connectButton = new JButton();
+ jPanelConfigSerial.add(connectButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigSerial.add(getJLabelCOMM(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
+ connectButton.setText(" connect ");
+ connectButton.setActionCommand("connect");
+ connectButton.addActionListener(this);
+ }
+ }
+ return jPanelConfigSerial;
+ }
+
+ /**
+ * Gets the j label host.
+ *
+ * @return the j label host
+ */
+ private JLabel getJLabelHost() {
+ if(jLabelHost == null) {
+ jLabelHost = new JLabel();
+ jLabelHost.setLayout(null);
+ jLabelHost.setText("Host");
+ jLabelHost.setPreferredSize(new java.awt.Dimension(39, 17));
+ jLabelHost.setLabelFor(getJTextFieldHost());
+ jLabelHost.setToolTipText("Set the hostname/IP address");
+ }
+ return jLabelHost;
+ }
+
+ /**
+ * Gets the j text field host.
+ *
+ * @return the j text field host
+ */
+ private JTextField getJTextFieldHost() {
+ if(jTextFieldHost == null) {
+ jTextFieldHost = new JTextField();
+ jTextFieldHost.setText("192.168.2.15");
+ jTextFieldHost.setToolTipText("Set the hostname/IP address");
+ }
+ return jTextFieldHost;
+ }
+
+ /**
+ * Gets the j panel1.
+ *
+ * @return the j panel1
+ */
+ private JPanel getJPanel1() {
+ if(jPanel1 == null) {
+ jPanel1 = new JPanel();
+ GridBagLayout jPanel1Layout = new GridBagLayout();
+ jPanel1Layout.rowWeights = new double[] {0.1};
+ jPanel1Layout.rowHeights = new int[] {7};
+ jPanel1Layout.columnWeights = new double[] {0.1, 0.1};
+ jPanel1Layout.columnWidths = new int[] {7, 7};
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1.setPreferredSize(new java.awt.Dimension(800,320));
+ {
+ ledPanel = new JPanel();
+ GridBagLayout ledPanelLayout = new GridBagLayout();
+ ledPanel.setLayout(ledPanelLayout);
+ jPanel1.add(ledPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ {
+ ledPanelSCIOnly = new JPanel(new GridLayout(3, 3));
+ GridBagLayout ledPanel1Layout = new GridBagLayout();
+ ledPanel.add(getLedPanelShared(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ ledPanel.add(getJPanel4x(), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ ledPanel.add(ledPanelSCIOnly, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ ledPanel1Layout.rowWeights = new double[] {0.1};
+ ledPanel1Layout.rowHeights = new int[] {7};
+ ledPanel1Layout.columnWeights = new double[] {0.1, 0.1, 0.1, 0.1};
+ ledPanel1Layout.columnWidths = new int[] {7, 7, 7, 7};
+ ledPanelSCIOnly.setLayout(ledPanel1Layout);
+ ButtonGroup group = new ButtonGroup();
+ String off="None";
+ String green="Green";
+ String red="Red";
+ String both="Orange";
+ {
+ JRadioButton statusOffButton = new JRadioButton(off);
+ ledPanelSCIOnly.add(statusOffButton, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ statusOffButton.setMnemonic(KeyEvent.VK_N);
+ statusOffButton.setActionCommand("StatusLED-" + off);
+ statusOffButton.setSelected(true);
+ group.add(statusOffButton);
+ statusOffButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, false);
+ setChgRedLED(roombaCommSerial, false);
+ }
+ });
+ }
+ {
+ JRadioButton statusGreenButton = new JRadioButton(green);
+ ledPanelSCIOnly.add(statusGreenButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ statusGreenButton.setMnemonic(KeyEvent.VK_G);
+ statusGreenButton.setActionCommand("StatusLED-" + green);
+ group.add(statusGreenButton);
+ statusGreenButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, true);
+ setChgRedLED(roombaCommSerial, false);
+ }
+ });
+ }
+ {
+ JRadioButton statusRedButton = new JRadioButton(red);
+ ledPanelSCIOnly.add(statusRedButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ statusRedButton.setMnemonic(KeyEvent.VK_R);
+ statusRedButton.setActionCommand("StatusLED-" + red);
+ group.add(statusRedButton);
+ statusRedButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, false);
+ setChgRedLED(roombaCommSerial, true);
+ }
+ });
+ }
+ {
+ JRadioButton statusBothButton = new JRadioButton(both);
+ ledPanelSCIOnly.add(statusBothButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ statusBothButton.setMnemonic(KeyEvent.VK_O);
+ statusBothButton.setActionCommand("StatusLED-" + both);
+ group.add(statusBothButton);
+ statusBothButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, true);
+ setChgRedLED(roombaCommSerial, true);
+ }
+ });
+ }
+ }
+ {
+ powerColorSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 100);
+ ledPanel.add(powerColorSlider, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2));
+ powerColorSlider.setPaintTicks(true);
+ powerColorSlider.setMajorTickSpacing(100);
+ powerColorSlider.setMinorTickSpacing(25);
+ powerColorSlider.setPaintLabels(true);
+ powerColorSlider.setSize(200, 46);
+ powerColorSlider.setPreferredSize(new java.awt.Dimension(224, 48));
+ powerColorSlider.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ JSlider src = (JSlider) e.getSource();
+ if (!src.getValueIsAdjusting()) {
+ setPower_color((int) src.getValue());
+ updateDisplay("setting Power Color = " + getPower_color()
+ + "\n");
+ setLEDs(roombaCommSerial);
+ }
+ }
+ });
+ }
+ {
+ JLabel powerColorLabel = new JLabel("PowerColor", JLabel.CENTER);
+ ledPanel.add(powerColorLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2));
+ }
+ {
+ powerColorIntensity = new JSlider(JSlider.HORIZONTAL, 0, 255, 100);
+ ledPanel.add(powerColorIntensity, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2));
+ powerColorIntensity.setPaintTicks(true);
+ powerColorIntensity.setMajorTickSpacing(100);
+ powerColorIntensity.setMinorTickSpacing(25);
+ powerColorIntensity.setPaintLabels(true);
+ powerColorIntensity.setPreferredSize(new java.awt.Dimension(248, 45));
+ powerColorIntensity.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ // System.err.println("stateChanged:"+e);
+ JSlider src = (JSlider) e.getSource();
+ if (!src.getValueIsAdjusting()) {
+ setPower_int((int) src.getValue());
+ updateDisplay("setting Power Color Intensity = "
+ + getPower_int() + "\n");
+ setLEDs(roombaCommSerial);
+ }
+ }
+ });
+ }
+ {
+ JLabel powerColorIntensityLabel = new JLabel("PowerColorIntensity",
+ JLabel.CENTER);
+ ledPanel.add(powerColorIntensityLabel, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2));
+ }
+ ledPanelLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
+ ledPanelLayout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7};
+ ledPanelLayout.columnWeights = new double[] {0.1};
+ ledPanelLayout.columnWidths = new int[] {7};
+ ledPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("LEDs"),BorderFactory.createEmptyBorder(5,5,5,5)));
+ ledPanel.setPreferredSize(new java.awt.Dimension(288, 313));
+ }
+
+ {
+ ctrlPanel = new JPanel();
+ jPanel1.add(ctrlPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ {
+ JPanel ctrlPanel1 = new JPanel();
+ ctrlPanel.add(ctrlPanel1);
+ {
+ JButton but_turnleft = new JButton();
+ ctrlPanel1.add(but_turnleft);
+ but_turnleft.setActionCommand("turnleft");
+ but_turnleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnleft.png")));
+ but_turnleft.addActionListener(this);
+ }
+ {
+ JButton but_forward = new JButton();
+ ctrlPanel1.add(but_forward);
+ but_forward.setActionCommand("forward");
+ but_forward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_forward.png")));
+ but_forward.addActionListener(this);
+ }
+ {
+ JButton but_turnright = new JButton();
+ ctrlPanel1.add(but_turnright);
+ but_turnright.setActionCommand("turnright");
+ but_turnright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnright.png")));
+ but_turnright.addActionListener(this);
+ }
+ {
+ JButton but_spinleft = new JButton();
+ ctrlPanel1.add(but_spinleft);
+ but_spinleft.setActionCommand("spinleft");
+ but_spinleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinleft.png")));
+ but_spinleft.addActionListener(this);
+ }
+ {
+ JButton but_stop = new JButton();
+ ctrlPanel1.add(but_stop);
+ but_stop.setActionCommand("stop");
+ but_stop.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_stop.png")));
+ but_stop.addActionListener(this);
+ }
+ {
+ JButton but_spinright = new JButton();
+ ctrlPanel1.add(but_spinright);
+ but_spinright.setActionCommand("spinright");
+ but_spinright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinright.png")));
+ but_spinright.addActionListener(this);
+ }
+ {
+ ctrlPanel1.add(new JLabel());
+ }
+ {
+ JButton but_backward = new JButton();
+ ctrlPanel1.add(but_backward);
+ but_backward.setActionCommand("backward");
+ but_backward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_backward.png")));
+ but_backward.setPreferredSize(new java.awt.Dimension(55, 78));
+ but_backward.addActionListener(this);
+ }
+ {
+ ctrlPanel1.add(new JLabel());
+ }
+ ctrlPanel1.setLayout(new GridLayout(3, 3));
+ ctrlPanel1.setPreferredSize(new java.awt.Dimension(194, 199));
+ ctrlPanel1.setSize(194, 199);
+// ctrlPanel1.setTabTitle("");
+ }
+ {
+ speedSlider = new JSlider(JSlider.HORIZONTAL, 0, 500, 200);
+ ctrlPanel.add(speedSlider);
+ speedSlider.setPaintTicks(true);
+ speedSlider.setMajorTickSpacing(100);
+ speedSlider.setMinorTickSpacing(25);
+ speedSlider.setPaintLabels(true);
+ speedSlider.addChangeListener(this);
+ }
+ {
+ JLabel sliderLabel = new JLabel();
+ ctrlPanel.add(sliderLabel);
+ sliderLabel.setText("speed (mm/s)");
+ sliderLabel.setAlignmentX(JLabel.CENTER);
+ }
+ ctrlPanel.setLayout(new BoxLayout(ctrlPanel, BoxLayout.Y_AXIS));
+ ctrlPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Movement"),BorderFactory.createEmptyBorder(5,5,5,5)));
+ }
+ }
+ return jPanel1;
+ }
+
+ /**
+ * Gets the j label port.
+ *
+ * @return the j label port
+ */
+ private JLabel getJLabelPort() {
+ if(jLabelPort == null) {
+ jLabelPort = new JLabel();
+ jLabelPort.setText("Port");
+ jLabelPort.setPreferredSize(new java.awt.Dimension(39, 33));
+ jLabelPort.setLabelFor(getJTextFieldPort());
+ jLabelPort.setToolTipText("Set the TCP port");
+ }
+ return jLabelPort;
+ }
+
+ /**
+ * Gets the j text field port.
+ *
+ * @return the j text field port
+ */
+ private JTextField getJTextFieldPort() {
+ if(jTextFieldPort == null) {
+ jTextFieldPort = new JTextField();
+ jTextFieldPort.setToolTipText("Set the TCP port");
+ jTextFieldPort.setText("5001");
+ }
+ return jTextFieldPort;
+ }
+
+ /**
+ * J panel config serial focus gained.
+ *
+ * @param evt the evt
+ */
+ private void jPanelConfigSerialFocusGained(FocusEvent evt) {
+ debugPrintln(debug,"jPanelConfigSerial.focusGained, event="+evt);
+ //TODO add your code for jPanelConfigSerial.focusGained
+ // make sure we have a roombaCommSerial object
+ setCommPorts();
+ }
+
+ /**
+ * Sets the comm ports.
+ */
+ private void setCommPorts() {
+ debugPrintln(debug,"setCommPorts-start");
+ if (roombaCommSerial == null){
+ debugPrintln(debug,"setCommPorts-roombaCommSerial is null");
+ roombaCommSerial = new RoombaCommSerial(this.debug);
+ }
+ if (portChoices != null) {
+ debugPrintln(debug,"setCommPorts-portChoices object is not null");
+ // if the list of ports is empty then try to fill it
+ if (portChoices.getItemCount() ==0){
+ // fill in the comm ports (combo box) with choices.
+ debugPrintln(debug,"setCommPorts-getting list of ports");
+ String[] ports = roombaCommSerial.listPorts();
+ // for now short cutting looking for serial ports to speed up start/stop of the UI
+ // String[] ports = {"a","b"};
+ debugPrintln(debug,"setCommPorts-found "+ports.length+" serialports");
+ for (int i = 0; i < ports.length; i++) {
+ String s = ports[i];
+ debugPrintln(debug,"setCommPorts-adding ["+i+"] as "+s);
+ portChoices.addItem(ports[i]);
+ if (s.equals(roombaCommSerial.getPortname())) {
+ debugPrintln(debug,"setCommPorts- setting port as selected due to "+roombaCommSerial.getPortname());
+ portChoices.setSelectedItem(s);
+ }
+ }
+ portChoices.validate();
+ portChoices.repaint();
+ }
+ }
+ debugPrintln(debug,"setCommPorts-start");
+ }
+
+ /**
+ * Gets the j label comm.
+ *
+ * @return the j label comm
+ */
+ private JLabel getJLabelCOMM() {
+ if(jLabelCOMM == null) {
+ jLabelCOMM = new JLabel();
+ jLabelCOMM.setText("COM");
+ }
+ return jLabelCOMM;
+ }
+
+ /**
+ * Gets the j panel2.
+ *
+ * @return the j panel2
+ */
+ private JPanel getJPanel2() {
+ if(jPanel2 == null) {
+ jPanel2 = new JPanel();
+ GridBagLayout jPanel2Layout = new GridBagLayout();
+ jPanel2Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
+ jPanel2Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7};
+ jPanel2Layout.columnWeights = new double[] {0.0};
+ jPanel2Layout.columnWidths = new int[] {150};
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2.setBorder(BorderFactory.createTitledBorder(null,"Commands",TitledBorder.LEADING,TitledBorder.DEFAULT_POSITION));
+ jPanel2.setPreferredSize(new java.awt.Dimension(233,505));
+ jPanel2.add(getJPanelModes(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelSounds(), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelTestPrograms(), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelVacuum(), new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelPower(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanel4(), new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ }
+ return jPanel2;
+ }
+
+ /**
+ * Gets the j panel3.
+ *
+ * @return the j panel3
+ */
+ private JPanel getJPanel3() {
+ if(jPanel3 == null) {
+ jPanel3 = new JPanel();
+ jPanel3.setVisible(false);
+ GridBagLayout jPanel3Layout = new GridBagLayout();
+ jPanel3Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
+ jPanel3Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+ jPanel3Layout.columnWeights = new double[] {0.1};
+ jPanel3Layout.columnWidths = new int[] {7};
+ jPanel3.setLayout(jPanel3Layout);
+ {
+ JButton but_nyi = new JButton();
+ jPanel3.add(but_nyi, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi.setText("nyi");
+ but_nyi.setVisible(false);
+ }
+ {
+ JButton but_nyi2 = new JButton();
+ jPanel3.add(but_nyi2, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi2.setText("nyi2");
+ but_nyi2.setVisible(false);
+ }
+ {
+ JButton but_nyi3 = new JButton();
+ jPanel3.add(but_nyi3, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi3.setText("nyi3");
+ but_nyi3.setVisible(false);
+ }
+ {
+ JButton but_nyi4 = new JButton();
+ jPanel3.add(but_nyi4, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi4.setText("nyi4");
+ but_nyi4.setVisible(false);
+ }
+ {
+ JButton but_nyi5 = new JButton();
+ jPanel3.add(but_nyi5, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi5.setText("nyi5");
+ but_nyi5.setVisible(false);
+ }
+ {
+ JButton but_nyi6 = new JButton();
+ jPanel3.add(but_nyi6, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi6.setText("nyi6");
+ but_nyi6.setVisible(false);
+ }
+ {
+ JButton but_nyi7 = new JButton();
+ jPanel3.add(but_nyi7, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi7.setText("nyi7");
+ but_nyi7.setVisible(false);
+ }
+ {
+ jButton2 = new JButton();
+ jPanel3.add(jButton2, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton2.setText("nyi5");
+ jButton2.setVisible(false);
+ }
+ {
+ jButton1 = new JButton();
+ jPanel3.add(jButton1, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton1.setText("nyi6");
+ jButton1.setVisible(false);
+ }
+ {
+ jButton3 = new JButton();
+ jPanel3.add(jButton3, new GridBagConstraints(0, 11, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton3.setText("nyi3");
+ jButton3.setVisible(false);
+ }
+ {
+ jButton4 = new JButton();
+ jPanel3.add(jButton4, new GridBagConstraints(0, 12, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton4.setText("nyi2");
+ jButton4.setVisible(false);
+ }
+ }
+ return jPanel3;
+ }
+
+ /**
+ * Gets the j panel modes.
+ *
+ * @return the j panel modes
+ */
+ private JPanel getJPanelModes() {
+ if(jPanelModes == null) {
+ jPanelModes = new JPanel();
+ GridBagLayout jPanelModesLayout = new GridBagLayout();
+ jPanelModesLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1};
+ jPanelModesLayout.rowHeights = new int[] {7, 7, 7, 7, 7};
+ jPanelModesLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelModesLayout.columnWidths = new int[] {95, 95};
+ jPanelModes.setLayout(jPanelModesLayout);
+ jPanelModes.setBorder(BorderFactory.createTitledBorder("Modes"));
+ {
+ JButton but_spot = new JButton();
+ jPanelModes.add(but_spot, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_spot.setText("spot");
+ but_spot.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_spot.addActionListener(this);
+ }
+ {
+ JButton but_full = new JButton();
+ jPanelModes.add(but_full, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_full.setText("full");
+ but_full.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_full.addActionListener(this);
+ }
+ {
+ JButton but_safe = new JButton();
+ jPanelModes.add(but_safe, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_safe.setText("safe");
+ but_safe.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_safe.addActionListener(this);
+ }
+ {
+ JButton but_dock = new JButton();
+ jPanelModes.add(but_dock, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_dock.setText("dock");
+ but_dock.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_dock.addActionListener(this);
+ }
+ {
+ JButton but_max = new JButton();
+ jPanelModes.add(but_max, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_max.setText("max");
+ but_max.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_max.addActionListener(this);
+ }
+ {
+ JButton but_clean = new JButton();
+ jPanelModes.add(but_clean, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_clean.setText("clean");
+ but_clean.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_clean.addActionListener(this);
+ }
+ {
+ JButton but_wakeup = new JButton();
+ jPanelModes.add(but_wakeup, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_wakeup.setText("wakeup");
+ but_wakeup.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_wakeup.addActionListener(this);
+ }
+ {
+ JButton but_reset = new JButton();
+ jPanelModes.add(but_reset, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_reset.setText("reset");
+ but_reset.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_reset.addActionListener(this);
+ }
+ {
+ JButton but_passive = new JButton();
+ jPanelModes.add(but_passive, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_passive.setText("passive");
+ but_passive.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_passive.addActionListener(this);
+ }
+ }
+ return jPanelModes;
+ }
+
+ /**
+ * Gets the j panel sounds.
+ *
+ * @return the j panel sounds
+ */
+ private JPanel getJPanelSounds() {
+ if(jPanelSounds == null) {
+ jPanelSounds = new JPanel();
+ GridBagLayout jPanelSoundsLayout = new GridBagLayout();
+ jPanelSoundsLayout.rowWeights = new double[] {0.1, 0.1};
+ jPanelSoundsLayout.rowHeights = new int[] {7, 7};
+ jPanelSoundsLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelSoundsLayout.columnWidths = new int[] {95, 95};
+ jPanelSounds.setLayout(jPanelSoundsLayout);
+ jPanelSounds.setBorder(BorderFactory.createTitledBorder("Sounds"));
+ {
+ JButton but_OSU = new JButton();
+ jPanelSounds.add(but_OSU, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_OSU.setText("OSU");
+ but_OSU.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_OSU.addActionListener(this);
+ }
+ {
+ JButton but_beeplo = new JButton();
+ jPanelSounds.add(but_beeplo, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_beeplo.setText("beep-lo");
+ but_beeplo.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_beeplo.addActionListener(this);
+ }
+ {
+ JButton but_beephi = new JButton();
+ jPanelSounds.add(but_beephi, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_beephi.setText("beep-hi");
+ but_beephi.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_beephi.addActionListener(this);
+ }
+ {
+ JButton but_playRTTL = getBut_playRTTL();
+ jPanelSounds.add(getBut_playRTTL(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_playRTTL.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_playRTTL.addActionListener(this);
+ }
+ }
+ return jPanelSounds;
+ }
+
+ /**
+ * Gets the j panel test programs.
+ *
+ * @return the j panel test programs
+ */
+ private JPanel getJPanelTestPrograms() {
+ if(jPanelTestPrograms == null) {
+ jPanelTestPrograms = new JPanel();
+ GridBagLayout jPanelTestProgramsLayout = new GridBagLayout();
+ jPanelTestProgramsLayout.rowWeights = new double[] {0.1};
+ jPanelTestProgramsLayout.rowHeights = new int[] {7};
+ jPanelTestProgramsLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelTestProgramsLayout.columnWidths = new int[] {95, 95};
+ jPanelTestPrograms.setLayout(jPanelTestProgramsLayout);
+ jPanelTestPrograms.setBorder(BorderFactory.createTitledBorder("Test Programs"));
+ {
+ JButton but_TribbleOn = new JButton();
+ jPanelTestPrograms.add(but_TribbleOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_TribbleOn.setText("Tribble On");
+ but_TribbleOn.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_TribbleOn.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_TribbleOn.setSize(93, 26);
+ but_TribbleOn.addActionListener(this);
+ }
+ {
+ JButton but_test = new JButton();
+ jPanelTestPrograms.add(but_test, new GridBagConstraints(1, 0, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_test.setText("LogoA.square");
+ but_test.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_test.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_test.setSize(93, 26);
+ but_test.addActionListener(this);
+ }
+ }
+ return jPanelTestPrograms;
+ }
+
+ /**
+ * Gets the j panel4.
+ *
+ * @return the j panel4
+ */
+ private JPanel getJPanel4() {
+ if(jPanelSensors == null) {
+ jPanelSensors = new JPanel();
+ GridBagLayout jPanelSensorsLayout = new GridBagLayout();
+ jPanelSensorsLayout.rowWeights = new double[] {0.1};
+ jPanelSensorsLayout.rowHeights = new int[] {7};
+ jPanelSensorsLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelSensorsLayout.columnWidths = new int[] {95, 95};
+ jPanelSensors.setLayout(jPanelSensorsLayout);
+ jPanelSensors.setBorder(BorderFactory.createTitledBorder(null, "Sensors", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ {
+ JButton but_chargedata = new JButton();
+ jPanelSensors.add(but_chargedata, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
+ but_chargedata.setText("chargedata");
+ but_chargedata.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_chargedata.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_chargedata.setSize(93, 26);
+ but_chargedata.addActionListener(this);
+ }
+ {
+ JButton but_sensors = new JButton();
+ jPanelSensors.add(but_sensors, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
+ but_sensors.setText("sensors");
+ but_sensors.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_sensors.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_sensors.setSize(93, 26);
+ but_sensors.addActionListener(this);
+ }
+ }
+ return jPanelSensors;
+ }
+
+ /**
+ * Gets the j panel vacuum.
+ *
+ * @return the j panel vacuum
+ */
+ private JPanel getJPanelVacuum() {
+ if(jPanelVacuum == null) {
+ jPanelVacuum = new JPanel();
+ GridBagLayout jPanelVacuumLayout = new GridBagLayout();
+ jPanelVacuumLayout.rowWeights = new double[] {0.1};
+ jPanelVacuumLayout.rowHeights = new int[] {7};
+ jPanelVacuumLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelVacuumLayout.columnWidths = new int[] {95, 95};
+ jPanelVacuum.setLayout(jPanelVacuumLayout);
+ jPanelVacuum.setBorder(BorderFactory.createTitledBorder(BorderFactory.createTitledBorder(""), "Vacuum", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ {
+ JButton but_vacon = new JButton();
+ jPanelVacuum.add(but_vacon, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_vacon.setText("vacuum-on");
+ but_vacon.setSize(93, 26);
+ but_vacon.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_vacon.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_vacon.addActionListener(this);
+ }
+ {
+ JButton but_vacoff = new JButton();
+ but_vacoff.setLayout(null);
+ jPanelVacuum.add(but_vacoff, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_vacoff.setText("vacuum-off");
+ but_vacoff.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_vacoff.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_vacoff.setSize(93, 26);
+ but_vacoff.addActionListener(this);
+ }
+ }
+ return jPanelVacuum;
+ }
+
+ /**
+ * Gets the j panel power.
+ *
+ * @return the j panel power
+ */
+ private JPanel getJPanelPower() {
+ if(jPanelPower == null) {
+ jPanelPower = new JPanel();
+ GridBagLayout jPanelPowerLayout = new GridBagLayout();
+ jPanelPower.setBorder(BorderFactory.createTitledBorder(null, "Power", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ jPanelPowerLayout.rowWeights = new double[] {0.1};
+ jPanelPowerLayout.rowHeights = new int[] {7};
+ jPanelPowerLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelPowerLayout.columnWidths = new int[] {95, 95};
+ jPanelPower.setLayout(jPanelPowerLayout);
+ {
+ JButton but_powerOn = new JButton();
+ jPanelPower.add(but_powerOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_powerOn.setText("power-on");
+ but_powerOn.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_powerOn.addActionListener(this);
+ }
+ {
+ JButton but_power = new JButton();
+ jPanelPower.add(but_power, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_power.setText("power-off");
+ but_power.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_power.addActionListener(this);
+ }
+ }
+ return jPanelPower;
+ }
+
+ /**
+ * Gets the j text pane1.
+ *
+ * @return the j text pane1
+ */
+ private JTextPane getJTextPane1() {
+ if(jTextPane1 == null) {
+ jTextPane1 = new JTextPane();
+ jTextPane1.setText("Set the Protocal, then connect via a network or Serial connection.\nThen you can use the Commands, LED's, and/or Movement controls to operate your Roomba.");
+ jTextPane1.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jTextPane1.setOpaque(false);
+ }
+ return jTextPane1;
+ }
+
+ /**
+ * Gets the j panel4x.
+ *
+ * @return the j panel4x
+ */
+ private JPanel getJPanel4x() {
+ if(ledPanelOIOnly == null) {
+ ledPanelOIOnly = new JPanel();
+ GridBagLayout jPanel4Layout = new GridBagLayout();
+ ledPanelOIOnly.setPreferredSize(new java.awt.Dimension(134, 49));
+ jPanel4Layout.rowWeights = new double[] {0.1};
+ jPanel4Layout.rowHeights = new int[] {7};
+ jPanel4Layout.columnWeights = new double[] {0.1, 0.1, 0.1, 0.1};
+ jPanel4Layout.columnWidths = new int[] {7, 7, 7, 7};
+ ledPanelOIOnly.setLayout(jPanel4Layout);
+ ledPanelOIOnly.setVisible(false);
+ {
+ JButton but_toggleDock = new JButton();
+ GridBagLayout but_toggleDockLayout = new GridBagLayout();
+ ledPanelOIOnly.add(but_toggleDock, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_toggleDock.setActionCommand("toggleDock");
+ but_toggleDock.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dockOn.png")));
+ but_toggleDock.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleDock.setSize(40, 40);
+ but_toggleDockLayout.rowWeights = new double[] {0.1};
+ but_toggleDockLayout.rowHeights = new int[] {7};
+ but_toggleDockLayout.columnWeights = new double[] {0.1, 0.1};
+ but_toggleDockLayout.columnWidths = new int[] {7, 7};
+ but_toggleDock.setLayout(but_toggleDockLayout);
+ but_toggleDock.addActionListener(this);
+ }
+ {
+ JButton but_toggleCheckRobot = new JButton();
+ GridBagLayout but_toggleCheckRobotLayout = new GridBagLayout();
+ ledPanelOIOnly.add(but_toggleCheckRobot, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_toggleCheckRobot.setActionCommand("toggleCheckRobot");
+ but_toggleCheckRobot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_checkrobotOn.png")));
+ but_toggleCheckRobot.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleCheckRobot.setSize(40, 40);
+ but_toggleCheckRobotLayout.rowWeights = new double[] {0.1};
+ but_toggleCheckRobotLayout.rowHeights = new int[] {7};
+ but_toggleCheckRobotLayout.columnWeights = new double[] {0.1, 0.1};
+ but_toggleCheckRobotLayout.columnWidths = new int[] {7, 7};
+ but_toggleCheckRobot.setLayout(but_toggleCheckRobotLayout);
+ but_toggleCheckRobot.addActionListener(this);
+ }
+ }
+ return ledPanelOIOnly;
+ }
+
+ /**
+ * Gets the led panel shared.
+ *
+ * @return the led panel shared
+ */
+ private JPanel getLedPanelShared() {
+ if(ledPanelShared == null) {
+ ledPanelShared = new JPanel();
+ {
+ JButton but_toggleSpot = new JButton();
+ ledPanelShared.add(but_toggleSpot);
+ but_toggleSpot.setActionCommand("toggleSpot");
+ but_toggleSpot.setIcon(getIcon("com/hackingroomba/roombacomm/images/but_spotOn.png"));
+ but_toggleSpot.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleSpot.setSize(40, 40);
+ but_toggleSpot.addActionListener(this);
+ }
+ {
+ JButton but_toggleClean = new JButton();
+ ledPanelShared.add(but_toggleClean);
+ but_toggleClean.setActionCommand("toggleClean");
+ but_toggleClean.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_cleanOn.png")));
+ but_toggleClean.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleClean.setSize(40, 40);
+ but_toggleClean.addActionListener(this);
+ }
+ {
+ JButton but_toggleDirt = new JButton();
+ ledPanelShared.add(but_toggleDirt);
+ but_toggleDirt.setActionCommand("toggleDirt");
+ but_toggleDirt.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dirtOn.png")));
+ but_toggleDirt.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleDirt.setSize(40, 40);
+ but_toggleDirt.addActionListener(this);
+ }
+ {
+ JButton but_toggleMax = new JButton();
+ ledPanelShared.add(but_toggleMax);
+ but_toggleMax.setActionCommand("toggleMax");
+ but_toggleMax.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_maxOn.png")));
+ but_toggleMax.setSize(40, 40);
+ but_toggleMax.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleMax.addActionListener(this);
+ }
+ }
+ return ledPanelShared;
+ }
+
+ /**
+ * Gets the icon.
+ *
+ * @param str the str
+ * @return the icon
+ * @return
+ */
+ private ImageIcon getIcon(String str) {
+ java.net.URL file =getClass().getClassLoader().getResource(str);
+ if (file != null){
+ return new ImageIcon(file);
+ }else{
+ // return the default icon
+ //TODO: Get a better default icon. :)
+ System.err.println("failed to find icon (" + str + ")");
+ return new ImageIcon("com/hackingroomba/roombacomm/images/but_spotOn.png");
+ }
+ }
+
+ /**
+ * Gets the j panel4xx.
+ *
+ * @return the j panel4xx
+ */
+ private JPanel getJPanel4xx() {
+ if(jPanel4 == null) {
+ jPanel4 = new JPanel();
+ GridBagLayout jPanel4Layout1 = new GridBagLayout();
+ jPanel4.setLayout(jPanel4Layout1);
+ jPanel4.setPreferredSize(new java.awt.Dimension(806, 608));
+ {
+ selectPanel = new JPanel();
+ GridBagLayout selectPanelLayout = new GridBagLayout();
+ selectPanel.setLayout(selectPanelLayout);
+ jPanel4.add(selectPanel, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ {
+ protocolChoices = new JComboBox(protocols);
+ selectPanel.add(protocolChoices, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 5, 0, 5), 0, 0));
+ protocolChoices.setSelectedIndex(protocolChoices.getSelectedIndex() >=0 ? protocolChoices.getSelectedIndex() : 0);
+ protocolChoices.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0), "Protocal", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ protocolChoices.setToolTipText("Set the protocal based on the roomba hardware version");
+ protocolChoices.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent evt) {
+ debugPrintln(debug,"protocolChoices.itemStateChanged, event="+evt);
+ //TODO add your code for protocolChoices.itemStateChanged
+ if (evt.getItem().equals(protocolChoices.getItemAt(0)) && (evt.getStateChange() == ItemEvent.SELECTED)){
+ ledPanelOIOnly.setVisible(false);
+ ledPanelSCIOnly.setVisible(true);
+ }
+ if (evt.getItem().equals(protocolChoices.getItemAt(1)) && (evt.getStateChange() == ItemEvent.SELECTED)){
+ ledPanelOIOnly.setVisible(true);
+ ledPanelSCIOnly.setVisible(false);
+ }
+ ledPanelOIOnly.repaint();
+ ledPanelSCIOnly.repaint();
+ }
+ });
+ protocolChoices.addActionListener(this);
+ }
+ {
+ handshakeButton = new JCheckBox("<html>h/w<br>handshake</html>");
+ selectPanel.add(handshakeButton, new GridBagConstraints(0, 2, 1, 1, 0.1, 0.1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ selectPanel.add(getJTabbedPanelConfig(), new GridBagConstraints(1, 1, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ selectPanel.add(getJTextPane1(), new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2));
+ handshakeButton.setPreferredSize(new java.awt.Dimension(148, 40));
+ handshakeButton.setText("h/w handshake");
+ handshakeButton.setToolTipText("check if you want to use the hardware handshake setting");
+ }
+ selectPanelLayout.rowWeights = new double[] {0.0, 0.0};
+ selectPanelLayout.rowHeights = new int[] {53, 58};
+ selectPanelLayout.columnWeights = new double[] {0.0, 0.0};
+ selectPanelLayout.columnWidths = new int[] {177, 375};
+ selectPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Select Roomba Type & Port"),BorderFactory.createEmptyBorder(1,1,1,1)));
+ selectPanel.setPreferredSize(new java.awt.Dimension(535,128));
+ }
+ {
+ displayPanel = new JPanel();
+ jPanel4.add(displayPanel, new GridBagConstraints(1, 2, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel4.add(getJPanel1(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel4.add(getJPanel2(), new GridBagConstraints(2, 0, 1, 2, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel4.add(getJPanel3(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ GridBagLayout displayPanelLayout = new GridBagLayout();
+ displayPanelLayout.rowWeights = new double[] {0.1};
+ displayPanelLayout.rowHeights = new int[] {7};
+ displayPanelLayout.columnWeights = new double[] {0.1};
+ displayPanelLayout.columnWidths = new int[] {7};
+ displayPanel.setLayout(displayPanelLayout);
+ displayPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Display"),BorderFactory.createEmptyBorder(1,1,1,1)));
+ displayPanel.setPreferredSize(new java.awt.Dimension(803, 67));
+ {
+ displayText = new JTextArea(10, 75);
+ displayText.setEditable(false);
+ JScrollPane scrollPane = new JScrollPane(displayText,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ displayPanel.add(scrollPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ scrollPane.setSize(795, 163);
+ scrollPane.setPreferredSize(new java.awt.Dimension(791, 38));
+ displayText.setLineWrap(false);
+ }
+ }
+ jPanel4Layout1.rowWeights = new double[] {0.1, 0.1, 2.0};
+ jPanel4Layout1.rowHeights = new int[] {7, 7, 80};
+ jPanel4Layout1.columnWeights = new double[] {0.1, 0.1, 0.1};
+ jPanel4Layout1.columnWidths = new int[] {7, 7, 7};
+ }
+ return jPanel4;
+ }
+
+ /**
+ * Debug println.
+ *
+ * @param debug the debug
+ * @param str the str
+ */
+ private void debugPrintln(boolean debug, String str){
+ if (debug){
+ System.out.println(str);
+ }
+ }
+
+ /**
+ * This window closed.
+ *
+ * @param evt the evt
+ */
+ private void thisWindowClosed(WindowEvent evt) {
+ System.out.println("this.windowClosed, event="+evt);
+ //TODO: add code for this.windowClosed ? Do we need anything else?
+ dispose();
+ }
+
+ /**
+ * Gets the but_play rttl.
+ *
+ * @return the but_play rttl
+ */
+ private JButton getBut_playRTTL() {
+ if(but_playRTTL == null) {
+ but_playRTTL = new JButton();
+ but_playRTTL.setText("Play RTTL");
+ but_playRTTL.setVisible(false);
+ }
+ return but_playRTTL;
+ }
+} \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java
new file mode 100644
index 0000000..4d588c3
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java
@@ -0,0 +1,125 @@
+/*
+ * RoombaCommGUI -- GUI to test out RoombaComm
+ *
+ * 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;
+
+/**
+ * A simple wrapper for RoombaCommFrame.
+ *
+ * This class shows someone how to use RoombaCommFrame from the "outside".
+ * AKA: No Java Swing skills needed for this class. (is the goal)
+ *
+ * SVN id value is $Id: RoombaCommGUI.java 136 2010-04-06 00:07:23Z black.123 $
+ */
+import jargs.examples.gnu.AutoHelpParser;
+//TODO: CMB: add support for different initial GUI sizes (640(w)x480(H),1024x768... maybe others)
+import jargs.gnu.CmdLineParser;
+
+/**
+ * This is an example class that will demonstrate how to use RoombaCommFrame.java.<br />
+ * It also demonstrates a use of jargs to support command line parsing for this class.
+ *
+ */
+public class RoombaCommGUI {
+
+ /** The hwhandshake. */
+ boolean hwhandshake = false;
+
+ /** The debug boolean will increase STDOUT to show details about the process at runtime. */
+ boolean debug = false;
+
+ /**
+ * The main method simply instantiates an instance of this class and passes in any command line arguments to that class.<br />
+ * See RoombaCommGUI(java.lang.String[] args) for details about Command Line Interface (CLI) values
+ *
+ * @param args the arguments
+ */
+ public static void main(String[] args) {
+ new RoombaCommGUI(args);
+ }
+
+ /**
+ * Instantiates a new RoombaComm GUI without any args.
+ */
+ public RoombaCommGUI() {
+ this(new String[0]);
+ }
+
+ /**
+ * Instantiates a new RoombaComm GUI with any args by use of jargs.
+ *
+ * @param args the args<br />
+ * --hwhandshake boolean value (true,false)<br />
+ * -d,--debug boolean value to increase STDOUT<br />
+ * -h,--help print usage input and exit<br />
+ */
+ public RoombaCommGUI(String[] args) {
+ AutoHelpParser parser = new AutoHelpParser();
+ CmdLineParser.Option hwhandshake_opt = parser.addHelp(parser.addBooleanOption("hwhandshake"),"Use this to turn on the hardware hand shake");
+ CmdLineParser.Option debug_opt = parser.addHelp(parser.addBooleanOption('d', "debug"),"output more information at runtime");
+ CmdLineParser.Option help_opt = parser.addHelp(parser.addBooleanOption('h', "help"),"Show this help message");
+ if (args != null){
+ String[] org_args = args;
+ try {
+ parser.parse(args);
+ }
+ catch ( CmdLineParser.OptionException e ) {
+ System.err.println(e.getMessage());
+ parser.printUsage();
+ System.exit(2);
+ }
+ if ( Boolean.TRUE.equals(parser.getOptionValue(hwhandshake_opt))) {
+ hwhandshake = true;
+ }
+ if ( Boolean.TRUE.equals(parser.getOptionValue(debug_opt))) {
+ debug = true;
+ }
+ if (debug){
+ for( int i=0; i < org_args.length; i++ ) {
+ System.out.println(i+": "+org_args[i]);
+ }
+ }
+ if ( Boolean.TRUE.equals(parser.getOptionValue(help_opt))) {
+ parser.printUsage();
+ System.exit(0);
+ }
+ }
+ // Schedule a job for the event dispatch thread:
+ // creating and showing this application's GUI.
+ javax.swing.SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ createAndShowGUI(debug);
+ }
+ });
+ }
+
+ /**
+ * Create the GUI and show it. For thread safety,
+ * this method should be invoked from the event dispatch thread.
+ *
+ * @param debug to increase STDOUT at runtime
+ */
+ private static void createAndShowGUI(boolean debug) {
+ RoombaCommFrame rcPanel = new RoombaCommFrame(debug);
+ rcPanel.setResizable(true);
+ rcPanel.pack();
+ rcPanel.setVisible(true);
+ }
+} \ No newline at end of file
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java
new file mode 100644
index 0000000..9ccdd0e
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java
@@ -0,0 +1,1854 @@
+/*
+ * RoombaCommPanel -
+ *
+ * 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 java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.JTextPane;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.DefaultCaret;
+
+/**
+ * A Panel containing controls for testing RoombaComm. Normally put inside of a
+ * frame, for example see RoombaCommTest
+ *
+ * SVN id value is $Id: RoombaCommPanel.java 136 2010-04-06 00:07:23Z black.123 $
+ */
+public class RoombaCommPanel extends JPanel implements ActionListener,
+ ChangeListener {
+
+ private static final long serialVersionUID = 1L;
+
+ {
+ // Set Look & Feel
+ try {
+ javax.swing.UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ JPanel ctrlPanel, selectPanel, buttonPanel, displayPanel, ledPanel;
+ private JTextPane jTextPane1;
+ private JPanel jPanelPower;
+ private JPanel jPanelVacuum;
+ private JPanel jPanelSensors;
+ private JPanel jPanelTestPrograms;
+ JComboBox portChoices;
+ JComboBox protocolChoices;
+ JCheckBox handshakeButton;
+ JTextArea displayText;
+ JButton connectButton;
+ JButton netButton;
+ JSlider speedSlider, powerColorSlider, powerColorIntensity;
+ private boolean debug = false;
+ boolean tribbleOn = false;
+ // default values for flags for LEDs
+ boolean redOn = false;
+ boolean greenOn = false;
+ boolean toggleSpot = false;
+ boolean toggleClean = false;
+ boolean toggleMax = false;
+ boolean toggleDirt = false;
+ boolean toggleCheckRobot = false;
+ boolean toggleDock = false;
+ private JPanel jPanelSounds;
+ private JPanel jPanelModes;
+ private JPanel jPanel3;
+ private JPanel jPanel2;
+ private JLabel jLabelCOMM;
+ private JTextField jTextFieldPort;
+ private JLabel jLabelPort;
+ private JPanel jPanel1;
+ private JTextField jTextFieldHost;
+ private JLabel jLabelHost;
+ private JPanel jPanelConfigSerial;
+ private JPanel jPanelConfigNet;
+ private JTabbedPane jTabbedPanelConfig;
+ private JButton jButton4;
+ private JButton jButton3;
+ private JButton jButton2;
+ private JButton jButton1;
+ int power_color = 0;
+ int power_int = 0;
+ RoombaCommSerial roombaCommSerial;
+ RoombaComm roombaComm;
+ RoombaCommTCPClient roombaCommTCPClient;
+ SimpleDateFormat formatter = new SimpleDateFormat(
+ "EEE, dd-MMM-yyyy HH:mm:ss");
+ public RoombaCommPanel() {
+ this(false);
+ }
+
+ public RoombaCommPanel(boolean debug) {
+// super(new BorderLayout());
+ super();
+// this.setContentPane(contentPane)
+ System.out.println("RoombaCommPanel-start");
+ System.out.println(" debug is ("+debug+")");
+ initialize();
+ roombaCommSerial = new RoombaCommSerial();
+ roombaCommTCPClient = new RoombaCommTCPClient();
+ this.debug = debug;
+ roombaCommSerial.debug = debug;
+ roombaCommTCPClient.debug = debug;
+ System.out.println("RoombaCommPanel-makePanels-start");
+ makePanels();
+ System.out.println("RoombaCommPanel-makePanels-end");
+ System.out.println("RoombaCommPanel-end");
+ }
+
+ /**
+ * This method initializes this
+ *
+ */
+ private void initialize() {
+ Dimension defaultSize = new Dimension(800,600);
+ this.addComponentListener(new java.awt.event.ComponentListener() {
+ public void componentResized(java.awt.event.ComponentEvent e) {
+ System.out.println("147-componentResized("+e.getComponent().getWidth()+","+e.getComponent().getHeight()+")"); // TODO Auto-generated Event stub componentResized()
+ }
+ public void componentMoved(java.awt.event.ComponentEvent e) {
+ }
+ public void componentShown(java.awt.event.ComponentEvent e) {
+ }
+ public void componentHidden(java.awt.event.ComponentEvent e) {
+ }
+ });
+ System.out.println("initialize : setting size to (" +defaultSize.getWidth()+","+defaultSize.getHeight()+")");
+ GridBagLayout thisLayout = new GridBagLayout();
+ this.setSize(defaultSize);
+ thisLayout.rowWeights = new double[] {0.0, 0.0, 0.1};
+ thisLayout.rowHeights = new int[] {137, 304, 5};
+ thisLayout.columnWeights = new double[] {0.0, 0.0};
+ thisLayout.columnWidths = new int[] {538, 206};
+ this.setLayout(thisLayout);
+ this.setPreferredSize(new java.awt.Dimension(800, 600)); //638
+// {
+// buttonPanel = new JPanel();
+// this.add(buttonPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+// GridBagLayout buttonPanelLayout = new GridBagLayout();
+// buttonPanelLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
+// buttonPanelLayout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+// buttonPanelLayout.columnWeights = new double[] {0.0, 0.0};
+// buttonPanelLayout.columnWidths = new int[] {10, 10};
+// buttonPanel.setLayout(buttonPanelLayout);
+// buttonPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Commands"),BorderFactory.createEmptyBorder(5,5,5,5)));
+// buttonPanel.setMinimumSize(new java.awt.Dimension(0,10));
+// buttonPanel.setSize(200, 411);
+// buttonPanel.setPreferredSize(new java.awt.Dimension(242, 249));
+// }
+ }
+
+ /**
+ * Set to 'false' to hide the "h/w handshake" button, which seems to be only
+ * needed on Windows
+ */
+ public void setShowHardwareHandhake(boolean b) {
+ handshakeButton.setVisible(b);
+ }
+
+ /** */
+ public boolean connect() {
+ System.out.println("connect-start");
+ String portname = (String) portChoices.getSelectedItem();
+ // roombacomm.debug=true;
+ roombaCommSerial.setWaitForDSR(handshakeButton.isSelected());
+ int i = protocolChoices.getSelectedIndex();
+ roombaCommSerial.setProtocol((i == 0) ? "SCI" : "OI");
+ if (portname == null){
+ // I guess we do not yet have ports so refresh the list
+ setCommPorts();
+ }
+ updateDisplay("connecting to " + portname + "\n");
+ connectButton.setText("connecting");
+ if (portname != null){
+ if (!roombaCommSerial.connect(portname)) {
+ updateDisplay("Couldn't connect to " + portname + "\n");
+ connectButton.setText(" connect ");
+ // roombacomm.debug=false;
+ System.out.println("connect-end (could not connect)");
+ return false;
+ }else{
+ updateDisplay("connected to " + portname + "\n");
+ }
+ }else{
+ updateDisplay("you must first select a COMM port to use before you can attempt to connect");
+ jTabbedPanelConfig.setSelectedIndex(1);
+ return false;
+ }
+ updateDisplay("Roomba startup\n");
+
+ roombaCommSerial.startup();
+ roombaCommSerial.control();
+ roombaCommSerial.playNote(72, 10); // C , test note
+ roombaCommSerial.pause(200);
+
+ connectButton.setText("disconnect");
+ connectButton.setActionCommand("disconnect");
+ roombaComm = roombaCommSerial;
+ // roombacomm.debug=true;
+ updateDisplay("Checking for Roomba... ");
+ if (roombaCommSerial.updateSensors()) {
+ updateDisplay("Roomba found!\n");
+ System.out.println("connect-end");
+ return true;
+ }else{
+ updateDisplay("No Roomba. :( Is it turned on?\n");
+ System.out.println("connect-end");
+ return true ;
+ }
+ }
+
+ public boolean connectNet(String portname) {
+ System.out.println("connectNet called");
+ int i = protocolChoices.getSelectedIndex();
+ if (roombaCommTCPClient == null){
+ roombaCommTCPClient = new RoombaCommTCPClient();
+ roombaCommTCPClient.setConnected(false);
+ }
+ roombaCommTCPClient.setProtocol((i == 0) ? "SCI" : "OI");
+
+ if (!roombaCommTCPClient.connect(portname)) {
+ updateDisplay("Couldn't connect to " + portname);
+ return false;
+ }
+
+ updateDisplay("Roomba startup on port " + portname);
+ roombaCommTCPClient.startup();
+ roombaCommTCPClient.control();
+ // roombacomm.setSensorsAutoUpdate(true);
+ roombaCommTCPClient.pause(30);
+
+ updateDisplay("Checking for Roomba... \n");
+ // roombaCommTCPClient.setDebug(true);
+ if (roombaCommTCPClient.updateSensors()) {
+ updateDisplay("Roomba found!\n");
+ updateDisplay(roombaCommTCPClient.getSensorsAsString());
+ } else {
+ updateDisplay("No Roomba. :( Is it turned on?\n");
+ }
+ updateDisplay("buffer is(" + roombaCommTCPClient.getBuffer() + ")",
+ this.debug);
+ // roombaCommTCPClient.setDebug(false);
+ // roombacomm.updateSensors();
+ updateDisplay("connected (" + roombaCommTCPClient.connected() + ")\n");
+ if (roombaCommTCPClient.connected()) {
+ updateDisplay("Playing some notes\n");
+ roombaCommTCPClient.playNote(72, 10); // C
+ roombaCommTCPClient.pause(200);
+ roombaCommTCPClient.playNote(79, 10); // G
+ roombaCommTCPClient.pause(200);
+ roombaCommTCPClient.playNote(76, 10); // E
+ roombaCommTCPClient.pause(200);
+ netButton.setText("disconnect-net");
+ netButton.setActionCommand("disconnect-net");
+ roombaComm = roombaCommTCPClient; // set the pointer so that the
+ // action stuff can work against
+ // any class
+ }
+
+ // roombaCommSerial.setWaitForDSR(handshakeButton.isSelected());
+ //
+ //
+ // connectButton.setText("connecting");
+ // if( ! roombaCommSerial.connect( portname ) ) {
+ // updateDisplay("Couldn't connect to "+portname+"\n");
+ // connectButton.setText(" connect ");
+ // //roombacomm.debug=false;
+ // return false;
+ // }
+ // updateDisplay("Roomba startup\n");
+ //
+ // roombaCommSerial.startup();
+ // roombaCommSerial.control();
+ // roombaCommSerial.playNote( 72, 10 ); // C , test note
+ // roombaCommSerial.pause( 200 );
+ //
+ // connectButton.setText("disconnect");
+ // connectButton.setActionCommand("disconnect");
+ // //roombacomm.debug=true;
+ // updateDisplay("Checking for Roomba... ");
+ // if( roombaCommSerial.updateSensors() )
+ // updateDisplay("Roomba found!\n");
+ // else
+ // updateDisplay("No Roomba. :( Is it turned on?\n");
+
+ return true;
+ }
+
+ /** */
+ public void disconnect() {
+ roombaCommSerial.disconnect();
+ connectButton.setText(" connect ");
+ connectButton.setActionCommand("connect");
+ }
+
+ public void disconnectNet() {
+ roombaCommTCPClient.disconnect();
+ netButton.setText(" net ");
+ netButton.setActionCommand("net");
+ }
+
+ /**
+ * Play a (MIDI) note, that is, make the Roomba a musical instrument
+ * notenums 32-127: notenum == corresponding note played thru beeper
+ * velocity == duration in number of 1/64s of a second (e.g. 64==1second)
+ * notenum 24: notenum == main vacuum velocity == non-zero turns on, zero
+ * turns off notenum 25: blink LEDs, velcoity is color of Power notenum 28 &
+ * 29: spin left & spin right, velocity is speed
+ *
+ */
+ public void playMidiNote(int notenum, int velocity) {
+ updateDisplay("play note: " + notenum + "," + velocity + "\n");
+ if (!roombaCommSerial.connected())
+ return;
+
+ if (notenum >= 31) { // G and above
+ if (velocity == 0)
+ return;
+ if (velocity < 4)
+ velocity = 4; // has problems at lower durations
+ else
+ velocity = velocity / 2;
+ roombaCommSerial.playNote(notenum, velocity);
+ } else if (notenum == 24) { // C
+ roombaCommSerial.vacuum(!(velocity == 0));
+ } else if (notenum == 25) { // C#
+ boolean lon = (velocity != 0);
+ int inten = (lon) ? 255 : 128; // either full bright or half bright
+ roombaCommSerial.setLEDs(lon, lon, lon, lon, lon, lon,
+ velocity * 2, inten);
+ } else if (notenum == 28) { // E
+ if (velocity != 0)
+ roombaCommSerial.spinLeftAt(velocity * 2);
+ else
+ roombaCommSerial.stop();
+ } else if (notenum == 29) { // F
+ if (velocity != 0)
+ roombaCommSerial.spinRightAt(velocity * 2);
+ else
+ roombaCommSerial.stop();
+ }
+ }
+
+ /** implement actionlistener */
+ public void actionPerformed(ActionEvent event) {
+ String action = event.getActionCommand();
+ updateDisplay(formatter.format(new Date()) + ": action (" + action
+ + ") happened\n", this.debug);
+ // roombacomm.setLEDs(false, false, false, false, false, false, 0, 0);
+ if ("comboBoxChanged".equals(action)) {
+// String portname = (String) portChoices.getSelectedItem();
+ int i = protocolChoices.getSelectedIndex();
+ if (roombaComm != null) {
+ roombaComm.setProtocol((i == 0) ? "SCI" : "OI");
+ } else {
+ updateDisplay(
+ formatter.format(new Date())
+ + ": null roombaComm object found in actionPerformed\n",
+ this.debug);
+ }
+ return;
+ }
+ // updateDisplay(action+"\n");
+ if ("net".equals(action)) {
+ if (jTextFieldHost != null && jTextFieldHost.getText() != null && jTextFieldPort != null && jTextFieldPort.getText() != null){
+ if(connectNet(jTextFieldHost.getText()+":"+jTextFieldPort.getText())){
+ // TODO: find a way to hide/disable the other connect tab until the session is disconnected
+// getJTabbedPanelConfig().getComponentAt(1).setVisible(false); // hide the Serial tab while we are connected with a Net port
+ }else{
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(0).setVisible(true); // make sure the net tab is not hidden when we fail to connect
+ }
+
+ }else{
+ updateDisplay("connect (via net) pressed with missing values:");
+ if (jTextFieldHost != null && jTextFieldHost.getText() != null){
+ updateDisplay(" host :"+jTextFieldHost.getText());
+ }else{
+ updateDisplay(" host : MISSING VALUE");
+ }
+ if (jTextFieldPort != null && jTextFieldPort.getText() != null){
+ updateDisplay(" port :"+jTextFieldPort.getText());
+ }else{
+ updateDisplay(" port : MISSING VALUE");
+ }
+ }
+ return;
+ } else if ("disconnect-net".equals(action)) {
+ disconnectNet();
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(1).setVisible(true); // show the serial tab while we disconnect from the net
+ return;
+ }
+ if ("connect".equals(action)) {
+ if (connect()){
+ // TODO: find a way to hide/disable the other connect tab until the session is disconnected
+ }else{
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // make sure the net tab is not hidden when we fail to connect
+// getJTabbedPanelConfig().repaint(100);
+ }
+ return;
+ } else if ("disconnect".equals(action)) {
+ disconnect();
+ // TODO: reenable/show the other tab(s)
+// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // show the net tab while we disconnect from the serial
+ return;
+ }
+ // stop right here if we're not connected
+ if (roombaComm == null || !roombaComm.connected()) {
+ updateDisplay("not connected!\n");
+ return;
+ }
+
+ if ("stop".equals(action)) {
+ roombaComm.stop();
+ } else if ("forward".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.goForward();
+ } else if ("backward".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.goBackward();
+ } else if ("spinleft".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.spinLeft();
+ } else if ("spinright".equals(action)) {
+ // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n");
+ roombaComm.spinRight();
+ } else if ("turnleft".equals(action)) {
+ roombaComm.turnLeft();
+ } else if ("turnright".equals(action)) {
+ roombaComm.turnRight();
+ } else if ("max".equals(action)) {
+ roombaComm.max();
+ } else if ("dock".equals(action)) {
+ roombaComm.dock();
+ } else if ("test".equals(action)) {
+ LogoA.square(roombaComm, 300);
+ /*
+ * updateDisplay("Playing some notes\n"); roombacomm.playNote( 72,
+ * 10 ); // C roombacomm.pause( 200 ); roombacomm.playNote( 79, 10
+ * ); // G roombacomm.pause( 200 ); roombacomm.playNote( 76, 10 );
+ * // E roombacomm.pause( 200 );
+ *
+ * updateDisplay("Spinning left, then right\n");
+ * roombacomm.spinLeft(); roombacomm.pause(1000);
+ * roombacomm.spinRight(); roombacomm.pause(1000);
+ * roombacomm.stop();
+ *
+ * updateDisplay("Going forward, then backward\n");
+ * roombacomm.goForward(); roombacomm.pause(1000);
+ * roombacomm.goBackward(); roombacomm.pause(1000);
+ * roombacomm.stop();
+ */
+ } else if ("OSU".equals(action)) {
+ updateDisplay("Going to play OSU\n");
+
+ roombaComm.stop();
+ roombaComm.pause(500);
+ // RTTTLPlay rp = new RTTTLPlay();
+ // /dev/cu.KeySerial1
+ // 'tron:d=4,o=5,b=200:8f6,8c6,8g,e,8p,8f6,8c6,8g,8f6,8c6,8g,e,8p,8f6,8c6,8g,e.,2d'
+ // String[]
+ // osuSong={roombacomm.getPortname(),"OSU:d=4,o=5,b=125:a,g,a#,a,8g#,a,8g#,2a,8p,8f,8g,8g#,a,8g#,a,8g#,a,g,f,a,g,8a,g,d,8f,8p,8f,8p,8f,8p,8f,8p,c6,a,g,f,8a#,a,8g,f,p,c6,a,g,a,8a#,a,8a#,c6,p,2d6,d,8c6,a#,g,f,8f,8f,8g,a#,8g,a#,a,2a#"};
+ playSong(
+ roombaComm,
+ "OSU:d=4,o=5,b=125:a,g,a#,a,8g#,a,8g#,2a,8p,8f,8g,8g#,a,8g#,a,8g#,a,g,f,a,g,8a,g,d,8f,8p,8f,8p,8f,8p,8f,8p,c6,a,g,f,8a#,a,8g,f,p,c6,a,g,a,8a#,a,8a#,c6,p,2d6,d,8c6,a#,g,f,8f,8f,8g,a#,8g,a#,a,2a#");
+
+ // playSong(roombacomm,
+ // "Baa Baa Black Sheep:d=4,o=5,b=125:c,c,g,g,8a,8b,8c6,8a,g,p,f,f,e,e,d,d,c");
+ roombaComm.stop();
+ } else if ("Tribble On".equals(action)) {
+ tribbleOn = true;
+ tribbleStart(roombaComm, displayText, tribbleOn);
+ } else if ("reset".equals(action)) {
+ roombaComm.stop();
+ roombaComm.startup();
+ roombaComm.control();
+ } else if ("passive".equals(action)) {
+ //passive / start command
+ roombaComm.start();
+ } else if ("safe".equals(action)) {
+ roombaComm.safe();
+ } else if ("full".equals(action)) {
+ roombaComm.full();
+ } else if ("power-off".equals(action)) {
+ roombaComm.powerOff();
+ } else if ("power-on".equals(action)) {
+ roombaComm.powerOn();
+ } else if ("wakeup".equals(action)) {
+ roombaComm.wakeup();
+ } else if ("beep-lo".equals(action)) {
+ roombaComm.playNote(50, 32); // C1
+ roombaComm.pause(200);
+ } else if ("beep-hi".equals(action)) {
+ roombaComm.playNote(90, 32); // C7
+ roombaComm.pause(200);
+ } else if ("clean".equals(action)) {
+ roombaComm.clean();
+ } else if ("spot".equals(action)) {
+ roombaComm.spot();
+ } else if ("vacuum-on".equals(action)) {
+ roombaComm.vacuum(true);
+ } else if ("vacuum-off".equals(action)) {
+ roombaComm.vacuum(false);
+ } else if ("blink-leds".equals(action)) {
+ roombaComm.setLEDs(true, true, true, true, true, true, 255, 255);
+ roombaComm.pause(300);
+ roombaComm
+ .setLEDs(false, false, false, false, false, false, 0, 128);
+ } else if ("sensors".equals(action)) {
+ if (roombaComm.updateSensors())
+ updateDisplay(roombaComm.sensorsAsString() + "\n");
+ else
+ updateDisplay("couldn't read Roomba. Is it connected?\n");
+ } else if ("chargedata".equals(action)) {
+ if (roombaComm.updateSensors())
+ updateDisplay("*****\n" + roombaComm.chargeDataAsString()
+ + "\n");
+ else
+ updateDisplay("couldn't read Roomba. Is it connected?\n");
+ } else if ("toggleGreen".equals(action)) {
+ setChgGreenLED(roombaComm, !greenOn);
+ } else if ("toggleRed".equals(action)) {
+ setChgRedLED(roombaComm, !redOn);
+ } else if ("toggleSpot".equals(action)) {
+ setChgSpotLED(roombaComm, !toggleSpot);
+ } else if ("toggleClean".equals(action)) {
+ setChgCleanLED(roombaComm, !toggleClean);
+ } else if ("toggleMax".equals(action)) {
+ setChgMaxLED(roombaComm, !toggleMax);
+ } else if ("toggleDirt".equals(action)) {
+ setChgDirtLED(roombaComm, !toggleDirt);
+ } else if ("toggleCheckRobot".equals(action)) {
+ setToggleCheckRobot(roombaComm, !toggleCheckRobot);
+ } else if ("toggleDock".equals(action)) {
+ setToggleDock(roombaComm, !toggleDock);
+ }
+
+ }
+
+ /** implement ChangeListener, for the slider */
+ public void stateChanged(ChangeEvent e) {
+ // System.err.println("stateChanged:"+e);
+ JSlider src = (JSlider) e.getSource();
+ if (!src.getValueIsAdjusting()) {
+ int speed = (int) src.getValue();
+ speed = (speed < 1) ? 1 : speed; // don't allow zero speed
+ updateDisplay("setting speed = " + speed + "\n");
+ roombaComm.setSpeed(speed);
+ }
+ }
+
+ /**
+ *
+ */
+ void makePanels() {
+ System.out.println("makePanels-start");
+ makeSelectPanel();
+ this.add(selectPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2));
+ makeCtrlPanel();
+ makeLedPanel();
+ makeButtonPanel();
+ makeDisplayPanel();
+ this.add(displayPanel, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2));
+ this.add(getJPanel1(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2));
+ this.add(getJPanel2(), new GridBagConstraints(1, 0, 1, 2, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2));
+ this.add(getJPanel3(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+
+ // pack(); //setVisible(true);
+ updateDisplay("RoombaComm, version " + RoombaComm.VERSION + "\n");
+ System.out.println("makePanels-finish");
+ }
+ void makeButtonPanel(){
+// jPanel1.add(buttonPanel, new GridBagConstraints(3, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ }
+ void makeSelectPanel() {
+ selectPanel = new JPanel();
+ GridBagLayout selectPanelLayout = new GridBagLayout();
+ selectPanelLayout.rowWeights = new double[] {0.0, 0.0};
+ selectPanelLayout.rowHeights = new int[] {53, 58};
+ selectPanelLayout.columnWeights = new double[] {0.0, 0.0};
+ selectPanelLayout.columnWidths = new int[] {181, 88};
+ selectPanel.setLayout(selectPanelLayout);
+
+ // Create a combo box with protocols
+ String[] protocols = { "Roomba 1xx-4xx (SCI)", "Roomba 5xx (OI)" };
+ protocolChoices = new JComboBox(protocols);
+ String p = roombaCommSerial.getProtocol();
+ protocolChoices.setSelectedIndex(p.equals("SCI") ? 0 : 1);
+
+// // Create a combo box with choices.
+// // String[] ports = roombaCommSerial.listPorts();
+// // for now short cutting looking for serial ports to speed up start/stop of the UI
+// String[] ports = {"a","b"};
+// System.out.println("found "+ports.length+" serialports");
+// // portChoices = new JComboBox(ports);
+ portChoices = new JComboBox();
+// for (int i = 0; i < ports.length; i++) {
+// String s = ports[i];
+// System.out.println("adding ["+i+"] as "+s);
+// portChoices.addItem(ports[i]);
+// if (s.equals(roombaCommSerial.getPortname())) {
+// System.out.println(" setting port as selected due to "+roombaCommSerial.getPortname());
+// portChoices.setSelectedItem(s);
+// }
+// }
+
+ handshakeButton = new JCheckBox("<html>h/w<br>handshake</html>");
+ // net button
+
+ // Add a border around the select panel.
+ selectPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory
+ .createTitledBorder("Select Roomba Type & Port"), BorderFactory
+ .createEmptyBorder(1, 1, 1, 1)));
+ System.out.println("577-setPreferredSize");
+ selectPanel.setPreferredSize(new java.awt.Dimension(535, 128));
+ System.out.println("577-setPreferredSize-done");
+ selectPanel.add(protocolChoices, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 5, 0, 5), 0, 0));
+ protocolChoices.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0), "Protocal", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ protocolChoices.setToolTipText("Set the protocal based on the roomba hardware version");
+// selectPanel.setJMenuBar(getJMenuBar1());
+ selectPanel.add(handshakeButton, new GridBagConstraints(0, 2, 1, 1, 0.1, 0.1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ handshakeButton.setPreferredSize(new java.awt.Dimension(148, 40));
+ handshakeButton.setText("h/w handshake");
+ handshakeButton.setToolTipText("check if you want to use the hardware handshake setting");
+ selectPanel.add(getJTabbedPanelConfig(), new GridBagConstraints(1, 1, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ selectPanel.add(getJTextPane1(), new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2));
+
+ // Listen to events from the combo box.
+ protocolChoices.addActionListener(this);
+ }
+
+ /**
+ *
+ */
+ void makeCtrlPanel() {
+
+ }
+
+ void makeLedPanel() {
+
+ // if (roombaCommSerial.getProtocol().equalsIgnoreCase("SCI")) {
+// String off = "None";
+// String green = "Green";
+// String red = "Red";
+// String both = "Orange";
+
+ // Group the radio buttons.
+// ButtonGroup group = new ButtonGroup();
+ // }
+
+ // powerColorIntensity.addChangeListener(this);
+ // powerColorIntensity.addChangeListener(new ChangeListener() {setPow
+ // });
+
+ } // End make led panel
+
+ /**
+ *
+ */
+ void makeDisplayPanel() {
+ displayPanel = new JPanel();
+ GridBagLayout displayPanelLayout = new GridBagLayout();
+ displayPanelLayout.rowWeights = new double[] {0.1};
+ displayPanelLayout.rowHeights = new int[] {7};
+ displayPanelLayout.columnWeights = new double[] {0.1};
+ displayPanelLayout.columnWidths = new int[] {7};
+ displayPanel.setLayout(displayPanelLayout);
+ displayPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory
+ .createTitledBorder("Display"), BorderFactory
+ .createEmptyBorder(1, 1, 1, 1)));
+ displayPanel.setPreferredSize(new java.awt.Dimension(412, 45));
+ {
+ displayText = new JTextArea(10, 75);
+ displayPanel.add(displayText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ displayText.setLineWrap(false);
+ DefaultCaret caret = (DefaultCaret)displayText.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+ displayText.setCaret(caret);
+ displayText.setPreferredSize(new java.awt.Dimension(825, 27));
+ displayText.setSize(795, 160);
+
+ }
+
+ JTextArea displayText2 = new JTextArea(25, 30);
+ displayText2.setLineWrap(true);
+ DefaultCaret dc = new DefaultCaret();
+ dc.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+ // only works on Java 1.5+
+ // dc.setUpdatePolicy( DefaultCaret.ALWAYS_UPDATE );
+ JScrollPane scrollPane = new JScrollPane(displayText,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ scrollPane.getVerticalScrollBar();
+ // scrollPane.add(displayText2,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ // JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ displayPanel.add(scrollPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ scrollPane.setSize(795, 163);
+ scrollPane.setPreferredSize(new java.awt.Dimension(766, 81));
+ }
+
+ protected void updateDisplay(String s) {
+ System.out.println("updateDisplay(string): start");
+ displayText.append(s);
+ if (s != null && !(s.endsWith("\n"))) {
+ displayText.append("\n");
+ }
+// displayText.setCaretPosition(displayText.getDocument().getLength());
+ System.out.println("updateDisplay(string): reposition to "+displayText.getDocument().getLength());
+ // TODO: need to find a way to auto scroll to the end of the text so the user can just follow along.
+ // currently on my PC. I am not even able to scroll down to the end of the text. sigh...
+ displayText.getParent().validate();
+ displayText.getParent().repaint(100);
+ displayText.setText(displayText.getText() );
+
+//// for (int i = 0; i < 20; i++) {
+//// displayText.append("This is text " + i + "\n");
+// scrollPaneVbar.setValue(scrollPaneVbar.getMaximum());
+// scrollPaneVbar.paint(scrollPaneVbar.getGraphics());
+// displayText.scrollRectToVisible(displayText.getVisibleRect());
+// if (displayText != null && displayText.getGraphics() != null){
+// displayText.paint(displayText.getGraphics());
+// scrollPaneVbar.getParent().getParent().repaint(100);
+//// displayText.repaint(100);
+// System.out.println("displayText calling paint on displayText "); // this should never happen
+// }else{
+// if (displayText != null){
+// System.out.println("displayText is null"); // this should never happen
+// }
+// if (displayText.getGraphics() != null){
+// System.out.println("displayText.getGraphics is null"); // this might happen?
+// }
+// }
+// try {
+// Thread.sleep(250);
+// } catch (InterruptedException ex) {
+//// Logger.getLogger(ScrollTextView.class.getName()).log(Level.SEVERE, null, ex);
+// System.out.println("InterruptedException while trying to update displayText");
+// }
+//// }
+// // displayText.getParent().validate();
+ System.out.println("updateDisplay(string): end");
+ }
+
+ protected void updateDisplay(String s, boolean onlyDebug) {
+ if (onlyDebug && (roombaCommSerial.debug || roombaCommTCPClient.debug)) {
+ updateDisplay(s);
+ System.out.println(s);
+ }
+ }
+
+ protected static void updateDisplay(RoombaComm roombacomm,
+ JTextArea jtextarea, String s, boolean onlyDebug) {
+ if (onlyDebug && roombacomm.debug) {
+ jtextarea.append(s);
+ jtextarea.setCaretPosition(jtextarea.getDocument().getLength());
+ jtextarea.getParent().validate();
+ jtextarea.getParent().repaint(100);
+ System.out.println(s);
+ }
+ }
+
+ /** Returns an ImageIcon, or null if the path was invalid. */
+ protected static ImageIcon createImageIcon(String path, String description) {
+ // yes, this is supposed to say "RoombaCommTest"
+ java.net.URL imgURL = RoombaCommPanel.class.getResource(path);
+ if (imgURL != null) {
+ return new ImageIcon(imgURL, description);
+ } else {
+ System.err.println("Couldn't find file: " + path);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected 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()", this.debug);
+ 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()\n",
+ this.debug);
+ 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);
+ }
+ }
+ }
+
+ protected static void tribbleStart(RoombaComm roombacomm,
+ JTextArea jtextarea, boolean tribbleOn) {
+ createTribblePurrSong(roombacomm);
+
+ updateDisplay(roombacomm, jtextarea, "Press return to exit.",
+ roombacomm.debug);
+
+ while (tribbleOn) {
+
+ purr(roombacomm, jtextarea);
+
+ if (Math.random() < 0.1)
+ bark(roombacomm, jtextarea);
+
+ roombacomm.pause(1500 + (int) (Math.random() * 500));
+ // tribbleOn = keyIsPressed();
+ roombacomm.updateSensors();
+ boolean b = roombacomm.maxButton();
+ updateDisplay(roombacomm, jtextarea, "max button is (" + b + ")",
+ roombacomm.debug);
+ tribbleOn = (!b);
+ }
+ }
+
+ protected static void purr(RoombaComm roombacomm, JTextArea jtextarea) {
+ updateDisplay(roombacomm, jtextarea, "purr", roombacomm.debug);
+ roombacomm.playSong(5);
+ for (int i = 0; i < 5; i++) {
+ roombacomm.spinLeftAt(75);
+ roombacomm.pause(100);
+ roombacomm.spinRightAt(75);
+ roombacomm.pause(100);
+ roombacomm.stop();
+ }
+ }
+
+ protected static void createTribblePurrSong(RoombaComm roombacomm) {
+ int song[] = { 68, 4, 67, 4, 66, 4, 65, 4, 64, 4, 63, 4, 62, 4, 61, 4,
+ 60, 4, 59, 4, 60, 4, 61, 4, };
+ roombacomm.createSong(5, song);
+ }
+
+ protected static void bark(RoombaComm roombacomm, JTextArea jtextarea) {
+ updateDisplay(roombacomm, jtextarea, "bark", roombacomm.debug);
+ roombacomm.vacuum(true);
+ roombacomm.playNote(50, 5);
+ roombacomm.pause(150);
+ roombacomm.vacuum(false);
+ }
+
+ protected int getPower_color() {
+ return power_color;
+ }
+
+ protected void setPower_color(int power_color) {
+ if (power_color >= 0 && power_color <= 255) {
+ this.power_color = power_color;
+ } else {
+ this.power_color = 0;
+ updateDisplay("invalid power color attempted (" + power_color + ")");
+ }
+ }
+
+ protected int getPower_int() {
+ return power_int;
+ }
+
+ protected void setPower_int(int power_int) {
+
+ if (power_color >= 0 && power_color <= 255) {
+ this.power_int = power_int;
+ } else {
+ this.power_int = 0;
+ updateDisplay("invalid power intensity attempted (" + power_color
+ + ")");
+ }
+ }
+
+ protected void setLEDs(RoombaComm roombacomm) {
+ if (!roombacomm.connected())
+ return;
+ if (roombacomm.getProtocol().equalsIgnoreCase("SCI")) {
+ roombacomm.setLEDs(this.greenOn, this.redOn, this.toggleSpot,
+ this.toggleClean, this.toggleMax, this.toggleDirt,
+ this.power_color, this.power_int);
+ }
+ if (roombacomm.getProtocol().equalsIgnoreCase("OI")) {
+ roombacomm.setLEDsOI(this.toggleCheckRobot, this.toggleSpot,
+ this.toggleDock, this.toggleDirt, this.power_color,
+ this.power_int);
+ }
+ return;
+ }
+
+ protected void setChgGreenLED(RoombaComm roombacomm, boolean green) {
+ this.greenOn = green;
+ updateDisplay("setChgGreenLED", this.debug);
+ this.setLEDs(roombacomm);
+ }
+
+ protected void setChgRedLED(RoombaComm roombacomm, boolean red) {
+ this.redOn = red;
+ updateDisplay("setChgRedLED", this.debug);
+ this.setLEDs(roombacomm);
+ }
+
+ protected void setChgSpotLED(RoombaComm roombacomm, boolean spot) {
+ updateDisplay("setChgSpotLED value(" + spot + ")", this.debug);
+ roombacomm.setChgSpotLED(roombacomm, spot);
+ this.toggleSpot = roombacomm.isToggleSpot();
+ }
+
+ protected void setChgCleanLED(RoombaComm roombacomm, boolean clean) {
+ updateDisplay("setChgCleanLED value(" + clean + ")", this.debug);
+ roombacomm.setChgCleanLED(roombacomm, clean);
+ this.toggleClean = roombacomm.isToggleClean();
+ }
+
+ protected void setChgMaxLED(RoombaComm roombacomm, boolean max) {
+ // updateDisplay("setChgMaxLED",this.debug);
+ // this.toggleMax=max;
+ // this.setLEDs(roombacomm);
+ updateDisplay("setChgMaxLED value(" + max + ")", this.debug);
+ roombacomm.setChgMaxLED(roombacomm, max);
+ this.toggleMax = roombacomm.isToggleMax();
+ }
+
+ protected void setChgDirtLED(RoombaComm roombacomm, boolean dirt) {
+ updateDisplay("setChgDirtLED value(" + dirt + ")", this.debug);
+ roombacomm.setChgDirtLED(roombacomm, dirt);
+ this.toggleDirt = roombacomm.isToggleDirt();
+ }
+
+ public void setToggleCheckRobot(RoombaComm roombacomm, boolean CheckRobot) {
+ updateDisplay("setChgCheckRobotLED value(" + CheckRobot + ")",
+ this.debug);
+ roombacomm.setChgCheckRobotLED(roombacomm, CheckRobot);
+ this.toggleCheckRobot = roombacomm.isToggleCheckRobot();
+ }
+
+ public void setToggleDock(RoombaComm roombacomm, boolean dock) {
+ updateDisplay("setChgDockLED value(" + dock + ")", this.debug);
+ roombacomm.setChgDockLED(roombacomm, dock);
+ this.toggleDock = roombacomm.isToggleDock();
+ }
+
+ protected void setChgPowerColorLED(RoombaComm roombacomm, int power_color) {
+ updateDisplay("setChgPowerColorLED value(" + power_color + ")",
+ this.debug);
+ // this.power_color=power_color;
+ roombacomm.setChgPowerColorLED(roombacomm, power_color);
+ // this.power_color
+ // TODO: Keep track of power color
+ }
+
+ protected void setChgPowerIntensityLED(RoombaComm roombacomm,
+ int power_intensity) {
+ updateDisplay("setChgPowerIntensityLED value(" + power_intensity + ")",
+ this.debug);
+ roombacomm.setChgPowerIntensityLED(roombacomm, power_intensity);
+ // TODO: Keep track of Power Intensity
+ }
+
+ public boolean getToggleCheckRobot() {
+ return toggleCheckRobot;
+ }
+
+ public boolean getToggleDock() {
+ return toggleDock;
+ }
+
+// private JPanel getJPanelMiddle() {
+// if(jPanelMiddle == null) {
+// jPanelMiddle = new JPanel();
+// }
+// return jPanelMiddle;
+// }
+
+ private JTabbedPane getJTabbedPanelConfig() {
+ if(jTabbedPanelConfig == null) {
+ jTabbedPanelConfig = new JTabbedPane();
+ jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(139, 28));
+ jTabbedPanelConfig.addTab("Net", null, getJPanelConfigNet(), "set the TCP network settings here");
+ jTabbedPanelConfig.addTab("Serial", null, getJPanelConfigSerial(), "set the serial settings here");
+ }
+ jTabbedPanelConfig.setMinimumSize(new Dimension(200,100));
+ jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(323, 100));
+ jTabbedPanelConfig.setToolTipText("Use one of the two connection methods to communicate with the Roomba");
+ // Register a change listener
+ jTabbedPanelConfig.addChangeListener(new ChangeListener() {
+ // This method is called whenever the selected tab changes
+ public void stateChanged(ChangeEvent evt) {
+ System.out.println("jTabbedPanelConfig - state changed");
+ JTabbedPane pane = (JTabbedPane)evt.getSource();
+ // Get current tab
+ int sel = pane.getSelectedIndex();
+ System.out.println("jTabbedPanelConfig - tab "+sel+" selected");
+ System.out.println("jTabbedPanelConfig - roombaCommSerial.isConnected() "+roombaCommSerial.isConnected());
+ System.out.println("jTabbedPanelConfig - roombaCommTCPClient.isConnected() "+roombaCommTCPClient.isConnected());
+ if (roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){
+ System.out.println("***** I have no idea why both should ever be connected at the same time... THIS IS STRANGE *****");
+ }
+ if (sel == 1){
+ setCommPorts();
+ if (roombaCommTCPClient.isConnected() && !roombaCommSerial.isConnected()){
+ pane.setSelectedIndex(0);
+ System.out.println("active net connection found setting focus to the correct tab");
+ }
+ }
+ if (sel == 0){
+ if (!roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){
+ pane.setSelectedIndex(1);
+ System.out.println("active Serial connection found setting focus to the correct tab");
+ }
+ }
+ }
+ });
+
+ return jTabbedPanelConfig;
+ }
+
+ private JPanel getJPanelConfigNet() {
+ if(jPanelConfigNet == null) {
+ jPanelConfigNet = new JPanel();
+ GridBagLayout jPanelConfigNetLayout = new GridBagLayout();
+ jPanelConfigNetLayout.rowWeights = new double[] {0.0, 0.1};
+ jPanelConfigNetLayout.rowHeights = new int[] {26, 7};
+ jPanelConfigNetLayout.columnWeights = new double[] {0.1, 0.1, 0.1};
+ jPanelConfigNetLayout.columnWidths = new int[] {7, 7, 7};
+ jPanelConfigNet.setLayout(jPanelConfigNetLayout);
+ jPanelConfigNet.setPreferredSize(new java.awt.Dimension(318, 72));
+ jPanelConfigNet.add(getJLabelHost(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigNet.add(getJTextFieldHost(), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigNet.add(getJLabelPort(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigNet.add(getJTextFieldPort(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ {
+ netButton = new JButton();
+ jPanelConfigNet.add(netButton, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ netButton.setText("connect");
+ netButton.setActionCommand("net");
+ netButton.addActionListener(this);
+ }
+ }
+ return jPanelConfigNet;
+ }
+
+ private JPanel getJPanelConfigSerial() {
+ if(jPanelConfigSerial == null) {
+ jPanelConfigSerial = new JPanel();
+ GridBagLayout jPanelConfigSerialLayout = new GridBagLayout();
+ jPanelConfigSerial.setPreferredSize(new java.awt.Dimension(318, 72));
+ jPanelConfigSerialLayout.rowWeights = new double[] {0.1};
+ jPanelConfigSerialLayout.rowHeights = new int[] {7};
+ jPanelConfigSerialLayout.columnWeights = new double[] {0.1, 0.1, 0.1};
+ jPanelConfigSerialLayout.columnWidths = new int[] {7, 7, 7};
+ jPanelConfigSerial.setLayout(jPanelConfigSerialLayout);
+ jPanelConfigSerial.addFocusListener(new FocusAdapter() {
+ public void focusGained(FocusEvent evt) {
+ jPanelConfigSerialFocusGained(evt);
+ }
+ });
+ {
+ portChoices = new JComboBox();
+ jPanelConfigSerial.add(portChoices, new GridBagConstraints(1, -1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ portChoices.validate();
+ portChoices.repaint();
+ portChoices.addActionListener(this);
+ }
+ {
+ connectButton = new JButton();
+ jPanelConfigSerial.add(connectButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanelConfigSerial.add(getJLabelCOMM(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
+ connectButton.setText(" connect ");
+ connectButton.setActionCommand("connect");
+ connectButton.addActionListener(this);
+ }
+ }
+ return jPanelConfigSerial;
+ }
+
+ private JLabel getJLabelHost() {
+ if(jLabelHost == null) {
+ jLabelHost = new JLabel();
+ jLabelHost.setLayout(null);
+ jLabelHost.setText("Host");
+ jLabelHost.setPreferredSize(new java.awt.Dimension(39, 17));
+ jLabelHost.setLabelFor(getJTextFieldHost());
+ jLabelHost.setToolTipText("Set the hostname/IP address");
+ }
+ return jLabelHost;
+ }
+
+ private JTextField getJTextFieldHost() {
+ if(jTextFieldHost == null) {
+ jTextFieldHost = new JTextField();
+ jTextFieldHost.setText("192.168.15.240");
+ jTextFieldHost.setToolTipText("Set the hostname/IP address");
+ }
+ return jTextFieldHost;
+ }
+
+ private JPanel getJPanel1() {
+ if(jPanel1 == null) {
+ jPanel1 = new JPanel();
+ GridBagLayout jPanel1Layout = new GridBagLayout();
+ jPanel1Layout.rowWeights = new double[] {0.1};
+ jPanel1Layout.rowHeights = new int[] {7};
+ jPanel1Layout.columnWeights = new double[] {0.1, 0.1};
+ jPanel1Layout.columnWidths = new int[] {7, 7};
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1.setPreferredSize(new java.awt.Dimension(800, 320));
+ {
+ ledPanel = new JPanel();
+ GridBagLayout ledPanelLayout = new GridBagLayout();
+ ledPanel.setLayout(ledPanelLayout);
+ jPanel1.add(ledPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ {
+ JPanel ledPanel1 = new JPanel(new GridLayout(3, 3));
+ ledPanel.add(ledPanel1, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ ButtonGroup group = new ButtonGroup();
+ String off="None";
+ String green="Green";
+ String red="Red";
+ String both="Orange";
+ {
+ JRadioButton statusOffButton = new JRadioButton(off);
+ ledPanel1.add(statusOffButton);
+ statusOffButton.setMnemonic(KeyEvent.VK_N);
+ statusOffButton.setActionCommand("StatusLED-" + off);
+ statusOffButton.setSelected(true);
+ group.add(statusOffButton);
+ statusOffButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, false);
+ setChgRedLED(roombaCommSerial, false);
+ }
+ });
+ }
+ {
+ JRadioButton statusGreenButton = new JRadioButton(green);
+ ledPanel1.add(statusGreenButton);
+ statusGreenButton.setMnemonic(KeyEvent.VK_G);
+ statusGreenButton.setActionCommand("StatusLED-" + green);
+ group.add(statusGreenButton);
+ statusGreenButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, true);
+ setChgRedLED(roombaCommSerial, false);
+ }
+ });
+ }
+ {
+ JRadioButton statusRedButton = new JRadioButton(red);
+ ledPanel1.add(statusRedButton);
+ statusRedButton.setMnemonic(KeyEvent.VK_R);
+ statusRedButton.setActionCommand("StatusLED-" + red);
+ group.add(statusRedButton);
+ statusRedButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, false);
+ setChgRedLED(roombaCommSerial, true);
+ }
+ });
+ }
+ {
+ JRadioButton statusBothButton = new JRadioButton(both);
+ ledPanel1.add(statusBothButton);
+ statusBothButton.setMnemonic(KeyEvent.VK_O);
+ statusBothButton.setActionCommand("StatusLED-" + both);
+ group.add(statusBothButton);
+ statusBothButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateDisplay("setting Power Color = " + e.getActionCommand()
+ + "\n");
+ setChgGreenLED(roombaCommSerial, true);
+ setChgRedLED(roombaCommSerial, true);
+ }
+ });
+ }
+ {
+ JButton but_toggleSpot = new JButton();
+ ledPanel1.add(but_toggleSpot);
+ but_toggleSpot.setActionCommand("toggleSpot");
+ but_toggleSpot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spotOn.png")));
+ but_toggleSpot.setPreferredSize(new java.awt.Dimension(46, 50));
+ but_toggleSpot.setSize(40, 40);
+ but_toggleSpot.addActionListener(this);
+ }
+ {
+ JButton but_toggleClean = new JButton();
+ ledPanel1.add(but_toggleClean);
+ but_toggleClean.setActionCommand("toggleClean");
+ but_toggleClean.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_cleanOn.png")));
+ but_toggleClean.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleClean.setSize(40, 40);
+ but_toggleClean.addActionListener(this);
+ }
+ {
+ JButton but_toggleDirt = new JButton();
+ ledPanel1.add(but_toggleDirt);
+ but_toggleDirt.setActionCommand("toggleDirt");
+ but_toggleDirt.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dirtOn.png")));
+ but_toggleDirt.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleDirt.setSize(40, 40);
+ but_toggleDirt.addActionListener(this);
+ }
+ {
+ JButton but_toggleMax = new JButton();
+ ledPanel1.add(but_toggleMax);
+ but_toggleMax.setActionCommand("toggleMax");
+ but_toggleMax.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_maxOn.png")));
+ but_toggleMax.setSize(40, 40);
+ but_toggleMax.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleMax.addActionListener(this);
+ }
+ {
+ JButton but_toggleDock = new JButton();
+ ledPanel1.add(but_toggleDock);
+ but_toggleDock.setActionCommand("toggleDock");
+ but_toggleDock.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dockOn.png")));
+ but_toggleDock.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleDock.setSize(40, 40);
+ but_toggleDock.addActionListener(this);
+ }
+ {
+ JButton but_toggleCheckRobot = new JButton();
+ ledPanel1.add(but_toggleCheckRobot);
+ but_toggleCheckRobot.setActionCommand("toggleCheckRobot");
+ but_toggleCheckRobot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_checkrobotOn.png")));
+ but_toggleCheckRobot.setPreferredSize(new java.awt.Dimension(40, 40));
+ but_toggleCheckRobot.setSize(40, 40);
+ but_toggleCheckRobot.addActionListener(this);
+ }
+ }
+ {
+ powerColorSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 100);
+ ledPanel.add(powerColorSlider, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2));
+ powerColorSlider.setPaintTicks(true);
+ powerColorSlider.setMajorTickSpacing(100);
+ powerColorSlider.setMinorTickSpacing(25);
+ powerColorSlider.setPaintLabels(true);
+ powerColorSlider.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ JSlider src = (JSlider) e.getSource();
+ if (!src.getValueIsAdjusting()) {
+ setPower_color((int) src.getValue());
+ updateDisplay("setting Power Color = " + getPower_color()
+ + "\n");
+ setLEDs(roombaCommSerial);
+ }
+ }
+ });
+ }
+ {
+ JLabel powerColorLabel = new JLabel("PowerColor", JLabel.CENTER);
+ ledPanel.add(powerColorLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2));
+ }
+ {
+ powerColorIntensity = new JSlider(JSlider.HORIZONTAL, 0, 255, 100);
+ ledPanel.add(powerColorIntensity, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2));
+ powerColorIntensity.setPaintTicks(true);
+ powerColorIntensity.setMajorTickSpacing(100);
+ powerColorIntensity.setMinorTickSpacing(25);
+ powerColorIntensity.setPaintLabels(true);
+ powerColorIntensity.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ // System.err.println("stateChanged:"+e);
+ JSlider src = (JSlider) e.getSource();
+ if (!src.getValueIsAdjusting()) {
+ setPower_int((int) src.getValue());
+ updateDisplay("setting Power Color Intensity = "
+ + getPower_int() + "\n");
+ setLEDs(roombaCommSerial);
+ }
+ }
+ });
+ }
+ {
+ JLabel powerColorIntensityLabel = new JLabel("PowerColorIntensity",
+ JLabel.CENTER);
+ ledPanel.add(powerColorIntensityLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2));
+ }
+ ledPanelLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1};
+ ledPanelLayout.rowHeights = new int[] {7, 7, 7, 7, 7};
+ ledPanelLayout.columnWeights = new double[] {0.1};
+ ledPanelLayout.columnWidths = new int[] {7};
+ ledPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("LEDs"),BorderFactory.createEmptyBorder(5,5,5,5)));
+ ledPanel.setPreferredSize(new java.awt.Dimension(288, 313));
+ }
+
+ {
+ ctrlPanel = new JPanel();
+ jPanel1.add(ctrlPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ {
+ JPanel ctrlPanel1 = new JPanel();
+ ctrlPanel.add(ctrlPanel1);
+ {
+ JButton but_turnleft = new JButton();
+ ctrlPanel1.add(but_turnleft);
+ but_turnleft.setActionCommand("turnleft");
+ but_turnleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnleft.png")));
+ but_turnleft.addActionListener(this);
+ }
+ {
+ JButton but_forward = new JButton();
+ ctrlPanel1.add(but_forward);
+ but_forward.setActionCommand("forward");
+ but_forward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_forward.png")));
+ but_forward.addActionListener(this);
+ }
+ {
+ JButton but_turnright = new JButton();
+ ctrlPanel1.add(but_turnright);
+ but_turnright.setActionCommand("turnright");
+ but_turnright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnright.png")));
+ but_turnright.addActionListener(this);
+ }
+ {
+ JButton but_spinleft = new JButton();
+ ctrlPanel1.add(but_spinleft);
+ but_spinleft.setActionCommand("spinleft");
+ but_spinleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinleft.png")));
+ but_spinleft.addActionListener(this);
+ }
+ {
+ JButton but_stop = new JButton();
+ ctrlPanel1.add(but_stop);
+ but_stop.setActionCommand("stop");
+ but_stop.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_stop.png")));
+ but_stop.addActionListener(this);
+ }
+ {
+ JButton but_spinright = new JButton();
+ ctrlPanel1.add(but_spinright);
+ but_spinright.setActionCommand("spinright");
+ but_spinright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinright.png")));
+ but_spinright.addActionListener(this);
+ }
+ {
+ ctrlPanel1.add(new JLabel());
+ }
+ {
+ JButton but_backward = new JButton();
+ ctrlPanel1.add(but_backward);
+ but_backward.setActionCommand("backward");
+ but_backward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_backward.png")));
+ but_backward.setPreferredSize(new java.awt.Dimension(55, 78));
+ but_backward.addActionListener(this);
+ }
+ {
+ ctrlPanel1.add(new JLabel());
+ }
+ ctrlPanel1.setLayout(new GridLayout(3, 3));
+ ctrlPanel1.setPreferredSize(new java.awt.Dimension(194, 199));
+ ctrlPanel1.setSize(194, 199);
+// ctrlPanel1.setTabTitle("");
+ }
+ {
+ speedSlider = new JSlider(JSlider.HORIZONTAL, 0, 500, 200);
+ ctrlPanel.add(speedSlider);
+ speedSlider.setPaintTicks(true);
+ speedSlider.setMajorTickSpacing(100);
+ speedSlider.setMinorTickSpacing(25);
+ speedSlider.setPaintLabels(true);
+ speedSlider.addChangeListener(this);
+ }
+ {
+ JLabel sliderLabel = new JLabel();
+ ctrlPanel.add(sliderLabel);
+ sliderLabel.setText("speed (mm/s)");
+ sliderLabel.setAlignmentX(JLabel.CENTER);
+ }
+ ctrlPanel.setLayout(new BoxLayout(ctrlPanel, BoxLayout.Y_AXIS));
+ ctrlPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Movement"),BorderFactory.createEmptyBorder(5,5,5,5)));
+ }
+ }
+ return jPanel1;
+ }
+
+ private JLabel getJLabelPort() {
+ if(jLabelPort == null) {
+ jLabelPort = new JLabel();
+ jLabelPort.setText("Port");
+ jLabelPort.setPreferredSize(new java.awt.Dimension(39, 33));
+ jLabelPort.setLabelFor(getJTextFieldPort());
+ jLabelPort.setToolTipText("Set the TCP port");
+ }
+ return jLabelPort;
+ }
+
+ private JTextField getJTextFieldPort() {
+ if(jTextFieldPort == null) {
+ jTextFieldPort = new JTextField();
+ jTextFieldPort.setToolTipText("Set the TCP port");
+ jTextFieldPort.setText("5001");
+ }
+ return jTextFieldPort;
+ }
+
+ private void jPanelConfigSerialFocusGained(FocusEvent evt) {
+ System.out.println("jPanelConfigSerial.focusGained, event="+evt);
+ //TODO add your code for jPanelConfigSerial.focusGained
+ // make sure we have a roombaCommSerial object
+ setCommPorts();
+ }
+
+ /**
+ *
+ */
+ private void setCommPorts() {
+ System.out.println("setCommPorts-start");
+ if (roombaCommSerial == null){
+ System.out.println("setCommPorts-roombaCommSerial is null");
+ roombaCommSerial = new RoombaCommSerial(this.debug);
+ }
+ if (portChoices != null) {
+ System.out.println("setCommPorts-portChoices object is not null");
+ // if the list of ports is empty then try to fill it
+ if (portChoices.getItemCount() ==0){
+ // fill in the comm ports (combo box) with choices.
+ System.out.println("setCommPorts-getting list of ports");
+ String[] ports = roombaCommSerial.listPorts();
+ // for now short cutting looking for serial ports to speed up start/stop of the UI
+ // String[] ports = {"a","b"};
+ System.out.println("setCommPorts-found "+ports.length+" serialports");
+ for (int i = 0; i < ports.length; i++) {
+ String s = ports[i];
+ System.out.println("setCommPorts-adding ["+i+"] as "+s);
+ portChoices.addItem(ports[i]);
+ if (s.equals(roombaCommSerial.getPortname())) {
+ System.out.println("setCommPorts- setting port as selected due to "+roombaCommSerial.getPortname());
+ portChoices.setSelectedItem(s);
+ }
+ }
+ portChoices.validate();
+ portChoices.repaint();
+ }
+ }
+ System.out.println("setCommPorts-start");
+ }
+
+ private JLabel getJLabelCOMM() {
+ if(jLabelCOMM == null) {
+ jLabelCOMM = new JLabel();
+ jLabelCOMM.setText("COM");
+ }
+ return jLabelCOMM;
+ }
+
+ private JPanel getJPanel2() {
+ if(jPanel2 == null) {
+ jPanel2 = new JPanel();
+ GridBagLayout jPanel2Layout = new GridBagLayout();
+ jPanel2Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
+ jPanel2Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7};
+ jPanel2Layout.columnWeights = new double[] {0.0};
+ jPanel2Layout.columnWidths = new int[] {150};
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2.setBorder(BorderFactory.createTitledBorder(null, "Commands", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ jPanel2.setPreferredSize(new java.awt.Dimension(233, 505));
+ jPanel2.add(getJPanelModes(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelSounds(), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelTestPrograms(), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelVacuum(), new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanelPower(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ jPanel2.add(getJPanel4(), new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));
+ }
+ return jPanel2;
+ }
+
+ private JPanel getJPanel3() {
+ if(jPanel3 == null) {
+ jPanel3 = new JPanel();
+ jPanel3.setVisible(false);
+ GridBagLayout jPanel3Layout = new GridBagLayout();
+ jPanel3Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
+ jPanel3Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+ jPanel3Layout.columnWeights = new double[] {0.1};
+ jPanel3Layout.columnWidths = new int[] {7};
+ jPanel3.setLayout(jPanel3Layout);
+ {
+ JButton but_nyi = new JButton();
+ jPanel3.add(but_nyi, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi.setText("nyi");
+ but_nyi.setVisible(false);
+ }
+ {
+ JButton but_nyi2 = new JButton();
+ jPanel3.add(but_nyi2, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi2.setText("nyi2");
+ but_nyi2.setVisible(false);
+ }
+ {
+ JButton but_nyi3 = new JButton();
+ jPanel3.add(but_nyi3, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi3.setText("nyi3");
+ but_nyi3.setVisible(false);
+ }
+ {
+ JButton but_nyi4 = new JButton();
+ jPanel3.add(but_nyi4, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi4.setText("nyi4");
+ but_nyi4.setVisible(false);
+ }
+ {
+ JButton but_nyi5 = new JButton();
+ jPanel3.add(but_nyi5, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi5.setText("nyi5");
+ but_nyi5.setVisible(false);
+ }
+ {
+ JButton but_nyi6 = new JButton();
+ jPanel3.add(but_nyi6, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi6.setText("nyi6");
+ but_nyi6.setVisible(false);
+ }
+ {
+ JButton but_nyi7 = new JButton();
+ jPanel3.add(but_nyi7, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ but_nyi7.setText("nyi7");
+ but_nyi7.setVisible(false);
+ }
+ {
+ jButton2 = new JButton();
+ jPanel3.add(jButton2, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton2.setText("nyi5");
+ jButton2.setVisible(false);
+ }
+ {
+ jButton1 = new JButton();
+ jPanel3.add(jButton1, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton1.setText("nyi6");
+ jButton1.setVisible(false);
+ }
+ {
+ jButton3 = new JButton();
+ jPanel3.add(jButton3, new GridBagConstraints(0, 11, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton3.setText("nyi3");
+ jButton3.setVisible(false);
+ }
+ {
+ jButton4 = new JButton();
+ jPanel3.add(jButton4, new GridBagConstraints(0, 12, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jButton4.setText("nyi2");
+ jButton4.setVisible(false);
+ }
+ }
+ return jPanel3;
+ }
+
+ private JPanel getJPanelModes() {
+ if(jPanelModes == null) {
+ jPanelModes = new JPanel();
+ GridBagLayout jPanelModesLayout = new GridBagLayout();
+ jPanelModesLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1};
+ jPanelModesLayout.rowHeights = new int[] {7, 7, 7, 7, 7};
+ jPanelModesLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelModesLayout.columnWidths = new int[] {95, 95};
+ jPanelModes.setLayout(jPanelModesLayout);
+ jPanelModes.setBorder(BorderFactory.createTitledBorder("Modes"));
+ {
+ JButton but_spot = new JButton();
+ jPanelModes.add(but_spot, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_spot.setText("spot");
+ but_spot.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_spot.addActionListener(this);
+ }
+ {
+ JButton but_full = new JButton();
+ jPanelModes.add(but_full, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_full.setText("full");
+ but_full.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_full.addActionListener(this);
+ }
+ {
+ JButton but_safe = new JButton();
+ jPanelModes.add(but_safe, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_safe.setText("safe");
+ but_safe.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_safe.addActionListener(this);
+ }
+ {
+ JButton but_dock = new JButton();
+ jPanelModes.add(but_dock, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_dock.setText("dock");
+ but_dock.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_dock.addActionListener(this);
+ }
+ {
+ JButton but_max = new JButton();
+ jPanelModes.add(but_max, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_max.setText("max");
+ but_max.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_max.addActionListener(this);
+ }
+ {
+ JButton but_clean = new JButton();
+ jPanelModes.add(but_clean, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_clean.setText("clean");
+ but_clean.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_clean.addActionListener(this);
+ }
+ {
+ JButton but_wakeup = new JButton();
+ jPanelModes.add(but_wakeup, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_wakeup.setText("wakeup");
+ but_wakeup.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_wakeup.addActionListener(this);
+ }
+ {
+ JButton but_reset = new JButton();
+ jPanelModes.add(but_reset, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_reset.setText("reset");
+ but_reset.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_reset.addActionListener(this);
+ }
+ {
+ JButton but_passive = new JButton();
+ jPanelModes.add(but_passive, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_passive.setText("passive");
+ but_passive.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_passive.addActionListener(this);
+ }
+ }
+ return jPanelModes;
+ }
+
+ private JPanel getJPanelSounds() {
+ if(jPanelSounds == null) {
+ jPanelSounds = new JPanel();
+ GridBagLayout jPanelSoundsLayout = new GridBagLayout();
+ jPanelSoundsLayout.rowWeights = new double[] {0.1, 0.1};
+ jPanelSoundsLayout.rowHeights = new int[] {7, 7};
+ jPanelSoundsLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelSoundsLayout.columnWidths = new int[] {95, 95};
+ jPanelSounds.setLayout(jPanelSoundsLayout);
+ jPanelSounds.setBorder(BorderFactory.createTitledBorder("Sounds"));
+ {
+ JButton but_OSU = new JButton();
+ jPanelSounds.add(but_OSU, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_OSU.setText("OSU");
+ but_OSU.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_OSU.addActionListener(this);
+ }
+ {
+ JButton but_beeplo = new JButton();
+ jPanelSounds.add(but_beeplo, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_beeplo.setText("beep-lo");
+ but_beeplo.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_beeplo.addActionListener(this);
+ }
+ {
+ JButton but_beephi = new JButton();
+ jPanelSounds.add(but_beephi, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_beephi.setText("beep-hi");
+ but_beephi.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_beephi.addActionListener(this);
+ }
+ }
+ return jPanelSounds;
+ }
+
+ private JPanel getJPanelTestPrograms() {
+ if(jPanelTestPrograms == null) {
+ jPanelTestPrograms = new JPanel();
+ GridBagLayout jPanelTestProgramsLayout = new GridBagLayout();
+ jPanelTestProgramsLayout.rowWeights = new double[] {0.1};
+ jPanelTestProgramsLayout.rowHeights = new int[] {7};
+ jPanelTestProgramsLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelTestProgramsLayout.columnWidths = new int[] {95, 95};
+ jPanelTestPrograms.setLayout(jPanelTestProgramsLayout);
+ jPanelTestPrograms.setBorder(BorderFactory.createTitledBorder("Test Programs"));
+ {
+ JButton but_TribbleOn = new JButton();
+ jPanelTestPrograms.add(but_TribbleOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_TribbleOn.setText("Tribble On");
+ but_TribbleOn.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_TribbleOn.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_TribbleOn.setSize(93, 26);
+ but_TribbleOn.addActionListener(this);
+ }
+ {
+ JButton but_test = new JButton();
+ jPanelTestPrograms.add(but_test, new GridBagConstraints(1, 0, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_test.setText("LogoA.square");
+ but_test.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_test.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_test.setSize(93, 26);
+ but_test.addActionListener(this);
+ }
+ }
+ return jPanelTestPrograms;
+ }
+
+ private JPanel getJPanel4() {
+ if(jPanelSensors == null) {
+ jPanelSensors = new JPanel();
+ GridBagLayout jPanelSensorsLayout = new GridBagLayout();
+ jPanelSensorsLayout.rowWeights = new double[] {0.1};
+ jPanelSensorsLayout.rowHeights = new int[] {7};
+ jPanelSensorsLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelSensorsLayout.columnWidths = new int[] {95, 95};
+ jPanelSensors.setLayout(jPanelSensorsLayout);
+ jPanelSensors.setBorder(BorderFactory.createTitledBorder(null, "Sensors", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ {
+ JButton but_chargedata = new JButton();
+ jPanelSensors.add(but_chargedata, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
+ but_chargedata.setText("chargedata");
+ but_chargedata.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_chargedata.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_chargedata.setSize(93, 26);
+ but_chargedata.addActionListener(this);
+ }
+ {
+ JButton but_sensors = new JButton();
+ jPanelSensors.add(but_sensors, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
+ but_sensors.setText("sensors");
+ but_sensors.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_sensors.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_sensors.setSize(93, 26);
+ but_sensors.addActionListener(this);
+ }
+ }
+ return jPanelSensors;
+ }
+
+ private JPanel getJPanelVacuum() {
+ if(jPanelVacuum == null) {
+ jPanelVacuum = new JPanel();
+ GridBagLayout jPanelVacuumLayout = new GridBagLayout();
+ jPanelVacuumLayout.rowWeights = new double[] {0.1};
+ jPanelVacuumLayout.rowHeights = new int[] {7};
+ jPanelVacuumLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelVacuumLayout.columnWidths = new int[] {95, 95};
+ jPanelVacuum.setLayout(jPanelVacuumLayout);
+ jPanelVacuum.setBorder(BorderFactory.createTitledBorder(BorderFactory.createTitledBorder(""), "Vacuum", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ {
+ JButton but_vacon = new JButton();
+ jPanelVacuum.add(but_vacon, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_vacon.setText("vacuum-on");
+ but_vacon.setSize(93, 26);
+ but_vacon.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_vacon.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_vacon.addActionListener(this);
+ }
+ {
+ JButton but_vacoff = new JButton();
+ but_vacoff.setLayout(null);
+ jPanelVacuum.add(but_vacoff, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_vacoff.setText("vacuum-off");
+ but_vacoff.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_vacoff.setPreferredSize(new java.awt.Dimension(93, 26));
+ but_vacoff.setSize(93, 26);
+ but_vacoff.addActionListener(this);
+ }
+ }
+ return jPanelVacuum;
+ }
+
+ private JPanel getJPanelPower() {
+ if(jPanelPower == null) {
+ jPanelPower = new JPanel();
+ GridBagLayout jPanelPowerLayout = new GridBagLayout();
+ jPanelPower.setBorder(BorderFactory.createTitledBorder(null, "Power", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION));
+ jPanelPowerLayout.rowWeights = new double[] {0.1};
+ jPanelPowerLayout.rowHeights = new int[] {7};
+ jPanelPowerLayout.columnWeights = new double[] {0.1, 0.1};
+ jPanelPowerLayout.columnWidths = new int[] {95, 95};
+ jPanelPower.setLayout(jPanelPowerLayout);
+ {
+ JButton but_powerOn = new JButton();
+ jPanelPower.add(but_powerOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_powerOn.setText("power-on");
+ but_powerOn.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_powerOn.addActionListener(this);
+ }
+ {
+ JButton but_power = new JButton();
+ jPanelPower.add(but_power, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0));
+ but_power.setText("power-off");
+ but_power.setMargin(new java.awt.Insets(2, 2, 2, 2));
+ but_power.addActionListener(this);
+ }
+ }
+ return jPanelPower;
+ }
+
+ private JTextPane getJTextPane1() {
+ if(jTextPane1 == null) {
+ jTextPane1 = new JTextPane();
+ jTextPane1.setText("Set the Protocal, then connect via a network or Serial connection.\nThen you can use the Commands, LED's, and/or Movement controls to operate your Roomba.");
+ jTextPane1.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jTextPane1.setOpaque(false);
+ }
+ return jTextPane1;
+ }
+
+}
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 );
+ }
+ }
+ }
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java
new file mode 100644
index 0000000..34c16cf
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java
@@ -0,0 +1,451 @@
+/*
+ * RoombaComm TCP Interface
+ *
+ *
+ * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com
+ *
+ * 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 java.net.*;
+import java.io.*;
+
+import com.hackingroomba.roombacomm.*;
+/*
+ * SVN id value is $Id: RoombaCommTCPClient.java 182 2010-11-02 03:49:10Z bouchier $
+ */
+public class RoombaCommTCPClient extends RoombaComm implements Runnable
+{
+ String host = null;
+ int port = -1;
+
+ Socket socket;
+ InputStream input;
+ OutputStream output;
+
+ Thread thread;
+ private byte buffer[] = new byte[32768];
+ int bufferIndex;
+ int bufferLast;
+
+
+ // constructor
+ public RoombaCommTCPClient() {
+ super();
+ }
+
+ public RoombaCommTCPClient(RobotConnection rc)
+ {
+ super(rc);
+ }
+
+ public boolean connect(String portid) {
+ String s[] = portid.split(":");
+ if( s.length < 2 ) {
+ logmsg("bad portid "+portid);
+ return false;
+ }else{
+ logmsg("portid is ("+portid+")");
+ port=0;
+ host = s[0];
+ if (host != null){
+ logmsg("Host is ("+host+")");
+ }else{
+ logmsg("Host is (null)");
+ return false;
+ }
+ try {
+ port = Integer.parseInt(s[1]);
+ } catch( Exception e ) {
+ logmsg("bad port "+e);
+ return false;
+ }
+ logmsg("connecting to '"+host+":"+port+"'");
+ try {
+ socket = new Socket(host, port);
+ socket.setKeepAlive(true);
+ socket.setSoTimeout(30000);// timeout in milliseconds - 30 sec
+ input = socket.getInputStream();
+ output = socket.getOutputStream();
+ thread = new Thread(this);
+ thread.start();
+ //this.setConnected(this.updateSensors()); // test if we are connected or not
+ this.setConnected(true); // TODO: fix this properly
+ } catch( Exception e ) {
+ logmsg("connect: "+e); //e.printStackTrace();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void disconnect() {
+ try {
+ // do io streams need to be closed first?
+ if (input != null) input.close();
+ if (output != null) output.close();
+ this.setConnected(false);
+ } catch (Exception e) {
+ System.out.print("exception in disconnect");
+ e.printStackTrace();
+ }
+ thread = null;
+ input = null;
+ output = null;
+
+ try {
+ if (socket != null) socket.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ socket = null;
+ }
+
+ public boolean send(byte[] bytes) {
+ // use robotConnection if it exists, else do it the old way
+ if (robotConnection != null) {
+ return(robotConnection.send(bytes));
+ }
+
+ try {
+ //logmsg("Send_byte( "+bytes+")");
+ output.write(bytes);
+ output.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ public boolean send(int b) { // will also cover char
+ // use robotConnection if it exists, else do it the old way
+ if (robotConnection != null) {
+ return(robotConnection.send(b));
+ }
+
+ try {
+ //logmsg("Send_( "+b+" & 0xff)");
+ output.write(b & 0xff); // for good measure do the &
+ output.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ return false;
+ }
+
+ public void wakeup() {
+ logmsg("wakup unimplemented");
+ }
+
+ public boolean updateSensorsOLD() {
+ sensorsValid = false;
+ for(int i=0; i < 20; i++) {
+ sensors();
+ if( sensorsValid ) {
+ if ((buffer[1] > 1) || (buffer[1] < 0)) {
+ sensorsValid = false;
+ logmsg("updateSensors: received invalid data while attempting to read Roomba sensors!");
+ } else {
+ logmsg("updateSensors: sensorsValid!");
+ }
+ break;
+ }
+ logmsg("updateSensors: pausing...");
+ pause( 50 );
+ }
+ return sensorsValid;
+ }
+
+ // copied from RoombaCommSerial
+ public void computeSensors() {
+ sensorsValid = true;
+ sensorsLastUpdateTime = System.currentTimeMillis();
+ computeSafetyFault();
+ }
+
+ public String[] listPorts() {
+ String s[] = new String[0];
+ if( host !=null && port !=-1 ) {
+ String p[] = { host+":"+port };
+ return p;
+ }
+ return s;
+ }
+
+
+ //////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ */
+ public void run() {
+ bufferLast = 0;
+ while (Thread.currentThread() == thread) {
+ try {
+ while (input != null) {
+ buffer[bufferLast++] = (byte) input.read(); // this will block - wait until there's something
+ //logmsg("TCP received sensor data: " + buffer[bufferLast-1]);
+ if( bufferLast == readRequestLength ) {
+ bufferLast = 0;
+ System.arraycopy(buffer, 0, sensor_bytes, 0, 26);
+ sensorsValid = true;
+ //computeSensors();
+ }
+
+// synchronized (getBuffer()) {
+// if (bufferLast == getBuffer().length) {
+// byte temp[] = new byte[bufferLast << 1];
+// System.arraycopy(getBuffer(), 0, temp, 0, bufferLast);
+// setBuffer(temp);
+// }
+// getBuffer()[bufferLast++] = (byte) input.read();
+// }
+ }
+
+// try {
+// // uhh.. not sure what's best here.. since blocking,
+// // do we need to worry about sleeping much? or is this
+// // gonna try to slurp cpu away from the main applet?
+// Thread.sleep(10);
+// } catch (InterruptedException ex) { }
+
+ } catch (IOException e) {
+ System.err.println("run:"+e);
+ }
+ }
+ }
+
+ /**
+ * Returns the number of bytes that have been read from serial
+ * and are waiting to be dealt with by the user.
+ */
+ public int available() {
+ return (bufferLast - bufferIndex);
+ }
+
+ /**
+ * Ignore all the bytes read so far and empty the buffer.
+ */
+ public void clear() {
+ bufferLast = 0;
+ bufferIndex = 0;
+ }
+
+ /**
+ * Returns a number between 0 and 255 for the next byte that's
+ * waiting in the buffer.
+ * Returns -1 if there was no byte (although the user should
+ * first check available() to see if things are ready to avoid this)
+ */
+ public int read() {
+ if (bufferIndex == bufferLast) return -1;
+
+ synchronized (getBuffer()) {
+ int outgoing = getBuffer()[bufferIndex++] & 0xff;
+ if (bufferIndex == bufferLast) { // rewind
+ bufferIndex = 0;
+ bufferLast = 0;
+ }
+ return outgoing;
+ }
+ }
+
+
+ /**
+ * Returns the next byte in the buffer as a char.
+ * Returns -1, or 0xffff, if nothing is there.
+ */
+ public char readChar() {
+ if (bufferIndex == bufferLast) return (char)(-1);
+ return (char) read();
+ }
+
+
+ /**
+ * Return a byte array of anything that's in the serial buffer.
+ * Not particularly memory/speed efficient, because it creates
+ * a byte array on each read, but it's easier to use than
+ * readBytes(byte b[]) (see below).
+ */
+ public byte[] readBytes() {
+ if (bufferIndex == bufferLast) return null;
+
+ synchronized (getBuffer()) {
+ int length = bufferLast - bufferIndex;
+ byte outgoing[] = new byte[length];
+ System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length);
+
+ bufferIndex = 0; // rewind
+ bufferLast = 0;
+ return outgoing;
+ }
+ }
+
+ /**
+ * Grab whatever is in the serial buffer, and stuff it into a
+ * byte buffer passed in by the user. This is more memory/time
+ * efficient than readBytes() returning a byte[] array.
+ *
+ * Returns an int for how many bytes were read. If more bytes
+ * are available than can fit into the byte array, only those
+ * that will fit are read.
+ */
+ public int readBytes(byte outgoing[]) {
+ if (bufferIndex == bufferLast) return 0;
+
+ synchronized (getBuffer()) {
+ int length = bufferLast - bufferIndex;
+ if (length > outgoing.length) length = outgoing.length;
+ System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length);
+
+ bufferIndex += length;
+ if (bufferIndex == bufferLast) {
+ bufferIndex = 0; // rewind
+ bufferLast = 0;
+ }
+ return length;
+ }
+ }
+
+
+ /**
+ * Reads from the serial port into a buffer of bytes up to and
+ * including a particular character. If the character isn't in
+ * the serial buffer, then 'null' is returned.
+ */
+ public byte[] readBytesUntil(int interesting) {
+ if (bufferIndex == bufferLast) return null;
+ byte what = (byte)interesting;
+
+ synchronized (getBuffer()) {
+ int found = -1;
+ for (int k = bufferIndex; k < bufferLast; k++) {
+ if (getBuffer()[k] == what) {
+ found = k;
+ break;
+ }
+ }
+ if (found == -1) return null;
+
+ int length = found - bufferIndex + 1;
+ byte outgoing[] = new byte[length];
+ System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length);
+
+ bufferIndex = 0; // rewind
+ bufferLast = 0;
+ return outgoing;
+ }
+ }
+
+
+ /**
+ * Reads from the serial port into a buffer of bytes until a
+ * particular character. If the character isn't in the serial
+ * buffer, then 'null' is returned.
+ *
+ * If outgoing[] is not big enough, then -1 is returned,
+ * and an error message is printed on the console.
+ * If nothing is in the buffer, zero is returned.
+ * If 'interesting' byte is not in the buffer, then 0 is returned.
+ */
+ public int readBytesUntil(int interesting, byte outgoing[]) {
+ if (bufferIndex == bufferLast) return 0;
+ byte what = (byte)interesting;
+
+ synchronized (getBuffer()) {
+ int found = -1;
+ for (int k = bufferIndex; k < bufferLast; k++) {
+ if (getBuffer()[k] == what) {
+ found = k;
+ break;
+ }
+ }
+ if (found == -1) return 0;
+
+ int length = found - bufferIndex + 1;
+ if (length > outgoing.length) {
+ System.err.println("readBytesUntil() byte buffer is" +
+ " too small for the " + length +
+ " bytes up to and including char " + interesting);
+ return -1;
+ }
+ //byte outgoing[] = new byte[length];
+ System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length);
+
+ bufferIndex += length;
+ if (bufferIndex == bufferLast) {
+ bufferIndex = 0; // rewind
+ bufferLast = 0;
+ }
+ return length;
+ }
+ }
+
+
+ /**
+ * Return whatever has been read from the serial port so far
+ * as a String. It assumes that the incoming characters are ASCII.
+ *
+ * If you want to move Unicode data, you can first convert the
+ * String to a byte stream in the representation of your choice
+ * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array.
+ */
+ public String readString() {
+ if (bufferIndex == bufferLast) return null;
+ return new String(readBytes());
+ }
+
+
+ /**
+ * Combination of readBytesUntil and readString. See caveats in
+ * each function. Returns null if it still hasn't found what
+ * you're looking for.
+ *
+ * If you want to move Unicode data, you can first convert the
+ * String to a byte stream in the representation of your choice
+ * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array.
+ */
+ public String readStringUntil(int interesting) {
+ byte b[] = readBytesUntil(interesting);
+ if (b == null) return null;
+ return new String(b);
+ }
+
+ /**
+ * @param buffer the buffer to set
+ */
+ public void setBuffer(byte buffer[]) {
+ this.buffer = buffer;
+ }
+
+ /**
+ * @return the buffer
+ */
+ public byte[] getBuffer() {
+ return buffer;
+ }
+
+
+}
+
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java
new file mode 100644
index 0000000..482f064
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java
@@ -0,0 +1,102 @@
+/*
+ * RoombaCommtest -- small GUI to test out RoombaComm
+ *
+ * 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 java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+/**
+ * A simple wrapper for RoombaCommPanel.
+ *
+ */
+public class RoombaCommTest extends JFrame implements WindowListener {
+
+ RoombaCommPanel rcPanel;
+ boolean hwhandshake = false;
+ boolean debug = false;
+
+ public static void main(String[] args) {
+ new RoombaCommTest(args);
+ }
+
+ public RoombaCommTest(String[] args) {
+ super("RoombaCommTest");
+ addWindowListener(this);
+
+ for( int i=0; i < args.length; i++ ) {
+ if( args[i].endsWith("hwhandshake") )
+ hwhandshake = true;
+ else if (args[i].endsWith("debug"))
+ debug = true;
+ }
+
+ rcPanel = new RoombaCommPanel(debug);
+
+ rcPanel.setShowHardwareHandhake( hwhandshake );
+
+ Container content = getContentPane();
+ //content.setBackground(Color.lightGray);
+ content.add( rcPanel ); // , BorderLayout.CENTER );
+
+ setResizable(false);
+ pack();
+ setVisible(true);
+ }
+
+
+ /** implement windowlistener */
+ public void windowClosing(WindowEvent event) {
+ rcPanel.disconnect();
+ System.exit(0);
+ }
+ /** implement windowlistener */
+ public void windowClosed(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowOpened(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowActivated(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowDeactivated(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowIconified(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowDeiconified(WindowEvent event) {
+ // do nothing
+ }
+
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java
new file mode 100644
index 0000000..00cc78b
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java
@@ -0,0 +1,387 @@
+//
+// RoombaCommtest -- small GUI to test out RoombaComm
+//
+// Tod E. Kurt, tod@todbot.com
+//
+//
+
+package com.hackingroomba.roombacomm;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+public class RoombaCommTestOld extends JFrame implements ActionListener,ChangeListener,WindowListener {
+ static final String VERSION = "DO NOT USE";
+
+ JPanel ctrlPanel, selectPanel, buttonPanel, displayPanel;
+ JComboBox portChoices;
+ JTextArea displayText;
+ JButton connectButton;
+ JSlider speedSlider;
+
+ RoombaComm roombacomm;
+
+ public static void main(String[] args) {
+ new RoombaCommTestOld();
+ }
+
+ public RoombaCommTestOld() {
+ super("RoombaCommTestOld");
+ setResizable(false);
+ //setNativeLookAndFeel();
+ addWindowListener(this);
+
+ roombacomm = new RoombaCommSerial();
+
+ Container content = getContentPane();
+ content.setBackground(Color.lightGray);
+
+ makeSelectPanel();
+ content.add( selectPanel, BorderLayout.NORTH );
+
+ makeCtrlPanel();
+ content.add( ctrlPanel, BorderLayout.EAST );
+
+ makeButtonPanel();
+ content.add( buttonPanel, BorderLayout.CENTER );
+
+ makeDisplayPanel();
+ content.add( displayPanel, BorderLayout.SOUTH );
+
+ pack();
+ setVisible(true);
+ displayText.append("RoombaCommTestOld, version "+VERSION+"\n");
+ }
+
+ /** implement actionlistener */
+ public void actionPerformed(ActionEvent event) {
+ String action = event.getActionCommand();
+ if( "comboBoxChanged".equals(action) ) {
+ return;
+ }
+ displayText.append(action+"\n");
+ if( "connect".equals(action) ) {
+ roombacomm.debug=true;
+ if( roombacomm.connected() ) {
+ roombacomm.disconnect(); // just in case
+ connectButton.setText(" connect ");
+ return;
+ }
+ else {
+ displayText.append("could not connect...darn\n");
+ }
+ connectButton.setText("connecting");
+ String portname = (String) portChoices.getSelectedItem();
+ if( ! roombacomm.connect( portname ) ) {
+ displayText.append("Couldn't connect to "+portname+"\n");
+ return;
+ }
+ displayText.append("Roomba startup\n");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.playNote( 72, 10 ); // C
+ roombacomm.pause( 200 );
+ connectButton.setText("disconnect");
+ displayText.append("Roomba connected\n");
+ return;
+ }
+
+ // stop right here if we're not connected
+ if( !roombacomm.connected() ) {
+ displayText.append("not connected!\n");
+ return;
+ }
+
+ if( "stop".equals(action) ) {
+ roombacomm.stop();
+ }
+ else if( "forward".equals(action) ) {
+ roombacomm.goForward();
+ }
+ else if( "backward".equals(action) ) {
+ roombacomm.goBackward();
+ }
+ else if( "spinleft".equals(action) ) {
+ roombacomm.spinLeft();
+ }
+ else if( "spinright".equals(action) ) {
+ roombacomm.spinRight();
+ }
+ else if( "test".equals(action) ) {
+ displayText.append("Playing some notes\n");
+ roombacomm.playNote( 72, 10 ); // C
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 79, 10 ); // G
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 76, 10 ); // E
+ roombacomm.pause( 200 );
+
+ displayText.append("Spinning left, then right\n");
+ roombacomm.spinLeft();
+ roombacomm.pause(1000);
+ roombacomm.spinRight();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+ displayText.append("Going forward, then backward\n");
+ roombacomm.goForward();
+ roombacomm.pause(1000);
+ roombacomm.goBackward();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+ }
+ else if( "reset".equals(action) ) {
+ roombacomm.stop();
+ roombacomm.startup();
+ roombacomm.control();
+ }
+ else if( "power-off".equals(action) ) {
+ roombacomm.powerOff();
+ }
+ else if( "wakeup".equals(action) ) {
+ roombacomm.wakeup();
+ }
+ else if( "beep-lo".equals(action) ) {
+ roombacomm.playNote( 36, 10 ); // C0
+ roombacomm.pause( 200 );
+ }
+ else if( "beep-hi".equals(action) ) {
+ roombacomm.playNote( 120, 10 ); // C7
+ roombacomm.pause( 200 );
+ }
+ else if( "clean".equals(action) ) {
+ roombacomm.clean();
+ }
+ else if( "spot".equals(action) ) {
+ roombacomm.spot();
+ }
+ else if( "vacuum-on".equals(action) ) {
+ roombacomm.vacuum(true);
+ }
+ else if( "vacuum-off".equals(action) ) {
+ roombacomm.vacuum(false);
+ }
+ else if( "sensors".equals(action) ) {
+ if( roombacomm.updateSensors() )
+ displayText.append( roombacomm.sensorsAsString()+"\n");
+ else
+ displayText.append("couldn't read Roomba. Is it connected?\n");
+ }
+ }
+
+ /** implement ChangeListener, for the slider */
+ public void stateChanged(ChangeEvent e) {
+ //System.err.println("stateChanged:"+e);
+ JSlider src = (JSlider)e.getSource();
+ if (!src.getValueIsAdjusting()) {
+ int speed = (int)src.getValue();
+ speed = (speed<1) ? 1 : speed; // don't allow zero speed
+ displayText.append("setting speed = "+speed+"\n");
+ roombacomm.setSpeed(speed);
+ }
+ }
+
+ /**
+ *
+ */
+ void makeCtrlPanel() {
+ JPanel ctrlPanel1 = new JPanel(new GridLayout(3,3));
+
+ ctrlPanel1.add(new JLabel());
+ JButton but_forward =
+ new JButton(createImageIcon("images/forward.png", "forward"));
+ ctrlPanel1.add( but_forward, BorderLayout.NORTH );
+ ctrlPanel1.add(new JLabel());
+
+ JButton but_spinleft =
+ new JButton(createImageIcon("images/spinleft.png", "spinleft"));
+ ctrlPanel1.add( but_spinleft, BorderLayout.WEST );
+ JButton but_stop =
+ new JButton(createImageIcon("images/stop.png", "stop"));
+ ctrlPanel1.add( but_stop, BorderLayout.CENTER );
+ JButton but_spinright =
+ new JButton( createImageIcon("images/spinright.png", "spinright"));
+ ctrlPanel1.add( but_spinright, BorderLayout.EAST);
+
+ ctrlPanel1.add(new JLabel());
+ JButton but_backward =
+ new JButton(createImageIcon("images/backward.png", "backward"));
+ ctrlPanel1.add( but_backward, BorderLayout.SOUTH );
+ ctrlPanel1.add(new JLabel());
+
+ JLabel sliderLabel = new JLabel("speed (mm/s)", JLabel.CENTER);
+ speedSlider = new JSlider(JSlider.HORIZONTAL, 0, 500, 200 );
+ speedSlider.setPaintTicks(true);
+ speedSlider.setMajorTickSpacing(100);
+ speedSlider.setMinorTickSpacing(25);
+ speedSlider.setPaintLabels(true);
+ speedSlider.addChangeListener(this);
+
+ ctrlPanel = new JPanel();
+ ctrlPanel.setLayout( new BoxLayout(ctrlPanel, BoxLayout.Y_AXIS ) );
+
+ ctrlPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Movement"), BorderFactory.createEmptyBorder(5,5,5,5)));
+ ctrlPanel.add( ctrlPanel1 );
+ ctrlPanel.add( speedSlider );
+ ctrlPanel.add( sliderLabel );
+
+ but_spinleft.setActionCommand("spinleft");
+ but_spinright.setActionCommand("spinright");
+ but_forward.setActionCommand("forward");
+ but_backward.setActionCommand("backward");
+ but_stop.setActionCommand("stop");
+ but_spinleft.addActionListener(this);
+ but_spinright.addActionListener(this);
+ but_forward.addActionListener(this);
+ but_backward.addActionListener(this);
+ but_stop.addActionListener(this);
+ }
+
+ /**
+ *
+ */
+ void makeSelectPanel() {
+ selectPanel = new JPanel();
+ //Create a combo box with choices.
+ String[] ports = roombacomm.listPorts();
+ portChoices = new JComboBox(ports);
+ portChoices.setSelectedIndex(0);
+ connectButton = new JButton();
+ connectButton.setText(" connect ");
+ connectButton.setActionCommand("connect");
+
+ //Add a border around the select panel.
+ selectPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Select Roomba Port"), BorderFactory.createEmptyBorder(5,5,5,5)));
+
+ selectPanel.add(portChoices);
+ selectPanel.add(connectButton);
+
+ //Listen to events from the combo box.
+ portChoices.addActionListener(this);
+ connectButton.addActionListener(this);
+ }
+
+ /**
+ *
+ */
+ void makeButtonPanel() {
+ //buttonPanel = new JPanel();
+ //buttonPanel.setLayout( new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));
+ buttonPanel = new JPanel( new GridLayout( 8,2 ) );
+ buttonPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Commands"), BorderFactory.createEmptyBorder(5,5,5,5)));
+
+ JButton but_reset = new JButton("reset");
+ buttonPanel.add( but_reset );
+ but_reset.addActionListener(this);
+
+ JButton but_test = new JButton("test");
+ buttonPanel.add( but_test );
+ but_test.addActionListener(this);
+
+ JButton but_power = new JButton("power-off");
+ buttonPanel.add( but_power );
+ but_power.addActionListener(this);
+
+ JButton but_wakeup = new JButton("wakeup");
+ buttonPanel.add( but_wakeup );
+ but_wakeup.addActionListener(this);
+
+ JButton but_beeplo = new JButton("beep-lo");
+ buttonPanel.add( but_beeplo );
+ but_beeplo.addActionListener(this);
+
+ JButton but_beephi = new JButton("beep-hi");
+ buttonPanel.add( but_beephi );
+ but_beephi.addActionListener(this);
+
+ JButton but_clean = new JButton("clean");
+ buttonPanel.add( but_clean );
+ but_clean.addActionListener(this);
+
+ JButton but_spot = new JButton("spot");
+ buttonPanel.add( but_spot );
+ but_spot.addActionListener(this);
+
+ JButton but_vacon = new JButton("vacuum-on");
+ buttonPanel.add( but_vacon );
+ but_vacon.addActionListener(this);
+
+ JButton but_vacoff = new JButton("vacuum-off");
+ buttonPanel.add( but_vacoff );
+ but_vacoff.addActionListener(this);
+
+ JButton but_sensors = new JButton("sensors");
+ buttonPanel.add( but_sensors );
+ but_sensors.addActionListener(this);
+ }
+
+ /**
+ *
+ */
+ void makeDisplayPanel() {
+ displayPanel = new JPanel();
+ displayPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Display"), BorderFactory.createEmptyBorder(1,1,1,1)));
+
+ displayText = new JTextArea(5,30);
+ displayText.setLineWrap(true);
+ DefaultCaret dc = new DefaultCaret();
+ // only works on Java 1.5+
+ // dc.setUpdatePolicy( DefaultCaret.ALWAYS_UPDATE );
+ displayText.setCaret(dc);
+ JScrollPane scrollPane =
+ new JScrollPane(displayText,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
+ displayPanel.add( scrollPane );
+
+ }
+
+ /** implement windowlistener */
+ public void windowClosing(WindowEvent event) {
+ if( roombacomm.connected() )
+ roombacomm.disconnect();
+ System.exit(0);
+ }
+ /** implement windowlistener */
+ public void windowClosed(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowOpened(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowActivated(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowDeactivated(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowIconified(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowDeiconified(WindowEvent event) {
+ // do nothing
+ }
+
+
+ /** Returns an ImageIcon, or null if the path was invalid. */
+ protected static ImageIcon createImageIcon(String path,
+ String description) {
+ java.net.URL imgURL = RoombaCommTestOld.class.getResource(path);
+ if (imgURL != null) {
+ return new ImageIcon(imgURL, description);
+ } else {
+ System.err.println("Couldn't find file: " + path);
+ return null;
+ }
+ }
+
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java
new file mode 100644
index 0000000..81cd305
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java
@@ -0,0 +1,591 @@
+/*
+ * RoombaRecorder --
+ *
+ * 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 java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import javax.swing.filechooser.*;
+import java.util.*;
+import java.io.*;
+
+/**
+ * A simple wrapper for multiple MacroRecorderPanels
+ *
+ */
+public class RoombaRecorder extends JFrame implements WindowListener,ActionListener {
+
+ static final String helpMsg = "<html>"+
+ //"<h2> </h2>"+
+ "<h2> Roomba Movement Keyboard Shortcuts </h2>"+
+ "<ul>"+
+ "<li> arrow keys -- move Roomba"+
+ "<li> space bar -- stop Roomba"+
+ "<li> L -- blink Roomba LEDs"+
+ "<li> V -- toggle Roomba vacuum"+
+ "<li> T -- test Roomba"+
+ "<li> R -- reset Roomba"+
+ "<li> 1,2,3,4 -- adjust speed"+
+ "</ul>"+
+ "<h2> Application Control Keyboard Shortcuts </h2>"+
+ "<ul>"+
+ "<li> cmd-T -- open new tab"+
+ "<li> cmd-W -- close current tab"+
+ "<li> cmd-arrow-left/right -- cycle through tabs"+
+ "<li> cmd-X/C/V -- cut/copy/paste events between tabs"+
+ "</ul>"+
+ "</html>";
+
+ JPanel contentPane;
+ JPanel recordPanel;
+ JTabbedPane tabbedPane;
+ JButton stopButton,recordButton,playButton;
+ JCheckBox loopButton;
+ JCheckBox playAllButton;
+ JFileChooser fc;
+ File file;
+ boolean hwhandshake = false;
+ int maxRoombas = 16;
+ int lastTab = 0;
+ HashMap clipboard;
+ RoombaRecorderPanel rrpanel; // just to make some of code lines shorter
+ boolean movingForward = false; // is roomba moving forward or not
+ boolean vacuuming = false; // is roomba vacuuming or not
+ int turnval = 0;
+
+ public static void main(String[] args) {
+ new RoombaRecorder(args);
+ }
+
+ public RoombaRecorder(String[] args) {
+ super("RoombaRecorder");
+ addWindowListener(this);
+
+ for(int i=0; i < args.length; i++) {
+ if(args[i].endsWith("hwhandshake"))
+ hwhandshake = true;
+ }
+
+ clipboard = new HashMap();
+ fc = new JFileChooser();
+
+ setResizable(false);
+
+ makeMenu();
+
+ makeRecordPanel();
+
+ // Create and set up the content pane.
+ tabbedPane = new JTabbedPane();
+ tabbedPane.setOpaque(true); //content panes must be opaque
+ openNewTab();
+
+ contentPane = new JPanel(new BorderLayout());
+
+ contentPane.add(recordPanel, BorderLayout.NORTH);
+ contentPane.add(tabbedPane, BorderLayout.CENTER);
+
+ //Container content = getContentPane();
+ setContentPane(contentPane);
+
+ addBindings();
+ bindKeys();
+
+ // Display the window.
+ pack(); //setSize(400, 400);
+ setVisible(true);
+
+ //setFocusableWindowState(false); // prevent focus
+ }
+
+ void makeMenu() {
+ JMenuBar menubar = new JMenuBar();
+ JMenuItem item;
+
+ JMenu file = new JMenu("File");
+ JMenu edit = new JMenu("Edit");
+ JMenu help = new JMenu("Help");
+
+ file.setMnemonic(KeyEvent.VK_F);
+ edit.setMnemonic(KeyEvent.VK_E);
+ help.setMnemonic(KeyEvent.VK_H);
+
+ item = new JMenuItem("New Tab");
+ item.addActionListener(this);
+ file.add(item);
+ item = new JMenuItem("Close Tab");
+ item.addActionListener(this);
+ file.add(item);
+ item = new JMenuItem("Open Set");
+ item.addActionListener(this);
+ file.add(item);
+ item = new JMenuItem("Save Set");
+ item.addActionListener(this);
+ file.add(item);
+ item = new JMenuItem("Quit");
+ item.addActionListener(this);
+ file.add(item);
+
+ item = new JMenuItem("Cut");
+ item.addActionListener(this);
+ edit.add(item);
+ item = new JMenuItem("Copy");
+ item.addActionListener(this);
+ edit.add(item);
+ item = new JMenuItem("Paste");
+ item.addActionListener(this);
+ edit.add(item);
+
+ item = new JMenuItem("Help");
+ item.addActionListener(this);
+ help.add(item);
+
+ menubar.add(file);
+ menubar.add(edit);
+ menubar.add(help);
+
+ setJMenuBar(menubar);
+ }
+
+ void makeRecordPanel() {
+ recordPanel = new JPanel();
+ recordPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Action Record"), BorderFactory.createEmptyBorder())); //1,1,1,1)));
+ stopButton = new JButton(createImageIcon("images/but_transport_stop.png","stop"));
+ stopButton.setActionCommand("stop");
+ stopButton.addActionListener(this);
+ recordPanel.add(stopButton);
+ playButton = new JButton(createImageIcon("images/but_transport_play.png","play"));
+ playButton.setSelectedIcon(createImageIcon("images/but_transport_play_push.png","play"));
+ playButton.setActionCommand("play");
+ playButton.addActionListener(this);
+ recordPanel.add(playButton);
+ recordButton = new JButton(createImageIcon("images/but_transport_record.png","record"));
+ recordButton.setSelectedIcon(createImageIcon("images/but_transport_record_push.png","record"));
+ recordButton.setActionCommand("record");
+ recordButton.addActionListener(this);
+ recordPanel.add(recordButton);
+
+ loopButton = new JCheckBox("loop");
+ loopButton.setText("loop");
+ loopButton.setActionCommand("loop");
+ loopButton.addActionListener(this);
+ recordPanel.add(loopButton);
+
+ playAllButton = new JCheckBox("play all");
+ playAllButton.addActionListener(this);
+ recordPanel.add(playAllButton);
+ }
+
+ void addBindings() {
+ InputMap inputMap = contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+ KeyStroke key;
+
+ //key = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0);
+ //contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).remove(key);
+
+ key = KeyStroke.getKeyStroke('T',KeyEvent.CTRL_MASK);
+ inputMap.put(key, "new-tab");
+ key = KeyStroke.getKeyStroke('T',KeyEvent.META_MASK);
+ inputMap.put(key, "new-tab");
+ key = KeyStroke.getKeyStroke('N',KeyEvent.CTRL_MASK);
+ inputMap.put(key, "new-tab");
+ key = KeyStroke.getKeyStroke('N',KeyEvent.META_MASK);
+ inputMap.put(key, "new-tab");
+
+ key = KeyStroke.getKeyStroke('W',KeyEvent.CTRL_MASK);
+ inputMap.put(key, "close-tab");
+ key = KeyStroke.getKeyStroke('W',KeyEvent.META_MASK);
+ inputMap.put(key, "close-tab");
+
+ key = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,KeyEvent.CTRL_MASK);
+ inputMap.put(key, "right-tab");
+ key = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,KeyEvent.CTRL_MASK);
+ inputMap.put(key, "left-tab");
+ key = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,KeyEvent.META_MASK);
+ inputMap.put(key, "right-tab");
+ key = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,KeyEvent.META_MASK);
+ inputMap.put(key, "left-tab");
+
+ key = KeyStroke.getKeyStroke('X',KeyEvent.CTRL_MASK);
+ inputMap.put(key, "cut");
+ key = KeyStroke.getKeyStroke('X',KeyEvent.META_MASK);
+ inputMap.put(key, "cut");
+ key = KeyStroke.getKeyStroke('C',KeyEvent.CTRL_MASK);
+ inputMap.put(key, "copy");
+ key = KeyStroke.getKeyStroke('C',KeyEvent.META_MASK);
+ inputMap.put(key, "copy");
+ key = KeyStroke.getKeyStroke('V',KeyEvent.CTRL_MASK);
+ inputMap.put(key, "paste");
+ key = KeyStroke.getKeyStroke('V',KeyEvent.META_MASK);
+ inputMap.put(key, "paste");
+
+ ActionMap actionMap = contentPane.getActionMap();
+ actionMap.put("new-tab", new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ openNewTab();
+ } });
+ actionMap.put("close-tab",new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ closeCurrentTab();
+ } });
+ actionMap.put("left-tab",new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ cycleTabLeft();
+ } });
+ actionMap.put("right-tab",new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ cycleTabRight();
+ } });
+ actionMap.put("cut",new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ clipboard = getCurrentTab().cut();
+ } });
+ actionMap.put("copy",new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ clipboard = getCurrentTab().copy();
+ } });
+ actionMap.put("paste",new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ getCurrentTab().paste(clipboard);
+ } });
+ }
+
+ /**
+ * Bind keys to actions using a custom KeyEventPostProcessor
+ * (fixme: why can't this go in the panel?)
+ */
+ void bindKeys() {
+ // ahh, the succinctness of java
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+
+ kfm.addKeyEventDispatcher( new KeyEventDispatcher() {
+ public boolean dispatchKeyEvent(KeyEvent e) {
+ String action=null;
+ if(e.getID() != KeyEvent.KEY_PRESSED)
+ return false;
+ if(e.getModifiers() != 0)
+ return false;
+ switch(e.getKeyCode()) {
+ case KeyEvent.VK_UP:
+ action = "forward";
+ movingForward=true;
+ turnval = 0;
+ break;
+ case KeyEvent.VK_DOWN:
+ action="backward";
+ movingForward=false;
+ turnval = 0;
+ break;
+ case KeyEvent.VK_LEFT:
+ if(movingForward) {
+ turnval = (turnval==0) ? 100 : turnval-turnval/2;
+ if(turnval<10) { action="spinleft"; turnval=0; }
+ else action = "turn"+turnval;
+ }
+ else action = "spinleft";
+ //action = (movingForward) ? "turn100" : "spinleft";
+ break;
+ case KeyEvent.VK_RIGHT:
+ if(movingForward) {
+ turnval = (turnval==0) ? -100 : turnval-turnval/2;
+ if(turnval>-10) { action="spinright"; turnval=0; }
+ else action = "turn"+turnval;
+ }
+ else action = "spinright";
+ //action = (movingForward) ? "turn-100" : "spinright";
+ break;
+ case KeyEvent.VK_SPACE:
+ action="stop";
+ movingForward=false;
+ turnval = 0;
+ break;
+ case KeyEvent.VK_L:
+ action="blink-leds";
+ break;
+ case KeyEvent.VK_R:
+ action="reset";
+ break;
+ case KeyEvent.VK_T:
+ action="test";
+ break;
+ case KeyEvent.VK_V:
+ vacuuming = !vacuuming;
+ action = (vacuuming) ? "vacuum-on":"vacuum-off";
+ break;
+ case KeyEvent.VK_1:
+ action="50";
+ break;
+ case KeyEvent.VK_2:
+ action="100";
+ break;
+ case KeyEvent.VK_3:
+ action="150";
+ break;
+ case KeyEvent.VK_4:
+ action="250";
+ break;
+ case KeyEvent.VK_5:
+ action="400";
+ break;
+ case KeyEvent.VK_6:
+ action="500";
+ break;
+ }
+
+ if(action!=null)
+ getCurrentTab().actionPerformed(new ActionEvent(this,0,action));
+ //System.out.println("process '"+e.getKeyCode()+"' "+e);
+ return true;
+ }
+ });
+ }
+
+ /** Implement Actionlistener */
+ public void actionPerformed(ActionEvent event) {
+ String action = event.getActionCommand();
+ //System.out.println("action:"+action+", event: "+event);
+ if("stop".equals(action)) {
+ playButton.setSelected(false);
+ recordButton.setSelected(false);
+ for(int i=0; i<tabbedPane.getTabCount(); i++) {
+ rrpanel = (RoombaRecorderPanel)tabbedPane.getComponentAt(i);
+ rrpanel.stop();
+ }
+ }
+ else if("play".equals(action)) {
+ playButton.setSelected(true);
+ recordButton.setSelected(false);
+ if( !playAllButton.isSelected() ) {
+ boolean playing = getCurrentTab().play(); // FIXME: hack
+ if(!playing) playButton.setSelected(false);
+ } else {
+ for(int i=0; i<tabbedPane.getTabCount(); i++) {
+ rrpanel= (RoombaRecorderPanel)tabbedPane.getComponentAt(i);
+ rrpanel.play();
+ }
+ }
+ }
+ else if("record".equals(action)) {
+ playButton.setSelected(false);
+ recordButton.setSelected(true);
+ if( !playAllButton.isSelected() ) {
+ getCurrentTab().record();
+ } else {
+ int ir = tabbedPane.getSelectedIndex();
+ for(int i=0; i<tabbedPane.getTabCount(); i++) {
+ rrpanel= (RoombaRecorderPanel)tabbedPane.getComponentAt(i);
+ if( i!=ir ) rrpanel.play();
+ else rrpanel.record();
+ }
+ }
+ }
+ else if("loop".equals(action)) {
+ for(int i=0; i<tabbedPane.getTabCount(); i++) {
+ rrpanel= (RoombaRecorderPanel)tabbedPane.getComponentAt(i);
+ rrpanel.setLooping(loopButton.isSelected());
+ }
+ }
+ else if("New Tab".equals(action)) {
+ openNewTab();
+ }
+ else if("Close Tab".equals(action)) {
+ closeCurrentTab();
+ }
+ else if("Open Set".equals(action)) {
+ loadSet();
+ }
+ else if("Save Set".equals(action)) {
+ saveSet();
+ }
+ else if("Save Set As...".equals(action)) {
+ file = null;
+ saveSet();
+ }
+ else if("Quit".equals(action)) {
+ closeAllTabs();
+ windowClosing(null);
+ }
+ else if("Cut".equals(action)) {
+ clipboard = getCurrentTab().cut();
+ }
+ else if("Copy".equals(action)) {
+ clipboard = getCurrentTab().copy();
+ }
+ else if("Paste".equals(action)) {
+ getCurrentTab().paste(clipboard);
+ }
+ else if("Help".equals(action)) {
+ JOptionPane.showMessageDialog(null,new JLabel(helpMsg));
+ }
+ }
+
+ public RoombaRecorderPanel getCurrentTab() {
+ return (RoombaRecorderPanel) tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
+ }
+
+ public void openNewTab() {
+ int i = tabbedPane.getTabCount();
+ if(i < maxRoombas) { // only 16 roombas supported
+ rrpanel = new RoombaRecorderPanel();
+ rrpanel.setShowHardwareHandhake(hwhandshake);
+ lastTab = lastTab+1;
+ tabbedPane.addTab("Roomba #"+lastTab, null, rrpanel);
+ tabbedPane.setSelectedIndex(lastTab-1);
+ }
+ }
+ public void closeAllTabs() {
+ int c = tabbedPane.getTabCount();
+ for( int i=0; i<c; i++)
+ closeCurrentTab();
+ lastTab = 0;
+ }
+ public void closeCurrentTab() {
+ int i = tabbedPane.getSelectedIndex();
+ rrpanel = (RoombaRecorderPanel)tabbedPane.getComponentAt(i);
+ rrpanel.disconnect();
+ tabbedPane.remove(i);
+ }
+
+ public void cycleTabLeft() {
+ int i = tabbedPane.getSelectedIndex() - 1;
+ if(i<0) i = tabbedPane.getTabCount() - 1;
+ tabbedPane.setSelectedIndex(i);
+ loopButton.setSelected( getCurrentTab().getLooping() );
+ }
+ public void cycleTabRight() {
+ int i = tabbedPane.getSelectedIndex() + 1;
+ if(i==tabbedPane.getTabCount()) i = 0;
+ tabbedPane.setSelectedIndex(i);
+ }
+
+ /**
+ * Load a save set from a chosen file
+ */
+ void loadSet() {
+ int returnVal = fc.showOpenDialog(this);
+ if (returnVal != JFileChooser.APPROVE_OPTION) {
+ System.out.println("Open command cancelled by user.");
+ return;
+ }
+ file = fc.getSelectedFile();
+ System.out.println("Opening: " + file.getName());
+ closeAllTabs();
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ //loopButton.setSelected( ois.readObject() );
+ ArrayList l = (ArrayList) ois.readObject();
+ System.out.println("read "+l.size()+" thingies");
+ ois.close();
+ for(int i=0; i<l.size(); i++) {
+ HashMap m = (HashMap) l.get(i);
+ openNewTab();
+ getCurrentTab().paste(m);
+ }
+ } catch(Exception e) {
+ System.out.println("Open error "+e);
+ }
+ }
+
+ /**
+ * Save the current set to a file.
+ * Will ask for a filename if one hasn't been chosen.
+ */
+ void saveSet() {
+ if( file==null ) {
+ int returnVal = fc.showSaveDialog(this);
+ if (returnVal != JFileChooser.APPROVE_OPTION) {
+ System.out.println("Open command cancelled by user.");
+ return;
+ }
+ file = fc.getSelectedFile();
+ }
+ System.out.println("Saving to: " + file.getName());
+ try {
+ int count = tabbedPane.getTabCount();
+ ArrayList l = new ArrayList();
+ for(int i=0; i< count; i++) {
+ rrpanel= (RoombaRecorderPanel)tabbedPane.getComponentAt(i);
+ l.add(rrpanel.copy());
+ }
+ FileOutputStream fos = new FileOutputStream(file);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ //oos.writeObject(loopButton);
+ //oos.writeObject(
+ oos.writeObject(l);
+ oos.close();
+ } catch( Exception e ) {
+ System.out.println("Save error "+e);
+ }
+ }
+
+ /** implement windowlistener */
+ public void windowClosing(WindowEvent event) {
+ //rcPanel.disconnect();
+ System.exit(0);
+ }
+ /** implement windowlistener */
+ public void windowClosed(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowOpened(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowActivated(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowDeactivated(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowIconified(WindowEvent event) {
+ // do nothing
+ }
+ /** implement windowlistener */
+ public void windowDeiconified(WindowEvent event) {
+ // do nothing
+ }
+
+
+ /** Returns an ImageIcon, or null if the path was invalid. */
+ protected static ImageIcon createImageIcon(String path,
+ String description) {
+ // yes, this is supposed to say "RoombaCommTest"
+ java.net.URL imgURL = RoombaCommPanel.class.getResource(path);
+ if (imgURL != null) {
+ return new ImageIcon(imgURL, description);
+ } else {
+ System.err.println("Couldn't find file: " + path);
+ return null;
+ }
+ }
+
+
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorderPanel.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorderPanel.java
new file mode 100644
index 0000000..5508019
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorderPanel.java
@@ -0,0 +1,582 @@
+/*
+ * RoombaRecorderPanel -
+ *
+ * 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 java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import java.util.*;
+
+/**
+ * A Panel containing controls for testing RoombaComm.
+ * Normally put inside of a frame, for example see RoombaCommTest
+ *
+ */
+public class RoombaRecorderPanel extends JPanel implements ActionListener,Runnable {
+
+ JPanel ctrlPanel, selectPanel, buttonPanel, displayPanel;
+ JComboBox portChoices;
+ JCheckBox handshakeButton;
+ JTextArea displayText;
+ JButton connectButton;
+ JSlider speedSlider;
+
+ RoombaCommSerial roombacomm;
+
+ Thread thread;
+ ArrayList events;
+ ArrayList eventTimes;
+ //ActionEvent[] events;
+ //long[] eventTimes;
+ int eventCount;
+ int eventIndex;
+ int eventMax = 1000; // fixme:
+ long playStartTime;
+ long recStartTime;
+ boolean recording = false;
+ boolean playing = false;
+ boolean looping = false;
+
+ public RoombaRecorderPanel() {
+ super(new BorderLayout());
+
+ roombacomm = new RoombaCommSerial();
+
+ events = new ArrayList();
+ eventTimes = new ArrayList();
+ eventIndex = 0;
+ eventCount = 0;
+
+ makeSelectPanel();
+ add(selectPanel, BorderLayout.NORTH);
+
+ makeCtrlPanel();
+ add(ctrlPanel, BorderLayout.EAST);
+
+ makeButtonPanel();
+ add(buttonPanel, BorderLayout.CENTER);
+
+ makeDisplayPanel();
+ add(displayPanel, BorderLayout.SOUTH);
+
+ updateDisplay("RoombaComm, version "+RoombaComm.VERSION+"\n");
+
+ //bindKeys();
+
+ thread = new Thread(this);
+ thread.start();
+ }
+
+ /**
+ * Set to 'false' to hide the "h/w handshake" button, which seems to be
+ * only needed on Windows
+ */
+ public void setShowHardwareHandhake(boolean b) {
+ handshakeButton.setVisible(b);
+ }
+
+ /** */
+ public boolean connect() {
+ String portname = (String) portChoices.getSelectedItem();
+ roombacomm.debug=true;
+ roombacomm.waitForDSR = handshakeButton.isSelected();
+
+ connectButton.setText("connecting");
+ if(!roombacomm.connect(portname)) {
+ updateDisplay("Couldn't connect to "+portname+"\n");
+ connectButton.setText(" connect ");
+ roombacomm.debug=false;
+ return false;
+ }
+ updateDisplay("Roomba startup\n");
+
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(50);
+ roombacomm.playNote(72, 10); // C , test note
+ roombacomm.pause(200);
+
+ connectButton.setText("disconnect");
+ connectButton.setActionCommand("disconnect");
+ updateDisplay("Roomba connected\n");
+ roombacomm.debug=true;
+ return true;
+ }
+
+ /** */
+ public void disconnect() {
+ roombacomm.disconnect();
+ connectButton.setText(" connect ");
+ connectButton.setActionCommand("connect");
+ }
+
+ public void stop() {
+ //thread.interrupt();
+ if(recording)
+ eventCount = eventIndex;
+ recording = false;
+ playing = false;
+ updateDisplay("Stopped\n");
+ }
+ public boolean play() {
+ if(eventCount==0) {
+ updateDisplay("nothing recorded\n");
+ return false;
+ }
+ if(recording)
+ eventCount = eventIndex;
+ playStartTime = millis();
+ playing = true;
+ recording = false;
+ eventIndex = 0;
+ updateDisplay("Playback...\n");
+ return true;
+ }
+ public void record() {
+ recStartTime = millis();
+ playing = false;
+ recording = true;
+ eventIndex = 0;
+ updateDisplay("Recording...\n");
+ }
+ public void setLooping(boolean b) { looping = b; }
+ public boolean getLooping() { return looping; }
+
+ public HashMap cut() {
+ HashMap m = copy();
+ stop();
+ eventCount=0;
+ return m;
+ }
+ public HashMap copy() {
+ HashMap m = new HashMap();
+ m.put("events",events);
+ m.put("eventTimes",eventTimes);
+ m.put("portname", portChoices.getSelectedItem());
+ updateDisplay("Copied "+eventCount+" events\n");
+ return m;
+ }
+ public void paste(HashMap m) {
+ stop();
+ ArrayList al1 = (ArrayList) m.get("events");
+ ArrayList al2 = (ArrayList) m.get("eventTimes");
+ portChoices.setSelectedItem(m.get("portname"));
+ if(al1!= null && al2!=null) {
+ events = al1;
+ eventTimes = al2;
+ eventCount = events.size();
+ updateDisplay("Pasted "+eventCount+" events\n");
+ }
+ }
+
+ /** Implement Runnable */
+ public void run() {
+ boolean done = false;
+ while(!done) {
+ if(playing && eventCount !=0) {
+ long t = ((Long)eventTimes.get(eventIndex)).longValue();
+ //long t = eventTimes[eventIndex];
+ if(millis() - playStartTime > t) {
+ ActionEvent event = (ActionEvent) events.get(eventIndex);
+ actionPerformed(event);
+ eventIndex++;
+ if(eventIndex == eventCount) {
+ if(!looping) {
+ playing = false;
+ } else {
+ eventIndex = 0;
+ playStartTime = millis();
+ }
+ }
+ }
+ }
+ try {
+ Thread.sleep(10);
+ } catch(Exception e) {
+ done = true;
+ }
+ }
+ }
+
+ /**
+ * Implement Actionlistener, also this is the locus for record & playback
+ *
+ */
+ public void actionPerformed(ActionEvent event) {
+ String action = event.getActionCommand();
+ String actstr = "";
+
+ if("comboBoxChanged".equals(action))
+ return; // don't care about comboBox
+
+ if(recording) {
+ long t = millis() - recStartTime;
+ actstr += t+":r#"+eventIndex+": ";
+ events.add(eventIndex, event);
+ eventTimes.add(eventIndex, new Long(millis()-recStartTime));
+ eventIndex++;
+ }
+ if(playing) {
+ long t = millis() - playStartTime;
+ actstr += t+":p#"+eventIndex+": ";
+ }
+ updateDisplay(actstr+action+"\n"); /// DEBUG
+
+ if("connect".equals(action)) {
+ updateDisplay("connecting...\n");
+ connect();
+ return;
+ }
+ else if("disconnect".equals(action)) {
+ updateDisplay("disconnecting.\n");
+ disconnect();
+ return;
+ }
+ else {
+ try {
+ int speed = Integer.parseInt(action);
+ //updateDisplay("setting speed = "+speed+"\n");
+ roombacomm.setSpeed(speed);
+ return;
+ } catch(NumberFormatException e) { }
+ }
+ // stop right here if we're not connected
+ if(!roombacomm.connected()) {
+ updateDisplay("not connected!\n");
+ return;
+ }
+
+ //updateDisplay(action+"\n");
+ if("stop".equals(action)) {
+ roombacomm.stop();
+ }
+ else if("forward".equals(action)) {
+ roombacomm.goForward();
+ }
+ else if("backward".equals(action)) {
+ roombacomm.goBackward();
+ }
+ else if("spinleft".equals(action)) {
+ roombacomm.spinLeft();
+ }
+ else if("spinright".equals(action)) {
+ roombacomm.spinRight();
+ }
+ else if("turnleft".equals(action)) {
+ roombacomm.turnLeft();
+ }
+ else if("turnright".equals(action)) {
+ roombacomm.turnRight();
+ }
+ else if(action.matches("^turn[-+0-9]+$")) {
+ int turnval = Integer.parseInt(action.substring(4));
+ System.out.println("turnval="+turnval);
+ roombacomm.turn(turnval);
+ }
+ else if("test".equals(action)) {
+ updateDisplay("Playing some notes\n");
+ roombacomm.playNote(72, 10); // C
+ roombacomm.pause(200);
+ roombacomm.playNote(79, 10); // G
+ roombacomm.pause(200);
+ roombacomm.playNote(76, 10); // E
+ roombacomm.pause(200);
+
+ updateDisplay("Spinning left, then right\n");
+ roombacomm.spinLeft();
+ roombacomm.pause(1000);
+ roombacomm.spinRight();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+ updateDisplay("Going forward, then backward\n");
+ roombacomm.goForward();
+ roombacomm.pause(1000);
+ roombacomm.goBackward();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+ }
+ else if("reset".equals(action)) {
+ roombacomm.stop();
+ roombacomm.startup();
+ roombacomm.control();
+ }
+ else if("power-off".equals(action)) {
+ roombacomm.powerOff();
+ }
+ else if("wakeup".equals(action)) {
+ roombacomm.wakeup();
+ }
+ else if("beep-lo".equals(action)) {
+ roombacomm.playNote(36, 10); // C1
+ roombacomm.pause(200);
+ }
+ else if("beep-hi".equals(action)) {
+ roombacomm.playNote(120, 10); // C7
+ roombacomm.pause(200);
+ }
+ else if("clean".equals(action)) {
+ roombacomm.clean();
+ }
+ else if("spot".equals(action)) {
+ roombacomm.spot();
+ }
+ else if("vacuum-on".equals(action)) {
+ roombacomm.vacuum(true);
+ }
+ else if("vacuum-off".equals(action)) {
+ roombacomm.vacuum(false);
+ }
+ else if("blink-leds".equals(action)) {
+ roombacomm.setLEDs(true,true,true, true,true,true, 255, 255);
+ roombacomm.pause(300);
+ roombacomm.setLEDs(false,false,false, false,false,false, 0, 128);
+ }
+ else if("sensors".equals(action)) {
+ if(roombacomm.updateSensors())
+ updateDisplay(roombacomm.sensorsAsString()+"\n");
+ else
+ updateDisplay("couldn't read Roomba. Is it connected?\n");
+ }
+ }
+
+ /**
+ *
+ */
+ void makeSelectPanel() {
+ selectPanel = new JPanel();
+ //Create a combo box with choices.
+ String[] ports = roombacomm.listPorts();
+ portChoices = new JComboBox(ports);
+ portChoices.setSelectedIndex(0);
+ connectButton = new JButton();
+ connectButton.setText(" connect ");
+ connectButton.setActionCommand("connect");
+ handshakeButton = new JCheckBox("<html>h/w<br>handshake</html>");
+
+ //Add a border around the select panel.
+ selectPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Select Roomba Port"), BorderFactory.createEmptyBorder()));
+
+ selectPanel.add(portChoices);
+ selectPanel.add(connectButton);
+ selectPanel.add(handshakeButton);
+
+ //Listen to events from the combo box.
+ portChoices.addActionListener(this);
+ connectButton.addActionListener(this);
+ }
+
+ /**
+ *
+ */
+ void makeCtrlPanel() {
+ JPanel ctrlPanel1 = new JPanel(new GridLayout(3,3));
+
+ JButton but_turnleft =
+ new JButton(createImageIcon("images/but_turnleft.png","turnleft"));
+ ctrlPanel1.add(but_turnleft);
+ JButton but_forward =
+ new JButton(createImageIcon("images/but_forward.png","forward"));
+ ctrlPanel1.add(but_forward);
+ JButton but_turnright =
+ new JButton(createImageIcon("images/but_turnright.png","turnright"));
+ ctrlPanel1.add(but_turnright);
+
+ JButton but_spinleft =
+ new JButton(createImageIcon("images/but_spinleft.png","spinleft"));
+ ctrlPanel1.add(but_spinleft);
+ JButton but_stop =
+ new JButton(createImageIcon("images/but_stop.png", "stop"));
+ ctrlPanel1.add(but_stop);
+ JButton but_spinright =
+ new JButton(createImageIcon("images/but_spinright.png","spinright"));
+ ctrlPanel1.add(but_spinright);
+
+ ctrlPanel1.add(new JLabel());
+ JButton but_backward =
+ new JButton(createImageIcon("images/but_backward.png","backward"));
+ ctrlPanel1.add(but_backward);
+ ctrlPanel1.add(new JLabel());
+
+ JLabel speedLabel = new JLabel("speed (mm/s)", JLabel.CENTER);
+ JRadioButton speed050button = new JRadioButton("50");
+ JRadioButton speed100button = new JRadioButton("100");
+ JRadioButton speed150button = new JRadioButton("150");
+ JRadioButton speed250button = new JRadioButton("250");
+ JRadioButton speed400button = new JRadioButton("400");
+ JRadioButton speed500button = new JRadioButton("500");
+ speed050button.setVerticalTextPosition(SwingConstants.BOTTOM);
+ speed050button.setHorizontalTextPosition(SwingConstants.CENTER);
+ speed100button.setVerticalTextPosition(SwingConstants.BOTTOM);
+ speed100button.setHorizontalTextPosition(SwingConstants.CENTER);
+ speed150button.setVerticalTextPosition(SwingConstants.BOTTOM);
+ speed150button.setHorizontalTextPosition(SwingConstants.CENTER);
+ speed250button.setVerticalTextPosition(SwingConstants.BOTTOM);
+ speed250button.setHorizontalTextPosition(SwingConstants.CENTER);
+ speed400button.setVerticalTextPosition(SwingConstants.BOTTOM);
+ speed400button.setHorizontalTextPosition(SwingConstants.CENTER);
+ speed500button.setVerticalTextPosition(SwingConstants.BOTTOM);
+ speed500button.setHorizontalTextPosition(SwingConstants.CENTER);
+
+ speed250button.setSelected(true);
+
+ speed050button.addActionListener(this);
+ speed100button.addActionListener(this);
+ speed150button.addActionListener(this);
+ speed250button.addActionListener(this);
+ speed400button.addActionListener(this);
+ speed500button.addActionListener(this);
+
+ ButtonGroup speedGroup = new ButtonGroup();
+ speedGroup.add(speed050button);
+ speedGroup.add(speed100button);
+ speedGroup.add(speed150button);
+ speedGroup.add(speed250button);
+ speedGroup.add(speed400button);
+ speedGroup.add(speed500button);
+
+ JPanel ctrlPanel2 = new JPanel();
+ ctrlPanel2.add(speed050button);
+ ctrlPanel2.add(speed100button);
+ ctrlPanel2.add(speed150button);
+ ctrlPanel2.add(speed250button);
+ ctrlPanel2.add(speed400button);
+ ctrlPanel2.add(speed500button);
+
+ ctrlPanel = new JPanel();
+ ctrlPanel.setLayout(new BoxLayout(ctrlPanel, BoxLayout.Y_AXIS));
+ ctrlPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Movement"), BorderFactory.createEmptyBorder()));//5,5,5,5)));
+ ctrlPanel.add(ctrlPanel1);
+ ctrlPanel.add(ctrlPanel2);
+ ctrlPanel.add(speedLabel);
+
+ but_turnleft.setActionCommand("turnleft");
+ but_turnright.setActionCommand("turnright");
+ but_spinleft.setActionCommand("spinleft");
+ but_spinright.setActionCommand("spinright");
+ but_forward.setActionCommand("forward");
+ but_backward.setActionCommand("backward");
+ but_stop.setActionCommand("stop");
+ but_turnleft.addActionListener(this);
+ but_turnright.addActionListener(this);
+ but_spinleft.addActionListener(this);
+ but_spinright.addActionListener(this);
+ but_forward.addActionListener(this);
+ but_backward.addActionListener(this);
+ but_stop.addActionListener(this);
+ }
+
+ /**
+ *
+ */
+ void makeButtonPanel() {
+ buttonPanel = new JPanel(new GridLayout(8,2));
+ buttonPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Commands"), BorderFactory.createEmptyBorder()));//5,5,5,5)));
+
+ JButton but_reset = new JButton("reset");
+ JButton but_test = new JButton("test");
+ JButton but_power = new JButton("power-off");
+ JButton but_wakeup = new JButton("wakeup");
+ JButton but_beeplo = new JButton("beep-lo");
+ JButton but_beephi = new JButton("beep-hi");
+ JButton but_clean = new JButton("clean");
+ JButton but_spot = new JButton("spot");
+ JButton but_vacon = new JButton("vacuum-on");
+ JButton but_vacoff = new JButton("vacuum-off");
+ JButton but_blinkleds = new JButton("blink-leds");
+ JButton but_sensors = new JButton("sensors");
+
+ buttonPanel.add(but_reset);
+ buttonPanel.add(but_test);
+ buttonPanel.add(but_power);
+ buttonPanel.add(but_wakeup);
+ buttonPanel.add(but_beeplo);
+ buttonPanel.add(but_beephi);
+ buttonPanel.add(but_clean);
+ buttonPanel.add(but_spot);
+ buttonPanel.add(but_vacon);
+ buttonPanel.add(but_vacoff);
+ buttonPanel.add(but_blinkleds);
+ buttonPanel.add(but_sensors);
+
+ but_reset.addActionListener(this);
+ but_test.addActionListener(this);
+ but_power.addActionListener(this);
+ but_wakeup.addActionListener(this);
+ but_beeplo.addActionListener(this);
+ but_beephi.addActionListener(this);
+ but_clean.addActionListener(this);
+ but_spot.addActionListener(this);
+ but_vacon.addActionListener(this);
+ but_vacoff.addActionListener(this);
+ but_blinkleds.addActionListener(this);
+ but_sensors.addActionListener(this);
+ }
+
+ /**
+ *
+ */
+ void makeDisplayPanel() {
+ displayPanel = new JPanel();
+ displayPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Display"), BorderFactory.createEmptyBorder()));//1,1,1,1)));
+
+ displayText = new JTextArea(5,30);
+ displayText.setEditable(false);
+ displayText.setLineWrap(true);
+ DefaultCaret dc = new DefaultCaret();
+ // only works on Java 1.5+
+ //dc.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+ displayText.setCaret(dc);
+ JScrollPane scrollPane =
+ new JScrollPane(displayText,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ displayPanel.add(scrollPane);
+ }
+
+ public void updateDisplay(String s) {
+ displayText.append(s);
+ displayText.setCaretPosition(displayText.getDocument().getLength());
+ }
+
+ /** Returns an ImageIcon, or null if the path was invalid. */
+ protected static ImageIcon createImageIcon(String path,
+ String description) {
+ // yes, this is supposed to say "RoombaCommTest"
+ java.net.URL imgURL = RoombaCommPanel.class.getResource(path);
+ if (imgURL != null) {
+ return new ImageIcon(imgURL, description);
+ } else {
+ System.err.println("Couldn't find file: " + path);
+ return null;
+ }
+ }
+
+ public int millis() {
+ int millisOffset = 0;
+ return (int) (System.currentTimeMillis() - millisOffset);
+ }
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java b/roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java
new file mode 100644
index 0000000..3c55daa
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java
@@ -0,0 +1,148 @@
+/*
+ * roombacomm.SimpleTest
+ *
+ * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com
+ *
+ * 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;
+
+/**
+ A simple test of RoombaComm and RoombaCommSerial functionality.
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.SimpleTest /dev/cu.KeySerial1<br>
+ Usage:
+ roombacomm.SimpleTest serialportname [protocol] [options]<br>
+ where:
+ protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ -flush -- flush on sends(), normally not needed
+ * </pre>
+ *
+ */
+public class SimpleTest {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.SimpleTest <serialportname> [protocol] [options]\n" +
+ "where:\n"+
+ "protocol (optional) is SCI or OI\n"+
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ " -flush -- flush on sends(), normally not needed\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+ static boolean flush = false;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ for( int i=1; i < args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[1].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ else if( args[i].endsWith("flush") )
+ flush = true;
+ }
+
+
+ roombacomm.debug = debug;
+ roombacomm.flushOutput = flush;
+
+ String portlist[] = roombacomm.listPorts();
+ System.out.println("Available ports:");
+ for(int i=0;i<portlist.length;i++)
+ System.out.println(" "+i+": "+portlist[i]);
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup on port "+portname);
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(30);
+
+ System.out.println("Checking for Roomba... ");
+ if( roombacomm.updateSensors() )
+ System.out.println("Roomba found!");
+ else
+ System.out.println("No Roomba. :( Is it turned on?");
+
+ //roombacomm.updateSensors();
+
+ System.out.println("Playing some notes");
+ roombacomm.playNote( 72, 10 ); // C
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 79, 10 ); // G
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 76, 10 ); // E
+ roombacomm.pause( 200 );
+
+ System.out.println("Spinning left, then right");
+ roombacomm.spinLeft();
+ roombacomm.pause(1000);
+ roombacomm.spinRight();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+ System.out.println("Going forward, then backward");
+ roombacomm.goForward();
+ roombacomm.pause(1000);
+ roombacomm.goBackward();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+
+ System.out.println("Moving via send()");
+ byte cmd[] = {(byte)RoombaComm.DRIVE,
+ (byte)0x00,(byte)0xfa, (byte)0x00,(byte)0x00};
+ roombacomm.send( cmd ) ;
+ roombacomm.pause(1000);
+ roombacomm.stop();
+ cmd[1] = (byte)0xff;
+ cmd[2] = (byte)0x05;
+ roombacomm.send( cmd ) ;
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Spiral.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiral.java
new file mode 100644
index 0000000..9333d04
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiral.java
@@ -0,0 +1,127 @@
+/*
+ * roombacomm.Spiral -- test out the DRIVE command, showing a spiral
+ *
+ * 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 java.io.*;
+
+/**
+ Make the Roomab drive in a spiral.
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.Spiral /dev/cu.KeySerial1<br>
+ Usage:
+ roombacomm.Spiral <serialportname> [protocol] [options]<br>
+ where: protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ </pre>
+
+ */
+public class Spiral {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Spiral <serialportname> [protocol] [options]\n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ for( int i=1; i<args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ }
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.pause(100);
+ roombacomm.control();
+ roombacomm.pause(100);
+
+ int pausetime = 500;
+ int speed = 100;
+ int r = 10;
+ int dr = 20;
+
+ System.out.println("Press return to exit.");
+ boolean done = false;
+ while( !done ) {
+ System.out.println("r:"+r);
+
+ roombacomm.drive( speed, r );
+ r += dr;
+ if( Math.abs(r) > 410 ) {
+ dr = -dr;
+ }
+
+ done = keyIsPressed();
+
+ roombacomm.pause( pausetime );
+ }
+
+ roombacomm.stop();
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+ /** check for keypress, return true if so */
+ public static boolean keyIsPressed() {
+ boolean press = false;
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ press = true;
+ }
+ } catch( IOException ioe ) { }
+ return press;
+ }
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java
new file mode 100644
index 0000000..6bac0ce
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java
@@ -0,0 +1,139 @@
+/*
+ * roombacomm.Spiro1 -- a Spirograph-like example
+ *
+ * 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 java.io.*;
+
+/**
+ A Spirograph-like example
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.Spiro1 /dev/cu.KeySerial1 velocity radius waittime<br>
+ Usage: \n"+
+ roombacomm.Spiro1 <serialportname> [protocol] <velocity> <radius> <waittime> [options]<br>
+ where:
+ protocol (optional) is SCI or OI
+ velocity and radius in mm, waittime in milliseconds
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ </pre>
+ */
+public class Spiro1 {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Spiro1 <serialportname> [protocol] <velocity> <radius> <waittime> [options]\n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ "velocity and radius in mm, waittime in milliseconds\n"+
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+
+ public static void main(String[] args) {
+ if( args.length < 4 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ int argOffset = 0;
+ if (args[1].equals("SCI") || (args[1].equals("OI"))) {
+ roombacomm.setProtocol(args[1]);
+ argOffset = 1;
+ }
+
+ int velocity=0, radius=0, waittime=0;
+ try {
+ velocity = Integer.parseInt( args[1+argOffset] );
+ radius = Integer.parseInt( args[2+argOffset] );
+ waittime = Integer.parseInt( args[3+argOffset] );
+ } catch( Exception e ) {
+ System.err.println("Couldn't parse velocity & radius");
+ System.exit(1);
+ }
+
+ for( int i=4+argOffset; i < args.length; i++ ) {
+ if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ }
+
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(30);
+ roombacomm.full();
+ roombacomm.pause(50);
+
+ int v = velocity;
+ int r = radius;
+ int dr = -10;
+
+ boolean done = false;
+ while( !done ) {
+ roombacomm.drive( v,r );
+ roombacomm.pause( waittime );
+ roombacomm.drive( v, (int) r / Math.abs(dr) );
+ roombacomm.pause( waittime );
+ r += -10;
+ done = keyIsPressed();
+ }
+
+ roombacomm.stop();
+ roombacomm.safe();
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+ /** check for keypress, return true if so */
+ public static boolean keyIsPressed() {
+ boolean press = false;
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ press = true;
+ }
+ } catch( IOException ioe ) { }
+ return press;
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java
new file mode 100644
index 0000000..42a9742
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java
@@ -0,0 +1,144 @@
+/*
+ * roombacomm.Spiro2 -- a Spirograph-like example
+ *
+ * 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 java.io.*;
+
+/**
+ * A Spirograph-like example
+ * <p>
+ * Run it with something like: <pre>
+ * java roombacomm.Spiro1 /dev/cu.KeySerial1 velocity radius waittime
+ * </pre>
+ *
+ */
+public class Spiro2 {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Spiro2 <serialportname> <velocity> <radius> <radius2> <waittime> <waittime2> [options]\n" +
+ "where [options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+
+ public static void main(String[] args) {
+ if( args.length < 6 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ int velocity=0, radius=0, waittime=0, waittime2=0, radius2=0;
+ try {
+ velocity = Integer.parseInt( args[1] );
+ radius = Integer.parseInt( args[2] );
+ radius2 = Integer.parseInt( args[3] );
+ waittime = Integer.parseInt( args[4] );
+ waittime2 = Integer.parseInt( args[5] );
+ } catch( Exception e ) {
+ System.err.println("Couldn't parse arguments");
+ System.exit(1);
+ }
+
+ for( int i=4; i < args.length; i++ ) {
+ if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("hwhandshake") )
+ hwhandshake = true;
+ }
+
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ roombacomm.debug = debug;
+ roombacomm.waitForDSR = hwhandshake;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(30);
+ roombacomm.full();
+ roombacomm.pause(50);
+
+ //int v = velocity;
+ //int r = radius;
+ //int dr = dradius;
+
+ int w,dr;
+
+ boolean done = false;
+ while( !done ) {
+ roombacomm.drive( velocity,radius );
+ //roombacomm.pause( waittime );
+
+ // lets try some easing
+ w = waittime / 10; // divide into 10 msec chucks
+ dr = (radius2 - radius) / 10;
+ System.out.println("easing "+w+" times at "+dr+" radius");
+ for( int i =0; i<w; i++) {
+ roombacomm.drive( velocity,radius + dr );
+ roombacomm.pause( 10 );
+ }
+
+ roombacomm.drive( velocity, radius2 );
+ //roombacomm.pause( waittime2 );
+
+ // lets try some easing
+ w = waittime2 / 10; // divide into 10 msec chucks
+ dr = (radius - radius2) / 10;
+ System.out.println("easing "+w+" times at "+dr+" radius");
+ for( int i =0; i<w; i++) {
+ roombacomm.drive( velocity,radius2 + dr );
+ roombacomm.pause( 10 );
+ }
+
+ done = keyIsPressed();
+ }
+
+ roombacomm.stop();
+ roombacomm.safe();
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+ /** check for keypress, return true if so */
+ public static boolean keyIsPressed() {
+ boolean press = false;
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ press = true;
+ }
+ } catch( IOException ioe ) { }
+ return press;
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Spy.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Spy.java
new file mode 100644
index 0000000..72ed42c
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Spy.java
@@ -0,0 +1,132 @@
+/*
+ * roombacomm.Spy
+ *
+ * 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 java.io.*;
+
+/**
+ Spy on the Roomba as it goes about its normal business
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.Spy /dev/cu.KeySerial1<br>
+ Usage:
+ roombacomm.Spy <serialportname> [protocol] [options]<br>
+ where: protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -pause <n> -- pause n millseconds between sensor read
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -flush -- flush on sends(), normally not needed
+ -power -- power on/off Roomba (if interface supports it)
+ </pre>
+ */
+public class Spy {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Spy <serialportname> [protocol] [options]\n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ " [options] can be one or more of:\n"+
+ " -pause <n> -- pause n millseconds between sensor read\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -flush -- flush on sends(), normally not needed\n"+
+ " -power -- power on/off Roomba (if interface supports it)\n"+
+ "\n";
+
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+ static boolean power = false;
+ static int pausetime = 500;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+
+ for( int i=1; i<args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[1]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("power") )
+ power = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ else if( args[i].endsWith("pause") ) {
+ i++;
+ int p = 0;
+ try { p = Integer.parseInt( args[i] ); }
+ catch( NumberFormatException nfe ) { }
+ if( p!=0 ) pausetime = p;
+ }
+ }
+
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+
+ System.out.println("Press return to exit.");
+ boolean running = true;
+ while( running ) {
+
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ running = false;
+ }
+ } catch( IOException ioe ) { }
+
+ boolean rc = roombacomm.updateSensors();
+ if( !rc ) {
+ System.out.println("No Roomba. :( Is it turned on?");
+ continue;
+ }
+
+ System.out.println( System.currentTimeMillis() + ":"+
+ roombacomm.sensorsAsString() );
+
+ roombacomm.pause( pausetime );
+ }
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+
+ System.out.println("goodbye.");
+ roombacomm.disconnect();
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/SpyAuto.java b/roombacomm-client/src/com/hackingroomba/roombacomm/SpyAuto.java
new file mode 100644
index 0000000..d2d0b70
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/SpyAuto.java
@@ -0,0 +1,147 @@
+
+
+package com.hackingroomba.roombacomm;
+
+import java.io.*;
+
+/**
+ Spy on the Roomba as it goes about its normal business
+
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.SpyAuto /dev/cu.KeySerial1
+ Usage:
+ roombacomm.SpyAuto serialportname [protocol] [options]<br>
+ where: protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -pause n -- pause n millseconds between sensor read
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ -flush -- flush on sends(), normally not needed
+ -power -- power on/off Roomba (if interface supports it)
+ </pre>
+ *
+ */
+public class SpyAuto {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.SpyAuto <serialportname> [protocol] [options]\n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ "[options] can be one or more of:\n"+
+ " -pause <n> -- pause n millseconds between sensor read\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ " -flush -- flush on sends(), normally not needed\n"+
+ " -power -- power on/off Roomba (if interface supports it)\n"+
+ "\n";
+
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+ static boolean power = false;
+ static int pausetime = 500;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial(true, pausetime);
+
+ for( int i=1; i<args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("power") )
+ power = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ else if( args[i].endsWith("pause") ) {
+ i++;
+ int p = 0;
+ try { p = Integer.parseInt( args[i] ); }
+ catch( NumberFormatException nfe ) { }
+ if( p!=0 ) pausetime = p;
+ }
+ }
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+
+ System.out.println("Press return to exit.");
+ boolean running = true;
+ while( running ) {
+
+ if( !roombacomm.sensorsValid ) {
+ System.out.println("No Roomba. :( Is it turned on?");
+ continue;
+ }
+
+ System.out.println( roombacomm.sensorsAsString() );
+
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ running = false;
+ }
+ } catch( IOException ioe ) { }
+
+ roombacomm.pause( pausetime );
+ }
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ System.exit(0);
+
+ }
+
+}
+
+ /*
+ public static void purr() {
+ System.out.println("purr");
+ float millis = 200;
+ float millisTo64ths = (1000 / 64 );
+ int s64ths = (int)(millis / millisTo64ths);
+ roombacomm.playSong( 2 );
+ for( int i=72; i>60; i-- ) {
+ roombacomm.spinLeftAt( 1000 );
+ roombacomm.pause( s64ths/2 );
+ roombacomm.spinRightAt( 1000 );
+ roombacomm.pause( s64ths/2 );
+ roombacomm.stop();
+ }
+ }
+
+ public static void createTribblePurrSong() {
+ byte cmd[] = {
+ (byte)RoombaComm.SONG, 2, 7, // define song
+ 68, 4, 67, 4, 66, 4, 65, 4,
+ 64, 4, 63, 4, 62, 4 };
+ roombacomm.send( cmd );
+ }
+
+
+ public static void bark() {
+ System.out.println("bark");
+ roombacomm.vacuum(true);
+ roombacomm.playNote( 50, 5 );
+ roombacomm.pause(150);
+ roombacomm.vacuum(false);
+ }
+ */
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java b/roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java
new file mode 100644
index 0000000..0ef24bc
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java
@@ -0,0 +1,136 @@
+/*
+ * roombacomm.SpySimple
+ *
+ * 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 java.io.*;
+
+/**
+ Spy on the Roomba as it goes about its normal business
+
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.SpySimple /dev/cu.KeySerial1
+ Usage:
+ roombacomm.SpySimple serialportname [protocol] [options]<br>
+ where: protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -pause n -- pause n millseconds between sensor read
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ -flush -- flush on sends(), normally not needed * </pre>
+ *
+ */
+public class SpySimple {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.SpySimple <serialportname> [protocol] [options]\n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ "[options] can be one or more of:\n"+
+ " -pause <n> -- pause n millseconds between sensor read\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ " -flush -- flush on sends(), normally not needed\n"+
+ "\n";
+
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+ static int pausetime = 500;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+
+ for( int i=1; i<args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ else if( args[i].endsWith("pause") ) {
+ i++;
+ int p = 0;
+ try { p = Integer.parseInt( args[i] ); }
+ catch( NumberFormatException nfe ) { }
+ if( p!=0 ) pausetime = p;
+ }
+ }
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+
+ boolean done = false;
+ while( !done ) {
+ roombacomm.updateSensors();
+ printSensors(roombacomm);
+ roombacomm.pause( pausetime );
+ done = keyIsPressed();
+ }
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+
+ }
+
+ public static void printSensors(RoombaCommSerial rc) {
+ System.out.println( System.currentTimeMillis() + ":"+
+ "bump:" +
+ (rc.bumpLeft()?"l":"_") +
+ (rc.bumpRight()?"r":"_") +
+ " wheel:" +
+ (rc.wheelDropLeft() ?"l":"_") +
+ (rc.wheelDropCenter()?"c":"_") +
+ (rc.wheelDropLeft() ?"r":"_")
+ );
+ }
+
+ /** check for keypress, return true if so */
+ public static boolean keyIsPressed() {
+ boolean press = false;
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ press = true;
+ }
+ } catch( IOException ioe ) { }
+ return press;
+ }
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Test.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Test.java
new file mode 100644
index 0000000..914685f
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Test.java
@@ -0,0 +1,177 @@
+/*
+ * RoombaComm Interface Test
+ *
+ * Copyright (c) 2005 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;
+
+
+/**
+ A fairly thorough test of the RoombaComm API.
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.Test /dev/cu.KeySerial1
+ Usage:
+ roombacomm.Test serialportname [protocol] [options]<br>
+ where: protocol (optional) is SCI or OI
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ -flush -- flush on sends(), normally not needed
+ -power -- power on/off Roomba (if interface supports it)
+ * </pre>
+ * </p>
+ */
+public class Test {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Test <serialportname> [protocol] [options]\n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ " -flush -- flush on sends(), normally not needed\n"+
+ " -power -- power on/off Roomba (if interface supports it)\n"+
+ "\n";
+
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+ static boolean power = false;
+ static boolean flush = false;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println(usage);
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+
+ for( int i=1; i<args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[i].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ } else if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("power") )
+ power = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ else if( args[i].endsWith("flush") )
+ flush = true;
+ }
+ roombacomm.debug = debug;
+ roombacomm.flushOutput = flush;
+
+ String portlist[] = roombacomm.listPorts();
+ System.out.println("Available ports:");
+ for( int i=0; i<portlist.length; i++ )
+ System.out.println(" "+i+": "+portlist[i]);
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ if( power ) {
+ System.out.println("waking up Roomba...");
+ roombacomm.wakeup();
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(30);
+
+ System.out.println("Checking for Roomba... ");
+ if( roombacomm.updateSensors() ) {
+ System.out.println("Roomba found!");
+ } else {
+ System.out.println("No Roomba. :(");
+ }
+
+ // must pause after every playNote to let to note sound
+ System.out.println("Playing some notes");
+ roombacomm.playNote( 72, 10 );
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 79, 10 );
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 76, 10 );
+ roombacomm.pause( 200 );
+
+ // test Logo-like functions (blocking)
+ // speed is in mm/s, go* is in mm, spin is in degrees
+ roombacomm.setSpeed( 100 ); // can be positive or negative
+ roombacomm.goStraight( 100 ); // can be positive or negative
+ roombacomm.goForward( 100 ); // negative numbers not allowed
+ roombacomm.goBackward( 200 ); // negative numbers not allowed
+
+ roombacomm.setSpeed( 150 );
+ roombacomm.spin( -360 ); // can be positive or negative
+ roombacomm.spinRight( 360 ); // negative numbers not allowed
+ roombacomm.spinLeft( 360 ); // negative numbers not allowed
+
+ // test non-blocking functions
+ roombacomm.goStraightAt(200); // speed argument
+ roombacomm.pause(1000);
+ roombacomm.goForwardAt(200); // speed argument
+ roombacomm.pause(1000);
+ roombacomm.goBackwardAt(400); // speed argument
+ roombacomm.pause(1000);
+
+ roombacomm.spinLeftAt( -15 ); // mm/s or degs/sec ?
+ roombacomm.pause(1000);
+ roombacomm.spinRightAt( 15 );
+ roombacomm.pause(1000);
+
+ roombacomm.stop();
+
+ //roombacomm.turn();
+
+ /*
+ // roombacomm.goStraight( 100, 100 );
+ roombacomm.spinLeft( 100, 360 );
+ roombacomm.spinRight( 200, 360 );
+ roombacomm.spinLeft( 300, 360 );
+ roombacomm.spinRight( 400, 360 );
+ roombacomm.spinLeft( 500, 360 );
+ roombacomm.spinRight( 600, 360 );
+ */
+
+ if( power ) {
+ System.out.println("Powering off in 3 seconds.");
+ roombacomm.pause( 3000 );
+ roombacomm.powerOff();
+ }
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Tribble.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Tribble.java
new file mode 100644
index 0000000..9995a42
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Tribble.java
@@ -0,0 +1,111 @@
+
+
+package com.hackingroomba.roombacomm;
+
+import java.io.*;
+
+/**
+ * Make Tribble noises
+
+ * <p>
+ * Run it with something like: <pre>
+ * java roombacomm.Tribble /dev/cu.KeySerial1 [protocol]<br>
+ * Where: protocol (optional) is SCI or OI
+ * </pre>
+ *
+ */
+public class Tribble {
+
+ static RoombaCommSerial roombacomm;
+
+ public static void main(String[] args) {
+ new Tribble(args);
+ }
+
+ Tribble(String[] args)
+ {
+ if( args.length == 0 ) {
+ System.out.println("Tribble <serialportname> [protocol]\nWhere: protocol (optional) is SCI or OI");
+ System.exit(0);
+ }
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+
+ roombacomm = new RoombaCommSerial();
+ for( int i=1; i < args.length; i++ ) {
+ if (args[i].equals("SCI") || (args[1].equals("OI"))) {
+ roombacomm.setProtocol(args[i]);
+ }
+ }
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.err.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.err.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(100);
+
+ createTribblePurrSong();
+
+ System.out.println("Press return to exit.");
+ boolean done = false;
+ while( !done ) {
+
+ purr();
+
+ if( Math.random() < 0.1 )
+ bark();
+
+ roombacomm.pause(1500 + (int)(Math.random()*500) );
+ done = keyIsPressed();
+ }
+
+ roombacomm.disconnect();
+ System.exit(0);
+ }
+
+ public static void purr() {
+ System.out.println("purr");
+ roombacomm.playSong( 5 );
+ for( int i=0; i<5; i++ ) {
+ roombacomm.spinLeftAt( 75 );
+ roombacomm.pause( 100 );
+ roombacomm.spinRightAt( 75 );
+ roombacomm.pause( 100 );
+ roombacomm.stop();
+ }
+ }
+
+ public static void createTribblePurrSong() {
+ int song[] =
+ { 68,4, 67,4, 66,4, 65,4,
+ 64,4, 63,4, 62,4, 61,4,
+ 60,4, 59,4, 60,4, 61,4,
+ };
+ roombacomm.createSong( 5, song );
+ }
+
+ public static void bark() {
+ System.out.println("bark");
+ roombacomm.vacuum(true);
+ roombacomm.playNote( 50, 5 );
+ roombacomm.pause(150);
+ roombacomm.vacuum(false);
+ }
+
+
+ /** check for keypress, return true if so */
+ public static boolean keyIsPressed() {
+ boolean press = false;
+ try {
+ if( System.in.available() != 0 ) {
+ System.out.println("key pressed");
+ press = true;
+ }
+ } catch( IOException ioe ) { }
+ return press;
+ }
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java
new file mode 100644
index 0000000..28507b2
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java
@@ -0,0 +1,115 @@
+/*
+ * roombacomm.Waggle -- test out the DRIVE command, showing a waggle
+ *
+ * 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;
+
+/**
+ Drive the Roomba in a Waggle, like when it's searching for something
+ <p>
+ Run it with something like: <pre>
+ java roombacomm.Waggle /dev/cu.KeySerial1 [protocol] velocity radius waittime [options]
+ where:
+ protocol (optional) is SCI or OI
+ velocity and radius in mm, waittime in milliseconds
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ * </pre>
+ *
+ */
+public class Waggle {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.Waggle <serialportname> [protocol] <velocity> <radius> <waittime> [options]\n" +
+ "where: protocol (optional) is SCI or OI\n" +
+ "[options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ " -nohwhandshake -- don't use hardware-handshaking\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+
+ public static void main(String[] args) {
+ if( args.length < 4 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+ RoombaCommSerial roombacomm = new RoombaCommSerial();
+ int argOffset = 0;
+ if (args[1].equals("SCI") || (args[1].equals("OI"))) {
+ roombacomm.setProtocol(args[1]);
+ argOffset = 1;
+ }
+
+ int velocity=0, radius=0, waittime=0;
+ try {
+ velocity = Integer.parseInt( args[1+argOffset] );
+ radius = Integer.parseInt( args[2+argOffset] );
+ waittime = Integer.parseInt( args[3+argOffset] );
+ } catch( Exception e ) {
+ System.err.println("Couldn't parse velocity or radius or waittime");
+ System.exit(1);
+ }
+
+ for( int i=4+argOffset; i < args.length; i++ ) {
+ if( args[i].endsWith("debug") )
+ debug = true;
+ else if( args[i].endsWith("nohwhandshake") )
+ roombacomm.setWaitForDSR(false);
+ else if( args[i].endsWith("hwhandshake") )
+ roombacomm.setWaitForDSR(true);
+ }
+
+ roombacomm.debug = debug;
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(100);
+
+ System.out.println("waggling 5 times\n");
+ for( int i=0; i<5; i++ ) {
+ roombacomm.drive( velocity, radius );
+ roombacomm.pause(waittime);
+ roombacomm.drive( velocity, -radius );
+ roombacomm.pause(waittime);
+ }
+ roombacomm.stop();
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/XML.java b/roombacomm-client/src/com/hackingroomba/roombacomm/XML.java
new file mode 100644
index 0000000..e64ce0f
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/XML.java
@@ -0,0 +1,173 @@
+package com.hackingroomba.roombacomm;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+/* The following XML class is a simple XML class meant to process the primitive XML
+ * that comes from RoboRealm. This class can be removed and replaced with a more
+ * extensive XML processing class as needed but is guaranteed to work with the RR
+ * XML. Do NOT use this class for generic XML processing as it is included for
+ * completeness and is intentionally kept simplistic to ease understanding
+ * */
+
+class XML
+{
+ Hashtable <String, String>table = new Hashtable<String, String>();
+ Vector <String>list = new Vector<String>();
+
+ private String replace(String txt, String src, String dest)
+ {
+ if (txt==null) return new String("");
+ int i,j;
+ int len=src.length();
+ StringBuffer sb=new StringBuffer(txt.length());
+
+ j=0;
+ while ((i=txt.indexOf(src,j))>=0)
+ {
+ sb.append(txt.substring(j,i));
+ sb.append(dest);
+ i+=len;
+ j=i;
+ }
+ sb.append(txt.substring(j));
+
+ return sb.toString();
+ }
+
+ /*
+ Unescapes strings that have been included in an XML message. This can be
+ accomplished by a sequence of replace statements.
+ &amp; -> &
+ &quote; -> "
+ &lt; -> <
+ &gt; -> >
+ */
+ private String unescape(String txt)
+ {
+ replace(txt, "&amp;", "&");
+ replace(txt, "&quote;", "\"");
+ replace(txt, "&lt;", "<");
+ replace(txt, "&gt;", ">");
+ return txt;
+ }
+
+ public boolean parse(String s)
+ {
+ table.clear();
+ return parse(s, table, null);
+ }
+
+ public Vector parseVector(String s)
+ {
+ list.removeAllElements();
+ if (parse(s, null, list))
+ return list;
+ else
+ return null;
+ }
+
+ public boolean parse(String s, Hashtable <String, String>h, Vector <String>v)
+ {
+ boolean isEndTag;
+ byte txt[] = s.getBytes();
+ int i, j;
+ int len = s.length();
+ StringBuffer keys[] = new StringBuffer[10];
+ StringBuffer value = new StringBuffer();
+ for (i=0;i<10;i++)
+ keys[i] = new StringBuffer();
+ int keyTop=-1;
+
+ for (i=0;i<len;)
+ {
+ // read in key
+ if (txt[i]=='<')
+ {
+ i++;
+ if (txt[i]=='/')
+ {
+ isEndTag = true;
+ i++;
+ }
+ else
+ isEndTag = false;
+
+ keyTop++;
+ keys[keyTop].setLength(0);
+ while ((i<len)&&(txt[i]!='>'))
+ {
+ keys[keyTop].append((char)txt[i]);
+ i++;
+ }
+ if (txt[i++]!='>')
+ {
+ System.out.println("Missing close > tag");
+ return false;
+ }
+
+ if (isEndTag)
+ {
+ if (!keys[keyTop].toString().equals(keys[keyTop-1].toString()))
+ {
+ System.out.println("Mismatched XML tags "+keys[keyTop]+" -> "+keys[keyTop-1]);
+ return false;
+ }
+ keyTop-=2;
+ }
+ }
+ else
+ {
+ // read in value
+ value.setLength(0);
+
+ while ((i<len)&&(txt[i]!='<'))
+ {
+ value.append((char)txt[i]);
+ i++;
+ }
+
+ StringBuffer key = new StringBuffer();
+ for (j=0;j<=keyTop;j++)
+ {
+ if (j>0) key.append('.');
+ key.append(keys[j]);
+ }
+
+ String escapedValue = unescape(value.toString());
+ if (h!=null) h.put(key.toString(), escapedValue);
+ if (v!=null) v.addElement(escapedValue);
+ }
+ }
+
+ return true;
+ }
+
+ public int getInt(String txt)
+ {
+ String s = (String)table.get(txt);
+ if (s!=null)
+ {
+ return Integer.parseInt(s);
+ }
+ return 0;
+ }
+
+ public String getFirst()
+ {
+ if (table.isEmpty())
+ return null;
+ else
+ return (String)table.elements().nextElement();
+ }
+/*
+ // This is where the program first starts
+ public static void main(String[] args)
+ {
+ XML xml = new XML();
+ xml.parse("<response><width>100</width><height>200</height></response>");
+ System.out.println(xml.getInt("response.width"));
+ System.out.println(xml.getInt("response.height"));
+ }
+*/
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.png
new file mode 100644
index 0000000..aa0c5d2
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.png
new file mode 100644
index 0000000..4032a78
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.png
new file mode 100644
index 0000000..49328a9
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.png
new file mode 100644
index 0000000..72c3c03
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.png
new file mode 100644
index 0000000..8866a47
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.png
new file mode 100644
index 0000000..b11d65b
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.png
new file mode 100644
index 0000000..4719cc6
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.png
new file mode 100644
index 0000000..4bdd361
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.png
new file mode 100644
index 0000000..c7a5c0d
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.png
new file mode 100644
index 0000000..af1e18d
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.png
new file mode 100644
index 0000000..a78b6dc
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.png
new file mode 100644
index 0000000..48e1159
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.png
new file mode 100644
index 0000000..0333e02
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.png
new file mode 100644
index 0000000..3dddea3
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.png
new file mode 100644
index 0000000..f47c7ff
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.png
new file mode 100644
index 0000000..b554358
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.png
Binary files differ
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java b/roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java
new file mode 100644
index 0000000..962629e
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java
@@ -0,0 +1,70 @@
+
+package com.hackingroomba.roombacomm.net;
+
+import java.lang.reflect.*;
+
+import com.hackingroomba.roombacomm.*;
+
+
+public class DumpMethods {
+
+ RoombaCommSerial rcs;
+
+ public static void main(String[] args) {
+ new DumpMethods(args);
+ }
+
+ public DumpMethods(String[] args) {
+ rcs = new RoombaCommSerial();
+
+ if( args.length == 0 ) {
+ dumpMethods(rcs);
+ return;
+ }
+
+ String method_name = args[0];
+
+ /*
+ for( int i=1; i<args.length; i++ ) {
+ String s = args[i];
+ try { a0 = Integer.parseInt( s );
+ } catch( Exception e ) { }
+
+ }
+ */
+ }
+
+ public void dumpMethods(Object obj) {
+ //Object result = method.invoke(obj, new Object[0]);
+ //Class c = Class.forName("roombacomm.RoombaComm");
+ try {
+ Method m[] = obj.getClass().getMethods();
+ for( int i=0; i< m.length; i++ )
+ System.out.println( m[i].getName() +" -- "+ m[i].toString() );
+ } catch( Exception e ) {
+ e.printStackTrace();
+ }
+ }
+
+ public void getMethod( String name, Object obj ) {
+ try {
+ String mname = name;
+ Class[] types = new Class[] {};
+ Method method = obj.getClass().getMethod(mname, types);
+ System.out.println("class: "+method.getDeclaringClass());
+ System.out.println("method: "+method.toString());
+ } catch( Exception e ) {
+ e.printStackTrace();
+ }
+ }
+
+}
+ /*
+ try {
+ serialEventMethod =
+ parent.getClass().getMethod("serialEvent",
+ new Class[] { Serial.class });
+ } catch (Exception e) {
+ // no such method, or an error.. which is fine, just ignore
+ }
+ */
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/net/RoombaCommTCPServer.java b/roombacomm-client/src/com/hackingroomba/roombacomm/net/RoombaCommTCPServer.java
new file mode 100644
index 0000000..d93ab2d
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/net/RoombaCommTCPServer.java
@@ -0,0 +1,94 @@
+/*
+ * RoombaComm TCP Interface
+ *
+ *
+ * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com
+ *
+ * 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.net;
+
+import java.net.*;
+import java.io.*;
+
+public class RoombaCommTCPServer
+{
+ // default port
+ int port = 8765;
+
+ // the shutdown command received
+ private boolean shutdown = false;
+
+ public RoombaCommTCPServer() {
+ }
+
+ public void await() {
+ ServerSocket serverSocket = null;
+ try {
+ serverSocket = new ServerSocket(port, 1, null );
+ // InetAddress.getByName("127.0.0.1"));
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ /*
+ // Loop waiting for a request
+ while (!shutdown) {
+ Socket socket = null;
+ InputStream input = null;
+ OutputStream output = null;
+ try {
+ socket = serverSocket.accept();
+ input = socket.getInputStream();
+ output = socket.getOutputStream();
+
+ if( input.available() ) {
+ }
+ // create Request object and parse
+ Request request = new Request(input);
+ request.parse();
+
+ // create Response object
+ Response response = new Response(output);
+ response.setRequest(request);
+ response.sendStaticResource();
+
+ // Close the socket
+ socket.close();
+
+ //check if the previous URI is a shutdown command
+ shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ continue;
+ }
+ }
+ */
+ }
+
+ public static void main(String[] args) {
+ RoombaCommTCPServer server = new RoombaCommTCPServer();
+ server.await();
+ }
+
+}
+
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/net/SimpleTest.java b/roombacomm-client/src/com/hackingroomba/roombacomm/net/SimpleTest.java
new file mode 100644
index 0000000..7f45ad1
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/net/SimpleTest.java
@@ -0,0 +1,131 @@
+/*
+ * roombacomm.net.SimpleTest
+ *
+ * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com
+ *
+ * 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.net;
+
+import com.hackingroomba.roombacomm.*;
+
+/**
+ * A simple test of RoombaComm and RoombaCommSerial functionality.
+ * <p>
+ * Run it with something like: <pre>
+ * java roombacomm.SimpleTest /dev/cu.KeySerial1
+ * </pre>
+ *
+ */
+public class SimpleTest {
+
+ static String usage =
+ "Usage: \n"+
+ " roombacomm.net.SimpleTest <host:port> [options]\n" +
+ "where [options] can be one or more of:\n"+
+ " -debug -- turn on debug output\n"+
+ //" -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+
+ //" -flush -- flush on sends(), normally not needed\n"+
+ "\n";
+ static boolean debug = false;
+ static boolean hwhandshake = false;
+ static boolean flush = false;
+
+ public static void main(String[] args) {
+ if( args.length == 0 ) {
+ System.out.println( usage );
+ System.exit(0);
+ }
+
+ String portname = args[0]; // e.g. "/dev/cu.KeySerial1"
+
+ for( int i=1; i < args.length; i++ ) {
+ if( args[i].endsWith("debug") )
+ debug = true;
+ //else if( args[i].endsWith("hwhandshake") )
+ // hwhandshake = true;
+ //else if( args[i].endsWith("flush") )
+ // flush = true;
+ }
+
+ RoombaComm roombacomm = new RoombaCommTCPClient();
+
+ roombacomm.debug = debug;
+ //roombacomm.waitForDSR = hwhandshake;
+ //roombacomm.flushOutput = flush;
+
+ String portlist[] = roombacomm.listPorts();
+ System.out.println("Available ports:");
+ for(int i=0;i<portlist.length;i++)
+ System.out.println(" "+i+": "+portlist[i]);
+
+ if( ! roombacomm.connect( portname ) ) {
+ System.out.println("Couldn't connect to "+portname);
+ System.exit(1);
+ }
+
+ System.out.println("Roomba startup on port "+portname);
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(30);
+
+ System.out.println("Checking for Roomba... ");
+ if( roombacomm.updateSensors() )
+ System.out.println("Roomba found!");
+ else
+ System.out.println("No Roomba. :( Is it turned on?");
+
+ //roombacomm.updateSensors();
+
+ System.out.println("Playing some notes");
+ roombacomm.playNote( 72, 10 ); // C
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 79, 10 ); // G
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 76, 10 ); // E
+ roombacomm.pause( 200 );
+
+ System.out.println("Spinning left, then right");
+ roombacomm.spinLeft();
+ roombacomm.pause(1000);
+ roombacomm.spinRight();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+ System.out.println("Going forward, then backward");
+ roombacomm.goForward();
+ roombacomm.pause(1000);
+ roombacomm.goBackward();
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+
+ System.out.println("Moving via send()");
+ byte cmd[] = {(byte)RoombaComm.DRIVE,
+ (byte)0x00,(byte)0xfa, (byte)0x00,(byte)0x00};
+ roombacomm.send( cmd ) ;
+ roombacomm.pause(1000);
+ roombacomm.stop();
+
+ System.out.println("Disconnecting");
+ roombacomm.disconnect();
+
+ System.out.println("Done");
+ }
+}
+
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/net/TextHttpServer.java b/roombacomm-client/src/com/hackingroomba/roombacomm/net/TextHttpServer.java
new file mode 100644
index 0000000..17450d3
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/net/TextHttpServer.java
@@ -0,0 +1,109 @@
+
+
+
+package com.hackingroomba.roombacomm.net;
+
+import java.net.*;
+import java.io.*;
+
+public class TextHttpServer {
+
+ int port = 6767;
+ /*
+ String cmds[] =
+ {
+ "reset", // zero args
+ "stop", // zero args
+ "goforward", // one optional arg
+ "gobackward", // one optional arg
+ "spinleft", // one optional arg
+ "spinright", // one optional arg
+ "beep", // two args
+ };
+ */
+
+ // the shutdown command received
+ private boolean shutdown = false;
+
+ public static void main(String[] args) {
+ TextHttpServer server = new TextHttpServer();
+ server.await();
+ }
+
+
+ public void await() {
+ System.out.println("awaiting connections on port "+port+"...");
+
+ ServerSocket serverSocket = null;
+ try {
+ serverSocket = new ServerSocket(port, 1, null);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ // Loop waiting for a request
+ while (!shutdown) {
+ Socket socket = null;
+ InputStream input = null;
+ OutputStream output = null;
+ try {
+ socket = serverSocket.accept(); // this blocks
+
+ input = socket.getInputStream();
+ output = socket.getOutputStream();
+
+ StringBuffer request = parseRequest( input );
+ String uristr = parseUri( request.toString() );
+ System.out.println("uristr: "+uristr);
+
+ URI uri = new URI( uristr );
+ System.out.println("path:"+uri.getPath()+", query:"+uri.getQuery());
+
+ // Close the socket
+ socket.close();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ //System.exit(1);
+ }
+ }
+
+ }
+
+
+
+ public StringBuffer parseRequest(InputStream input) {
+ // Read a set of characters from the socket
+ StringBuffer request = new StringBuffer(2048);
+ int i;
+ byte[] buffer = new byte[2048];
+ try {
+ i = input.read(buffer);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ i = -1;
+ }
+ for (int j=0; j<i; j++) {
+ request.append((char) buffer[j]);
+ }
+ System.out.print(request.toString());
+
+ return request;
+ }
+
+ private String parseUri(String requestString) {
+ int index1, index2;
+ index1 = requestString.indexOf(' ');
+ if (index1 != -1) {
+ index2 = requestString.indexOf(' ', index1 + 1);
+ if (index2 > index1)
+ return requestString.substring(index1 + 1, index2);
+ }
+ return null;
+ }
+
+
+}
diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java b/roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java
new file mode 100644
index 0000000..978459a
--- /dev/null
+++ b/roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java
@@ -0,0 +1,255 @@
+/*
+ * roombacomm.WatchVideo -- test out the video subsystem without robot motion
+ *
+ * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net
+ *
+ * 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 jargs.gnu.CmdLineParser;
+import java.io.*;
+
+
+/**
+ Run it with something like: <pre>
+ java roombacomm.WatchVideo --videoServer 192.168.0.150 --videoPortNum 5005 -x 160 -y 120 --debug<br>
+ Usage:
+ roombacomm.WatchVideo --videoServer <IP> --videoPortNum <port>
+ -x <image X size> -y <image Y size> [options]<br>
+ where
+ [options] can be one or more of:
+ -debug -- turn on debug output
+ -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+ -nohwhandshake -- don't use hardware-handshaking
+ </pre>
+*/
+ public class watchVideo {
+
+ private int height = 120;
+ private int width = 160;
+ String usage =
+ "Usage: \n"+
+ " roombacomm.Roborama --videoServer <IP> --videoPortNum <port> -x <image X size> -y <image Y size>[options]\n" +
+ "where [options] can be one or more of:\n"+
+ " -X | --debug -- turn on debug output\n"+
+ " -x <width> or --width <width>: set width\n" +
+ " -y <height> or --height <height> -- set height\n" +
+ " -c | --color -- use color mode, otherwise grayscale\n" +
+ " --videoserver <IP> : set IP address of video server\n" +
+ " --videoPortNum <port> : set port number on video server\n" +
+ " -t | --threshold <threshold> : set quantization threshold\n" +
+ " -R | --RoboRealm : connect to Roborealm & send image to it\n" +
+ " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n";
+
+ boolean debug = false;
+ private int waittime;
+ private int thresholdOverride = 0;
+ private String videoServer = "";
+ private int videoPortNum = 5005 ;
+ private int videoRowStart = 50;
+ private int videoRowEnd = 54;
+ private int pixelCnt;
+ private boolean color = false; // when true, capture images in color
+ private boolean roborealm = false; // when true, connect & send images to RoboRealm
+ FrameProcessor fp;
+
+ public watchVideo() {
+ // constructor, mustn't throw exceptions. Do nothing for now
+ }
+
+ // main - it all starts here
+ public static void main(String[] args) {
+ watchVideo b = new watchVideo();
+ b.parseCmd(args);
+ b.initVideo();
+ b.displayVideo();
+ }
+
+ public void displayVideo()
+ {
+
+ while (true) {
+ getShowFrame();
+ if (roborealm) {
+ Boolean rv = fp.frame2Roborealm();
+ if (rv == false)
+ System.out.println("error sending image to Roborealm");
+ //fp.getShapeData();
+ }
+ }
+ //frameSize = fp.readFrame();
+ //System.out.println("getVideo read " + frameSize + " bytes");
+ //fp.disconnect();
+ }
+
+ /**
+ * Get a frame from the video server and display it
+ */
+ public void getShowFrame() {
+ pixelCnt = fp.readFrame(getWidth(), getHeight(), color?1:0);
+ if (pixelCnt != (getWidth() * getHeight())) {
+ System.out.println("getVideo read " + pixelCnt + " bytes - abandoning frame");
+ return;
+ }
+
+ // output the frame to wherever it's wanted
+ fp.displayFrame();
+ return;
+ }
+
+ /**
+ * Connect to the video server, and to RoboRealm if requested
+ */
+ public FrameProcessor initVideo() {
+ if (getVideoServer() == null || getVideoServer().length() == 0) {
+ System.err.println(" you must supply a --videoServer value to use the command \"getVideo\"");
+ if (getVideoPortNum() <= 0) {
+ System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\"");
+ }
+ System.exit(6);
+ }
+ if (getVideoPortNum() <= 0) {
+ System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\"");
+ System.exit(7);
+ }
+
+ fp = new FrameProcessor(getVideoServer(),getVideoPortNum(), 0, 0, 0, 0, getThresholdOverride());
+ //fp.testQuantization();
+ fp.createAndShowGUI();
+ fp.connect(roborealm); // connect to the video server, and to Roborealm too if requested
+
+ return fp;
+ }
+
+ public void parseCmd(String[] args){
+ System.out.println("*** start of WatchVideo WparseCmd");
+
+ CmdLineParser parser = new CmdLineParser();
+ CmdLineParser.Option debugOption = parser.addBooleanOption('X', "debug");
+// CmdLineParser.Option verboseOption = parser.addBooleanOption('W', "Verbose");
+ CmdLineParser.Option widthOption = parser.addIntegerOption('x', "width");
+ CmdLineParser.Option heightOption = parser.addIntegerOption('y', "height");
+ CmdLineParser.Option videoServerOption = parser.addStringOption("videoServer");
+ CmdLineParser.Option videoPortNumOption = parser.addIntegerOption("videoPortNum");
+ CmdLineParser.Option thresholdOption = parser.addIntegerOption('t', "threshold");
+ CmdLineParser.Option hwHandShakeOption = parser.addBooleanOption("nohwhandshake");
+ CmdLineParser.Option colorOption = parser.addBooleanOption('c', "color");
+ CmdLineParser.Option roborealmOption = parser.addBooleanOption('R', "roborealm");
+ try {
+ parser.parse(args);
+ }
+ catch ( CmdLineParser.OptionException e ) {
+ System.err.println(e.getMessage());
+ System.out.println("parseCmd had an error\n"+ usage );
+ System.exit(2);
+ }
+
+ // String portname = args[0]; // e.g. "/dev/cu.KeySerial1", or "COM5" or "192.168.1.1"
+ setThresholdOverride(((Integer)parser.getOptionValue(thresholdOption, getThresholdOverride())).intValue());
+ setWidth(((Integer)parser.getOptionValue(widthOption,getWidth())).intValue());
+ setHeight(((Integer)parser.getOptionValue(heightOption,getHeight())).intValue());
+ setVideoServer(((String)parser.getOptionValue(videoServerOption)));
+ setVideoPortNum(((Integer)parser.getOptionValue(videoPortNumOption, getVideoPortNum())).intValue());
+ // String cmd = args[1+argOffset];
+
+ Boolean debugBool = (Boolean)parser.getOptionValue(debugOption,new Boolean(false));
+ setDebug(debugBool.booleanValue());
+ System.out.println("debug is ("+isDebug()+")");
+ Boolean hwHandShakeBool = (Boolean)parser.getOptionValue(hwHandShakeOption, new Boolean(false));
+ setThresholdOverride((Integer)parser.getOptionValue(thresholdOption, new Integer(0)));
+ System.out.println("thresholdOverride is " + getThresholdOverride());
+ color = (Boolean)parser.getOptionValue(colorOption, new Boolean(false));
+ roborealm = (Boolean)parser.getOptionValue(roborealmOption, new Boolean(false));
+ System.out.println("color mode is " + color + ", roborealm mode is " + roborealm);
+ System.out.println("*** end of parseCmd");
+ }
+
+ public boolean isDebug() {
+ return debug;
+ }
+ public void setDebug(boolean debug_) {
+ debug = debug_;
+ }
+ public int getWaittime() {
+ return waittime;
+ }
+ public void setWaittime(int waittime_) {
+ waittime = waittime_;
+ }
+ public int getThresholdOverride() {
+ return thresholdOverride;
+ }
+ public void setThresholdOverride(int thresholdOverride) {
+ this.thresholdOverride = thresholdOverride;
+ }
+ protected int getHeight() {
+ return height;
+ }
+ protected void setHeight(int height) {
+ this.height = height;
+ }
+ protected int getWidth() {
+ return width;
+ }
+ protected void setWidth(int width) {
+ this.width = width;
+ }
+ /**
+ * @return the videoServer
+ */
+ protected String getVideoServer() {
+ return videoServer;
+ }
+ /**
+ * @param videoServer the videoServer to set
+ */
+ protected void setVideoServer(String videoServer) {
+ this.videoServer = videoServer;
+ }
+ /**
+ * @return the videoPortNum
+ */
+ protected int getVideoPortNum() {
+ return videoPortNum;
+ }
+ /**
+ * @param videoPortNum the videoPortNum to set
+ */
+ protected void setVideoPortNum(int videoPortNum) {
+ this.videoPortNum = videoPortNum;
+ }
+
+ public boolean isColor() {
+ return color;
+ }
+
+ public void setColor(boolean color) {
+ this.color = color;
+ }
+
+ public boolean isRoborealm() {
+ return roborealm;
+ }
+
+ public void setRoborealm(boolean roborealm) {
+ this.roborealm = roborealm;
+ }
+
+ }
+
+ \ No newline at end of file