All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.diozero.devices.TSL2561 Maven / Gradle / Ivy

The newest version!
package com.diozero.devices;

/*
 * #%L
 * Organisation: diozero
 * Project:      diozero - Core
 * Filename:     TSL2561.java
 * 
 * This file is part of the diozero project. More information about this project
 * can be found at https://www.diozero.com/.
 * %%
 * Copyright (C) 2016 - 2024 diozero
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import java.nio.ByteOrder;

import com.diozero.api.I2CConstants;
import com.diozero.api.I2CDevice;
import com.diozero.api.RuntimeIOException;
import com.diozero.util.SleepUtil;

/**
 * Datasheet
 * Pins:
 * 
 * 
 * +-----------------------------+
 * |           TSL2561           |
 * |-----+-----+-----+-----+-----|
 * | INT | SDA | SCL | GND | VCC |
 * +-----+-----+-----+-----+-----+
 * 
*/ @SuppressWarnings("unused") public class TSL2561 implements LuminositySensorInterface { private static final int TSL2561_VISIBLE = 2; // channel 0 - channel 1 private static final int TSL2561_INFRARED = 1; // channel 1 private static final int TSL2561_FULLSPECTRUM = 0; // channel 0 // // Device address for TSL2561 private static final int DEVICE_ADDRESS = 0x39; // Default address (pin left floating) // Lux calculations differ slightly for CS package public static enum TSL2561Package { CHIP_SCALE, T_FN_CL; } private static final int TSL2561_COMMAND_BIT = 0x80; // Must be 1 private static final int TSL2561_CLEAR_BIT = 0x40; // Clears any pending interrupt (write 1 to clear) private static final int TSL2561_WORD_BIT = 0x20; // 1 = read/write word (rather than byte) private static final int TSL2561_BLOCK_BIT = 0x10; // 1 = using block read/write private static final byte TSL2561_CONTROL_POWERON = 0x03; private static final byte TSL2561_CONTROL_POWEROFF = 0x00; private static final int TSL2561_LUX_LUXSCALE = 14; // Scale by 2^14 private static final int TSL2561_LUX_RATIOSCALE = 9; // Scale ratio by 2^9 private static final int TSL2561_LUX_CHSCALE = 10; // Scale channel values by 2^10 private static final int TSL2561_LUX_CHSCALE_TINT0 = 0x7517; // 322/11 * 2^TSL2561_LUX_CHSCALE private static final int TSL2561_LUX_CHSCALE_TINT1 = 0x0FE7; // 322/81 * 2^TSL2561_LUX_CHSCALE // T, FN and CL package values private static final int TSL2561_LUX_K1T = 0x0040; // 0.125 * 2^RATIO_SCALE private static final int TSL2561_LUX_B1T = 0x01f2; // 0.0304 * 2^LUX_SCALE private static final int TSL2561_LUX_M1T = 0x01be; // 0.0272 * 2^LUX_SCALE private static final int TSL2561_LUX_K2T = 0x0080; // 0.250 * 2^RATIO_SCALE private static final int TSL2561_LUX_B2T = 0x0214; // 0.0325 * 2^LUX_SCALE private static final int TSL2561_LUX_M2T = 0x02d1; // 0.0440 * 2^LUX_SCALE private static final int TSL2561_LUX_K3T = 0x00c0; // 0.375 * 2^RATIO_SCALE private static final int TSL2561_LUX_B3T = 0x023f; // 0.0351 * 2^LUX_SCALE private static final int TSL2561_LUX_M3T = 0x037b; // 0.0544 * 2^LUX_SCALE private static final int TSL2561_LUX_K4T = 0x0100; // 0.50 * 2^RATIO_SCALE private static final int TSL2561_LUX_B4T = 0x0270; // 0.0381 * 2^LUX_SCALE private static final int TSL2561_LUX_M4T = 0x03fe; // 0.0624 * 2^LUX_SCALE private static final int TSL2561_LUX_K5T = 0x0138; // 0.61 * 2^RATIO_SCALE private static final int TSL2561_LUX_B5T = 0x016f; // 0.0224 * 2^LUX_SCALE private static final int TSL2561_LUX_M5T = 0x01fc; // 0.0310 * 2^LUX_SCALE private static final int TSL2561_LUX_K6T = 0x019a; // 0.80 * 2^RATIO_SCALE private static final int TSL2561_LUX_B6T = 0x00d2; // 0.0128 * 2^LUX_SCALE private static final int TSL2561_LUX_M6T = 0x00fb; // 0.0153 * 2^LUX_SCALE private static final int TSL2561_LUX_K7T = 0x029a; // 1.3 * 2^RATIO_SCALE private static final int TSL2561_LUX_B7T = 0x0018; // 0.00146 * 2^LUX_SCALE private static final int TSL2561_LUX_M7T = 0x0012; // 0.00112 * 2^LUX_SCALE private static final int TSL2561_LUX_K8T = 0x029a; // 1.3 * 2^RATIO_SCALE private static final int TSL2561_LUX_B8T = 0x0000; // 0.000 * 2^LUX_SCALE private static final int TSL2561_LUX_M8T = 0x0000; // 0.000 * 2^LUX_SCALE // CS package values private static final int TSL2561_LUX_K1C = 0x0043; // 0.130 * 2^RATIO_SCALE private static final int TSL2561_LUX_B1C = 0x0204; // 0.0315 * 2^LUX_SCALE private static final int TSL2561_LUX_M1C = 0x01ad; // 0.0262 * 2^LUX_SCALE private static final int TSL2561_LUX_K2C = 0x0085; // 0.260 * 2^RATIO_SCALE private static final int TSL2561_LUX_B2C = 0x0228; // 0.0337 * 2^LUX_SCALE private static final int TSL2561_LUX_M2C = 0x02c1; // 0.0430 * 2^LUX_SCALE private static final int TSL2561_LUX_K3C = 0x00c8; // 0.390 * 2^RATIO_SCALE private static final int TSL2561_LUX_B3C = 0x0253; // 0.0363 * 2^LUX_SCALE private static final int TSL2561_LUX_M3C = 0x0363; // 0.0529 * 2^LUX_SCALE private static final int TSL2561_LUX_K4C = 0x010a; // 0.520 * 2^RATIO_SCALE private static final int TSL2561_LUX_B4C = 0x0282; // 0.0392 * 2^LUX_SCALE private static final int TSL2561_LUX_M4C = 0x03df; // 0.0605 * 2^LUX_SCALE private static final int TSL2561_LUX_K5C = 0x014d; // 0.65 * 2^RATIO_SCALE private static final int TSL2561_LUX_B5C = 0x0177; // 0.0229 * 2^LUX_SCALE private static final int TSL2561_LUX_M5C = 0x01dd; // 0.0291 * 2^LUX_SCALE private static final int TSL2561_LUX_K6C = 0x019a; // 0.80 * 2^RATIO_SCALE private static final int TSL2561_LUX_B6C = 0x0101; // 0.0157 * 2^LUX_SCALE private static final int TSL2561_LUX_M6C = 0x0127; // 0.0180 * 2^LUX_SCALE private static final int TSL2561_LUX_K7C = 0x029a; // 1.3 * 2^RATIO_SCALE private static final int TSL2561_LUX_B7C = 0x0037; // 0.00338 * 2^LUX_SCALE private static final int TSL2561_LUX_M7C = 0x002b; // 0.00260 * 2^LUX_SCALE private static final int TSL2561_LUX_K8C = 0x029a; // 1.3 * 2^RATIO_SCALE private static final int TSL2561_LUX_B8C = 0x0000; // 0.000 * 2^LUX_SCALE private static final int TSL2561_LUX_M8C = 0x0000; // 0.000 * 2^LUX_SCALE // Auto-gain thresholds private static final int TSL2561_AGC_THI_13MS = 4850; // Max value at Ti 13ms = 5047 private static final int TSL2561_AGC_TLO_13MS = 100; private static final int TSL2561_AGC_THI_101MS = 36000; // Max value at Ti 101ms = 37177 private static final int TSL2561_AGC_TLO_101MS = 200; private static final int TSL2561_AGC_THI_402MS = 63000; // Max value at Ti 402ms = 65535 private static final int TSL2561_AGC_TLO_402MS = 500; // Clipping thresholds private static final int TSL2561_CLIPPING_13MS = 4900; private static final int TSL2561_CLIPPING_101MS = 37000; private static final int TSL2561_CLIPPING_402MS = 65000; private static final int TSL2561_REGISTER_CONTROL = 0x00; private static final int TSL2561_REGISTER_TIMING = 0x01; private static final int TSL2561_REGISTER_THRESHHOLDL_LOW = 0x02; private static final int TSL2561_REGISTER_THRESHHOLDL_HIGH = 0x03; private static final int TSL2561_REGISTER_THRESHHOLDH_LOW = 0x04; private static final int TSL2561_REGISTER_THRESHHOLDH_HIGH = 0x05; private static final int TSL2561_REGISTER_INTERRUPT = 0x06; private static final int TSL2561_REGISTER_CRC = 0x08; private static final int TSL2561_REGISTER_ID = 0x0A; private static final int TSL2561_REGISTER_CHAN0_LOW = 0x0C; private static final int TSL2561_REGISTER_CHAN0_HIGH = 0x0D; private static final int TSL2561_REGISTER_CHAN1_LOW = 0x0E; private static final int TSL2561_REGISTER_CHAN1_HIGH = 0x0F; private static final int TSL2561_INTEGRATIONTIME_13MS = 0x00; // 13.7ms private static final int TSL2561_INTEGRATIONTIME_101MS = 0x01; // 101ms private static final int TSL2561_INTEGRATIONTIME_402MS = 0x02; // 402ms private static final int TSL2561_GAIN_1X = 0x00; // No gain private static final int TSL2561_GAIN_16X = 0x10; // 16x gain private boolean initialised; private boolean autoGain; private int integrationTime; private int gain; private int broadband; private int ir; private TSL2561Package tsl2561Package; private I2CDevice i2cDevice; public TSL2561(TSL2561Package tsl2561Package) throws RuntimeIOException { this(I2CConstants.CONTROLLER_1, tsl2561Package); } public TSL2561(int controller, TSL2561Package tsl2561Package) throws RuntimeIOException { i2cDevice = I2CDevice.builder(DEVICE_ADDRESS).setController(controller).setByteOrder(ByteOrder.LITTLE_ENDIAN) .build(); this.tsl2561Package = tsl2561Package; initialised = false; autoGain = false; integrationTime = TSL2561_INTEGRATIONTIME_13MS; gain = TSL2561_GAIN_1X; broadband = 0; ir = 0; } /** * Enables or disables the auto-gain settings when reading data from the sensor * * @param autoGain enable/disable */ public void setAutoGain(boolean autoGain) { this.autoGain = autoGain; } private boolean begin() throws RuntimeIOException { int x = i2cDevice.readByteData(TSL2561_REGISTER_ID); // if not(x & 0x0A): if ((x & 0x0A) == 0) { return false; } initialised = true; // Set default integration time and gain setIntegrationTime(integrationTime); setGain(gain); // Note by default the device is in power down mode on bootup disable(); return true; } /** * Enables the device */ private void enable() throws RuntimeIOException { i2cDevice.writeByteData(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON); } /** * Disables the device (putting it in lower power sleep mode) */ private void disable() throws RuntimeIOException { i2cDevice.writeByteData(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWEROFF); } /** * Private function to read luminosity on both channels */ private void getData() throws RuntimeIOException { enable(); // Wait x ms for ADC to complete */ if (integrationTime == TSL2561_INTEGRATIONTIME_13MS) { SleepUtil.sleepSeconds(0.014); } else if (integrationTime == TSL2561_INTEGRATIONTIME_101MS) { SleepUtil.sleepSeconds(0.102); } else { SleepUtil.sleepSeconds(0.403); } // Reads a two byte value from channel 0 (visible + infrared) broadband = i2cDevice.readUShort(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW); // Reads a two byte value from channel 1 (infrared) ir = i2cDevice.readUShort(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW); // Turn the device off to save power disable(); } void setIntegrationTime(int time) throws RuntimeIOException { // Enable the device by setting the control bit to 0x03 enable(); // Update the timing register i2cDevice.writeByteData(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, (byte) (time | gain)); integrationTime = time; // Turn the device off to save power disable(); } /** * Adjusts the gain on the TSL2561 (adjusts the sensitivity to light) * * @param gain gain value * @throws RuntimeIOException if an I/O error occurs */ public void setGain(int gain) throws RuntimeIOException { // Enable the device by setting the control bit to 0x03 enable(); // Update the timing register i2cDevice.writeByteData(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, (byte) (integrationTime | gain)); this.gain = gain; // Turn the device off to save power disable(); } private void getRawLuminosity() throws RuntimeIOException { if (!initialised) { begin(); } // If auto gain disabled get a single reading and continue if (!autoGain) { getData(); return; } boolean valid = false; boolean auto_gain_check = false; while (!valid) { int hi; int lo; // Get the hi/low threshold for the current integration time if (integrationTime == TSL2561_INTEGRATIONTIME_13MS) { hi = TSL2561_AGC_THI_13MS; lo = TSL2561_AGC_TLO_13MS; } else if (integrationTime == TSL2561_INTEGRATIONTIME_101MS) { hi = TSL2561_AGC_THI_101MS; lo = TSL2561_AGC_TLO_101MS; } else { hi = TSL2561_AGC_THI_402MS; lo = TSL2561_AGC_TLO_402MS; } getData(); // Run an auto-gain check if we haven't already done so if (!auto_gain_check) { if ((broadband < lo) && (gain == TSL2561_GAIN_1X)) { // Increase the gain and try again setGain(TSL2561_GAIN_16X); // Drop the previous conversion results getData(); // Set a flag to indicate we've adjusted the gain auto_gain_check = true; } else if ((broadband > hi) && (gain == TSL2561_GAIN_16X)) { // Drop gain to 1x and try again setGain(TSL2561_GAIN_1X); // Drop the previous conversion results getData(); // Set a flag to indicate we've adjusted the gain auto_gain_check = true; } else { // Nothing to look at here, keep moving .... // Reading is either valid, or we're already at the chips // limits valid = true; } } else { // If we've already adjusted the gain once, just return the new // results. // This avoids endless loops where a value is at one extreme // pre-gain, // and the the other extreme post-gain valid = true; } } } /** * Converts the raw sensor values to the standard SI lux equivalent. Returns 0 * if the sensor is saturated and the values are unreliable. */ @Override public float getLuminosity() throws RuntimeIOException { getRawLuminosity(); // Make sure the sensor isn't saturated! int clipThreshold; if (integrationTime == TSL2561_INTEGRATIONTIME_13MS) { clipThreshold = TSL2561_CLIPPING_13MS; } else if (integrationTime == TSL2561_INTEGRATIONTIME_101MS) { clipThreshold = TSL2561_CLIPPING_101MS; } else { clipThreshold = TSL2561_CLIPPING_402MS; } // Return 0 lux if the sensor is saturated if ((broadband > clipThreshold) || (ir > clipThreshold)) { return 0; } // Get the correct scale depending on the integration time int chScale; if (integrationTime == TSL2561_INTEGRATIONTIME_13MS) { chScale = TSL2561_LUX_CHSCALE_TINT0; } else if (integrationTime == TSL2561_INTEGRATIONTIME_101MS) { chScale = TSL2561_LUX_CHSCALE_TINT1; } else { chScale = (1 << TSL2561_LUX_CHSCALE); } // Scale for gain (1x or 16x) if (gain == 0) { chScale = chScale << 4; } // Scale the channel values int channel0 = (broadband * chScale) >> TSL2561_LUX_CHSCALE; int channel1 = (ir * chScale) >> TSL2561_LUX_CHSCALE; // Find the ratio of the channel values (Channel1/Channel0) int ratio1 = 0; if (channel0 != 0) { ratio1 = (channel1 << (TSL2561_LUX_RATIOSCALE + 1)) / channel0; } // round the ratio value int ratio = (ratio1 + 1) >> 1; // ratio = (ratio1 + 1) >> 1; int b = 0; int m = 0; switch (tsl2561Package) { case CHIP_SCALE: if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C)) { b = TSL2561_LUX_B1C; m = TSL2561_LUX_M1C; } else if (ratio <= TSL2561_LUX_K2C) { b = TSL2561_LUX_B2C; m = TSL2561_LUX_M2C; } else if (ratio <= TSL2561_LUX_K3C) { b = TSL2561_LUX_B3C; m = TSL2561_LUX_M3C; } else if (ratio <= TSL2561_LUX_K4C) { b = TSL2561_LUX_B4C; m = TSL2561_LUX_M4C; } else if (ratio <= TSL2561_LUX_K5C) { b = TSL2561_LUX_B5C; m = TSL2561_LUX_M5C; } else if (ratio <= TSL2561_LUX_K6C) { b = TSL2561_LUX_B6C; m = TSL2561_LUX_M6C; } else if (ratio <= TSL2561_LUX_K7C) { b = TSL2561_LUX_B7C; m = TSL2561_LUX_M7C; } else if (ratio > TSL2561_LUX_K8C) { b = TSL2561_LUX_B8C; m = TSL2561_LUX_M8C; } break; case T_FN_CL: if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T)) { b = TSL2561_LUX_B1T; m = TSL2561_LUX_M1T; } else if (ratio <= TSL2561_LUX_K2T) { b = TSL2561_LUX_B2T; m = TSL2561_LUX_M2T; } else if (ratio <= TSL2561_LUX_K3T) { b = TSL2561_LUX_B3T; m = TSL2561_LUX_M3T; } else if (ratio <= TSL2561_LUX_K4T) { b = TSL2561_LUX_B4T; m = TSL2561_LUX_M4T; } else if (ratio <= TSL2561_LUX_K5T) { b = TSL2561_LUX_B5T; m = TSL2561_LUX_M5T; } else if (ratio <= TSL2561_LUX_K6T) { b = TSL2561_LUX_B6T; m = TSL2561_LUX_M6T; } else if (ratio <= TSL2561_LUX_K7T) { b = TSL2561_LUX_B7T; m = TSL2561_LUX_M7T; } else if (ratio > TSL2561_LUX_K8T) { b = TSL2561_LUX_B8T; m = TSL2561_LUX_M8T; } break; } // endif int temp = ((channel0 * b) - (channel1 * m)); // Do not allow negative lux value if (temp < 0) { temp = 0; } // Round lsb (2^(LUX_SCALE-1)) temp += (1 << (TSL2561_LUX_LUXSCALE - 1)); // Strip off fractional portion int lux = temp >> TSL2561_LUX_LUXSCALE; // FIXME Work with floating point numbers rather than integers! // Signal I2C had no errors return lux; } @Override public void close() { i2cDevice.close(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy