Hardware components | ||||||
| × | 3 | ||||
| × | 3 | ||||
| × | 3 | ||||
| × | 3 | ||||
| × | 3 | ||||
| × | 6 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
|
credit to James Webb space telescope for cover image
In a world where the downtown nightlife and local racetrack don't care about light pollution, one brave group of students must rise up against the corporate agenda and engineer a solution to help amateur astrophotographers get the the cool footage they seek and raise awareness about our ever fading grasp over the wonders of the night sky.
So how did we plan to accomplish this? We set out to track the particulate and radiation emissions around a metropolitan area to create a large virtual light pollution "observatory". Inspired by general IOT clusters that use many devices for one purpose but also professional astronomy tools whose observations are sometimes combined to create an artificial earth sized observatory. We went for a similar concept but at a much smaller scale.
The sensors we chose for this project include a tilt-ball sensor for orientation of the device as well as the two main sensors: the TSL2591 and the AGS02MA both from Adafruit. the TSL2591 will enable us to take long-range readings of the sky's brightness at multiple exposure levels at tunable sensitivity. The AGS02MA is a TVOC or volatile gas sensor which will allow us to refine our initial light measurements, by understanding the compositional changes in the localized atmosphere, we can predict how certain light levels may affect our view. Certain gasses diffract in certain ways, less noisy light + less distorting air = higher overall viewing quality. Of course this interpretation is up to the user, our raw data is output to an integration directly from photon's cloud dashboard which means that further processing is up to you! In the future we envision machine learning could assist in such a matter as well.
The most important metric our device records is the intensity of light it receives. All three devices publish their own light data and subscribe to one another's so that each device has access to all the data. Each device's light value is graphed using Thingspeak. Click here to access our public channel which updates in real-time whenever the devices are connected. You'll see that in addition to light measurements from each device, there is also another graph for luxavg, the average of the lux values obtained by each device. Any or all devices are capable of averaging the data from all three and publishing it to this field to obtain a more uniform metric over a distributed area, provided the devices are all in a similar environment without much artificial lighting. If all three devices are not operating at once, the average will not be published. In the public channels the average is currently empty, but we've verified it works when all three sensors are obtaining values.
Obviously, depending on time of day, the lux values given will differ drastically. In the test shot below you can see the data in around the area of 300-400 lux in a brightly lit room. On a clear night with no artificial lighting, typical lux values range from .0001 to about .2 or .3. In a natural setting, the primary factors in this are the cycle of the moon and the weather. On a clear night with good starlight, one can expect a lux value on the order of .001. Lower than that can indicate that it's overcast or that there may be smog, etc., and it's likely not a good night for viewing. On a clear night with a nice nightglow, you can expect values on the order of about .05 lux. These sensors were all tested independently to see if they give good nighttime readings and, true to their specs, they're fairly accurate. Anthony tested the readings and found about .0002 to .0003 to be the minimum value it would put out on a cloudy night away from buildings, and in November under an almost full waxing gibbous, the sensor was tested to put out values of around .15. We would suggest playing with the integration time and gain settings for best results as, effective as they were for us, these are affordable sensors. They don't even approach the accuracy of something like a photon detector, but they certainly function well enough with a little work.
Unfortunately our shared Particle hit its data limit in testing, so we couldn't show the averaging function, but it's a very simple implementation that does just what it says on the tin.
In the future we hope to implement an enclosure and battery to make these the truly portable and outdoors-capable devices they need to be, as well as to implement a system to take into account the current moon cycle, smog reports, gas readings, and perhaps some other information to tell the user directly how good of a night for stargazing it is, rather than letting them infer from the data. Stay tuned for more! For now, though, it's certainly a functional and neat little implementation of an affordable light sensor and gas sensor. We hope you like it.
SkyWatcher Breadboard Diagram
SkyWatcher
C/C++// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_I2CDevice.h"
#include "Adafruit_TSL2591.h"
#include "Adafruit_Sensor.h"
#include "Wire.h"
// I2C address of the TVOC sensor
#define TVOC_SENSOR_ADDR 0x1A
// Register addresses for gas resistance and ppb data
#define GAS_RESISTANCE_REGISTER 0x20
#define PPB_DATA_REGISTER 0x00
// Variables to store sensor data plus other variable initialization
uint16_t gasResistance = 0;
uint16_t ppbData = 0;
int digitalVal = 0;
uint16_t x = 0;
//LED pin variable for visually indicating tilt
const int ledPin = D7;
// Time interval between readings (in milliseconds) and time interval between publishing webhooks
const unsigned long readingInterval = 1500;
const unsigned long webhookInterval = 45000;
//Device and program setup
Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591); // pass in a number for the sensor identifier (for your use later)
// Full options for gain and integration time, selectable by uncommenting
void configureTSL() {
//tsl.setGain(TSL2591_GAIN_LOW); // 1x gain (bright light)
tsl.setGain(TSL2591_GAIN_MED); // 25x gain
//tsl.setGain(TSL2591_GAIN_HIGH); // 428x gain
// Changing the integration time gives you a longer time over which to sense light
// longer timelines are slower, but are good in very low light situtations!
//tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS); // shortest integration time (bright light)
// tsl.setTiming(TSL2591_INTEGRATIONTIME_200MS);
// tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS);
// tsl.setTiming(TSL2591_INTEGRATIONTIME_400MS);
tsl.setTiming(TSL2591_INTEGRATIONTIME_500MS);
// tsl.setTiming(TSL2591_INTEGRATIONTIME_600MS); // longest integration time (dim light)
}
// Variable to store the last time a reading or webhook was done
unsigned long lastReadingTime = 0;
unsigned long lastWebhookTime = 0;
// Initialize subscribe values (part of inter-device communication)
float lux2 = 0;
float lux3 = 0;
float luxavg = 0;
// Defining subscription handler function to compare the event name being subscribed to with the two possible events (in this case all but device 1, because we're using device 1) so that it can update the correct variable.
void myHandler(const char *event, const char *data) {
if (strcmp(event, "Luminosity (device 2), lux") == 0)
{
lux2 == atof(data);
}
else if (strcmp(event, "Luminosity (device 3), lux") == 0)
{
lux3 == atof(data);
}
}
void setup() {
// Set up led indicator
pinMode(ledPin, OUTPUT);
// Set up tilt sensor pin with internal pull-up resistor
pinMode(D2, INPUT_PULLUP);
tsl.begin();
configureTSL();
Wire.begin();
//A pair of subscribe functions which gather data from each of the two other devices (incoming communication of most recent lux measurement)
Particle.subscribe("Luminosity (device 2), lux", myHandler);
Particle.subscribe("Luminosity (device 3), lux", myHandler);
}
void loop() {
// Get the current time
unsigned long currentTime = millis();
// Check if the specified interval has passed since the last reading
if (currentTime - lastReadingTime >= readingInterval) {
// Update the last reading time
lastReadingTime = currentTime;
// Set tilt sensor variable according to current reading
digitalVal = digitalRead(D2);
// Publish value to Particle cloud
Particle.publish("TiltVal", String(digitalVal), PRIVATE);
// Conditional to only run data acquisition when tilt sensor is not tilted
if (digitalVal == 0) {
// Turn tilt indicator LED off if it's on
digitalWrite(ledPin, LOW);
// Read raw light sensor data
uint32_t lum = tsl.getFullLuminosity();
uint16_t ch1, ch0;
ch1 = lum >> 16;
ch0 = lum & 0xFFFF;
// Calculate lux
float lux1 = tsl.calculateLux(ch0, ch1);
// Publish luminosity data to Particle cloud for monitoring and for sharing with other devices.
Particle.publish("Luminosity (device 1), lux", String(lux1), PRIVATE);
// Conditional for whether or not it's been long enough for another webhook
if (currentTime - lastWebhookTime >= webhookInterval) {
// update last webhook time
lastWebhookTime = currentTime;
String data = String(lux1);
// Publish luminosity data to webhook for visualization
Particle.publish("lux1", data, PRIVATE);
// Check that each individual lux value is nonzero for this cycle and, if so, average them, using a webhook to visualize this along all the individual values
if (lux1 != 0 && lux2 != 0 && lux3 != 0) {
luxavg = (lux1 + lux2 + lux3)/3;
Particle.publish ("lux(average)", String(luxavg), PRIVATE);
}
}
// Request gas resistance data
Wire.beginTransmission(TVOC_SENSOR_ADDR);
Wire.write(GAS_RESISTANCE_REGISTER);
Wire.endTransmission();
// Request 2 bytes of gas resistance data
Wire.requestFrom(TVOC_SENSOR_ADDR, 2);
if (Wire.available() >= 2) {
gasResistance = Wire.read() << 8 | Wire.read();
// Gas resistance conversion to kilo-ohms (stated unit measured)
float gasResistanceKiloOhms = gasResistance * 0.1;
// Publish gas resistance data to Particle Cloud
Particle.publish("Gas Resistance, KiloOhms", String(gasResistanceKiloOhms), PRIVATE);
}
// Request TVOC ppb data
Wire.beginTransmission(TVOC_SENSOR_ADDR);
Wire.write(PPB_DATA_REGISTER);
Wire.endTransmission();
// Request 2 bytes of ppb data
Wire.requestFrom(TVOC_SENSOR_ADDR, 2);
if (Wire.available() >= 2) {
ppbData = Wire.read() << 8 | Wire.read();
// Publish ppb data to Particle Cloud
Particle.publish("TVOC ppb", String(ppbData), PRIVATE);
}
} else {
digitalWrite(ledPin, HIGH); //Do nothing except turn on the LED for this cycle when the tilt switch is on
}
}
}
#include "Adafruit_I2CDevice.h"
//#define DEBUG_SERIAL Serial
/*!
* @brief Create an I2C device at a given address
* @param addr The 7-bit I2C address for the device
* @param theWire The I2C bus to use, defaults to &Wire
*/
Adafruit_I2CDevice::Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire) {
_addr = addr;
_wire = theWire;
_begun = false;
#ifdef ARDUINO_ARCH_SAMD
_maxBufferSize = 250; // as defined in Wire.h's RingBuffer
#elif defined(ESP32)
_maxBufferSize = I2C_BUFFER_LENGTH;
#else
_maxBufferSize = 32;
#endif
}
/*!
* @brief Initializes and does basic address detection
* @param addr_detect Whether we should attempt to detect the I2C address
* with a scan. 99% of sensors/devices don't mind, but once in a while they
* don't respond well to a scan!
* @return True if I2C initialized and a device with the addr found
*/
bool Adafruit_I2CDevice::begin(bool addr_detect) {
_wire->begin();
_begun = true;
if (addr_detect) {
return detected();
}
return true;
}
/*!
* @brief De-initialize device, turn off the Wire interface
*/
void Adafruit_I2CDevice::end(void) {
// Not all port implement Wire::end(), such as
// - ESP8266
// - AVR core without WIRE_HAS_END
// - ESP32: end() is implemented since 2.0.1 which is latest at the moment.
// Temporarily disable for now to give time for user to update.
#if !(defined(ESP8266) || \
(defined(ARDUINO_ARCH_AVR) && !defined(WIRE_HAS_END)) || \
defined(ARDUINO_ARCH_ESP32))
_wire->end();
_begun = false;
#endif
}
/*!
* @brief Scans I2C for the address - note will give a false-positive
* if there's no pullups on I2C
* @return True if I2C initialized and a device with the addr found
*/
bool Adafruit_I2CDevice::detected(void) {
// Init I2C if not done yet
if (!_begun && !begin()) {
return false;
}
// A basic scanner, see if it ACK's
_wire->beginTransmission(_addr);
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("Address 0x"));
DEBUG_SERIAL.print(_addr);
#endif
if (_wire->endTransmission() == 0) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F(" Detected"));
#endif
return true;
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F(" Not detected"));
#endif
return false;
}
/*!
* @brief Write a buffer or two to the I2C device. Cannot be more than
* maxBufferSize() bytes.
* @param buffer Pointer to buffer of data to write. This is const to
* ensure the content of this buffer doesn't change.
* @param len Number of bytes from buffer to write
* @param prefix_buffer Pointer to optional array of data to write before
* buffer. Cannot be more than maxBufferSize() bytes. This is const to
* ensure the content of this buffer doesn't change.
* @param prefix_len Number of bytes from prefix buffer to write
* @param stop Whether to send an I2C STOP signal on write
* @return True if write was successful, otherwise false.
*/
bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop,
const uint8_t *prefix_buffer,
size_t prefix_len) {
if ((len + prefix_len) > maxBufferSize()) {
// currently not guaranteed to work if more than 32 bytes!
// we will need to find out if some platforms have larger
// I2C buffer sizes :/
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice could not write such a large buffer"));
#endif
return false;
}
_wire->beginTransmission(_addr);
// Write the prefix data (usually an address)
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
if (_wire->write(prefix_buffer, prefix_len) != prefix_len) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice failed to write"));
#endif
return false;
}
}
// Write the data itself
if (_wire->write(buffer, len) != len) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice failed to write"));
#endif
return false;
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CWRITE @ 0x"));
DEBUG_SERIAL.print(_addr, HEX);
DEBUG_SERIAL.print(F(" :: "));
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
for (uint16_t i = 0; i < prefix_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
}
}
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (i % 32 == 31) {
DEBUG_SERIAL.println();
}
}
if (stop) {
DEBUG_SERIAL.print("\tSTOP");
}
#endif
if (_wire->endTransmission(stop) == 0) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println();
// DEBUG_SERIAL.println("Sent!");
#endif
return true;
} else {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("\tFailed to send!");
#endif
return false;
}
}
/*!
* @brief Read from I2C into a buffer from the I2C device.
* Cannot be more than maxBufferSize() bytes.
* @param buffer Pointer to buffer of data to read into
* @param len Number of bytes from buffer to read.
* @param stop Whether to send an I2C STOP signal on read
* @return True if read was successful, otherwise false.
*/
bool Adafruit_I2CDevice::read(uint8_t *buffer, size_t len, bool stop) {
size_t pos = 0;
while (pos < len) {
size_t read_len =
((len - pos) > maxBufferSize()) ? maxBufferSize() : (len - pos);
bool read_stop = (pos < (len - read_len)) ? false : stop;
if (!_read(buffer + pos, read_len, read_stop))
return false;
pos += read_len;
}
return true;
}
bool Adafruit_I2CDevice::_read(uint8_t *buffer, size_t len, bool stop) {
#if defined(TinyWireM_h)
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len);
#elif defined(ARDUINO_ARCH_MEGAAVR)
size_t recv = _wire->requestFrom(_addr, len, stop);
#else
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len, (uint8_t)stop);
#endif
if (recv != len) {
// Not enough data available to fulfill our obligation!
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CDevice did not receive enough data: "));
DEBUG_SERIAL.println(recv);
#endif
return false;
}
for (uint16_t i = 0; i < len; i++) {
buffer[i] = _wire->read();
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CREAD @ 0x"));
DEBUG_SERIAL.print(_addr, HEX);
DEBUG_SERIAL.print(F(" :: "));
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
return true;
}
/*!
* @brief Write some data, then read some data from I2C into another buffer.
* Cannot be more than maxBufferSize() bytes. The buffers can point to
* same/overlapping locations.
* @param write_buffer Pointer to buffer of data to write from
* @param write_len Number of bytes from buffer to write.
* @param read_buffer Pointer to buffer of data to read into.
* @param read_len Number of bytes from buffer to read.
* @param stop Whether to send an I2C STOP signal between the write and read
* @return True if write & read was successful, otherwise false.
*/
bool Adafruit_I2CDevice::write_then_read(const uint8_t *write_buffer,
size_t write_len, uint8_t *read_buffer,
size_t read_len, bool stop) {
if (!write(write_buffer, write_len, stop)) {
return false;
}
return read(read_buffer, read_len);
}
/*!
* @brief Returns the 7-bit address of this device
* @return The 7-bit address of this device
*/
uint8_t Adafruit_I2CDevice::address(void) { return _addr; }
/*!
* @brief Change the I2C clock speed to desired (relies on
* underlying Wire support!
* @param desiredclk The desired I2C SCL frequency
* @return True if this platform supports changing I2C speed.
* Not necessarily that the speed was achieved!
*/
bool Adafruit_I2CDevice::setSpeed(uint32_t desiredclk) {
#if defined(__AVR_ATmega328__) || \
defined(__AVR_ATmega328P__) // fix arduino core set clock
// calculate TWBR correctly
if ((F_CPU / 18) < desiredclk) {
#ifdef DEBUG_SERIAL
Serial.println(F("I2C.setSpeed too high."));
#endif
return false;
}
uint32_t atwbr = ((F_CPU / desiredclk) - 16) / 2;
if (atwbr > 16320) {
#ifdef DEBUG_SERIAL
Serial.println(F("I2C.setSpeed too low."));
#endif
return false;
}
if (atwbr <= 255) {
atwbr /= 1;
TWSR = 0x0;
} else if (atwbr <= 1020) {
atwbr /= 4;
TWSR = 0x1;
} else if (atwbr <= 4080) {
atwbr /= 16;
TWSR = 0x2;
} else { // if (atwbr <= 16320)
atwbr /= 64;
TWSR = 0x3;
}
TWBR = atwbr;
#ifdef DEBUG_SERIAL
Serial.print(F("TWSR prescaler = "));
Serial.println(pow(4, TWSR));
Serial.print(F("TWBR = "));
Serial.println(atwbr);
#endif
return true;
#elif (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER) && \
!defined(TinyWireM_h)
_wire->setClock(desiredclk);
return true;
#else
(void)desiredclk;
return false;
#endif
}
Adafruit_I2CDevice.h
C Header File#ifndef Adafruit_I2CDevice_h
#define Adafruit_I2CDevice_h
#include <Arduino.h>
#include <Wire.h>
///< The class which defines how we will talk to this device over I2C
class Adafruit_I2CDevice {
public:
Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire = &Wire);
uint8_t address(void);
bool begin(bool addr_detect = true);
void end(void);
bool detected(void);
bool read(uint8_t *buffer, size_t len, bool stop = true);
bool write(const uint8_t *buffer, size_t len, bool stop = true,
const uint8_t *prefix_buffer = nullptr, size_t prefix_len = 0);
bool write_then_read(const uint8_t *write_buffer, size_t write_len,
uint8_t *read_buffer, size_t read_len,
bool stop = false);
bool setSpeed(uint32_t desiredclk);
/*! @brief How many bytes we can read in a transaction
* @return The size of the Wire receive/transmit buffer */
size_t maxBufferSize() { return _maxBufferSize; }
private:
uint8_t _addr;
TwoWire *_wire;
bool _begun;
size_t _maxBufferSize;
bool _read(uint8_t *buffer, size_t len, bool stop);
};
#endif // Adafruit_I2CDevice_h
Adafruit_Sensor.h
C Header File/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software< /span>
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and
* extended sensor support to include color, voltage and current */
#ifndef _ADAFRUIT_SENSOR_H
#define _ADAFRUIT_SENSOR_H
#ifndef ARDUINO
#include <stdint.h>
#elif ARDUINO >= 100
#include "Arduino.h"
#include "Print.h"
#else
#include "WProgram.h"
#endif
/* Constants */
#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */
#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */
#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */
#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH)
#define SENSORS_MAGFIELD_EARTH_MAX \
(60.0F) /**< Maximum magnetic field on Earth's surface */
#define SENSORS_MAGFIELD_EARTH_MIN \
(30.0F) /**< Minimum magnetic field on Earth's surface */
#define SENSORS_PRESSURE_SEALEVELHPA \
(1013.25F) /**< Average sea level pressure is 1013.25 hPa */
#define SENSORS_DPS_TO_RADS \
(0.017453293F) /**< Degrees/s to rad/s multiplier \
*/
#define SENSORS_RADS_TO_DPS \
(57.29577793F) /**< Rad/s to degrees/s multiplier */
#define SENSORS_GAUSS_TO_MICROTESLA \
(100) /**< Gauss to micro-Tesla multiplier */
/** Sensor types */
typedef enum {
SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */
SENSOR_TYPE_MAGNETIC_FIELD = (2),
SENSOR_TYPE_ORIENTATION = (3),
SENSOR_TYPE_GYROSCOPE = (4),
SENSOR_TYPE_LIGHT = (5),
SENSOR_TYPE_PRESSURE = (6),
SENSOR_TYPE_PROXIMITY = (8),
SENSOR_TYPE_GRAVITY = (9),
SENSOR_TYPE_LINEAR_ACCELERATION =
(10), /**< Acceleration not including gravity */
SENSOR_TYPE_ROTATION_VECTOR = (11),
SENSOR_TYPE_RELATIVE_HUMIDITY = (12),
SENSOR_TYPE_AMBIENT_TEMPERATURE = (13),
SENSOR_TYPE_OBJECT_TEMPERATURE = (14),
SENSOR_TYPE_VOLTAGE = (15),
SENSOR_TYPE_CURRENT = (16),
SENSOR_TYPE_COLOR = (17),
SENSOR_TYPE_TVOC = (18),
SENSOR_TYPE_VOC_INDEX = (19),
SENSOR_TYPE_NOX_INDEX = (20),
SENSOR_TYPE_CO2 = (21),
SENSOR_TYPE_ECO2 = (22),
SENSOR_TYPE_PM10_STD = (23),
SENSOR_TYPE_PM25_STD = (24),
SENSOR_TYPE_PM100_STD = (25),
SENSOR_TYPE_PM10_ENV = (26),
SENSOR_TYPE_PM25_ENV = (27),
SENSOR_TYPE_PM100_ENV = (28),
SENSOR_TYPE_GAS_RESISTANCE = (29),
SENSOR_TYPE_UNITLESS_PERCENT = (30),
SENSOR_TYPE_ALTITUDE = (31)
} sensors_type_t;
/** struct sensors_vec_s is used to return a vector in a common format. */
typedef struct {
union {
float v[3]; ///< 3D vector elements
struct {
float x; ///< X component of vector
float y; ///< Y component of vector
float z; ///< Z component of vector
}; ///< Struct for holding XYZ component
/* Orientation sensors */
struct {
float roll; /**< Rotation around the longitudinal axis (the plane body, 'X
axis'). Roll is positive and increasing when moving
downward. -90 degrees <= roll <= 90 degrees */
float pitch; /**< Rotation around the lateral axis (the wing span, 'Y
axis'). Pitch is positive and increasing when moving
upwards. -180 degrees <= pitch <= 180 degrees) */
float heading; /**< Angle between the longitudinal axis (the plane body)
and magnetic north, measured clockwise when viewing from
the top of the device. 0-359 degrees */
}; ///< Struct for holding roll/pitch/heading
}; ///< Union that can hold 3D vector array, XYZ components or
///< roll/pitch/heading
int8_t status; ///< Status byte
uint8_t reserved[3]; ///< Reserved
} sensors_vec_t;
/** struct sensors_color_s is used to return color data in a common format. */
typedef struct {
union {
float c[3]; ///< Raw 3-element data
/* RGB color space */
struct {
float r; /**< Red component */
float g; /**< Green component */
float b; /**< Blue component */
}; ///< RGB data in floating point notation
}; ///< Union of various ways to describe RGB colorspace
uint32_t rgba; /**< 24-bit RGBA value */
} sensors_color_t;
/* Sensor event (36 bytes) */
/** struct sensor_event_s is used to provide a single sensor event in a common
* format. */
typedef struct {
int32_t version; /**< must be sizeof(struct sensors_event_t) */
int32_t sensor_id; /**< unique sensor identifier */
int32_t type; /**< sensor type */
int32_t reserved0; /**< reserved */
int32_t timestamp; /**< time is in milliseconds */
union {
float data[4]; ///< Raw data */
sensors_vec_t acceleration; /**< acceleration values are in meter per second
per second (m/s^2) */
sensors_vec_t
magnetic; /**< magnetic vector values are in micro-Tesla (uT) */
sensors_vec_t orientation; /**< orientation values are in degrees */
sensors_vec_t gyro; /**< gyroscope values are in rad/s */
float temperature; /**< temperature is in degrees centigrade (Celsius) */
float distance; /**< distance in centimeters */
float light; /**< light in SI lux units */
float pressure; /**< pressure in hectopascal (hPa) */
float relative_humidity; /**< relative humidity in percent */
float current; /**< current in milliamps (mA) */
float voltage; /**< voltage in volts (V) */
float tvoc; /**< Total Volatile Organic Compounds, in ppb */
float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is
normal (unitless) */
float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal
(unitless) */
float CO2; /**< Measured CO2 in parts per million (ppm) */
float eCO2; /**< equivalent/estimated CO2 in parts per million (ppm
estimated from some other measurement) */
float pm10_std; /**< Standard Particulate Matter <=1.0 in parts per million
(ppm) */
float pm25_std; /**< Standard Particulate Matter <=2.5 in parts per million
(ppm) */
float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per
million (ppm) */
float pm10_env; /**< Environmental Particulate Matter <=1.0 in parts per
million (ppm) */
float pm25_env; /**< Environmental Particulate Matter <=2.5 in parts per
million (ppm) */
float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per
million (ppm) */
float gas_resistance; /**< Proportional to the amount of VOC particles in
the air (Ohms) */
float unitless_percent; /**<Percentage, unit-less (%) */
sensors_color_t color; /**< color in RGB component values */
float altitude; /**< Distance between a reference datum and a point or
object, in meters. */
}; ///< Union for the wide ranges of data we can carry
} sensors_event_t;
/* Sensor details (40 bytes) */
/** struct sensor_s is used to describe basic information about a specific
* sensor. */
typedef struct {
char name[12]; /**< sensor name */
int32_t version; /**< version of the hardware + driver */
int32_t sensor_id; /**< unique sensor identifier */
int32_t type; /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
float max_value; /**< maximum value of this sensor's value in SI units */
float min_value; /**< minimum value of this sensor's value in SI units */
float resolution; /**< smallest difference between two values reported by this
sensor */
int32_t min_delay; /**< min delay in microseconds between events. zero = not a
constant rate */
} sensor_t;
/** @brief Common sensor interface to unify various sensors.
* Intentionally modeled after sensors.h in the Android API:
* https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h
*/
class Adafruit_Sensor {
public:
// Constructor(s)
Adafruit_Sensor() {}
virtual ~Adafruit_Sensor() {}
// These must be defined by the subclass
/*! @brief Whether we should automatically change the range (if possible) for
higher precision
@param enabled True if we will try to autorange */
virtual void enableAutoRange(bool enabled) {
(void)enabled; /* suppress unused warning */
};
/*! @brief Get the latest sensor event
@returns True if able to fetch an event */
virtual bool getEvent(sensors_event_t *) = 0;
/*! @brief Get info about the sensor itself */
virtual void getSensor(sensor_t *) = 0;
void printSensorDetails(void);
};
#endif
Adafruit_TSL2591.cpp
C/C++/**************************************************************************/
/*!
@file Adafruit_TSL2591.cpp
@author KT0WN (adafruit.com)
This is a library for the Adafruit TSL2591 breakout board
This library works with the Adafruit TSL2591 breakout
----> https://www.adafruit.com/products/1980
Check out the links above for our tutorials and wiring diagrams
These chips use I2C to communicate
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2014 Adafruit Industries
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
#include "Adafruit_TSL2591.h"
#include <stdlib.h>
/**************************************************************************/
/*!
@brief Instantiates a new Adafruit TSL2591 class
@param sensorID An optional ID # so you can track this sensor, it will tag
sensorEvents you create.
*/
/**************************************************************************/
Adafruit_TSL2591::Adafruit_TSL2591(int32_t sensorID) {
_initialized = false;
_integration = TSL2591_INTEGRATIONTIME_100MS;
_gain = TSL2591_GAIN_MED;
_sensorID = sensorID;
// we cant do wire initialization till later, because we havent loaded Wire
// yet
}
Adafruit_TSL2591::~Adafruit_TSL2591() {
if (i2c_dev)
delete i2c_dev;
}
/**************************************************************************/
/*!
@brief Setups the I2C interface and hardware, identifies if chip is found
@param theWire a reference to TwoWire instance
@param addr The I2C adress of the sensor (Default 0x29)
@returns True if a TSL2591 is found, false on any failure
*/
/**************************************************************************/
boolean Adafruit_TSL2591::begin(TwoWire *theWire, uint8_t addr) {
if (i2c_dev)
delete i2c_dev;
i2c_dev = new Adafruit_I2CDevice(addr, theWire);
if (!i2c_dev->begin())
return false;
/*
for (uint8_t i=0; i<0x20; i++)
{
uint8_t id = read8(0x12);
Serial.print("$"); Serial.print(i, HEX);
Serial.print(" = 0x"); Serial.println(read8(i), HEX);
}
*/
uint8_t id = read8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_DEVICE_ID);
if (id != 0x50) {
return false;
}
// Serial.println("Found Adafruit_TSL2591");
_initialized = true;
// Set default integration time and gain
setTiming(_integration);
setGain(_gain);
// Note: by default, the device is in power down mode on bootup
disable();
return true;
}
/**************************************************************************/
/*!
@brief Setups the I2C interface and hardware, identifies if chip is found
@param addr The I2C adress of the sensor (Default 0x29)
@returns True if a TSL2591 is found, false on any failure
*/
/**************************************************************************/
boolean Adafruit_TSL2591::begin(uint8_t addr) { return begin(&Wire, addr); }
/**************************************************************************/
/*!
@brief Enables the chip, so it's ready to take readings
*/
/**************************************************************************/
void Adafruit_TSL2591::enable(void) {
if (!_initialized) {
if (!begin()) {
return;
}
}
// Enable the device by setting the control bit to 0x01
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE,
TSL2591_ENABLE_POWERON | TSL2591_ENABLE_AEN | TSL2591_ENABLE_AIEN |
TSL2591_ENABLE_NPIEN);
}
/**************************************************************************/
/*!
@brief Disables the chip, so it's in power down mode
*/
/**************************************************************************/
void Adafruit_TSL2591::disable(void) {
if (!_initialized) {
if (!begin()) {
return;
}
}
// Disable the device by setting the control bit to 0x00
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE,
TSL2591_ENABLE_POWEROFF);
}
/************************************************************************/
/*!
@brief Setter for sensor light gain
@param gain {@link tsl2591Gain_t} gain value
*/
/**************************************************************************/
void Adafruit_TSL2591::setGain(tsl2591Gain_t gain) {
if (!_initialized) {
if (!begin()) {
return;
}
}
enable();
_gain = gain;
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL, _integration | _gain);
disable();
}
/************************************************************************/
/*!
@brief Getter for sensor light gain
@returns {@link tsl2591Gain_t} gain value
*/
/**************************************************************************/
tsl2591Gain_t Adafruit_TSL2591::getGain() { return _gain; }
/************************************************************************/
/*!
@brief Setter for sensor integration time setting
@param integration {@link tsl2591IntegrationTime_t} integration time setting
*/
/**************************************************************************/
void Adafruit_TSL2591::setTiming(tsl2591IntegrationTime_t integration) {
if (!_initialized) {
if (!begin()) {
return;
}
}
enable();
_integration = integration;
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL, _integration | _gain);
disable();
}
/************************************************************************/
/*!
@brief Getter for sensor integration time setting
@returns {@link tsl2591IntegrationTime_t} integration time
*/
/**************************************************************************/
tsl2591IntegrationTime_t Adafruit_TSL2591::getTiming() { return _integration; }
/************************************************************************/
/*!
@brief Calculates the visible Lux based on the two light sensors
@param ch0 Data from channel 0 (IR+Visible)
@param ch1 Data from channel 1 (IR)
@returns Lux, based on AMS coefficients (or < 0 if overflow)
*/
/**************************************************************************/
float Adafruit_TSL2591::calculateLux(uint16_t ch0, uint16_t ch1) {
float atime, again;
float cpl, lux;
// Check for overflow conditions first
if ((ch0 == 0xFFFF) | (ch1 == 0xFFFF)) {
// Signal an overflow
return -1;
}
// Note: This algorithm is based on preliminary coefficients
// provided by AMS and may need to be updated in the future
switch (_integration) {
case TSL2591_INTEGRATIONTIME_100MS:
atime = 100.0F;
break;
case TSL2591_INTEGRATIONTIME_200MS:
atime = 200.0F;
break;
case TSL2591_INTEGRATIONTIME_300MS:
atime = 300.0F;
break;
case TSL2591_INTEGRATIONTIME_400MS:
atime = 400.0F;
break;
case TSL2591_INTEGRATIONTIME_500MS:
atime = 500.0F;
break;
case TSL2591_INTEGRATIONTIME_600MS:
atime = 600.0F;
break;
default: // 100ms
atime = 100.0F;
break;
}
switch (_gain) {
case TSL2591_GAIN_LOW:
again = 1.0F;
break;
case TSL2591_GAIN_MED:
again = 25.0F;
break;
case TSL2591_GAIN_HIGH:
again = 428.0F;
break;
case TSL2591_GAIN_MAX:
again = 9876.0F;
break;
default:
again = 1.0F;
break;
}
// cpl = (ATIME * AGAIN) / DF
cpl = (atime * again) / TSL2591_LUX_DF;
// Original lux calculation (for reference sake)
// float lux1 = ( (float)ch0 - (TSL2591_LUX_COEFB * (float)ch1) ) / cpl;
// float lux2 = ( ( TSL2591_LUX_COEFC * (float)ch0 ) - ( TSL2591_LUX_COEFD *
// (float)ch1 ) ) / cpl; lux = lux1 > lux2 ? lux1 : lux2;
// Alternate lux calculation 1
// See: https://github.com/adafruit/Adafruit_TSL2591_Library/issues/14
lux = (((float)ch0 - (float)ch1)) * (1.0F - ((float)ch1 / (float)ch0)) / cpl;
// Alternate lux calculation 2
// lux = ( (float)ch0 - ( 1.7F * (float)ch1 ) ) / cpl;
// Signal I2C had no errors
return lux;
}
/************************************************************************/
/*!
@brief Reads the raw data from both light channels
@returns 32-bit raw count where high word is IR, low word is IR+Visible
*/
/**************************************************************************/
uint32_t Adafruit_TSL2591::getFullLuminosity(void) {
if (!_initialized) {
if (!begin()) {
return 0;
}
}
// Enable the device
enable();
// Wait x ms for ADC to complete
for (uint8_t d = 0; d <= _integration; d++) {
delay(120);
}
// CHAN0 must be read before CHAN1
// See: https://forums.adafruit.com/viewtopic.php?f=19&t=124176
uint32_t x;
uint16_t y;
y = read16(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN0_LOW);
x = read16(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN1_LOW);
x <<= 16;
x |= y;
disable();
return x;
}
/************************************************************************/
/*!
@brief Reads the raw data from the channel
@param channel Can be 0 (IR+Visible, 1 (IR) or 2 (Visible only)
@returns 16-bit raw count, or 0 if channel is invalid
*/
/**************************************************************************/
uint16_t Adafruit_TSL2591::getLuminosity(uint8_t channel) {
uint32_t x = getFullLuminosity();
if (channel == TSL2591_FULLSPECTRUM) {
// Reads two byte value from channel 0 (visible + infrared)
return (x & 0xFFFF);
} else if (channel == TSL2591_INFRARED) {
// Reads two byte value from channel 1 (infrared)
return (x >> 16);
} else if (channel == TSL2591_VISIBLE) {
// Reads all and subtracts out just the visible!
return ((x & 0xFFFF) - (x >> 16));
}
// unknown channel!
return 0;
}
/************************************************************************/
/*!
@brief Set up the interrupt to go off when light level is outside the
lower/upper range.
@param lowerThreshold Raw light data reading level that is the lower value
threshold for interrupt
@param upperThreshold Raw light data reading level that is the higher value
threshold for interrupt
@param persist How many counts we must be outside range for interrupt to
fire, default is any single value
*/
/**************************************************************************/
void Adafruit_TSL2591::registerInterrupt(
uint16_t lowerThreshold, uint16_t upperThreshold,
tsl2591Persist_t persist = TSL2591_PERSIST_ANY) {
if (!_initialized) {
if (!begin()) {
return;
}
}
enable();
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_PERSIST_FILTER, persist);
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_THRESHOLD_AILTL,
lowerThreshold);
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_THRESHOLD_AILTH,
lowerThreshold >> 8);
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_THRESHOLD_AIHTL,
upperThreshold);
write8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_THRESHOLD_AIHTH,
upperThreshold >> 8);
disable();
}
/************************************************************************/
/*!
@brief Clear interrupt status
*/
/**************************************************************************/
void Adafruit_TSL2591::clearInterrupt() {
if (!_initialized) {
if (!begin()) {
return;
}
}
enable();
write8(TSL2591_CLEAR_INT);
disable();
}
/************************************************************************/
/*!
@brief Gets the most recent sensor event from the hardware status register.
@return Sensor status as a byte. Bit 0 is ALS Valid. Bit 4 is ALS Interrupt.
Bit 5 is No-persist Interrupt.
*/
/**************************************************************************/
uint8_t Adafruit_TSL2591::getStatus(void) {
if (!_initialized) {
if (!begin()) {
return 0;
}
}
// Enable the device
enable();
uint8_t x;
x = read8(TSL2591_COMMAND_BIT | TSL2591_REGISTER_DEVICE_STATUS);
disable();
return x;
}
/************************************************************************/
/*!
@brief Gets the most recent sensor event
@param event Pointer to Adafruit_Sensor sensors_event_t object that will be
filled with sensor data
@return True on success, False on failure
*/
/**************************************************************************/
bool Adafruit_TSL2591::getEvent(sensors_event_t *event) {
uint16_t ir, full;
uint32_t lum = getFullLuminosity();
/* Early silicon seems to have issues when there is a sudden jump in */
/* light levels. :( To work around this for now sample the sensor 2x */
lum = getFullLuminosity();
ir = lum >> 16;
full = lum & 0xFFFF;
/* Clear the event */
memset(event, 0, sizeof(sensors_event_t));
event->version = sizeof(sensors_event_t);
event->sensor_id = _sensorID;
event->type = SENSOR_TYPE_LIGHT;
event->timestamp = millis();
/* Calculate the actual lux value */
/* 0 = sensor overflow (too much light) */
event->light = calculateLux(full, ir);
return true;
}
/**************************************************************************/
/*!
@brief Gets the overall sensor_t data including the type, range and
resulution
@param sensor Pointer to Adafruit_Sensor sensor_t object that will be
filled with sensor type data
*/
/**************************************************************************/
void Adafruit_TSL2591::getSensor(sensor_t *sensor) {
/* Clear the sensor_t object */
memset(sensor, 0, sizeof(sensor_t));
/* Insert the sensor name in the fixed length char array */
strncpy(sensor->name, "TSL2591", sizeof(sensor->name) - 1);
sensor->name[sizeof(sensor->name) - 1] = 0;
sensor->version = 1;
sensor->sensor_id = _sensorID;
sensor->type = SENSOR_TYPE_LIGHT;
sensor->min_delay = 0;
sensor->max_value = 88000.0;
sensor->min_value = 0.0;
sensor->resolution = 0.001;
}
/*******************************************************/
uint8_t Adafruit_TSL2591::read8(uint8_t reg) {
uint8_t buffer[1];
buffer[0] = reg;
i2c_dev->write_then_read(buffer, 1, buffer, 1);
return buffer[0];
}
uint16_t Adafruit_TSL2591::read16(uint8_t reg) {
uint8_t buffer[2];
buffer[0] = reg;
i2c_dev->write_then_read(buffer, 1, buffer, 2);
return uint16_t(buffer[1]) << 8 | uint16_t(buffer[0]);
}
void Adafruit_TSL2591::write8(uint8_t reg, uint8_t value) {
uint8_t buffer[2];
buffer[0] = reg;
buffer[1] = value;
i2c_dev->write(buffer, 2);
}
void Adafruit_TSL2591::write8(uint8_t reg) {
uint8_t buffer[1];
buffer[0] = reg;
i2c_dev->write(buffer, 1);
}
Adafruit_TSL2591.h
C Header File/**************************************************************************/
/*!
@file Adafruit_TSL2591.h
@author KT0WN (adafruit.com)
This is a library for the Adafruit TSL2591 breakout board
This library works with the Adafruit TSL2591 breakout
----> https://www.adafruit.com/products/1980
Check out the links above for our tutorials and wiring diagrams
These chips use I2C to communicate
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
*/
/**************************************************************************/
#ifndef _TSL2591_H_
#define _TSL2591_H_
#include <Adafruit_I2CDevice.h>
#include <Adafruit_Sensor.h>
#include <Arduino.h>
#define TSL2591_VISIBLE (2) ///< (channel 0) - (channel 1)
#define TSL2591_INFRARED (1) ///< channel 1
#define TSL2591_FULLSPECTRUM (0) ///< channel 0
#define TSL2591_ADDR (0x29) ///< Default I2C address
#define TSL2591_COMMAND_BIT \
(0xA0) ///< 1010 0000: bits 7 and 5 for 'command normal'
///! Special Function Command for "Clear ALS and no persist ALS interrupt"
#define TSL2591_CLEAR_INT (0xE7)
///! Special Function Command for "Interrupt set - forces an interrupt"
#define TSL2591_TEST_INT (0xE4)
#define TSL2591_WORD_BIT (0x20) ///< 1 = read/write word (rather than byte)
#define TSL2591_BLOCK_BIT (0x10) ///< 1 = using block read/write
#define TSL2591_ENABLE_POWEROFF (0x00) ///< Flag for ENABLE register to disable
#define TSL2591_ENABLE_POWERON (0x01) ///< Flag for ENABLE register to enable
#define TSL2591_ENABLE_AEN \
(0x02) ///< ALS Enable. This field activates ALS function. Writing a one
///< activates the ALS. Writing a zero disables the ALS.
#define TSL2591_ENABLE_AIEN \
(0x10) ///< ALS Interrupt Enable. When asserted permits ALS interrupts to be
///< generated, subject to the persist filter.
#define TSL2591_ENABLE_NPIEN \
(0x80) ///< No Persist Interrupt Enable. When asserted NP Threshold conditions
///< will generate an interrupt, bypassing the persist filter
#define TSL2591_LUX_DF (408.0F) ///< Lux cooefficient
#define TSL2591_LUX_COEFB (1.64F) ///< CH0 coefficient
#define TSL2591_LUX_COEFC (0.59F) ///< CH1 coefficient A
#define TSL2591_LUX_COEFD (0.86F) ///< CH2 coefficient B
/// TSL2591 Register map
enum {
TSL2591_REGISTER_ENABLE = 0x00, // Enable register
TSL2591_REGISTER_CONTROL = 0x01, // Control register
TSL2591_REGISTER_THRESHOLD_AILTL = 0x04, // ALS low threshold lower byte
TSL2591_REGISTER_THRESHOLD_AILTH = 0x05, // ALS low threshold upper byte
TSL2591_REGISTER_THRESHOLD_AIHTL = 0x06, // ALS high threshold lower byte
TSL2591_REGISTER_THRESHOLD_AIHTH = 0x07, // ALS high threshold upper byte
TSL2591_REGISTER_THRESHOLD_NPAILTL =
0x08, // No Persist ALS low threshold lower byte
TSL2591_REGISTER_THRESHOLD_NPAILTH =
0x09, // No Persist ALS low threshold higher byte
TSL2591_REGISTER_THRESHOLD_NPAIHTL =
0x0A, // No Persist ALS high threshold lower byte
TSL2591_REGISTER_THRESHOLD_NPAIHTH =
0x0B, // No Persist ALS high threshold higher byte
TSL2591_REGISTER_PERSIST_FILTER = 0x0C, // Interrupt persistence filter
TSL2591_REGISTER_PACKAGE_PID = 0x11, // Package Identification
TSL2591_REGISTER_DEVICE_ID = 0x12, // Device Identification
TSL2591_REGISTER_DEVICE_STATUS = 0x13, // Internal Status
TSL2591_REGISTER_CHAN0_LOW = 0x14, // Channel 0 data, low byte
TSL2591_REGISTER_CHAN0_HIGH = 0x15, // Channel 0 data, high byte
TSL2591_REGISTER_CHAN1_LOW = 0x16, // Channel 1 data, low byte
TSL2591_REGISTER_CHAN1_HIGH = 0x17, // Channel 1 data, high byte
};
/// Enumeration for the sensor integration timing
typedef enum {
TSL2591_INTEGRATIONTIME_100MS = 0x00, // 100 millis
TSL2591_INTEGRATIONTIME_200MS = 0x01, // 200 millis
TSL2591_INTEGRATIONTIME_300MS = 0x02, // 300 millis
TSL2591_INTEGRATIONTIME_400MS = 0x03, // 400 millis
TSL2591_INTEGRATIONTIME_500MS = 0x04, // 500 millis
TSL2591_INTEGRATIONTIME_600MS = 0x05, // 600 millis
} tsl2591IntegrationTime_t;
/// Enumeration for the persistance filter (for interrupts)
typedef enum {
// bit 7:4: 0
TSL2591_PERSIST_EVERY = 0x00, // Every ALS cycle generates an interrupt
TSL2591_PERSIST_ANY = 0x01, // Any value outside of threshold range
TSL2591_PERSIST_2 = 0x02, // 2 consecutive values out of range
TSL2591_PERSIST_3 = 0x03, // 3 consecutive values out of range
TSL2591_PERSIST_5 = 0x04, // 5 consecutive values out of range
TSL2591_PERSIST_10 = 0x05, // 10 consecutive values out of range
TSL2591_PERSIST_15 = 0x06, // 15 consecutive values out of range
TSL2591_PERSIST_20 = 0x07, // 20 consecutive values out of range
TSL2591_PERSIST_25 = 0x08, // 25 consecutive values out of range
TSL2591_PERSIST_30 = 0x09, // 30 consecutive values out of range
TSL2591_PERSIST_35 = 0x0A, // 35 consecutive values out of range
TSL2591_PERSIST_40 = 0x0B, // 40 consecutive values out of range
TSL2591_PERSIST_45 = 0x0C, // 45 consecutive values out of range
TSL2591_PERSIST_50 = 0x0D, // 50 consecutive values out of range
TSL2591_PERSIST_55 = 0x0E, // 55 consecutive values out of range
TSL2591_PERSIST_60 = 0x0F, // 60 consecutive values out of range
} tsl2591Persist_t;
/// Enumeration for the sensor gain
typedef enum {
TSL2591_GAIN_LOW = 0x00, /// low gain (1x)
TSL2591_GAIN_MED = 0x10, /// medium gain (25x)
TSL2591_GAIN_HIGH = 0x20, /// medium gain (428x)
TSL2591_GAIN_MAX = 0x30, /// max gain (9876x)
} tsl2591Gain_t;
/**************************************************************************/
/*!
@brief Class that stores state and functions for interacting with TSL2591
Light Sensor
*/
/**************************************************************************/
class Adafruit_TSL2591 : public Adafruit_Sensor {
public:
Adafruit_TSL2591(int32_t sensorID = -1);
~Adafruit_TSL2591();
boolean begin(TwoWire *theWire, uint8_t addr = TSL2591_ADDR);
boolean begin(uint8_t addr = TSL2591_ADDR);
void enable(void);
void disable(void);
float calculateLux(uint16_t ch0, uint16_t ch1);
void setGain(tsl2591Gain_t gain);
void setTiming(tsl2591IntegrationTime_t integration);
uint16_t getLuminosity(uint8_t channel);
uint32_t getFullLuminosity();
tsl2591IntegrationTime_t getTiming();
tsl2591Gain_t getGain();
// Interrupt
void clearInterrupt(void);
void registerInterrupt(uint16_t lowerThreshold, uint16_t upperThreshold,
tsl2591Persist_t persist);
uint8_t getStatus();
/* Unified Sensor API Functions */
bool getEvent(sensors_event_t *);
void getSensor(sensor_t *);
private:
Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
void write8(uint8_t r);
void write8(uint8_t r, uint8_t v);
uint16_t read16(uint8_t reg);
uint8_t read8(uint8_t reg);
tsl2591IntegrationTime_t _integration;
tsl2591Gain_t _gain;
int32_t _sensorID;
uint8_t _addr;
boolean _initialized;
};
#endif
Comments