001/* 002 * Copyright (c) 2019-2021 REV Robotics 003 * 004 * Redistribution and use in source and binary forms, with or without 005 * modification, are permitted provided that the following conditions are met: 006 * 007 * 1. Redistributions of source code must retain the above copyright notice, 008 * this list of conditions and the following disclaimer. 009 * 2. Redistributions in binary form must reproduce the above copyright 010 * notice, this list of conditions and the following disclaimer in the 011 * documentation and/or other materials provided with the distribution. 012 * 3. Neither the name of REV Robotics nor the names of its 013 * contributors may be used to endorse or promote products derived from 014 * this software without specific prior written permission. 015 * 016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 026 * POSSIBILITY OF SUCH DAMAGE. 027 */ 028 029package com.revrobotics; 030 031import edu.wpi.first.hal.SimDevice; 032import edu.wpi.first.hal.SimDouble; 033import edu.wpi.first.wpilibj.DriverStation; 034import edu.wpi.first.wpilibj.I2C; 035import edu.wpi.first.wpilibj.util.Color; 036 037/** REV Robotics Color Sensor V3 */ 038public class ColorSensorV3 { 039 private static final byte kAddress = 0x52; 040 private static final byte kPartID = (byte) 0xC2; 041 042 // This is a transformation matrix given by the chip 043 // manufacturer to transform the raw RGB to CIE XYZ 044 // private static final double Cmatrix[] = { 045 // 0.048112847, 0.289453437, -0.084950826, 046 // 0.030754752, 0.339680186, -0.071569905, 047 // -0.093947499, 0.072838494, 0.34024948 048 // }; 049 050 private I2C m_i2c; 051 private SimDevice m_simDevice; 052 private SimDouble m_simR, m_simG, m_simB, m_simIR, m_simProx; 053 054 public static class RawColor { 055 public RawColor(int r, int g, int b, int _ir) { 056 red = r; 057 green = g; 058 blue = b; 059 ir = _ir; 060 } 061 062 public int red; 063 public int green; 064 public int blue; 065 public int ir; 066 } 067 068 /** 069 * Constructs a ColorSensor. 070 * 071 * @param port The I2C port the color sensor is attached to 072 */ 073 public ColorSensorV3(I2C.Port port) { 074 m_i2c = new I2C(port, kAddress); 075 076 m_simDevice = SimDevice.create("REV Color Sensor V3", port.value, kAddress); 077 if (m_simDevice != null) { 078 m_simR = m_simDevice.createDouble("Red", false, 0.0); 079 m_simG = m_simDevice.createDouble("Green", false, 0.0); 080 m_simB = m_simDevice.createDouble("Blue", false, 0.0); 081 m_simIR = m_simDevice.createDouble("IR", false, 0.0); 082 m_simProx = m_simDevice.createDouble("Proximity", false, 0.0); 083 return; 084 } 085 086 // Only report errors if this is not a simulation 087 if (!checkDeviceID(m_simDevice == null)) { 088 return; 089 } 090 091 initializeDevice(); 092 093 // Clear the reset flag 094 hasReset(); 095 } 096 097 public enum Register { 098 kMainCtrl(0x00), 099 kProximitySensorLED(0x01), 100 kProximitySensorPulses(0x02), 101 kProximitySensorRate(0x03), 102 kLightSensorMeasurementRate(0x04), 103 kLightSensorGain(0x05), 104 kPartID(0x06), 105 kMainStatus(0x07), 106 kProximityData(0x08), 107 kDataInfrared(0x0A), 108 kDataGreen(0x0D), 109 kDataBlue(0x10), 110 kDataRed(0x13); 111 112 public final byte bVal; 113 114 Register(int i) { 115 this.bVal = (byte) i; 116 } 117 } 118 119 public enum MainControl { 120 kRGBMode(0x04), /* If bit is set to 1, color channels are activated */ 121 kLightSensorEnable(0x02), /* Enable light sensor */ 122 kProximitySensorEnable(0x01), /* Proximity sensor active */ 123 OFF(0x00); /* Nothing on */ 124 125 public final byte bVal; 126 127 MainControl(int i) { 128 this.bVal = (byte) i; 129 } 130 } 131 132 public enum GainFactor { 133 kGain1x(0x00), 134 kGain3x(0x01), 135 kGain6x(0x02), 136 kGain9x(0x03), 137 kGain18x(0x04); 138 139 public final byte bVal; 140 141 GainFactor(int i) { 142 this.bVal = (byte) i; 143 } 144 } 145 146 public enum LEDCurrent { 147 kPulse2mA(0x00), 148 kPulse5mA(0x01), 149 kPulse10mA(0x02), 150 kPulse25mA(0x03), 151 kPulse50mA(0x04), 152 kPulse75mA(0x05), 153 kPulse100mA(0x06), /* default value */ 154 kPulse125mA(0x07); 155 156 public final byte bVal; 157 158 LEDCurrent(int i) { 159 this.bVal = (byte) i; 160 } 161 } 162 163 public enum LEDPulseFrequency { 164 kFreq60kHz(0x18), /* default value */ 165 kFreq70kHz(0x40), 166 kFreq80kHz(0x28), 167 kFreq90kHz(0x30), 168 kFreq100kHz(0x38); 169 170 public final byte bVal; 171 172 LEDPulseFrequency(int i) { 173 this.bVal = (byte) i; 174 } 175 } 176 177 public enum ProximitySensorResolution { 178 kProxRes8bit(0x00), 179 kProxRes9bit(0x08), 180 kProxRes10bit(0x10), 181 kProxRes11bit(0x18); 182 183 public final byte bVal; 184 185 ProximitySensorResolution(int i) { 186 this.bVal = (byte) i; 187 } 188 } 189 190 public enum ProximitySensorMeasurementRate { 191 kProxRate6ms(0x01), 192 kProxRate12ms(0x02), 193 kProxRate25ms(0x03), 194 kProxRate50ms(0x04), 195 kProxRate100ms(0x05), /* default value */ 196 kProxRate200ms(0x06), 197 kProxRate400ms(0x07); 198 199 public final byte bVal; 200 201 ProximitySensorMeasurementRate(int i) { 202 this.bVal = (byte) i; 203 } 204 } 205 206 public enum ColorSensorResolution { 207 kColorSensorRes20bit(0x00), 208 kColorSensorRes19bit(0x10), 209 kColorSensorRes18bit(0x20), 210 kColorSensorRes17bit(0x30), 211 kColorSensorRes16bit(0x40), 212 kColorSensorRes13bit(0x50); 213 214 public final byte bVal; 215 216 ColorSensorResolution(int i) { 217 this.bVal = (byte) i; 218 } 219 } 220 221 public enum ColorSensorMeasurementRate { 222 kColorRate25ms(0), 223 kColorRate50ms(1), 224 kColorRate100ms(2), 225 kColorRate200ms(3), 226 kColorRate500ms(4), 227 kColorRate1000ms(5), 228 kColorRate2000ms(7); 229 230 public final byte bVal; 231 232 ColorSensorMeasurementRate(int i) { 233 this.bVal = (byte) i; 234 } 235 }; 236 237 /** 238 * Configure the the IR LED used by the proximity sensor. 239 * 240 * <p>These settings are only needed for advanced users, the defaults will work fine for most 241 * teams. Consult the APDS-9151 for more information on these configuration settings and how they 242 * will affect proximity sensor measurements. 243 * 244 * @param freq The pulse modulation frequency for the proximity sensor LED 245 * @param curr The pulse current for the proximity sensor LED 246 * @param pulses The number of pulses per measurement of the proximity sensor LED (0-255) 247 */ 248 public void configureProximitySensorLED(LEDPulseFrequency freq, LEDCurrent curr, int pulses) { 249 write8(Register.kProximitySensorLED, freq.bVal | curr.bVal); 250 write8(Register.kProximitySensorPulses, (byte) pulses); 251 } 252 253 /** 254 * Configure the proximity sensor. 255 * 256 * <p>These settings are only needed for advanced users, the defaults will work fine for most 257 * teams. Consult the APDS-9151 for more information on these configuration settings and how they 258 * will affect proximity sensor measurements. 259 * 260 * @param res Bit resolution output by the proximity sensor ADC. 261 * @param rate Measurement rate of the proximity sensor 262 */ 263 public void configureProximitySensor( 264 ProximitySensorResolution res, ProximitySensorMeasurementRate rate) { 265 write8(Register.kProximitySensorRate, res.bVal | rate.bVal); 266 } 267 268 /** 269 * Configure the color sensor. 270 * 271 * <p>These settings are only needed for advanced users, the defaults will work fine for most 272 * teams. Consult the APDS-9151 for more information on these configuration settings and how they 273 * will affect color sensor measurements. 274 * 275 * @param res Bit resolution output by the respective light sensor ADCs 276 * @param rate Measurement rate of the light sensor 277 * @param gain Gain factor applied to light sensor (color) outputs 278 */ 279 public void configureColorSensor( 280 ColorSensorResolution res, ColorSensorMeasurementRate rate, GainFactor gain) { 281 write8(Register.kLightSensorMeasurementRate, res.bVal | rate.bVal); 282 write8(Register.kLightSensorGain, gain.bVal); 283 } 284 285 /** 286 * Get the most likely color. Works best when within 2 inches and perpendicular to surface of 287 * interest. 288 * 289 * @return Color enum of the most likely color, including unknown if the minimum threshold is not 290 * met 291 */ 292 public Color getColor() { 293 double r = (double) getRed(); 294 double g = (double) getGreen(); 295 double b = (double) getBlue(); 296 double mag = r + g + b; 297 return new Color(r / mag, g / mag, b / mag); 298 } 299 300 /** 301 * Get the raw proximity value from the sensor ADC (11 bit). This value is largest when an object 302 * is close to the sensor and smallest when far away. 303 * 304 * @return Proximity measurement value, ranging from 0 to 2047 305 */ 306 public int getProximity() { 307 if (m_simDevice != null) { 308 return (int) m_simProx.get(); 309 } 310 return read11BitRegister(Register.kProximityData); 311 } 312 313 /** 314 * Get the raw color values from their respective ADCs (20-bit). 315 * 316 * @return ColorValues struct containing red, green, blue and IR values 317 */ 318 public RawColor getRawColor() { 319 return new RawColor(getRed(), getGreen(), getBlue(), getIR()); 320 } 321 322 /** 323 * Get the raw color value from the red ADC 324 * 325 * @return Red ADC value 326 */ 327 public int getRed() { 328 if (m_simDevice != null) { 329 return (int) m_simR.get(); 330 } 331 return read20BitRegister(Register.kDataRed); 332 } 333 334 /** 335 * Get the raw color value from the green ADC 336 * 337 * @return Green ADC value 338 */ 339 public int getGreen() { 340 if (m_simDevice != null) { 341 return (int) m_simG.get(); 342 } 343 return read20BitRegister(Register.kDataGreen); 344 } 345 346 /** 347 * Get the raw color value from the blue ADC 348 * 349 * @return Blue ADC value 350 */ 351 public int getBlue() { 352 if (m_simDevice != null) { 353 return (int) m_simB.get(); 354 } 355 return read20BitRegister(Register.kDataBlue); 356 } 357 358 /** 359 * Get the raw color value from the IR ADC 360 * 361 * @return IR ADC value 362 */ 363 public int getIR() { 364 if (m_simDevice != null) { 365 return (int) m_simIR.get(); 366 } 367 return read20BitRegister(Register.kDataInfrared); 368 } 369 370 // This is a transformation matrix given by the chip 371 // manufacturer to transform the raw RGB to CIE XYZ 372 private final double Cmatrix[] = { 373 0.048112847, 374 0.289453437, 375 -0.084950826, 376 -0.030754752, 377 0.339680186, 378 -0.071569905, 379 -0.093947499, 380 0.072838494, 381 0.34024948 382 }; 383 384 /** 385 * Get the color converted to CIE XYZ color space using factory calibrated constants. 386 * 387 * <p>https://en.wikipedia.org/wiki/CIE_1931_color_space 388 * 389 * @return CIEColor value from sensor 390 */ 391 public CIEColor getCIEColor() { 392 RawColor raw = getRawColor(); 393 return new CIEColor( 394 Cmatrix[0] * raw.red + Cmatrix[1] * raw.green + Cmatrix[2] * raw.blue, 395 Cmatrix[3] * raw.red + Cmatrix[4] * raw.green + Cmatrix[5] * raw.blue, 396 Cmatrix[6] * raw.red + Cmatrix[7] * raw.green + Cmatrix[8] * raw.blue); 397 } 398 399 /** 400 * Indicates if the device reset. Based on the power on status flag in the status register. Per 401 * the datasheet: 402 * 403 * <p>Part went through a power-up event, either because the part was turned on or because there 404 * was power supply voltage disturbance (default at first register read). 405 * 406 * <p>This flag is self clearing 407 * 408 * @return true if the device was reset 409 */ 410 public boolean hasReset() { 411 if (m_simDevice != null) { 412 return false; 413 } 414 415 byte[] raw = new byte[1]; 416 417 m_i2c.read(Register.kMainStatus.bVal, 1, raw); 418 419 return (raw[0] & 0x20) != 0; 420 } 421 422 /** 423 * Indicates if the device can currently be communicated with. 424 * 425 * @return true if the device is currently connected and responsive 426 */ 427 public boolean isConnected() { 428 if (m_simDevice != null) { 429 return true; 430 } 431 432 return checkDeviceID(false); 433 } 434 435 private boolean checkDeviceID(boolean reportErrors) { 436 byte[] raw = new byte[1]; 437 if (m_i2c.read(Register.kPartID.bVal, 1, raw)) { 438 if (reportErrors) { 439 DriverStation.reportError("Could not find REV color sensor", false); 440 } 441 return false; 442 } 443 444 if (kPartID != raw[0]) { 445 if (reportErrors) { 446 DriverStation.reportError( 447 "Unknown device found with same I2C address as REV color sensor", false); 448 } 449 return false; 450 } 451 452 return true; 453 } 454 455 private void initializeDevice() { 456 write8( 457 Register.kMainCtrl, 458 MainControl.kRGBMode.bVal 459 | MainControl.kLightSensorEnable.bVal 460 | MainControl.kProximitySensorEnable.bVal); 461 462 write8( 463 Register.kProximitySensorRate, 464 ProximitySensorResolution.kProxRes11bit.bVal 465 | ProximitySensorMeasurementRate.kProxRate100ms.bVal); 466 467 write8(Register.kProximitySensorPulses, (byte) 32); 468 } 469 470 private int read11BitRegister(Register reg) { 471 byte[] raw = new byte[2]; 472 473 m_i2c.read(reg.bVal, 2, raw); 474 475 return (((int) raw[0] & 0xFF) | (((int) raw[1] & 0xFF) << 8)) & 0x7FF; 476 } 477 478 private int read20BitRegister(Register reg) { 479 byte[] raw = new byte[3]; 480 481 m_i2c.read(reg.bVal, 3, raw); 482 483 return (((int) raw[0] & 0xFF) | (((int) raw[1] & 0xFF) << 8) | (((int) raw[2] & 0xFF) << 16)) 484 & 0x03FFFF; 485 } 486 487 private void write8(Register reg, int data) { 488 m_i2c.write(reg.bVal, data); 489 } 490}