package lejos.nxt; import lejos.robotics.*; /* * WARNING: THIS CLASS IS SHARED BETWEEN THE classes AND pccomms PROJECTS. * DO NOT EDIT THE VERSION IN pccomms AS IT WILL BE OVERWRITTEN WHEN THE PROJECT IS BUILT. */ /** * LEGO Color Sensor driver. * This driver provides access to the LEGO Color sensor. It allows the reading of * raw and processed color values. The sensor has a tri-color LED and this can * be set to output red/green/blue or off. It also has a full mode in which * four samples are read (off/red/green/blue) very quickly. These samples can * then be combined using the calibration data provided by the device to * determine the "LEGO" color currently being viewed. * @author andy */ public class ColorLightSensor implements LampLightDetector, ColorDetector, SensorConstants { protected Colors.Color[] colorMap = Colors.Color.values(); protected SensorPort port; protected int type; private int _zero = 1023; private int _hundred = 0; private Colors.Color lampColor = Colors.Color.NONE; /** * Create a new Color Sensor instance and bind it to a port. * @param port Port to use for the sensor. * @param type Initial operating mode. */ // TODO: We need a default constructor that doesn't need type, which is just the lamp color it uses to scan. // NXT LightSensor and RCXLightSensor set default to red LED on. Perhaps this should too? Make it an interface requirement? public ColorLightSensor(SensorPort port, int type) { this.port = port; port.enableColorSensor(); port.setTypeAndMode(type, SensorPort.MODE_RAW); } /** * Change the type of the sensor * @param type new sensor type. */ // TODO: Type changes the lamp color, so maybe should use Colors.Color enum? // TODO: I'm aware the user can use ColorLightSensor to change type (lamp), and lampColor // won't be updated and it will give wrong value for getFloodlight(). Don't care until we work out the API. public void setType(int type) { port.setType(type); this.type = type; } /** * Return a single raw value from the device. * When in single color mode this returns the raw sensor reading. * Values range from 0 to 1023 but usually don't get over 600. * @return the raw value or < 0 if there is an error. */ public int readRawValue() { return port.readRawValue(); } /** * When in full color mode this returns all four raw color values from the * device by doing four very quick reads and flashing all colors. * The raw values theoretically range from 0 to 1023 but in practice they usually * do not go higher than 600. You can access the index of each color * using RGB_RED, RGB_GREEN, RGB_BLUE and RGB_BLANK. e.g. to retrieve the Blue value: * <code>vals[ColorLightSensor.RGB_BLUE]</code> * * @param vals array of four color values. * @return < 0 if there is an error number of values returned if ok. */ public int readRawValues(int [] vals) { return port.readRawValues(vals); } /** * Return a set of calibrated data. * If in single color mode the returned data is a simple percentage. If in * full color mode the data is a set of calibrated red/blue/green/blank * readings that range from 0 to 255. You can access the index of each color * using RGB_RED, RGB_GREEN, RGB_BLUE and RGB_BLANK. e.g. to retrieve the Blue value: * <code>vals[ColorLightSensor.RGB_BLUE]</code> * * @param vals 4 element array for the results * @return < 0 if error. Number of values if ok. */ public int readValues(int [] vals) { return port.readValues(vals); } /** * Return a single processed value. * If in single color mode this returns a single reading as a percentage. If * in full color mode it returns a Lego color value that identifies the * color of the object in view. * @return processed color value. */ public int readValue() { return port.readValue(); } /** * Read the current color and return an enum value. This is usually only accurate at a distance * of about 1 cm.It is not very good at detecting yellow. * @return The color enumeration under the sensor. */ public Colors.Color readColor() { int col = readValue(); if (col <= 0) return Colors.Color.NONE; return colorMap[col]; } public int getLightValue() { // TODO: Problem! If lamp is on for illumination, this shuts it down. // So either turn on red lamp, then switch back, or turn off lamp (if passive mode) then switch back. int temp_type = this.type; setType(TYPE_COLORNONE); int val = readValue(); this.setType(temp_type); return val; } public int getNormalizedLightValue() { int temp_type = this.type; setType(ColorLightSensor.TYPE_COLORNONE); int val = readRawValue(); this.setType(temp_type); return val; } public void setFloodlight(boolean floodlight) { this.lampColor = (floodlight ? Colors.Color.RED : Colors.Color.NONE); setType(floodlight ? TYPE_COLORRED : TYPE_COLORNONE); } public int getBlueComponent() { int temp_type = this.type; setType(ColorLightSensor.TYPE_COLORBLUE); int val = readRawValue(); // TODO: Normalize? // Scale to max 255: val = val * 255 / 1023; this.setType(temp_type); return val; } public int getGreenComponent() { // TODO: Since lampColor state is included now, can we use that instead for all these temp_type? int temp_type = this.type; setType(ColorLightSensor.TYPE_COLORGREEN); int val = readRawValue(); // TODO: Normalize? Use _zero instead of 1023? (changes when calibrated) // TODO: What about scaling to max 255 using _hundred? (changes when calibrated) val = val * 255 / 1023; this.setType(temp_type); return val; } public int[] getColor() { int temp_type = this.type; setType(ColorLightSensor.TYPE_COLORFULL); int [] all_vals = new int[4]; readValues(all_vals); int [] rgb_vals = new int[3]; System.arraycopy(all_vals, 0, rgb_vals, 0, 3); this.setType(temp_type); return rgb_vals; } // TODO: Three getXXComponent() methods share code. Could reuse code. Or Sven has suggestion of getColorComponent(RED). public int getRedComponent() { int temp_type = this.type; setType(ColorLightSensor.TYPE_COLORRED); int val = readRawValue(); // TODO: Normalize? // Scale to max 255: val = val * 255 / 1023; this.setType(temp_type); return val; } public Colors.Color getFloodlight() { return lampColor; } public boolean isFloodlightOn() { return (lampColor != Colors.Color.NONE); } public boolean setFloodlight(Colors.Color color) { if(color == Colors.Color.RED) { this.lampColor = color; setType(ColorLightSensor.TYPE_COLORRED); return true; } else if(color == Colors.Color.BLUE) { this.lampColor = color; setType(ColorLightSensor.TYPE_COLORBLUE); return true; } else if(color == Colors.Color.GREEN) { this.lampColor = color; setType(ColorLightSensor.TYPE_COLORGREEN); return true; } else if(color == Colors.Color.NONE) { this.lampColor = color; setType(ColorLightSensor.TYPE_COLORNONE); return true; } else return false; } // TODO: Since this calibrate code (and other code) is the same for every sensor, perhaps we should consider abstract classes to inherit shared code from /** * call this method when the light sensor is reading the low value - used by readValue **/ public void calibrateLow() { _zero = port.readRawValue(); } /** *call this method when the light sensor is reading the high value - used by readValue */ public void calibrateHigh() { _hundred = port.readRawValue(); } /** * set the normalized value corresponding to readValue() = 0 * @param low the low value */ public void setLow(int low) { _zero = 1023 - low;} /** * set the normalized value corresponding to readValue() = 100; * @param high the high value */ public void setHigh(int high) { _hundred = 1023 - high;} /** * return the normalized value corresponding to readValue() = 0 */ public int getLow() { return 1023 - _zero;} /** * return the normalized value corresponding to readValue() = 100; */ public int getHigh() {return 1023 - _hundred;} }