summaryrefslogtreecommitdiff
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
parent654ca6d59e99b5e72b05dfb917bc5888c4ba6b1b (diff)
added stuff
-rw-r--r--roombacomm-client/README.html14
-rw-r--r--roombacomm-client/bin/AudioLocalizerClient.bat3
-rw-r--r--roombacomm-client/build.xml93
-rw-r--r--roombacomm-client/docs/Asus Router HowTo.html50
-rwxr-xr-xroombacomm-client/docs/CHANGELOG.txt62
-rw-r--r--roombacomm-client/docs/DirectoryStructure.html145
-rw-r--r--roombacomm-client/docs/FileDescriptions.html512
-rw-r--r--roombacomm-client/docs/Index.html26
-rw-r--r--roombacomm-client/docs/README73
-rw-r--r--roombacomm-client/docs/README-0.9570
-rw-r--r--roombacomm-client/docs/README.eclipse.txt22
-rw-r--r--roombacomm-client/docs/README.windows.txt103
-rw-r--r--roombacomm-client/docs/SVN_Info.html176
-rw-r--r--roombacomm-client/docs/ToDo.html121
-rw-r--r--roombacomm-client/lib/bluecove/bluecove-2.1.0.jarbin0 -> 547156 bytes
-rw-r--r--roombacomm-client/lib/jargs.jarbin0 -> 11586 bytes
-rw-r--r--roombacomm-client/lib/rxtx/README.rxtx16
-rw-r--r--roombacomm-client/lib/rxtx/RXTXcomm.jarbin0 -> 59464 bytes
-rw-r--r--roombacomm-client/lib/rxtx/librxtxSerial.jnilibbin0 -> 326432 bytes
-rw-r--r--roombacomm-client/lib/rxtx/librxtxSerial.sobin0 -> 154682 bytes
-rw-r--r--roombacomm-client/lib/rxtx/macosx_setup.command54
-rw-r--r--roombacomm-client/lib/rxtx/rxtxSerial.dllbin0 -> 77759 bytes
-rwxr-xr-xroombacomm-client/src/7z.exebin0 -> 150016 bytes
-rw-r--r--roombacomm-client/src/Makefile6
-rw-r--r--roombacomm-client/src/TODO.txt6
-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
-rw-r--r--roombacomm-client/src/jargs/examples/gnu/AutoHelpParser.java87
-rw-r--r--roombacomm-client/src/packaging/BSquare-jar.xml20
-rw-r--r--roombacomm-client/src/packaging/Manifest.txt2
-rw-r--r--roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Info.plist41
-rwxr-xr-xroombacomm-client/src/packaging/RoombaCommTest.app/Contents/MacOS/JavaApplicationStubbin0 -> 23192 bytes
-rw-r--r--roombacomm-client/src/packaging/RoombaCommTest.app/Contents/PkgInfo1
-rw-r--r--roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/RXTXcomm.jarbin0 -> 59464 bytes
-rw-r--r--roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/librxtxSerial.jnilibbin0 -> 326432 bytes
-rw-r--r--roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/roombacomm.jarbin0 -> 66775 bytes
-rw-r--r--roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/RoombaTest.icnsbin0 -> 75895 bytes
-rw-r--r--roombacomm-client/src/packaging/processing-export.txt5
-rw-r--r--roombacomm-client/src/packaging/processing-readme.txt5
-rw-r--r--roombacomm-client/src/processing-examples/roombaring/roombaring.pde54
-rw-r--r--roombacomm-client/src/processing-examples/roombatune/roombatune.pde111
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/CourierNewPSMT-9.vlwbin0 -> 13206 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/but_backward.pngbin0 -> 1058 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/but_forward.pngbin0 -> 1061 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/but_spinleft.pngbin0 -> 1339 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/but_spinright.pngbin0 -> 1367 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/but_stop.pngbin0 -> 990 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/but_turnleft.pngbin0 -> 1182 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/but_turnright.pngbin0 -> 1184 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/turnfrontleft.pngbin0 -> 2766 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/data/turnfrontright.pngbin0 -> 2766 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI.zipbin0 -> 226318 bytes
-rwxr-xr-xroombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/MyGUI_readme.txt51
-rwxr-xr-xroombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/library/MyGUI.jarbin0 -> 233350 bytes
-rw-r--r--roombacomm-client/src/processing-examples/roombaview/roombaview.pde425
-rwxr-xr-xroombacomm-client/src/scripts/RoombaCommTest.bat3
-rwxr-xr-xroombacomm-client/src/scripts/RoombaCommTest.sh4
-rwxr-xr-xroombacomm-client/src/scripts/build-jar.bat12
-rwxr-xr-xroombacomm-client/src/scripts/build-jar.sh15
-rwxr-xr-xroombacomm-client/src/scripts/build-processing-library.bat22
-rwxr-xr-xroombacomm-client/src/scripts/build-processing-library.sh23
-rwxr-xr-xroombacomm-client/src/scripts/makedoc.bat2
-rwxr-xr-xroombacomm-client/src/scripts/makedoc.sh7
-rwxr-xr-xroombacomm-client/src/scripts/makeit.bat8
-rwxr-xr-xroombacomm-client/src/scripts/makeit.sh10
-rwxr-xr-xroombacomm-client/src/scripts/one.sh1
-rwxr-xr-xroombacomm-client/src/scripts/runit.bat3
-rwxr-xr-xroombacomm-client/src/scripts/runit.sh6
128 files changed, 19613 insertions, 0 deletions
diff --git a/roombacomm-client/README.html b/roombacomm-client/README.html
new file mode 100644
index 0000000..d686f8b
--- /dev/null
+++ b/roombacomm-client/README.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+ <title>README</title>
+</head>
+<body>
+<h1>Roombacomm Release 0.97</h1>
+<a href="docs/Index.html">Documentation for Roombacomm</a> is rooted in
+the docs directory. API documentation is linked from the page in the
+docs directory
+</body>
+</html>
diff --git a/roombacomm-client/bin/AudioLocalizerClient.bat b/roombacomm-client/bin/AudioLocalizerClient.bat
new file mode 100644
index 0000000..93acf10
--- /dev/null
+++ b/roombacomm-client/bin/AudioLocalizerClient.bat
@@ -0,0 +1,3 @@
+REM run simple serial test .bat file
+
+java -classpath .;../lib/jargs.jar com.hackingroomba.roombacomm.AudioLocalizerClient
diff --git a/roombacomm-client/build.xml b/roombacomm-client/build.xml
new file mode 100644
index 0000000..a3ec6a9
--- /dev/null
+++ b/roombacomm-client/build.xml
@@ -0,0 +1,93 @@
+<project name="roombaNet" basedir="." default="main">
+
+ <property name="src.dir" value="src" />
+ <property name="docs.dir" value="docs" />
+ <property name="lib.dir" value="lib" />
+ <property name="build.dir" value="build" />
+ <property name="classes.dir" value="${build.dir}/classes" />
+ <property name="javadocs.dir" value="${docs.dir}/api" />
+ <property name="jar.dir" value="${build.dir}/jar" />
+ <property name="dist.dir" value="dist" />
+ <property name="main-class" value="com.hackingroomba.roombacomm.RoombaCommGUI" />
+
+ <path id="classpath">
+ <fileset dir="${lib.dir}">
+ <include name="*.jar" />
+ <include name="rxtx/*.jar" />
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <echo>cleaning ${build.dir}</echo>
+ <delete defaultexcludes="true" includeemptydirs="true">
+ <fileset dir="${build.dir}"/>
+ </delete>
+ </target>
+
+ <target name="compile">
+ <echo>compiling project to ${classes.dir}...</echo>
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" />
+ <copy todir="${classes.dir}">
+ <fileset dir="${src.dir}" excludes="**/*.java"/>
+ </copy>
+ <echo>copying ${src.dir}/processing-examples into ${build.dir}...</echo>
+ <copy todir="${build.dir}">
+ <fileset dir="${src.dir}/processing-examples" excludes="**/*"/>
+ </copy>
+ </target>
+ <target name="dist" depends="jar">
+ <echo>buidling distribution directory for the project ${dist.dir}...</echo>
+ <mkdir dir="${dist.dir}" />
+ <echo>copying ready to use Jars to ${dist.dir}</echo>
+ <copy todir="${dist.dir}" file="${jar.dir}/roombacomm.jar"/>
+ <copy todir="${dist.dir}" file="${lib.dir}/rxtx/RXTXcomm.jar"/>
+ <copy todir="${dist.dir}" file="${lib.dir}/jargs.jar"/>
+ <echo>double click on ${dist.dir}/roombacomm.jar to start a GUI</echo>
+ </target>
+
+ <target name="jar" depends="compile">
+ <echo>buidling roombacomm.jar...</echo>
+ <mkdir dir="${jar.dir}" />
+ <copy todir="${jar.dir}" file="${lib.dir}/rxtx/RXTXcomm.jar"/>
+ <copy todir="${jar.dir}" file="${lib.dir}/rxtx/librxtxSerial.so"/>
+ <copy todir="${jar.dir}" file="${lib.dir}/rxtx/rxtxSerial.dll"/>
+ <copy todir="${jar.dir}" file="${lib.dir}/jargs.jar"/>
+ <jar destfile="${jar.dir}/roombacomm.jar" basedir="${classes.dir}">
+ <manifest>
+ <attribute name="Main-Class" value="${main-class}" />
+ <attribute name="Class-Path" value="RXTXcomm.jar jargs.jar"/>
+ </manifest>
+ </jar>
+ </target>
+
+ <target name="run" depends="jar">
+ <echo>running ${jar.dir}/roombacomm.jar</echo>
+ <java jar="${jar.dir}/roombacomm.jar" fork="true" />
+ </target>
+
+ <target name="clean-build" depends="clean,jar" />
+
+ <target name="main" depends="clean,compile" />
+
+ <target name="javadoc" depends="compile">
+ <echo>buidling roombacomm java docs in ${javadocs.dir}...</echo>
+ <echo> for this ant target to work you need to have a java SDK installed (not just a JRE) and in the PATH environment setting too.</echo>
+ <javadoc
+ classpathref="classpath"
+ destdir="${javadocs.dir}"
+ access="public"
+ author="true"
+ version="true"
+ use="true"
+ noindex="false"
+ nonavbar="false"
+ notree="false"
+ nodeprecated="false"
+ nodeprecatedlist="false"
+ source="1.6"
+ splitindex="true"
+ sourcepath="${src.dir}">
+ </javadoc>
+ </target>
+</project>
diff --git a/roombacomm-client/docs/Asus Router HowTo.html b/roombacomm-client/docs/Asus Router HowTo.html
new file mode 100644
index 0000000..6fa458e
--- /dev/null
+++ b/roombacomm-client/docs/Asus Router HowTo.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+ <title>Asus Router HowTo</title>
+</head>
+<body>
+<h1>ASUS Router HowTo</h1>
+<h3>Updating the package list and stty utilities</h3>
+The purpose of this step is to get updated stty utilities onto the ASUS
+router so that the baud rate of the console port can be set.<br>
+<div id=":1y" class="ii gt">
+<div>opkg update</div>
+<div>opkg install coreutils-stty</div>
+<div><br>
+</div>
+</div>
+<h3>To start ser2net</h3>
+To use console port, comment out the serial port in /etc/inittab &amp;
+reboot. File with commented out serial port should be<br>
+::ttyS0::askfirst:/bin/ash --login<br>
+<br>
+The following command sets the port to 115200<br>
+# stty -F /dev/ttyS0 115200<br>
+<br>
+Now start ser2net<br>
+# cd /roomba<br>
+# ./ser2netconf.sh<br>
+<br>
+<br>
+<br>
+<h3>Reference</h3>
+# cat sercon.sh<br>
+ser2net -c /roomba/ser2netcon.conf<br>
+<br>
+<br>
+# cat ser2netcon.conf<br>
+5001:raw:0:/dev/ttyS0:115200<br>
+<br>
+<br>
+# cat /etc/inittab<br>
+::sysinit:/etc/init.d/rcS S boot<br>
+::shutdown:/etc/init.d/rcS K stop<br>
+tts/0::askfirst:/bin/ash --login<br>
+::ttyS0::askfirst:/bin/ash --login<br>
+tty1::askfirst:/bin/ash --login<br>
+<br>
+</body>
+</html>
diff --git a/roombacomm-client/docs/CHANGELOG.txt b/roombacomm-client/docs/CHANGELOG.txt
new file mode 100755
index 0000000..d9660ff
--- /dev/null
+++ b/roombacomm-client/docs/CHANGELOG.txt
@@ -0,0 +1,62 @@
+Changes in Roombacomm 0.96
+--------------------------
+- The primary goal of this release was to enable Roomba 500 models,
+ which use a different baud rate. This has been done while retaining
+ backward compatibility - programs which use the roombacomm class for
+ 400 and older models shouldn't have to change for the older models, and
+ may not have to change for the 500 models. Commands should run unchanged
+ on older models.
+- Secondary goals were to:
+ - get roombacomm to remember it's serial port settings, so you don't have to
+ change them each time you run a GUI
+ - Make life easier for Windows users
+ - Fix bugs along the way.
+
+The following changes were made:
+- Updated RXTX library to latest that is included in Processsing-1.01
+- Created README.windows.txt, README.eclipse.txt, CHANGELOG.txt, TODO.txt
+- Created runit.bat - Windows command prompt equivalent of runit.sh for Linux,
+ makeit.bat (makes all on windows), makedoc.bat (makes all javadoc on windows)
+- Added bluecove-2.1.0.jar to rxtxlib for windows bluetooth support
+- Made SimpleTest go backwards after it went forward using send,
+ so it ends up where it started (and doesn't creep forward)
+- Changed playNote to use song 3, because 500-series roombas only support
+ 4 or 5 songs (vs. 16 for 400-series & ealier).
+- Changed RoombaCommTest beep-hi and beep-lo duration from 10 to 32,
+ and notes more toward the middle of the range to make the beeps audible.
+ (They were too short & high/low pitched to be readily discernable.)
+- Added actions in RoombaCommPanel to make left & right forward turn buttons work
+- Added a combo box in RoombaCommPanel to let the user select the protocol
+ (SCI - Serial Command Interface, for Roomba 400 & older, and
+ OI (Open Interface,for Roomba 500 series.)
+- Put logic into RoombaCommSerial to store the protocol & port &
+ DSRwait settings in .roomba_config file, & retrieve them when when the
+ object is created. This lets an app "remember" the config settings that
+ were last used.
+- Updated the command-line apps to allow the protocol to be unspecified
+ (uses default from config file), or to specify the protocol
+- Made the -debug flag work in RoombaCommTest - eliminated the hard-coded
+ enable/disable statements in RoombaCommPanel
+- Made RoombaCommTest check for whether Roomba is connected (runs sensors
+ command) immediately after connecting, & print connected message.
+- Fixed disconnect button to do disconnect action
+- Fixed typo where RoombaComm.start() said it was sending control
+- Added Roomba 5xx new commands, and a sensors method that lets the caller
+ pass in the length of the expected reply.
+ Make RoombaCommSerial.serialEvent() indicate completion when the requested
+ reply length has been received.
+- Added queryList method to support 5xx Query List command
+- Beefed up the javadoc for the executables
+- Made the Spy* programs terminate cleanly
+- Fixed bug in RoombaComm.goBackward() which made it go forward
+- Made the Test application stop when complete
+- Created build-jar.bat and build-processing-library.bat.
+- Installed 7z.exe to support zip archive creation of build-processing-library
+- Fixed roombaview - framerate() should be frameRate()
+- Made the control() command turn LEDs on, so we know roomba is powered on
+ and we don't run the batteries flat thinking it's off.
+ Bug: The first control command after a poweron, the LEDs flash,
+ but don't stay on.
+- Added programs Stop (stops the robot), PowerOff (powers off the robot),
+ and Bsquare (runs a Borenstein square test - see the UMB benchmark paper
+ by Borenstein et. al.)
diff --git a/roombacomm-client/docs/DirectoryStructure.html b/roombacomm-client/docs/DirectoryStructure.html
new file mode 100644
index 0000000..4cdf458
--- /dev/null
+++ b/roombacomm-client/docs/DirectoryStructure.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+ <title>DirectoryStructure</title>
+</head>
+<body>
+<h1>Directory Structure</h1>
+This page defines the directory structure standard used in this
+project. It is based on the recommendations in the developer.com <a
+ href="http://www.developer.com/java/other/article.php/994991/Building-with-Ant-Directory-Structure.htm">article
+here</a>.&nbsp;
+<br>
+<h2>The Directories</h2>
+The directories described below are relative to the project source
+tree, as would be checked out of SVN at
+http://svn.dprg.org/repos/roomba/roombacomm/trunk<br>
+<br>
+Two trees are supplied by the project: <br>
+<ol>
+ <li>the full developer tree, described below</li>
+ <li>the user tree, which is the contents of the dist directory after
+building the full developer tree</li>
+</ol>
+The intent is that the dist directory contains everything a user needs
+to run the applications, but not the source. It is considered good
+practice to separate the runnable application from the source, which
+contains much more that users aren't typically interested in. <a
+ name="dir">
+</a>
+<table border="1">
+ <tbody>
+ <tr>
+ <th colspan="2">Directory Structure of /: </th>
+ </tr>
+ <tr>
+ <td>/ </td>
+ <td>project root directory
+(run all tools from here) </td>
+ </tr>
+ <tr>
+ <td>&nbsp;build.xml </td>
+ <td>Ant build file </td>
+ </tr>
+ <tr>
+ <td>&nbsp;lib/ </td>
+ <td>jar files required for
+build </td>
+ </tr>
+ <tr>
+ <td>&nbsp;src/ </td>
+ <td>Java source code for
+servlets and utilities </td>
+ </tr>
+ <tr>
+ <td>&nbsp;doc/</td>
+ <td>Project documentation,
+READMEs, HowTo's, etc</td>
+ </tr>
+ <tr>
+ <td>&nbsp;build/ </td>
+ <td>files compiled/copied
+into here (this directory can be
+safely deleted, and is not stored in the repository) </td>
+ </tr>
+ <tr>
+ <td>dist/</td>
+ <td>"ready to use" JAR's and other applications for
+users.</td>
+ </tr>
+ </tbody>
+</table>
+<br>
+<table style="width: 688px; height: 238px;" border="1">
+ <tbody>
+ <tr>
+ <th colspan="2">Directory Structure of /src: </th>
+ </tr>
+ <tr>
+ <td>com/hackingroomba/roombacomm </td>
+ <td>Source directory of roombacomm library + applications</td>
+ </tr>
+ <tr>
+ <td>&nbsp;scripts </td>
+ <td>scripts to run the applications, and obsolete scripts that
+used to build the applications </td>
+ </tr>
+ <tr>
+ <td>&nbsp;processing-examples </td>
+ <td>contains directories for the /roombaring, /roombatune &amp;
+/roombaview processing
+apps. These apps are intended to be copied as-is into a processing
+directory. (Processing is a graphic development environment - read
+"Hacking Roomba" for more details.)</td>
+ </tr>
+ <tr>
+ <td>processing-examples/roombaview</td>
+ <td>In addition to the processing application, contains
+subdirectories which support the application. See fileDescriptions.</td>
+ </tr>
+ <tr>
+ <td>packaging</td>
+ <td>???A directory containing RoombaCommTest.app and directories
+which help a Mac build an app???</td>
+ </tr>
+ </tbody>
+</table>
+<br>
+<table style="width: 688px; height: 115px;" border="1">
+ <tbody>
+ <tr>
+ <th style="height: 34px;" colspan="2">Directory Structure of
+/lib: </th>
+ </tr>
+ <tr>
+ <td style="height: 34px;">/</td>
+ <td style="height: 34px;">jar files which support roombacomm apps</td>
+ </tr>
+ <tr>
+ <td style="height: 35px;">/rxtx </td>
+ <td style="height: 35px;">Files which provide serial I/O for java</td>
+ </tr>
+ </tbody>
+</table>
+<br>
+<table style="width: 688px; height: 115px;" border="1">
+ <tbody>
+ <tr>
+ <th style="height: 34px;" colspan="2">Directory Structure of
+/dist: </th>
+ </tr>
+ <tr>
+ <td style="height: 34px;"></td>
+ <td style="height: 34px;"></td>
+ </tr>
+ <tr>
+ <td style="height: 35px;"></td>
+ <td style="height: 35px;"></td>
+ </tr>
+ </tbody>
+</table>
+<br>
+</body>
+</html>
diff --git a/roombacomm-client/docs/FileDescriptions.html b/roombacomm-client/docs/FileDescriptions.html
new file mode 100644
index 0000000..0175b84
--- /dev/null
+++ b/roombacomm-client/docs/FileDescriptions.html
@@ -0,0 +1,512 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+</head>
+<body>
+Below are a description of each of the files of this project.<br>
+<h2>/</h2>
+<ul>
+ <li>COPYRIGHT</li>
+ <ul>
+ <li>GNU copyright notice for
+the project</li>
+ </ul>
+ <li>index.html</li>
+ <ul>
+ <li>Redirects to
+docs/index.html</li>
+ </ul>
+ <li>README.txt</li>
+ <ul>
+ <li>text file which tells the
+user to open index.html in a browser</li>
+ </ul>
+ <li>build.xml</li>
+ <ul>
+ <li>ant build file for the project</li>
+ </ul>
+</ul>
+<h2>/src/com/hackingroomba/roombacomm</h2>
+<ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/FrameProcessor.java">FrameProcessor.java</a></li>
+ <ul>
+ <li>An class to handle visual
+processing from a camera mounted on
+the roomba.</li>
+ <li>Used by:
+com.hackingroomba.roombacomm.Bsquare<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/JImagePanel.java">JImagePanel.java</a></li>
+ <ul>
+ <li>It looks like nothing
+references this in the project. Maybe it
+was an early version/contriubutor to FrameProcessor.java ? <br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Note.java">Note.java</a></li>
+ <ul>
+ <li>Simple wrapper for musical
+notes</li>
+ <li>Used by:&nbsp;
+com.hackingroomba.roombacomm.</li>
+ <ul>
+ <li>&nbsp;RoombaComm</li>
+ <li>RTTTLPlay</li>
+ <li>RoombaCommFrame</li>
+ <li>RoombaCommPanel</li>
+ <li>RoombaCommSerial<br>
+ </li>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RTTTLParser.java">RTTTLParser.java</a></li>
+ <ul>
+ <li>A simple class to allow a
+"rttlstring" into an ArrayList of
+notes. The array can later be passed to a connected Roomba to make it
+sing a song. How to use this is deminstrated in
+com.hackingroomba.roombacomm.RoombaCommFrame.playSong(RoombaComm
+roombacomm, String rtttl)<br>
+ </li>
+ <li>Used by:
+com.hackingroomba.roombacomm.</li>
+ <ul>
+ <li>RTTTLPlay</li>
+ <li>RoombaCommFrame</li>
+ <li>RoombaCommPanel</li>
+ <li>RoombaCommSerial</li>
+ </ul>
+ <li>A few sample rttlstrings
+you could use are included here ( you
+can find more via google. :) )</li>
+ <ul>
+ <li>(close encounters of the
+third kind)<br>
+closeencounter:d=16,o=5,b=125:d,p,e,p,c,p.,c4,p,g4,1p.,d6,p,e6,p,c6,p.,c,p,g,1p.,</li>
+ <li>Thunderstruck, ACDC)<br>
+thunderstr:d=16,o=5,b=160:c6,c,a#,c,a,c,a#,c,a,c,g,c,a,c,f,c,g,c,e,c,f,c,e,c,f,c,e,c,f,c,e,c</li>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaComm.java">RoombaComm.java</a></li>
+ <ul>
+ <li>This is the primary
+implementation for this project. It is an
+abstract class that acts as an abstraction layer from the iRobot API's
+for the models of Roomba's that this project is attempting to support.
+The class is designed to simplify users use of the iRobot API and to
+provide as consistent as possible abstraction layer between versions of
+the iRobot API versions. This interface is currently implemented based
+on how the Roombacomm will connect to the Roomba device. There are two
+main implementations of this interface:&nbsp; <a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommSerial.java">RoombaCommSerial.java</a>
+and <a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java">RoombaCommTCPClient.java</a>
+.<br>
+ </li>
+ </ul>
+ <ul>
+ <ul>
+ <ul>
+ </ul>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommFrame.java">RoombaCommFrame.java</a></li>
+ <ul>
+ <li>This is a Java Swing User
+Interface(UI). It is intended to
+allow a user to manually control their roomba from their PC. GUI size
+options may be available to support desktop and/or Netbook screen
+resolutions in the future.<br>
+ </li>
+ <ul>
+ <ul>
+ </ul>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommGUI.java">RoombaCommGUI.java</a></li>
+ <ul>
+ <li>This is a class designed
+to start a graphical interface (mainly
+RoombaCommFrame.java, but others may be possible in the future) that
+can be used to manually control the features of the RoombaComm library.
+The main function of this class is to handle command line interfaces
+(CLI) inputs and to provide an example of how other applications could
+reuse RoombaCommFrame.java for their own use.<br>
+ </li>
+ <ul>
+ <ul>
+ </ul>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommSerial.java">RoombaCommSerial.java</a></li>
+ <ul>
+ <li>The serial-port based
+implementation of RoombaComm. Designed
+for physical RS-232 ports, USB adapter ports like Keyspan USA-19HS, and
+Bluetooth serial port profiles.<br>
+ </li>
+ <ul>
+ <ul>
+ </ul>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java">RoombaCommTCPClient.java</a></li>
+ <ul>
+ <li>This class is an extension
+of the RoombaComm interface. This
+implementation is designed to communicate with the Roomba device across
+a TCP/IP network.<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/images/">images/</a></li>
+ <ul>
+ <li>This is a directory of
+image files used in the the GUI's.<br>
+ </li>
+ </ul>
+ <ul>
+ <ul>
+ </ul>
+ </ul>
+</ul>
+<br>
+Classes designed for specific test(s)<br>
+<ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Bsquare.java">Bsquare.java</a></li>
+ <ul>
+ <li>Drive the Roomba forward,
+back, or CW or CCW square<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/BumpTurn.java">BumpTurn.java</a></li>
+ <ul>
+ <li>Read sensors to detect
+bumps and turn away from them while
+driving<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Drive.java">Drive.java</a></li>
+ <ul>
+ <li>command line controls to
+move a Roomba (RoombaCommSerial only)</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/DriveRealTime.java">DriveRealTime.java</a></li>
+ <ul>
+ <li>GUI to allow user to use
+arrow keys to drive a Roomba in real
+time (RoombaCommSerial only)</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/ListSerialPorts.java">ListSerialPorts.java</a></li>
+ <ul>
+ <li>A simple command line
+program to enumerate the Serial ports
+that the application sees in the environment.<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/LogoA.java">LogoA.java</a></li>
+ <ul>
+ <li>Make a square with a
+Roomba.<br>
+Leaves Roomba in same place it began (theoretically)<br>
+ </li>
+ </ul>
+</ul>
+<br>
+Older classes (Legacy?/Depreciated?) that may not be maintained by this
+project going forward:<br>
+<ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommPanel.java">RoombaCommPanel.java</a></li>
+ <ul>
+ <li>This
+is an older graphical interface that can be used to manually control
+some of the features of the project. This UI is the orginal work of Tod
+E. Kurt, and is not being maintained so that any external to the
+project references can be perserved. (However, the location of the
+class has been changed to match the project ongoing source tree
+structure.)</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommTest.java">RoombaCommTest.java</a></li>
+ <ul>
+ <li>A small GUI to test out
+RoombaComm.</li>
+ <ul>
+ <li>This class is simlar in
+concept to the RoombaCommGUI class.
+However it uses the RoombaCommPanel for the UI that it starts for the
+user.</li>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java">RoombaCommTestOld.java</a></li>
+ <ul>
+ <li>A small GUI to test out
+RoombaComm. (an Even earlier version of
+RoombacommTest.java)<br>
+ </li>
+ <ul>
+ <li>This class is simlar in
+concept to the RoombaCommGUI class.
+However it uses the RoombaCommPanel for the UI that it starts for the
+user.</li>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RTTTLPlay.java">RTTTLPlay.java</a></li>
+ <ul>
+ <li>Play RTTL formatted
+ringtones on the Roomba.<br>
+This class only supports a serial connection to a Roomba to play a RTTL
+string from a command line parameter.</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaRecorder.java">RoombaRecorder.java</a></li>
+ <ul>
+ <li>A simple wrapper for
+multiple MacroRecorderPanels</li>
+ <ul>
+ <li>This UI allows the user
+to record and play back movements for
+a roomba.<br>
+ </li>
+ </ul>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/RoombaRecorderPanel.java">RoombaRecorderPanel.java</a></li>
+ <ul>
+ <li>GUI (tab) for
+RoombaRecorder<br>
+A Panel containing controls for testing RoombaComm.</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/SimpleTest.java">SimpleTest.java</a></li>
+ <ul>
+ <li>A simple test of
+RoombaCommSerial functionality.<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Spiral.java">Spiral.java</a></li>
+ <ul>
+ <li>Make the Roomba drive in a
+spiral. (RoombaCommSerial only)<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Spiro1.java">Spiro1.java</a></li>
+ <ul>
+ <li>A Spirograph-like example
+(RoombaCommSerial only)</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Spiro2.java">Spiro2.java</a></li>
+ <ul>
+ <li>A Spirograph-like example with additional parameters and
+behaviors
+(RoombaCommSerial only)&nbsp;<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Spy.java">Spy.java</a></li>
+ <ul>
+ <li>Connect to a roomba
+(RoombaCommSerial only) and watch sensor
+data comming back from the device.<br>
+NEED to find the differences between Spy.java, SpyAuto.java,
+and SpySimple.java</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/SpyAuto.java">SpyAuto.java</a></li>
+ <ul>
+ <li>Connect to a roomba
+(RoombaCommSerial only) and watch sensor
+data comming back from the device.<br>
+NEED to find the differences between Spy.java, SpyAuto.java,
+and SpySimple.java<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/SpySimple.java">SpySimple.java</a></li>
+ <ul>
+ <li>Connect to a roomba
+(RoombaCommSerial only) and watch sensor
+data comming back from the device.<br>
+NEED to find the differences between Spy.java, SpyAuto.java,
+and SpySimple.java</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Test.java">Test.java</a></li>
+ <ul>
+ <li>Connect to a roomba
+(RoombaCommSerial only) and run through a
+static set of operations to exercise the older RoombaComm Library.<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Tribble.java">Tribble.java</a></li>
+ <ul>
+ <li>Connect to a roomba
+(RoombaCommSerial only) and make it make
+Tribble noises<br>
+ </li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/Waggle.java">Waggle.java</a></li>
+ <ul>
+ <li>Connect to a roomba
+(RoombaCommSerial only) Drive the Roomba in
+a Waggle, like when it's searching for something</li>
+ </ul>
+ <li><a
+ href="http://svn.dprg.org/repos/roomba/roombacomm/branches/roombaNet-JMP-0.97Working/src/com/hackingroomba/roombacomm/net/">net/</a></li>
+ <ul>
+ <li>DumpMethods</li>
+ <ul>
+ <li>use Java reflection to
+output methods of the RoombaCommSerial
+class.<br>
+Looks to be heavily based on a Simple Example from
+http://java.sun.com/developer/technicalArticles/ALT/Reflection/<br>
+ </li>
+ </ul>
+ <li>RoombaCommTCPServer</li>
+ <ul>
+ <li>an early ServerSocket
+based attempt to communicate with a
+Roomba<br>
+ </li>
+ </ul>
+ <li>SimpleTest</li>
+ <ul>
+ <li>Connect to a roomba
+(RoombaCommSerial only) and run through a
+static set of operations to exercise the older RoombaComm Library.
+&nbsp;A<span style="color: rgb(255, 0, 0);">R: Paul look at
+whether
+this is really a duplicate of the file in roombacomm - if so, delete it</span></li>
+ </ul>
+ <li>TextHttpServer</li>
+ <ul>
+ <li>Appears to be a
+crude(maybe a sample from somewhere?) class
+to
+implement an Http Server. It appears to have no direct links/references
+to RoombaComm.</li>
+ </ul>
+ </ul>
+</ul>
+<h2>/src/scripts</h2>
+<ul>
+ <li><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);">runit.bat and runit.sh:
+Windows and Unix
+scripts that set CLASSPATH &amp; run the programs from the proper
+places</span></span></li>
+ <li><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);">RoombaCommTest.bat,
+RoombaCommTest.sh,
+RoombaCommGUI.bat and RoombaCommGUI.sh:&nbsp;</span></span></li>
+</ul>
+<h2>/src/processing-examples</h2>
+Directories intended to be copied into the users Processing work
+directory<br>
+<ul>
+ <li><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);"><a
+ href="../src/processing-examples/roombaring/roombaring.pde">roombaring/roombaring.pde</a>:
+Play a tune on Roomba (possibly the tron theme).</span></span></li>
+ <li><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);"><a
+ href="../src/processing-examples/roombatune/roombatune.pde">roombatune/roombatune.pde</a>:
+Allow the user to use their PC keyboard as a a musical keyboard to play
+notes in real time on the Roomba</span></span></li>
+ <li><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);"><a
+ href="../src/processing-examples/roombaring/roombaring.pde">roombaview/roombaview.pde</a>:
+Provide a GUI</span></span><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);"> to drive roomba which runs inside the
+Processing environment. The GUI uses direction buttons to control
+Roomba and moves it across a grid in the GUI as Roomba drives.</span></span></li>
+ <ul>
+ <li><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);">roombaview/data/: Directory containing
+images displayed by the GUI.</span></span></li>
+ <li><span style="color: rgb(255, 0, 0);"><span
+ style="color: rgb(0, 0, 0);">roombaview/libraries/MyGUI/library: </span></span>Directory
+contains
+MyGUI.jar, which provides button handling etc for the application.
+Check the processing documentation for where to copy the MyGUI library.</li>
+ </ul>
+</ul>
+<h2>/docs</h2>
+<ul>
+ <li>Index.html:&nbsp;Project
+overview and lead-in to documentation.
+How to use the project (for non-developers.) Contains much of the
+content from README in the original project structure</li>
+ <li>DevelopersGuide.html:
+Instructions on recommended configuration,
+packages &amp; tools that should be installed to enable
+development.
+Refers to the book where appropriate.</li>
+ <li>DirectoryStructure.html: a
+listing of directories in the project
+- both source and those built automatically, and their purpose</li>
+ <li>FileDescriptions.html: This
+file. A description of each source
+file in the project. <span style="color: rgb(255, 0, 0);">Q: is there
+a need to describe generated files?</span></li>
+ <li>ToDo.html: list of things to
+do to the project</li>
+ <li>SVN_Info.html: Subversion
+policies for the project</li>
+ <li>eclipseHowTo.html:
+Instructions for configuring eclipse for this
+project. Contents from removed README.eclipse.txt file</li>
+ <li>windowsHowTo.html:
+Instructions specific to running roombacomm
+under windows</li>
+ <li>wifiRouterHowTo.html:
+Instructions for connecting a wifi router
+and installing &amp; configuring the necessary daemons to provide
+wifi
+access to Roomba. (Content needs to come from Jonathan.)</li>
+ <br>
+</ul>
+<h2>/lib</h2>
+<ul>
+ <li>jargs.jar: library which supports posix-style command-line
+arguments (hyphen-argument style</li>
+</ul>
+<h2>/lib/rxtxlib</h2>
+<ul>
+ <li>librxtxSerial.jnilib: Java extension which lets serial I/O escape
+from the virtual machine</li>
+ <li>rxtxSerial.dll: Windows dll which provides COM access to
+librxtxSerial.jnilib</li>
+ <li>librxtxSerial.so: Mac OS-X library which provices serial driver
+access to librxtxSerial.jnilib</li>
+ <li>RXTXcomm.jar: <span style="color: rgb(255, 0, 0);">???</span></li>
+ <li>macosx_setup.command:a script to fix permissions for loc files on
+Mac OS X</li>
+ <li>README.rxtx: the name says it all</li>
+</ul>
+</body>
+</html>
diff --git a/roombacomm-client/docs/Index.html b/roombacomm-client/docs/Index.html
new file mode 100644
index 0000000..15091e3
--- /dev/null
+++ b/roombacomm-client/docs/Index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+ <title>Index</title>
+</head>
+<body style="height: 318px;">
+<h1>Roombacomm 0.97</h1>
+<a href="README">README</a><br>
+<a href="README.eclipse.txt">Eclipse HowTo</a><br>
+<a href="README.windows.txt">Windows-related information</a><br>
+<a href="CHANGELOG.txt">Changelog</a><br>
+<a href="DirectoryStructure.html">Directory Structure</a><br>
+<a href="FileDescriptions.html">File Descriptions</a>
+<br>
+<a href="SVN_Info.html">Subversion Tree structure</a>
+<br>
+<a href="Asus%20Router%20HowTo.html">ASUS Router How To</a><br>
+<h2>To Do</h2>
+The <a href="ToDo.html">To Do list</a> enumerates the remaining work
+for the 0.97 release, as well as work remaining beyond 0.97<br>
+<h2>Previous Versions</h2>
+<a href="README-0.95">README for 0.95 release</a>
+</body>
+</html>
diff --git a/roombacomm-client/docs/README b/roombacomm-client/docs/README
new file mode 100644
index 0000000..b5909e0
--- /dev/null
+++ b/roombacomm-client/docs/README
@@ -0,0 +1,73 @@
+RoombaComm: Roomba Control & Communications API, release 0.96
+Copyright (c) 2006-9 Tod E. Kurt, tod-at-todbot.com &
+ Paul Bouchier, bouchier-at-classicnet.net
+
+Quick Start for Java on Linux
+-----------------------------
+If you have a Roomba Serial Interface built and the 'roombacomm-java.zip'
+library, you can quickly try both out by unzipping this file and running
+one of the examples:
+
+% unzip roombacomm.zip
+% cd roombacomm
+% ./run-it.sh roombacomm.SimpleTest /dev/cu.KeySerial1
+
+Assuming you have a serial device called "/dev/cu.KeySerial1", which you
+probably do if you have a Keyspan USA-19HS USB serial adapter.
+
+
+Quick Start for Processing
+--------------------------
+If you have a Roomba Serial Interface built and the 'roombacomm-processing.zip'
+library, you can quickly try out both by unzipping this file and running
+one of the examples:
+
+1. Unzip the 'roombacomm-processing.zip' file inside of Processing's
+ 'libraries' directory. This will create a directory called 'roombacomm'.
+2. Start Processing (or Restart if already running)
+3. Create a New Sketch
+3. Choose "Sketch->Import Library->roombacomm" menu item
+4. Copy one of the Processing examples in the 'examples' directory of the
+ roombacomm library (zipfile). Note that if you want to run roombaview
+ you must install the MyGUI package.
+ The MyGUI library is located in the 'libraries' directory of the 'roombaview'
+ sketch. You may need to run an older version of Processing for it to work.
+
+
+
+Compatibility
+-------------
+This library has only been tested on Mac OS X 10.4, and a little on
+Windows 2000, but everything is in place (particularly the RXTX native
+libraries) for it to work on any version of Mac OS X, Windows, and Linux.
+
+
+
+Platform-specific Notes
+-----------------------
+Mac OS X:
+---------
+If you've never used RXTX before on your Mac OS X box, run the command:
+
+% ./rxtxlib/macosx_setup.command
+
+And then shutdown (not just log out) and log back in again.
+And read this faq from Processing: http://processing.org/faq/bugs.html#serial
+
+Also, if you use Bluetooth in 10.3, you can only input numeric passkeys,
+which can make using Bluetooth adapters difficult. If your Bluetooth
+adapter can change its passkey (possible with the SparkFun/BlueRadios
+devices), change it to a numeric passkey on a differernt computer.
+Or upgrade your OS. :)
+
+Windows:
+--------
+
+The serial port implementation is a little different (at least to RXTX)
+so use the '-hwhandshake' flag with the programs, if using Bluetooth on
+Windows. See, 'RoomabCommSerial.waitForDSR' for details.
+
+(And yes, I know the build, configuration, and release of this library
+is a little goofball, but I wanted to get something out so others can play)
+
+-eof-
diff --git a/roombacomm-client/docs/README-0.95 b/roombacomm-client/docs/README-0.95
new file mode 100644
index 0000000..68c1e17
--- /dev/null
+++ b/roombacomm-client/docs/README-0.95
@@ -0,0 +1,70 @@
+RoombaComm: Roomba Control & Communications API, release 0.95
+Copyright (c) 2006 Tod E. Kurt, tod@todbot.com
+
+Quick Start for Java
+--------------------
+If you have a Roomba Serial Interface built and the 'roombacomm-java.zip'
+library, you can quickly try both out by unzipping this file and running
+one of the examples:
+
+% unzip roombacomm.zip
+% cd roombacomm
+% ./run-it.sh roombacomm.SimpleTest /dev/cu.KeySerial1
+
+Assuming you have a serial device called "/dev/cu.KeySerial1", which you
+probably do if you have a Keyspan USA-19HS USB serial adapter.
+
+
+Quick Start for Processing
+--------------------------
+If you have a Roomba Serial Interface built and the 'roombacomm-processing.zip'
+library, you can quickly try out both by unzipping this file and running
+one of the examples:
+
+1. Unzip the 'roombacomm-processing.zip' file inside of Processing's
+ 'libraries' directory. This will create a directory called 'roombacomm'.
+2. Start Processing (or Restart if already running)
+3. Create a New Sketch
+3. Choose "Sketch->Import Library->roombacomm" menu item
+4. Copy one of the Processing examples in the 'examples' directory of the
+ roombacomm library (zipfile)
+
+
+
+
+Compatibility
+-------------
+This library has only been tested on Mac OS X 10.4, and a little on
+Windows 2000, but everything is in place (particularly the RXTX native
+libraries) for it to work on any version of Mac OS X, Windows, and Linux.
+
+
+
+Platform-specific Notes
+-----------------------
+Mac OS X:
+---------
+If you've never used RXTX before on your Mac OS X box, run the command:
+
+% ./rxtxlib/macosx_setup.command
+
+And then shutdown (not just log out) and log back in again.
+And read this faq from Processing: http://processing.org/faq/bugs.html#serial
+
+Also, if you use Bluetooth in 10.3, you can only input numeric passkeys,
+which can make using Bluetooth adapters difficult. If your Bluetooth
+adapter can change its passkey (possible with the SparkFun/BlueRadios
+devices), change it to a numeric passkey on a differernt computer.
+Or upgrade your OS. :)
+
+Windows:
+--------
+
+The serial port implementation is a little different (at least to RXTX)
+so use the '-hwhandshake' flag with the programs, if using Bluetooth on
+Windows. See, 'RoomabCommSerial.waitForDSR' for details.
+
+(And yes, I know the build, configuration, and release of this library
+is a little goofball, but I wanted to get something out so others can play)
+
+-eof-
diff --git a/roombacomm-client/docs/README.eclipse.txt b/roombacomm-client/docs/README.eclipse.txt
new file mode 100644
index 0000000..08957d8
--- /dev/null
+++ b/roombacomm-client/docs/README.eclipse.txt
@@ -0,0 +1,22 @@
+Instructions for running eclipse on windows with Roombacomm
+-----------------------------------------------------------
+Disclaimer: I'm not an eclipse expert. Please improve these instructions. However, eclipse is a really great
+java IDE - once you learn how to use it you won't want to go back to the command-line.
+
+Prerequisite: You have installed eclipse and a jdk, and pointed eclipse to a workspace.
+
+- Create a new project from existing source in the folder you unzipped into. (Do not create it in the directory with the
+source files.)
+- Give the project the same name as the directory you downloaded into
+- Change project layout: choose "use project folder as root for source & class files"
+- Optionally select a project-specific jre that is the same as makeit.bat uses
+- Check "create project from existing source"
+- click next
+- click the libraries tab
+- Click the button to add external jars. Navigate to rxtxlib and select RXTXcomm.jar
+- Open "Run Configurations" and make a new run configuration called SimpleSerial. Search for the main class and select
+SimpleSerial in the popup. In the arguments tab, type the name of the COM port you're using.
+- Run the configuration you just created. You should see the same output in the console pane of eclipse as you saw
+in the command prompt window if you followed the README.windows instructions.
+- Make a new run configuration called RoombaCommTest, and select RoombaCommTest as the main class, as above. Run it and you
+should get the GUI for driving Roomba around. \ No newline at end of file
diff --git a/roombacomm-client/docs/README.windows.txt b/roombacomm-client/docs/README.windows.txt
new file mode 100644
index 0000000..3978727
--- /dev/null
+++ b/roombacomm-client/docs/README.windows.txt
@@ -0,0 +1,103 @@
+Quick Start for Windows
+-----------------------
+This procedure has been tested on Windows XP and Vista.
+
+Prerequisites
+-------------
+- You must have java installed, and the path to java must be in your environment.
+To test, open a command prompt window and type "java". You should get a help menu
+printed.
+
+Roombacomm Installation
+-----------------------
+- unzip roombacomm somewhere
+
+rxtxSerial.dll Installation
+---------------------------
+- You need to copy the rxtxSerial.dll file into the folder containing java.exe
+- Identify your Java Runtime Environment's folder. For version 1.6.0, this usually
+is c:\Program Files\Java\jre1.6.0_??\. java.exe is usually in the bin folder under the
+runtime folder.
+- Note that the java.exe program can live anywhere (even in \Windows\System32!), and rxtxSerial.dll
+needs to be copied to the same directory as the java.exe version that will be run from the
+command prompt. If you have several versions of Java installed, you need to find the right
+directory containing the version of Java which will be run when you type java from the command
+prompt. One way to do this is open up a command prompt window and type java -version. Then
+look in suitable directories for that version. The version of java.exe which runs is determined
+by the order of directories in the PATH environment variable. Test that you found the right version of java
+by typing \<path>\java -version and looking for a match to the version when you don't explicitly
+type the path. For example:
+ > \windows\system32\java.exe -version
+will print the version of java in the windows system32 folder.
+- Copy rxtxSerial.dll from roombacomm\rxtxlib folder to the folder with java.exe. For
+example: c:\Program Files\Java\jre1.6.0_01\bin\
+- Further help is available on the Wiki at http://www.rxtx.org
+- If you don't get rxtxSerial.dll installed in the right place, RoombacommTest will hang when you
+press the connect button, command-line apps will hang at startup, and the Windows bluetooth device
+icon will flash to connected then back to not connected.
+
+Bluetooth
+---------
+- It may be that bluetooth is problematic when you've been using the device from one computer,
+which is still turned on, and you try to use it from another computer. You may have to turn the
+other computer off, and/or delete the bluetooth device and re-add it in the Bluetooth devices
+window before it will allow opening it.
+- Open up the bluetooth Devices window (in the control panel). You can monitor the connected state
+there, as well as identify which COM ports are assigned to the bluetooth 1SPP board. The device
+should show connected once you've hit the connect button on RoombacommTest, or when any of the
+sample programs are active.
+- Bluetooth on Windows may not work too well if you select no passcode. Try creating the device
+with a passcode of 0000, even if the bluetooth device is set up for no passcode.
+- A Philips BGB203 chip in automatic server mode as a bluetooth transceiver, and
+Windows XP and Vista, and RXTXlib does not seem reliable - YMMV! Please help if you can.
+
+Identifying which COM port Roomba is connected to
+-------------------------------------------------
+- Find which COM port your Roomba is connected to.
+- If you've hard-wired Roomba to a PC serial port, this should be in your computer's documentation.
+- If you're using a USB to serial adapter, you can find the COM port by going to device manager
+(right click on My Computer, click on Manage, and click on Device Manger - expand the
+"Ports (COM & LPT)" item) and looking for a COM port that comes and goes as you plug and unplug
+the USB to serial adapter.
+- If you're using bluetooth with the Windows bluetooth stack (default), go to control panel
+and open up "bluetooth Devices" and look on the COM ports tab
+- The SimpleTest program helps you by listing which COM ports it found. You can try them all.
+
+Testing with SimpleTest
+-----------------------
+- Before you jump to running SimpleTest (which actually relies on a lot of SW underneath it)
+check that you can actually connect to the COM port you intend to use. Run a terminal emulator
+program such as PuTTY, and open up the serial port with the name COMx (whichever port your Roomba
+is connected to). Jumper the Tx port to the Rx port. If your USB to Serial adapter has data LEDs,
+try hitting keys, and make sure the LEDs blink, and the key you pressed is echoed back. Similarly
+for bluetooth. If you can't get a terminal emulator to talk to your interface, SimpleTest has
+no hope.
+- SimpleTest is a java application which connects to Roomba through a serial port you specify
+and sends commands to make it do a few basic things.
+- Open a command prompt window and cd to the directory where you unzipped roombacomm
+- run the runit.bat file with the name of your com port, e.g.
+ > runit COM5.
+ Note that COMx is case sensitive - "COM" in Windows COM ports is capitalized
+- If you get the dreaded "Couldn't connect to com4" message, you likely either mistyped
+the COM port (e.g. didn't capitalize COM), or you have not installed rxtxSerial.dll in the
+same directory as the java.exe that you're running.
+- If it connects to the COM port, and you have lights on your USB to serial port, they should
+start flashing as the program prints the commands it's sending to Roomba. If you have Roomba
+connected and powered on, it will move around a little.
+- If you're using bluetooth on Windows, you may have to give the -hwhandshake option on
+the command line. e.g.
+ > runit roombacomm.DriveRealTime COM4 -hwhandshake
+
+Modifying & Rebuilding
+----------------------
+- The "Hacking Roomba" book describes the makeit.sh script, which builds all the java code.
+For windows, use the makeit.bat batch file from the command prompt to rebuild all java code
+after modifications.
+- For windows, the analog to the makedoc.sh script is the makedoc.bat batch file. This rebuilds
+the javadoc. Please keep the javadoc up to date if you're going to submit changes back to Tod.
+
+Processing
+----------
+- If you change any RoombaComm functions, you'll need to re-make the roombacomm jar file. Use
+the build-jar.bat batch file for this, then run the build-processing-library.bat batch file.
+
diff --git a/roombacomm-client/docs/SVN_Info.html b/roombacomm-client/docs/SVN_Info.html
new file mode 100644
index 0000000..02c270c
--- /dev/null
+++ b/roombacomm-client/docs/SVN_Info.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><head>
+
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" />
+ <title>SVN_Info</title>
+
+
+</head><body>
+<h1>Getting started (for the Subversion unaware):</h1>
+
+<p>The O'Reilly Media <a href="http://svnbook.red-bean.com/en/1.5">book</a>
+(free on line, but you can buy it too)<br />
+Good parts to read first: (<span class="sect1"><a href="http://svnbook.red-bean.com/en/1.5/svn.intro.whatis.html">What
+Is Subversion?</a></span>, <span class="chapter"><a href="http://svnbook.red-bean.com/en/1.5/svn.basic.html">Fundamental
+Concepts</a> )</span><br />
+</p>
+
+<h1>A few SVN quotes:</h1>
+
+<h3>
+<a href="http://svnbook.red-bean.com/en/1.5/svn.branchmerge.html">ref</a>:
+svnbook.red-bean.com, Chapter&nbsp;4.&nbsp;Branching and Merging
+</h3>
+
+<div class="quote">
+For projects that have a large number of contributors, it's common for
+most people to have working copies of the trunk. Whenever someone needs
+to make a long-running change that is likely to disrupt the trunk, a
+standard procedure is to create a private branch and commit changes
+there until all the work is complete.<br />
+</div>
+
+<h3><a href="http://svnbook.red-bean.com/en/1.5/svn.branchmerge.whatis.html">ref</a>:
+What is a branch?</h3>
+
+<div class="quote">This is the basic concept of a <em class="firstterm">branch</em>-namely,
+a line of development that exists independently of another line, yet
+still shares a common history if you look far enough back in time. A
+branch always begins life as a copy of something, and moves on from
+there, generating its own history (see <a class="xref" href="http://svnbook.red-bean.com/en/1.5/svn.branchmerge.whatis.html#svn.branchmerge.whatis.dia-1" title="Figure&nbsp;4.1.&nbsp;Branches of development">Figure&nbsp;4.1, Branches
+of development</a>).</div>
+
+<h3><a href="http://svnbook.red-bean.com/en/1.5/svn.branchmerge.using.html#svn.branchmerge.using.concepts">ref</a>:
+What's the point of a branch?</h3>
+
+<div class="quote">You should
+remember two important lessons from this section. First, Subversion has
+no internal concept of a branch-it knows only how to make copies. When
+you copy a directory, the resultant directory is only a "<span>branch</span>"
+because <span class="emphasis"><em>you</em></span> attach that meaning
+to it. You may think of the directory differently, or treat it
+differently, but to Subversion it's just an ordinary directory that
+happens to carry some extra historical information.<br />
+<br />
+Second, because of this copy mechanism, Subversion's branches exist as <span class="emphasis"><em>normal filesystem directories</em></span> in the
+repository. This is different from other version control systems, where
+branches are typically defined by adding extra-dimensional "<span class="quote">labels</span>" to collections of files. The location
+of your branch directory doesn't matter to Subversion. Most teams
+follow a convention of putting all branches into a <code class="filename">/branches</code> directory, but you're free to invent
+any policy you wish.<br />
+</div>
+
+<h2>A few words about this project's tree managment approach:</h2>
+
+The structure will be as follows:<br />
+
+/roombacomm<br />
+
+&nbsp; ../trunk<br />
+
+&nbsp;&nbsp;&nbsp;&nbsp; This is where the bleading edge development
+and believed to be fully working code for this project can be found.
+The structure should be
+usable as an SVN checkout&nbsp; for the project source tree. In general
+the idea is that /trunk will be usable at all times. More deatils about
+the structure of the project in a seperate <a href="DirectoryStructure.html">document</a>.<br />
+
+<br />
+
+&nbsp; ../branches<br />
+
+&nbsp;&nbsp;&nbsp;&nbsp; These branches are planed to be of one of two
+types: experemental or releases.<br />
+
+&nbsp;&nbsp;&nbsp; Experemental branches:<br />
+
+&nbsp;&nbsp;&nbsp;&nbsp; This is where long running development efforts
+will take place for the project. Each branch should follow the projects
+naming
+convention based on the type of branch. (See "SVN policies of this
+project" for details) In general, these branches should be considered
+"under development" or "experimental" and under the control of a
+developer. When the branch reaches a stability point, where the code
+implmentes the changes that are desired for the branch, has implemented
+any now needed testing code, and the code still passes all tests for
+the project then the branch is
+ready to be merged back into the trunk.<br />
+
+&nbsp;&nbsp;&nbsp; Release branches are points of code development
+where it has been tested, prepaired, and packaged as a step forward for
+the project.<br />
+
+<br />
+
+<h2>SVN policies of this project:</h2>
+
+<ol>
+
+ <li>all svn commits should have comments/log entries. (prefereably
+identifying each file that was altered and in laymen terms what was
+changed in that file)</li>
+ <li>the keyword Id should be included in a comment in every file (do
+not forget to set the svn:keywords property on the file too.)</li>
+ <li>/branches/release-N.N.N will be official releases of the project</li>
+ <li>experemental branches are encouraged when <strong><em>any or all</em></strong><span style="font-weight: bold;"> </span>of the following
+is true:
+ <table style="text-align: left; width: 618px; margin-left: 34px;" border="1" cellpadding="2" cellspacing="2">
+ <tbody>
+ <tr>
+ <td style="vertical-align: top; width: 516px;">the
+development effort will
+take more than one commit to achieve</td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top; width: 516px;">the change is
+likely to
+break any backwards compatibility in
+the current/previous release</td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top; width: 516px;">automated
+testing failed,
+or the changes are not yet covered by
+automated testing</td>
+ </tr>
+ </tbody>
+ </table>
+ </li>
+ <li>code in compiled form should not be in SVN unless it is <strong><em>at
+least one</em></strong> of the following:
+ <table style="text-align: left; width: 618px; margin-left: 34px;" border="1" cellpadding="2" cellspacing="2">
+ <tbody>
+ <tr>
+ <td style="vertical-align: top; width: 529px;">an outside
+library that is
+included for simplicity/compatibility (best done with a <span class="sect1"><a href="http://svnbook.red-bean.com/en/1.5/svn.advanced.vendorbr.html">Vendor
+Branch</a>)</span></td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top; width: 529px;">it is in a
+packaged release for the
+project</td>
+ </tr>
+ </tbody>
+ </table>
+ </li>
+ <li>All new features developed must include automated testing code to
+validate the function.The tests should be constructed in accordance with the format and approach that
+the project is using for automated testing.<br />
+ </li>
+</ol>
+
+<h2>More general SVN references:</h2>
+
+<ul>
+
+ <li><a href="http://svnbook.red-bean.com/">Version Control with
+Subversion</a></li>
+ <li><a href="http://en.wikipedia.org/wiki/Subversion_%28software%29">wikipedia</a><a href="http://aymanh.com/subversion-a-quick-tutorial"><span style="font-weight: bold;" /></a></li>
+ <li><a href="http://aymanh.com/subversion-a-quick-tutorial"><span style="font-weight: bold;">Subversion - A Quick Tutorial</span></a></li>
+</ul>
+
+<p><br />
+</p>
+
+</body></html> \ No newline at end of file
diff --git a/roombacomm-client/docs/ToDo.html b/roombacomm-client/docs/ToDo.html
new file mode 100644
index 0000000..c1e6125
--- /dev/null
+++ b/roombacomm-client/docs/ToDo.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title>To Do</title>
+ <style>
+ /* added the "done" class to mark what was completed */
+ .done {
+ color: green;
+ }
+ /* added the "help" class to mark what need more information */
+ .help{
+ color: red;
+ }
+ </style>
+</head>
+<body>
+<h1>TO
+Do for 0.97 Release</h1>
+<h2>MUST (we will hold shipment
+for these)</h2>
+<ul>
+ <li>GUI
+needs to have symbol/icon for LED buttons
+ <ul>
+ <li class="help">Spot will become ____</li>
+ <li class="help">Clean will become ____</li>
+ <li class="help">Dirt will become ____</li>
+ <li class="help">Max will become ____</li>
+ <li class="help">Doc (D rotated) will become ____</li>
+ <li class="help">CheckRobot (C rotated) will become ____</li>
+ <li class="done">top line(None-green-red-orange) Only visible for
+400(SCI)</li>
+ <li class="done">Bottom line (Dock and Checkrobot) Only visible
+for 500 (OI)</li>
+ </ul>
+ </li>
+ <li>Check in web GUI into TOT &amp; make it work/build</li>
+ <li>Update web app interface to include network IP connection &amp;
+make web GUI match functionality of roombacomm GUI</li>
+ <li>Rename bsquare to something better </li>
+ <li>Put ser2net into the tree (subject to licensing)<br>
+ <font class="help">What is the licensing question here?</font></li>
+ <li>make ant build the tree, make a build &amp; a doc target &amp; an
+install target (does what the build script used to do)</li>
+ <li>make the dummy's buildit/makeit shell scripts &amp; put them in
+the right directory both for source control &amp; for distribution</li>
+ <li>restructure TOT to a new directory structure that&rsquo;s
+documented &amp; standard </li>
+ <li>Merge changes from JMP097Working to TOT</li>
+</ul>
+<h3>Docs</h3>
+<ul>
+ <li>Create readme for svn &amp; eclipse </li>
+ <li>fix copyrights <br>
+ <font class="help">What should the copyrights be? Can someone put
+the standard info into this doc?</font></li>
+ <li>make docs into hyperlinked html files </li>
+ <li>document how to set up apache to make the web server work </li>
+ <li>document how to make ser2net work as a network server on a router
+(how to build/run) </li>
+ <li>document the new directory structure</li>
+</ul>
+<h2>HI-WANT (we plan to put these in, but priorities may intercede) </h2>
+<ul>
+ <li>Make BumpTurn &amp; others commandline programs work over the
+network</li>
+ <li>Make front bumper press stop the roomba's motors when driving in
+controlled mode. </li>
+ <li>make javadocs &amp; link into doc system </li>
+ <li>Get permission to bundle MyGUI with this package, or document
+where it can be found. Bundle it into the zip file</li>
+</ul>
+<h2>WANT (these are desirable and will be worked on opportunistically)</h2>
+<ul>
+ <li>First time after poweron, the LEDs flash but don't stay on. Fix
+it. (sometime, not necessarily for 0.97)</li>
+ <li>Add Motor exerciser panel to RoombacommGui</li>
+ <li>Make it so the model of roomba can be discovered (400 vs. 500
+family, &amp; maybe sub-models within the family)</li>
+ <li>move connection settings in roombacommGUI to its own dialog</li>
+ <li>Fill out the the sensor numbers/groups for 5xx models, &amp;
+develop masks &amp; figure out the corresponding reply length for the
+extra groups, and for querylist (sometime, not necessarily for
+0.97)</li>
+ <li>Implement failsafe in ser2net or shell script on router</li>
+ <li>Make roombasrvr able to serve the serial connection</li>
+ <li>Evaluate additional API capabilities that Roombacomm should maybe
+support, such as other sensor reading modes (querylist, stream),
+scheduling capability or others </li>
+ <li>Request permission from iRobot to publish the OI spec in the
+release&nbsp;</li>
+ <li>Fix net folder under roombacomm root - it won't build</li>
+ <li>&nbsp;Fill out the the sensor numbers/groups for 5xx models,
+&amp; develop masks &amp; figure out the corresponding reply length for
+the extra groups, and for querylist</li>
+ <li>First time after poweron, the LEDs flash but don't stay on. Fix
+it.</li>
+</ul>
+<h3>VERIFY (these are thought to be complete but need checking)</h3>
+<ul>
+ <li>Change the GUI to deal with with smaller screens<br>
+We have some users
+running it on netbooks. Looking at the specs, seems they may have a
+display as low as 1024x600 resolution. This does not leave room for the
+task bar. So I think a version that is 480 height would make sense,
+allowing for title-bar height etc, but width can be wider than 640 if
+you want. </li>
+ <li>Maybe move to some command line options for GUI layout
+ <ul>
+ <li>--GUI=1024x768 or --GUI=640x480</li>
+ <li>--GUI=NNNxYYY (support of variable values?)</li>
+ </ul>
+ </li>
+ <li>Update Power LEDS for 500 series, also make the sliders work the
+power leds in realtime. Paul thinks it works - what's broken?</li>
+ <li>Update roombacmd - change baud rate to SCI/OI </li>
+ <li>fix bug in simpletest on router if no parameters are given.</li>
+ <li>Verify that roombacomm doesn't exit if there are no serial ports </li>
+</ul>
+</body>
+</html>
diff --git a/roombacomm-client/lib/bluecove/bluecove-2.1.0.jar b/roombacomm-client/lib/bluecove/bluecove-2.1.0.jar
new file mode 100644
index 0000000..405de6d
--- /dev/null
+++ b/roombacomm-client/lib/bluecove/bluecove-2.1.0.jar
Binary files differ
diff --git a/roombacomm-client/lib/jargs.jar b/roombacomm-client/lib/jargs.jar
new file mode 100644
index 0000000..b1d7266
--- /dev/null
+++ b/roombacomm-client/lib/jargs.jar
Binary files differ
diff --git a/roombacomm-client/lib/rxtx/README.rxtx b/roombacomm-client/lib/rxtx/README.rxtx
new file mode 100644
index 0000000..6cfdc06
--- /dev/null
+++ b/roombacomm-client/lib/rxtx/README.rxtx
@@ -0,0 +1,16 @@
+These RXTX files were just copied directly from the rxtx website http://rxtx.qbang.org/wiki/index.php/Download
+
+Their versions are reported as:
+ Native lib Version = RXTX 2.1-7r2
+ Java lib Version = RXTX 2.1-7r2
+
+
+
+
+If you've never used RXTX before on your Mac OS X box, run the command:
+
+% ./macosx_setup.command
+
+And then shutdown (not just log out) and log back in again.
+And read this faq from Processing: http://processing.org/faq/bugs.html#serial
+
diff --git a/roombacomm-client/lib/rxtx/RXTXcomm.jar b/roombacomm-client/lib/rxtx/RXTXcomm.jar
new file mode 100644
index 0000000..84e5f01
--- /dev/null
+++ b/roombacomm-client/lib/rxtx/RXTXcomm.jar
Binary files differ
diff --git a/roombacomm-client/lib/rxtx/librxtxSerial.jnilib b/roombacomm-client/lib/rxtx/librxtxSerial.jnilib
new file mode 100644
index 0000000..2d3f20d
--- /dev/null
+++ b/roombacomm-client/lib/rxtx/librxtxSerial.jnilib
Binary files differ
diff --git a/roombacomm-client/lib/rxtx/librxtxSerial.so b/roombacomm-client/lib/rxtx/librxtxSerial.so
new file mode 100644
index 0000000..e60c5e6
--- /dev/null
+++ b/roombacomm-client/lib/rxtx/librxtxSerial.so
Binary files differ
diff --git a/roombacomm-client/lib/rxtx/macosx_setup.command b/roombacomm-client/lib/rxtx/macosx_setup.command
new file mode 100644
index 0000000..f6d9c9b
--- /dev/null
+++ b/roombacomm-client/lib/rxtx/macosx_setup.command
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# modified to change from /var/spool/uucp to /var/lock for rxtx 2.1_7r2
+# for processing release 0112, 13 april 2006 [fry]
+
+# originally fixperm.sh from rxtx-2.1_6/contrib
+# with a couple additions from pathsetup.command from fink [fry]
+
+# A script to fix permissions for lock files on Mac OS X
+# Contributed by Dmitry Markman <dimitry.markman@verizon.net>
+# Fri Aug 23 15:46:46 MDT 2002
+
+echo ""
+echo ""
+echo "This command will take care of a couple system things"
+echo "so that you can properly use the serial port to communicate"
+echo "between hardware devices and processing."
+echo ""
+echo "If there are actually changes that need to be made, then"
+echo "enter your password when prompted. The command uses sudo,"
+echo "and you'll need to have administrator access."
+echo ""
+
+echo -n "Do you want to continue? [Y/n] "
+read answer
+answer=`echo $answer | sed 's/^[yY].*$/y/'`
+if [ -z "$answer" -o "x$answer" = "xy" ]; then
+ curruser=`sudo id -p | grep 'login' | sed 's/login.//'`
+
+ if [ ! -d /var/lock ]
+ then
+ sudo mkdir /var/lock
+ fi
+
+ sudo chgrp uucp /var/lock
+ sudo chmod 775 /var/lock
+
+ if [ ! `sudo niutil -readprop / /groups/uucp users | grep $curruser > /dev/null` ]
+ then
+ sudo niutil -mergeprop / /groups/uucp users $curruser
+ fi
+
+ echo "Finished making changes, you should be all set"
+else
+ echo "Ok, nevermind then."
+fi
+
+echo ""
+echo ""
+echo " (All done... you can close this window now)"
+echo ""
+echo ""
+echo ""
+
diff --git a/roombacomm-client/lib/rxtx/rxtxSerial.dll b/roombacomm-client/lib/rxtx/rxtxSerial.dll
new file mode 100644
index 0000000..c0e6b58
--- /dev/null
+++ b/roombacomm-client/lib/rxtx/rxtxSerial.dll
Binary files differ
diff --git a/roombacomm-client/src/7z.exe b/roombacomm-client/src/7z.exe
new file mode 100755
index 0000000..b4d6c3f
--- /dev/null
+++ b/roombacomm-client/src/7z.exe
Binary files differ
diff --git a/roombacomm-client/src/Makefile b/roombacomm-client/src/Makefile
new file mode 100644
index 0000000..a49a388
--- /dev/null
+++ b/roombacomm-client/src/Makefile
@@ -0,0 +1,6 @@
+
+# pick one
+#JAVAC=javac
+JAVAC=/System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Commands/javac
+#JAVAC=/System/Library/Frameworks/JavaVM.framework/Versions/1.5/Commands/javac
+
diff --git a/roombacomm-client/src/TODO.txt b/roombacomm-client/src/TODO.txt
new file mode 100644
index 0000000..fe3be80
--- /dev/null
+++ b/roombacomm-client/src/TODO.txt
@@ -0,0 +1,6 @@
+To Do List
+- Fix net folder under roombacomm root - it won't build
+- Fill out the the sensor numbers/groups for 5xx models, & develop masks & figure out the corresponding reply length for the extra
+groups, and for querylist
+- First time after poweron, the LEDs flash but don't stay on. Fix it.
+
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
diff --git a/roombacomm-client/src/jargs/examples/gnu/AutoHelpParser.java b/roombacomm-client/src/jargs/examples/gnu/AutoHelpParser.java
new file mode 100644
index 0000000..f42ec5f
--- /dev/null
+++ b/roombacomm-client/src/jargs/examples/gnu/AutoHelpParser.java
@@ -0,0 +1,87 @@
+package jargs.examples.gnu;
+
+import jargs.gnu.CmdLineParser;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This example shows how to dynamically create basic output for a --help option.
+ */
+public class AutoHelpParser extends CmdLineParser {
+ List optionHelpStrings = new ArrayList();
+
+ public Option addHelp(Option option, String helpString) {
+ optionHelpStrings.add(" -" + option.shortForm() + "/--" + option.longForm() + ": " + helpString);
+ return option;
+ }
+
+ public void printUsage() {
+ System.err.println("usage: prog [options]");
+ for (Iterator i = optionHelpStrings.iterator(); i.hasNext(); ) {
+ System.err.println(i.next());
+ }
+ }
+
+ public static void main( String[] args ) {
+ AutoHelpParser parser = new AutoHelpParser();
+ CmdLineParser.Option verbose = parser.addHelp(
+ parser.addBooleanOption('v', "verbose"),
+ "Print extra information");
+ CmdLineParser.Option size = parser.addHelp(
+ parser.addIntegerOption('s', "size"),
+ "The extent of the thing");
+ CmdLineParser.Option name = parser.addHelp(
+ parser.addStringOption('n', "name"),
+ "Name given to the widget");
+ CmdLineParser.Option fraction = parser.addHelp(
+ parser.addDoubleOption('f', "fraction"),
+ "What percentage should be discarded");
+ CmdLineParser.Option help = parser.addHelp(
+ parser.addBooleanOption('h', "help"),
+ "Show this help message");
+
+ try {
+ parser.parse(args);
+ }
+ catch ( CmdLineParser.OptionException e ) {
+ System.err.println(e.getMessage());
+ parser.printUsage();
+ System.exit(2);
+ }
+
+ if ( Boolean.TRUE.equals(parser.getOptionValue(help))) {
+ parser.printUsage();
+ System.exit(0);
+ }
+
+ // Extract the values entered for the various options -- if the
+ // options were not specified, the corresponding values will be
+ // null.
+ Boolean verboseValue = (Boolean)parser.getOptionValue(verbose);
+ Integer sizeValue = (Integer)parser.getOptionValue(size);
+ String nameValue = (String)parser.getOptionValue(name);
+ Double fractionValue = (Double)parser.getOptionValue(fraction);
+
+ // For testing purposes, we just print out the option values
+ System.out.println("verbose: " + verboseValue);
+ System.out.println("size: " + sizeValue);
+ System.out.println("name: " + nameValue);
+ System.out.println("fraction: " + fractionValue);
+
+ // Extract the trailing command-line arguments ('a_nother') in the
+ // usage string above.
+ String[] otherArgs = parser.getRemainingArgs();
+ System.out.println("remaining args: ");
+ for ( int i = 0; i < otherArgs.length; ++i ) {
+ System.out.println(otherArgs[i]);
+ }
+
+ // In a real program, one would pass the option values and other
+ // arguments to a function that does something more useful.
+
+ System.exit(0);
+ }
+
+}
diff --git a/roombacomm-client/src/packaging/BSquare-jar.xml b/roombacomm-client/src/packaging/BSquare-jar.xml
new file mode 100644
index 0000000..4f186fa
--- /dev/null
+++ b/roombacomm-client/src/packaging/BSquare-jar.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project default="create_run_jar" name="Create Runnable Jar for Project roombaNet-JMP-0.97Working">
+ <!--this file was created by Eclipse Runnable JAR Export Wizard-->
+ <!--ANT 1.7 is required -->
+ <property name="projectDir" value="../.."/>
+ <target name="create_run_jar">
+ <jar destfile="${projectDir}/dist/BSquare.jar" filesetmanifest="mergewithoutmain">
+ <manifest>
+ <attribute name="Built-By" value="${user.name}"/>
+ <attribute name="Main-Class" value="com.hackingroomba.roombacomm.Bsquare"/>
+ <attribute name="Class-Path" value="."/>
+ </manifest>
+ <fileset dir="${projectDir}/bin"/>
+ <zipfileset excludes="META-INF/*.SF" src="${projectDir}/lib/bluecove/bluecove-2.1.0.jar"/>
+ <zipfileset excludes="META-INF/*.SF" src="${projectDir}/lib/rxtx/RXTXcomm.jar"/>
+ <zipfileset excludes="META-INF/*.SF" src="${projectDir}/lib/looks-2.1.4.jar"/>
+ <zipfileset excludes="META-INF/*.SF" src="${projectDir}/lib/jargs.jar"/>
+ </jar>
+ </target>
+</project>
diff --git a/roombacomm-client/src/packaging/Manifest.txt b/roombacomm-client/src/packaging/Manifest.txt
new file mode 100644
index 0000000..a4ded5c
--- /dev/null
+++ b/roombacomm-client/src/packaging/Manifest.txt
@@ -0,0 +1,2 @@
+Main-Class: roombacomm.RoombaCommTest
+Class-Path: RXTXcomm.jar
diff --git a/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Info.plist b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Info.plist
new file mode 100644
index 0000000..2799470
--- /dev/null
+++ b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Info.plist
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+ <key>CFBundleName</key>
+ <string>RoombaCommTest</string>
+ <key>CFBundleVersion</key>
+ <string>10.2</string>
+ <key>CFBundleAllowMixedLocalizations</key>
+ <string>true</string>
+ <key>CFBundleExecutable</key>
+ <string>JavaApplicationStub</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleIconFile</key>
+ <string>RoombaTest.icns</string>
+ <key>Java</key>
+ <dict>
+ <key>MainClass</key>
+ <string>roombacomm.RoombaCommTest</string>
+ <key>JVMVersion</key>
+ <string>1.5</string>
+ <key>ClassPath</key>
+ <array>
+ <string>$JAVAROOT/roombacomm.jar</string>
+ <string>$JAVAROOT/RXTXcomm.jar</string>
+ </array>
+ <key>Properties</key>
+ <dict>
+ <key>java.library.path</key>
+ <string>$JAVAROOT</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/MacOS/JavaApplicationStub b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/MacOS/JavaApplicationStub
new file mode 100755
index 0000000..6f21701
--- /dev/null
+++ b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/MacOS/JavaApplicationStub
Binary files differ
diff --git a/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/PkgInfo b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/PkgInfo
new file mode 100644
index 0000000..bd04210
--- /dev/null
+++ b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/PkgInfo
@@ -0,0 +1 @@
+APPL???? \ No newline at end of file
diff --git a/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/RXTXcomm.jar b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/RXTXcomm.jar
new file mode 100644
index 0000000..84e5f01
--- /dev/null
+++ b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/RXTXcomm.jar
Binary files differ
diff --git a/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/librxtxSerial.jnilib b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/librxtxSerial.jnilib
new file mode 100644
index 0000000..2d3f20d
--- /dev/null
+++ b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/librxtxSerial.jnilib
Binary files differ
diff --git a/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/roombacomm.jar b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/roombacomm.jar
new file mode 100644
index 0000000..34e02ef
--- /dev/null
+++ b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/Java/roombacomm.jar
Binary files differ
diff --git a/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/RoombaTest.icns b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/RoombaTest.icns
new file mode 100644
index 0000000..25a04a9
--- /dev/null
+++ b/roombacomm-client/src/packaging/RoombaCommTest.app/Contents/Resources/RoombaTest.icns
Binary files differ
diff --git a/roombacomm-client/src/packaging/processing-export.txt b/roombacomm-client/src/packaging/processing-export.txt
new file mode 100644
index 0000000..3c566b1
--- /dev/null
+++ b/roombacomm-client/src/packaging/processing-export.txt
@@ -0,0 +1,5 @@
+application.macosx = roombacomm.jar, RXTXcomm.jar, librxtxSerial.jnilib
+application.linux = roombacomm.jar, RXTXcomm.jar, librxtxSerial.so
+application.windows = roombacomm.jar, RXTXcomm.jar, rxtxSerial.dll
+
+
diff --git a/roombacomm-client/src/packaging/processing-readme.txt b/roombacomm-client/src/packaging/processing-readme.txt
new file mode 100644
index 0000000..1c951b2
--- /dev/null
+++ b/roombacomm-client/src/packaging/processing-readme.txt
@@ -0,0 +1,5 @@
+#
+# Note that this specifies the RXTX library and accompanying native libs.
+# This may cause a problem if the 'Serial' library is also used
+#
+#
diff --git a/roombacomm-client/src/processing-examples/roombaring/roombaring.pde b/roombacomm-client/src/processing-examples/roombaring/roombaring.pde
new file mode 100644
index 0000000..74aee3d
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaring/roombaring.pde
@@ -0,0 +1,54 @@
+//
+// roombaring --
+//
+// 2006, Tod E. Kurt, tod@todbot.com, http://roombahacking.com/
+//
+
+import roombacomm.*;
+
+String rtttl = "tron:d=4,o=5,b=100:8f6,8c6,8g,e,8p,8f6,8c6,8g,8f6,8c6,8g,e,8p,8f6,8c6,8g,e.,2d";
+
+String roombacommPort = "/dev/cu.KeySerial1";
+//String roombacommPort = "/dev/cu.BlueRadios-COM0-1";
+
+RoombaCommSerial roombacomm = new RoombaCommSerial();
+if( ! roombacomm.connect( roombacommPort ) ) {
+ println("couldn't connect. goodbye."); System.exit(1);
+}
+println("Roomba startup");
+roombacomm.startup();
+roombacomm.control();
+roombacomm.pause(50);
+
+ArrayList notelist = RTTTLParser.parse( rtttl );
+int songsize = notelist.size();
+// if within the size of a roomba song, make the song, then play
+if( songsize <= 16 ) {
+ 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 {
+ 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();
diff --git a/roombacomm-client/src/processing-examples/roombatune/roombatune.pde b/roombacomm-client/src/processing-examples/roombatune/roombatune.pde
new file mode 100644
index 0000000..56fa470
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombatune/roombatune.pde
@@ -0,0 +1,111 @@
+//
+// roombatune --
+//
+// 2006, Tod E. Kurt, tod@todbot.com, http://roombahacking.com/
+//
+
+import mkv.MyGUI.*;
+import roombacomm.*;
+
+// if you set roombaCommPort to null, it will bring up the port picker dialog
+// if you set it to "testdummy", it will be a bring up a non-functioning test of the GUI
+//String roombacommPort = "testdummy";
+String roombacommPort = "/dev/cu.KeySerial1";
+//String roombacommPort = "/dev/cu.BlueRadios-COM0-1";
+//String roombacommPort = null;
+
+RoombaCommSerial roombacomm;
+int octave = 6; // which octave we're on when playing notes
+
+void setup() {
+ roombacommSetup();
+}
+
+void draw() {
+ if( roombacomm == null ) return;
+}
+
+void keyPressed() {
+
+ int note=-1; // -1 means no note yet
+ switch( key ) {
+ // pseudo-keyboard to play notes on
+ case 'a': note = 0; break;
+ case 'w': note = 1; break;
+ case 's': note = 2; break;
+ case 'e': note = 3; break;
+ case 'd': note = 4; break;
+ case 'f': note = 5; break;
+ case 't': note = 6; break;
+ case 'g': note = 7; break;
+ case 'y': note = 8; break;
+ case 'h': note = 9; break;
+ case 'u': note =10; break;
+ case 'j': note =11; break;
+ case 'k': note =12; break;
+ case 'o': note =13; break;
+ case 'l': note =14; break;
+ case 'z': octave--; break; // change octaves
+ case 'x': octave++; break; // change octaves
+ }
+ // we actually hit a note key
+ if( note >= 0 ) {
+ octave = (octave < 2) ? 2 : (octave > 9) ? 9 : octave;
+ note = note + ((octave+1)*12);
+ if( roombacomm != null )
+ roombacomm.playNote( note, 10);
+ println("playing note: "+note+" (octave:"+octave+")");
+ }
+
+}
+
+//
+void roombacommPortSelected()
+{
+ roombacomm = new RoombaCommSerial();
+ if( roombacommPort.equals("testdummy") ) return; // so we can test UI w/o Roomba
+ println("opening roomba serial port '" +roombacommPort+"'");
+ if( ! roombacomm.connect( roombacommPort ) ) {
+ println("oh noes");
+ }
+ println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(50);
+ println("Playing some notes");
+ roombacomm.playNote( 72, 8 );
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 72, 10 );
+ roombacomm.pause( 200 );
+}
+
+void roombacommSetup()
+{
+ if( roombacommPort != null ) {
+ roombacommPortSelected();
+ return;
+ }
+
+ RoombaComm rc = new RoombaCommSerial(); // throwaway just to find ports
+ final String portlist[] = rc.listPorts(); // FIXME: should be made static
+ javax.swing.SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ String port = (String) javax.swing.JOptionPane.showInputDialog( null,
+ "Select Roomba Port",
+ "RoombaTune",
+ javax.swing.JOptionPane.QUESTION_MESSAGE,
+ null, portlist, null);
+ if( port == null ) {
+ javax.swing.JOptionPane.showMessageDialog(null, "No port chosen, goodbye");
+ System.exit(1);
+ }
+ roombacommPort = port;
+ roombacommPortSelected();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+}
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/CourierNewPSMT-9.vlw b/roombacomm-client/src/processing-examples/roombaview/data/CourierNewPSMT-9.vlw
new file mode 100644
index 0000000..77af18c
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/CourierNewPSMT-9.vlw
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/but_backward.png b/roombacomm-client/src/processing-examples/roombaview/data/but_backward.png
new file mode 100644
index 0000000..aa0c5d2
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/but_backward.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/but_forward.png b/roombacomm-client/src/processing-examples/roombaview/data/but_forward.png
new file mode 100644
index 0000000..b11d65b
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/but_forward.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/but_spinleft.png b/roombacomm-client/src/processing-examples/roombaview/data/but_spinleft.png
new file mode 100644
index 0000000..4bdd361
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/but_spinleft.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/but_spinright.png b/roombacomm-client/src/processing-examples/roombaview/data/but_spinright.png
new file mode 100644
index 0000000..c7a5c0d
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/but_spinright.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/but_stop.png b/roombacomm-client/src/processing-examples/roombaview/data/but_stop.png
new file mode 100644
index 0000000..a78b6dc
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/but_stop.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/but_turnleft.png b/roombacomm-client/src/processing-examples/roombaview/data/but_turnleft.png
new file mode 100644
index 0000000..f47c7ff
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/but_turnleft.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/but_turnright.png b/roombacomm-client/src/processing-examples/roombaview/data/but_turnright.png
new file mode 100644
index 0000000..b554358
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/but_turnright.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/turnfrontleft.png b/roombacomm-client/src/processing-examples/roombaview/data/turnfrontleft.png
new file mode 100644
index 0000000..01c1794
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/turnfrontleft.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/data/turnfrontright.png b/roombacomm-client/src/processing-examples/roombaview/data/turnfrontright.png
new file mode 100644
index 0000000..a0d43d6
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/data/turnfrontright.png
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI.zip b/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI.zip
new file mode 100644
index 0000000..3242bb7
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI.zip
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/MyGUI_readme.txt b/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/MyGUI_readme.txt
new file mode 100755
index 0000000..2426fe2
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/MyGUI_readme.txt
@@ -0,0 +1,51 @@
+MyGUI Library
+=======================
+Created by John Beech
+Contact: processing@mkv25.net
+Version: 0010
+=======================
+-Quick installation notes-
+
+Extract contents of this zip file to your processing directory /libraries/
+
+This should create the sub folder called MyGUI/ with MyGUI_readme.txt inside, and a folder
+called MyGUI/library/ with the file MyGUI.jar inside.
+
+=======================
+-Useage notes-
+
+Please see the Documentation files contained within the MyGUI.jar archive.
+
+Alternatively visit http://mkv25.net/MyGUI/doc/
+
+=======================
+-Version History-
+
+ver 0010 - 2005-04-01
+-Fixed Set abstract class MyGUIObject to public (from default) so that it can be extended from within Processing
+
+Ver 0009 - 2005-12-29
+-Fixed Arial-48.vlw moved to sub-folder /data/ inside .JAR, Processing now finds file.
+-Fixed MyGUIButton does not print message "MyGUIStyle has been set!" after setting a specific style.
+
+Ver 0008
+-Bug Arial-48.vlw not found by processing, please include this in your project/sketch folder to use MyGUI library.
+
+Ver 0007
+*Note First release as a library compiled using Eclipse
++Added Basic documentation written
++Added MyGUITextInput, which allows a user to enter and edit text in a field. No copy and paste support.
++Added MyGUICheckbox, check box object.
++Added MyGUIGroup, groups of objects can be moved/rotated
+-Changed MyGUI now inherits properties/methods from MyGUIGroup (see documentation)
+
+Ver 0006 < earlier
+*Note Original classes written from within Processing IDE including MyGUIButton, MyGUIButtonDrag, MyGuiImage, MyGUILabel, MyGUIPinSlider, MyGUIStyle. MyGUIObject used as an interface for compatable objects as well as containing basic box based hit detection code.
+
+=======================
+-Latest version-
+Please check http://mkv25.net/MyGUI/
+
+=======================
+-Credits-
+Created and designed by John Beech \ No newline at end of file
diff --git a/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/library/MyGUI.jar b/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/library/MyGUI.jar
new file mode 100755
index 0000000..7973ffa
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/libraries/MyGUI/library/MyGUI.jar
Binary files differ
diff --git a/roombacomm-client/src/processing-examples/roombaview/roombaview.pde b/roombacomm-client/src/processing-examples/roombaview/roombaview.pde
new file mode 100644
index 0000000..4c03600
--- /dev/null
+++ b/roombacomm-client/src/processing-examples/roombaview/roombaview.pde
@@ -0,0 +1,425 @@
+//
+// roombaview --
+//
+// 2006, Tod E. Kurt, tod@todbot.com, http://roombahacking.com/
+//
+
+import mkv.MyGUI.*;
+import roombacomm.*;
+
+// if you set roombaCommPort to null, it will bring up the port picker dialog
+// if you set it to "testdummy", it will be a bring up a non-functioning test of the GUI
+String roombacommPort = "testdummy";
+//String roombacommPort = "/dev/cu.KeySerial1";
+//String roombacommPort = "/dev/cu.BlueRadios-COM0-1";
+//String roombacommPort = null;
+
+RoombaCommSerial roombacomm;
+
+MyGUI gui;
+MyGUIButton butForward, butBackward, butStop;
+MyGUIButton butSpinLeft, butSpinRight, butTurnLeft, butTurnRight;
+MyGUIButton butReset, butSafe, butFull, butSpy, butVacuum, butBeep;
+MyGUIPinSlider sliderSpeed;
+PFont fontA;
+// all the colors used
+color cBack = color(150);
+color cOff = color(100);
+color cWhl = color(80);
+color cOn = color(255, 0, 0);
+color cTxt = color(40);
+color cGrid = color(120);
+color cFlor = color(190);
+color cStat = color(170);
+color cBut = color(170,170,210);
+
+float rx,ry,rangle; // location of displayed roomba icon
+float scalex = 0.4; // convert mm to pixels
+float scaley = 0.4; // convert mm to pixels
+
+int frameCounter=0;
+
+// called once on startup
+void setup() {
+ size(600, 500);
+ frameRate(5);
+ smooth();
+ background(cFlor);
+ stroke(100);
+ ellipseMode(CENTER_RADIUS);
+ rectMode(CENTER);
+ // Initiate GUI objects
+ fontA = loadFont("CourierNewPSMT-9.vlw");
+ textFont(fontA, 12);
+ fill(0);
+ gui = new MyGUI(this);
+ makeMoveButtons(width-75,height-100);
+ makeControlButtons(width-75,height-225);
+
+ roombacommSetup();
+ reset();
+}
+
+void reset() {
+ rx = width/2;
+ ry = height/2;
+ rangle = 0;
+}
+
+// called 'framerate' times a second
+void draw() {
+ frameCounter++;
+ background(cFlor);
+ fill(0);
+ stroke(100);
+
+ drawGridlines();
+ drawStatus();
+ if( roombacomm != null ) {
+ computeRoombaLocation();
+ drawRoombaStatus( rx, ry, rangle );
+ updateRoombaState();
+ }
+}
+
+//
+// Get roomba sensors and update display
+//
+void updateRoombaState() {
+ roombacomm.sensors();
+}
+
+//
+void computeRoombaLocation() {
+ int distance = roombacomm.distance();
+ float angle = roombacomm.angleInRadians();
+ rangle = rangle - angle ;
+ rangle %= TWO_PI;
+ float dx = distance * sin(rangle);
+ float dy = distance * cos(rangle);
+ rx = rx + (dx * scaley);
+ ry = ry - (dy * scalex);
+
+ // torroidial mapping, sir
+ if( rx > width ) rx = 0;
+ if( rx < 0 ) rx = width;
+ if( ry > height ) ry = 0;
+ if( ry < 0 ) ry = height;
+}
+
+//
+//
+void actionPerformed(ActionEvent e) {
+ println("e="+e);
+ if( !roombacomm.connected() ) return;
+ String cmd = e.getActionCommand();
+ Object src = e.getSource();
+
+ if( cmd.equals("speed-update") ) {
+ roombacomm.setSpeed( sliderSpeed.getValue() );
+ return;
+ }
+ else if( src == butStop ) {
+ roombacomm.stop();
+ } else if( src == butForward ) {
+ roombacomm.goForward();
+ } else if( src == butBackward ) {
+ roombacomm.goBackward();
+ } else if( src == butSpinLeft ) {
+ roombacomm.spinLeft();
+ } else if( src == butSpinRight ) {
+ roombacomm.spinRight();
+ } else if( src == butTurnLeft ) {
+ roombacomm.turnLeft();
+ } else if( src == butTurnRight ) {
+ roombacomm.turnRight();
+ } else if( src == butReset ) {
+ roombacomm.reset();
+ reset();
+ } else if( src == butSafe ) {
+ roombacomm.start();
+ roombacomm.control();
+ } else if( src == butFull ) {
+ roombacomm.start();
+ roombacomm.control();
+ roombacomm.full();
+ } else if( src == butSpy ) {
+ roombacomm.start();
+ roombacomm.clean(); // clean mode
+ } else if( src == butBeep ) {
+ int song[] = { 69,8, 72,18, 76,8};
+ roombacomm.createSong(1, song );
+ roombacomm.playSong(1);
+ }
+}
+
+//
+// called on key press
+//
+void keyPressed() {
+ if( key == CODED ) {
+ if( keyCode == UP )
+ roombacomm.goForward();
+ else if( keyCode == DOWN )
+ roombacomm.goBackward();
+ else if( keyCode == LEFT )
+ roombacomm.spinLeft();
+ else if( keyCode == RIGHT )
+ roombacomm.spinRight();
+ }
+ else if( keyCode == RETURN || keyCode == ENTER ) {
+ println("resetting");
+ roombacomm.reset();
+ reset();
+ }
+ else if( key == ' ' ) {
+ roombacomm.stop();
+ }
+ else if( key == '.' || key == '>' ) {
+ sliderSpeed.setValue( sliderSpeed.getValue() + 25 );
+ roombacomm.setSpeed( sliderSpeed.getValue() );
+ }
+ else if( key == '.' || key == '>' ) {
+ sliderSpeed.setValue( sliderSpeed.getValue() - 25 );
+ roombacomm.setSpeed( sliderSpeed.getValue() );
+ }
+}
+
+//
+// drawRoombaStatus -- called during every draw()
+// draws a little virtual roomba with in-built sensor icons
+//
+void drawRoombaStatus(float posx, float posy, float angle) {
+ pushMatrix();
+ translate( (int)posx,(int)posy);
+ rotate(angle);
+ smooth();
+ textFont(fontA, 10);
+ textAlign(CENTER);
+
+ fill(cBack);
+ strokeWeight(2);
+ stroke(100);
+ ellipseMode(CENTER_RADIUS);
+ //rectMode(CENTER);
+ ellipse(0,0, 50,50);
+
+ fill(0);
+ stroke(cTxt);
+ strokeWeight(4);
+ text("cliff", 0, -30);
+ if( roombacomm.cliffLeft() ) stroke(cOn);
+ else stroke(cOff);
+ line(-35,-25, -40,-15);
+ if( roombacomm.cliffRight() ) stroke(cOn);
+ else stroke(cOff);
+ line( 35,-25, 40,-15);
+
+ if( roombacomm.cliffFrontLeft() ) stroke(cOn);
+ else stroke(cOff);
+ line(-30,-30, -20,-30);
+ if( roombacomm.cliffFrontRight() ) stroke(cOn);
+ else stroke(cOff);
+ line( 30,-30, 20,-30);
+
+ stroke(cTxt);
+ strokeWeight(7);
+ text("bump", 0,-17);
+ if( roombacomm.bumpLeft() ) stroke(cOn);
+ else stroke(cOff);
+ line(-25,-20, -20,-20);
+ if( roombacomm.bumpRight() ) stroke(cOn);
+ else stroke(cOff);
+ line( 25,-20, 20,-20);
+
+ stroke(cTxt);
+ text("wheeldrop", 0,3);
+ if( roombacomm.wheelDropLeft() ) stroke(cOn);
+ else stroke(cWhl);
+ line(-35,8, -20, 8);
+ if( roombacomm.wheelDropRight() ) stroke(cOn);
+ else stroke(cWhl);
+ line( 35,8, 20, 8);
+ if( roombacomm.wheelDropCenter()) stroke(cOn);
+ else stroke(cWhl);
+ line(-2,-8, 2,-8);
+
+ stroke(cTxt);
+ strokeWeight(5);
+ text("over",0,18);
+ if( roombacomm.motorOvercurrentDriveLeft() ) stroke(cOn);
+ else stroke(cOff);
+ line(-30,16, -20, 16);
+ if( roombacomm.motorOvercurrentDriveRight() ) stroke(cOn);
+ else stroke(cOff);
+ line( 30,16, 20, 16);
+
+ stroke(cTxt);
+ strokeWeight(7);
+ text("dirt",0,42);
+ if( roombacomm.dirtLeft()>0 ) stroke(40,40,roombacomm.dirtLeft());
+ else stroke(cOff);
+ line(-22,40,-18,40);
+ if( roombacomm.dirtRight()>0 ) stroke(40,40,roombacomm.dirtRight());
+ else stroke(cOff);
+ line( 22,40, 18,40);
+
+ popMatrix();
+}
+
+// called during each draw()
+void drawStatus() {
+ fill(cStat);
+ rectMode(CENTER);
+ rect( width-75,height-125, 150,250 );
+
+ rectMode(CORNER);
+ rect(0,0, width,50);
+ fill(0);
+ textAlign(LEFT);
+ textFont(fontA,14);
+ String status = "unknown";
+ int batt_percent=0, batt_charge=0, batt_volts=0, batt_mA=0, batt_cap=0;
+ if( roombacomm == null ) {
+ status = "not connected. no roomba. please restart.";
+ } else if( ! roombacomm.connected() ) {
+ status = "not connected. no roomba. please restart.";
+ } else if( ! roombacomm.sensorsValid() ) {
+ status = "connected. sensors invalid. unplugged?";
+ } else if( roombacomm.safetyFault() ) {
+ status = "connected. safety fault. reposition roomba and reset.";
+ } else {
+ status = "connected. roomba detected. ok.";
+ }
+ if( roombacomm != null ) {
+ batt_mA = roombacomm.current();
+ batt_volts = roombacomm.voltage();
+ batt_charge = roombacomm.charge();
+ batt_cap = roombacomm.capacity();
+ batt_percent = (batt_cap == 0) ? 0 : (batt_charge*100)/batt_cap;
+ }
+ text(" status: "+status, 8,12);
+ text(" mode: "+((roombacomm==null)?"no roomba":roombacomm.modeAsString()), 8,24);
+ text("battery: "+batt_percent+"% ("+batt_charge+"/"+batt_cap+" mAh) voltage: "
+ +batt_volts+" mV @ "+batt_mA+" mA", 8,36);
+}
+
+// called during each draw()
+void drawGridlines() {
+ int gridspacing = 50;
+ stroke(cGrid);
+ strokeWeight(1);
+ for( int i=0; i< 15; i++ ) {
+ line(0, i*gridspacing, width, i*gridspacing);
+ line(i*gridspacing,0, i*gridspacing, height);
+ }
+}
+
+//
+void makeMoveButtons(int posx, int posy) {
+ MyGUIGroup buttonGroup = new MyGUIGroup(this, posx, posy);
+ buttonGroup.setStyle( new MyGUIStyle(this, cBut) );
+ PImage forward = loadImage("but_forward.png");
+ PImage backward = loadImage("but_backward.png");
+ PImage spinleft = loadImage("but_spinleft.png");
+ PImage spinright = loadImage("but_spinright.png");
+ PImage stopit = loadImage("but_stop.png");
+ PImage turnFL = loadImage("but_turnleft.png");
+ PImage turnFR = loadImage("but_turnright.png");
+ butForward = new MyGUIButton(this, 0,-45, forward);
+ butBackward = new MyGUIButton(this, 0, 45, backward);
+ butSpinLeft = new MyGUIButton(this,-45, 0, spinleft);
+ butSpinRight = new MyGUIButton(this, 45, 0, spinright);
+ butStop = new MyGUIButton(this, 0, 0, stopit);
+ butTurnLeft = new MyGUIButton(this,-45,-45, turnFL);
+ butTurnRight = new MyGUIButton(this, 45,-45, turnFR);
+ sliderSpeed = new MyGUIPinSlider(this, 0,80, 100,20, 0,500);
+ sliderSpeed.setValue(200);
+ sliderSpeed.setActionCommand("speed-update");
+
+ gui.add(buttonGroup);
+ //buttonGroup.add( label );
+ buttonGroup.add( butForward );
+ buttonGroup.add( butBackward );
+ buttonGroup.add( butSpinLeft );
+ buttonGroup.add( butSpinRight);
+ buttonGroup.add( butStop );
+ buttonGroup.add( butTurnLeft );
+ buttonGroup.add( butTurnRight );
+ buttonGroup.add( sliderSpeed );
+}
+
+//
+void makeControlButtons(int posx, int posy) {
+ MyGUIGroup buttonGroup = new MyGUIGroup(this, posx,posy);
+ buttonGroup.setStyle( new MyGUIStyle(this, cBut) );
+ butReset = new MyGUIButton(this, -40, 25, "reset", 35,20 );
+ butSafe = new MyGUIButton(this, 0, 25, "safe", 35,20 );
+ butFull = new MyGUIButton(this, 40, 25, "full", 35,20 );
+ butSpy = new MyGUIButton(this, -40, 0, "SPY", 35,20 );
+ butBeep = new MyGUIButton(this, 40, 0, "beep", 35,20 );
+// butVacuum = new MyGUIButton(this, 40, 25, "vacuum", 35,20 );
+
+ gui.add( buttonGroup );
+ buttonGroup.add( butReset );
+ buttonGroup.add( butSafe );
+ buttonGroup.add( butFull );
+ buttonGroup.add( butSpy );
+ buttonGroup.add( butBeep );
+ //buttonGroup.add( butVacuum );
+}
+
+//
+void roombacommPortSelected()
+{
+ roombacomm = new RoombaCommSerial();
+ if( roombacommPort.equals("testdummy") ) return; // so we can test UI w/o Roomba
+ println("opening roomba serial port '" +roombacommPort+"'");
+ if( ! roombacomm.connect( roombacommPort ) ) {
+ println("oh noes");
+ }
+ println("Roomba startup");
+ roombacomm.startup();
+ roombacomm.control();
+ roombacomm.pause(50);
+ println("Playing some notes");
+ roombacomm.playNote( 72, 8 );
+ roombacomm.pause( 200 );
+ roombacomm.playNote( 72, 10 );
+ roombacomm.pause( 200 );
+}
+
+//
+// On startup, popup a dialog to choose roomba port
+//
+void roombacommSetup()
+{
+ if( roombacommPort != null ) {
+ roombacommPortSelected();
+ return;
+ }
+
+ RoombaComm rc = new RoombaCommSerial(); // throwaway just to find ports
+ final String portlist[] = rc.listPorts(); // FIXME: should be made static
+ javax.swing.SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ String port = (String) javax.swing.JOptionPane.showInputDialog( null,
+ "Select Roomba Port",
+ "RoombaView",
+ javax.swing.JOptionPane.QUESTION_MESSAGE,
+ null, portlist, null);
+ if( port == null ) {
+ javax.swing.JOptionPane.showMessageDialog(null, "No port chosen, goodbye");
+ System.exit(1);
+ }
+ roombacommPort = port;
+ roombacommPortSelected();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ } );
+
+}
diff --git a/roombacomm-client/src/scripts/RoombaCommTest.bat b/roombacomm-client/src/scripts/RoombaCommTest.bat
new file mode 100755
index 0000000..0ac7df8
--- /dev/null
+++ b/roombacomm-client/src/scripts/RoombaCommTest.bat
@@ -0,0 +1,3 @@
+REM run RoombaCommTest .bat file
+
+java -Djava.library.path=rxtxlib -classpath .;rxtxlib/RXTXcomm.jar;rxtxlib/bluecove-2.1.0.jar roombacomm.RoombaCommTest
diff --git a/roombacomm-client/src/scripts/RoombaCommTest.sh b/roombacomm-client/src/scripts/RoombaCommTest.sh
new file mode 100755
index 0000000..dbd5ec2
--- /dev/null
+++ b/roombacomm-client/src/scripts/RoombaCommTest.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+
+java -Djava.library.path=rxtxlib -classpath .:rxtxlib/RXTXcomm.jar roombacomm.RoombaCommTest
diff --git a/roombacomm-client/src/scripts/build-jar.bat b/roombacomm-client/src/scripts/build-jar.bat
new file mode 100755
index 0000000..58ac9cd
--- /dev/null
+++ b/roombacomm-client/src/scripts/build-jar.bat
@@ -0,0 +1,12 @@
+::
+:: Builds the jar file for the core RoombaComm classes
+:: This core is used for all direct-connect (serial,etc) cases
+::
+:: Before running this script, compile all the .java files to .class files
+:: and "cd build"
+::
+::
+
+REM "buidling roombacomm.jar..."
+jar -cfm roombacomm.jar packaging/Manifest.txt roombacomm
+REM "done"
diff --git a/roombacomm-client/src/scripts/build-jar.sh b/roombacomm-client/src/scripts/build-jar.sh
new file mode 100755
index 0000000..5ad5a8c
--- /dev/null
+++ b/roombacomm-client/src/scripts/build-jar.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# Builds the jar file for the core RoombaComm classes
+# This core is used for all direct-connect (serial,etc) cases
+#
+# Before running this script, compile all the .java files to .class files
+# and "cd build"
+#
+#
+
+base=`dirname "$0"`
+
+echo "buidling roombacomm.jar..."
+jar -cfm roombacomm.jar packaging/Manifest.txt roombacomm
+echo "done"
diff --git a/roombacomm-client/src/scripts/build-processing-library.bat b/roombacomm-client/src/scripts/build-processing-library.bat
new file mode 100755
index 0000000..2773382
--- /dev/null
+++ b/roombacomm-client/src/scripts/build-processing-library.bat
@@ -0,0 +1,22 @@
+::
+:: Builds the distribution zip file that contains a properly formatted
+:: Processing library. Unzip the resulting file in the 'libraries' directory
+:: in Processing to install.
+::
+:: Before running this script, run 'build-jar.sh'
+::
+
+cd packaging
+rd /s /q processing
+md processing\roombacomm\library
+copy ..\roombacomm.jar processing\roombacomm\library\roombacomm.jar
+copy processing-export.txt processing\roombacomm\library\export.txt
+copy ..\rxtxlib\* processing\roombacomm\library
+md processing\roombacomm\examples
+xcopy ..\processing-examples processing\roombacomm\examples /s
+copy ..\README processing\roombacomm\README
+copy processing-readme.txt processing\roombacomm\README-processing.txt
+cd processing
+..\..\7z a roombacomm-processing.zip roombacomm -r
+copy roombacomm-processing.zip ..\..
+cd ..\..
diff --git a/roombacomm-client/src/scripts/build-processing-library.sh b/roombacomm-client/src/scripts/build-processing-library.sh
new file mode 100755
index 0000000..1ac59ee
--- /dev/null
+++ b/roombacomm-client/src/scripts/build-processing-library.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Builds the distribution zip file that contains a properly formatted
+# Processing library. Unzip the resulting file in the 'libraries' directory
+# in Processing to install.
+#
+# Before running this script, run 'build-jar.sh'
+#
+
+pushd packaging
+rm -rf processing
+mkdir -p processing/roombacomm/library
+cp ../roombacomm.jar processing/roombacomm/library/roombacomm.jar
+cp processing-export.txt processing/roombacomm/library/export.txt
+cp ../rxtxlib/* processing/roombacomm/library
+cp -r ../processing-examples processing/roombacomm/examples
+cp ../README processing/roombacomm/README
+cp processing-readme.txt processing/roombacomm/README-processing.txt
+pushd processing
+zip -r roombacomm-processing.zip roombacomm
+cp roombacomm-processing.zip ../..
+popd
+popd
diff --git a/roombacomm-client/src/scripts/makedoc.bat b/roombacomm-client/src/scripts/makedoc.bat
new file mode 100755
index 0000000..fca42f2
--- /dev/null
+++ b/roombacomm-client/src/scripts/makedoc.bat
@@ -0,0 +1,2 @@
+REM javadoc -d html -classpath $MYCP ../rxtx/src/*.java -exclude gnu.io roombacomm
+javadoc -d html -classpath .:./rxtxlib/RXTXcomm.jar roombacomm/*.java
diff --git a/roombacomm-client/src/scripts/makedoc.sh b/roombacomm-client/src/scripts/makedoc.sh
new file mode 100755
index 0000000..db6cec0
--- /dev/null
+++ b/roombacomm-client/src/scripts/makedoc.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+MYCP="`pwd`:`pwd`/rxtxlib/RXTXcomm.jar"
+
+rm -rf html
+#javadoc -d html -classpath $MYCP ../rxtx/src/*.java -exclude gnu.io roombacomm
+javadoc -d html -classpath $MYCP roombacomm
diff --git a/roombacomm-client/src/scripts/makeit.bat b/roombacomm-client/src/scripts/makeit.bat
new file mode 100755
index 0000000..3365411
--- /dev/null
+++ b/roombacomm-client/src/scripts/makeit.bat
@@ -0,0 +1,8 @@
+:: pick one
+set JAVAC=javac
+:: JAVAC=/System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Commands/javac
+:: #JAVAC=/System/Library/Frameworks/JavaVM.framework/Versions/1.5/Commands/javac
+
+
+REM %JAVAC% -classpath .;rxtxlib/RXTXcomm.jar roombacomm/*.java
+%JAVAC% -Xlint:unchecked -classpath .;rxtxlib/RXTXcomm.jar roombacomm/*.java
diff --git a/roombacomm-client/src/scripts/makeit.sh b/roombacomm-client/src/scripts/makeit.sh
new file mode 100755
index 0000000..89b342b
--- /dev/null
+++ b/roombacomm-client/src/scripts/makeit.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# pick one
+#JAVAC=javac
+JAVAC=/System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Commands/javac
+#JAVAC=/System/Library/Frameworks/JavaVM.framework/Versions/1.5/Commands/javac
+
+
+$JAVAC -classpath .:rxtxlib/RXTXcomm.jar roombacomm/*.java roombacomm/net/*.java
+#javac -Xlint:unchecked -classpath .:rxtxlib/RXTXcomm.jar roombacomm/*.java \ No newline at end of file
diff --git a/roombacomm-client/src/scripts/one.sh b/roombacomm-client/src/scripts/one.sh
new file mode 100755
index 0000000..77b730d
--- /dev/null
+++ b/roombacomm-client/src/scripts/one.sh
@@ -0,0 +1 @@
+./runit.sh roombacomm.Spiro2 /dev/tty.RooTooth-COM0-3 240 401 1 1230 1000
diff --git a/roombacomm-client/src/scripts/runit.bat b/roombacomm-client/src/scripts/runit.bat
new file mode 100755
index 0000000..8ecd040
--- /dev/null
+++ b/roombacomm-client/src/scripts/runit.bat
@@ -0,0 +1,3 @@
+REM run simple serial test .bat file
+
+java -Djava.library.path=rxtxlib -classpath .;rxtxlib/RXTXcomm.jar;rxtxlib/bluecove-2.1.0.jar %*
diff --git a/roombacomm-client/src/scripts/runit.sh b/roombacomm-client/src/scripts/runit.sh
new file mode 100755
index 0000000..6c14eb5
--- /dev/null
+++ b/roombacomm-client/src/scripts/runit.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+
+java -Djava.library.path=rxtxlib -classpath .:rxtxlib/RXTXcomm.jar $*
+#jamvm -Djava.library.path=rxtxlib -classpath .:rxtxlib/RXTXcomm.jar:/opt/local/share/java/glibj.jar $*
+