summaryrefslogtreecommitdiff
path: root/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java
diff options
context:
space:
mode:
authorIdo Hadanny <ido.hadanny@gmail.com>2011-09-22 21:30:13 +0300
committerIdo Hadanny <ido.hadanny@gmail.com>2011-09-22 21:30:13 +0300
commit53b947df484159f5934898ba6c0aa69a9c869007 (patch)
tree189d5903a67765c390de8f1e3423e6b80cde57d1 /roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java
parent654ca6d59e99b5e72b05dfb917bc5888c4ba6b1b (diff)
added stuff
Diffstat (limited to 'roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java')
-rw-r--r--roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java1690
1 files changed, 1690 insertions, 0 deletions
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;
+ }
+}