diff options
author | Daniel Friesel <derf@finalrewind.org> | 2021-12-27 14:54:01 +0100 |
---|---|---|
committer | Daniel Friesel <derf@finalrewind.org> | 2021-12-27 14:54:01 +0100 |
commit | 53de8b4b7ae2c99d16ebe43876368c2d51a791e2 (patch) | |
tree | 4b09775a80d4d56d1e40d19d38ed3a7557295ba6 | |
parent | 095e405c93ccdb6f8a358697f68a394ce1654999 (diff) |
add VEML6075 driver
-rw-r--r-- | include/driver/veml6075.h | 84 | ||||
-rw-r--r-- | src/app/datalogger/main.cc | 18 | ||||
-rw-r--r-- | src/driver/Kconfig | 4 | ||||
-rw-r--r-- | src/driver/veml6075.cc | 82 |
4 files changed, 188 insertions, 0 deletions
diff --git a/include/driver/veml6075.h b/include/driver/veml6075.h new file mode 100644 index 0000000..4604b12 --- /dev/null +++ b/include/driver/veml6075.h @@ -0,0 +1,84 @@ +/* + * Copyright 2021 Daniel Friesel + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#ifndef VEML6075_H +#define VEML6075_H + +#include <stdint.h> + +/** + * Driver for VEML6075 UV Sensor. + */ +class VEML6075 { + private: + VEML6075(const VEML6075 ©); + unsigned char const address; + unsigned char txbuf[2]; + unsigned char rxbuf[2]; + + /* + * Calibration data from <https://cdn.sparkfun.com/assets/3/9/d/4/1/designingveml6075.pdf>, + * used to remove the visible and infrared response from UVA / UVB readings. + */ + const float uva_a_coef = 2.22; + const float uva_b_coef = 1.33; + const float uvb_c_coef = 2.95; + const float uvb_d_coef = 1.74; + + /* + * Datasheet values for counts → µW/cm² conversion at 50ms integration time. + * I'm not sure whether this is before or after adjusting for visible and IR response. + */ + const float uva_counts_per_uwcm2 = 0.93; + const float uvb_counts_per_uwcm2 = 2.1; + + /* + * Responsivity for UV Index calculation from <https://cdn.sparkfun.com/assets/3/9/d/4/1/designingveml6075.pdf>. + * Used after visible/IR compensation. + */ + const float uva_uvi_response = 0.001461; + const float uvb_uvi_response = 0.002591; + + bool readUVCounts(float *uva, float *uvb); + + public: + /** + * Create a new VEML6075 object for the specified I2C address. + * This is a no-op; the sensor is not initialized. In its default + * configuration, it takes a light reading every 800ms and uses + * auto-ranging to select a suitable measurement range. + * + * @param addr I2C address of light sensor, default 0x4a + */ + VEML6075(unsigned char const addr = 0x10) : address(addr) {} + + /** + * Initialize VEML6075 to power on, normal mode, normal dynamic, 50ms + * integration time. + * @return true if initialization was successful. + */ + bool init(); + + /** + * Read UVA/UVB irradiance. + * @param uva UVA irradiance [µW/cm²] + * @param uvb UVB irradiance [µW/cm²] + * @return true if readout was successful. + */ + bool readUV(float *uva, float *uvb); + + /** + * Read UVA/UVB Index. 0 is no UV, higher values indicate increased UV exposure. + * The overall UV index is mean(UVA Index, UV Index) == (UVA Index + UVB Index)/2. + * @param uva UVA Index + * @param uvb UVB Index + * @return true if readout was successful. + */ + bool readUVI(float *uva, float *uvb); +}; + +extern VEML6075 veml6075; + +#endif diff --git a/src/app/datalogger/main.cc b/src/app/datalogger/main.cc index 6cdba9e..9083825 100644 --- a/src/app/datalogger/main.cc +++ b/src/app/datalogger/main.cc @@ -54,6 +54,9 @@ #ifdef CONFIG_driver_scd4x #include "driver/scd4x.h" #endif +#ifdef CONFIG_driver_veml6075 +#include "driver/veml6075.h" +#endif void loop(void) { @@ -195,6 +198,17 @@ void loop(void) kout.printf_float((100.0 * scd4x.rawHumidity) / 65536); kout << " %" << endl; #endif + +#ifdef CONFIG_driver_veml6075 + float uva, uvb; + if (veml6075.readUV(&uva, &uvb)) { + kout << "VEML6075 UVA: " << uva << " µW / cm²" << endl; + kout << "VEML6075 UVB: " << uvb << " µW / cm²" << endl; + } + if (veml6075.readUVI(&uva, &uvb)) { + kout << "VEML6075 UV Index: " << (uva + uvb)/2 << endl; + } +#endif } int main(void) @@ -299,6 +313,10 @@ int main(void) scd4x.start(); #endif +#ifdef CONFIG_driver_veml6075 + veml6075.init(); +#endif + arch.idle_loop(); return 0; diff --git a/src/driver/Kconfig b/src/driver/Kconfig index 4b3693c..3ff88e7 100644 --- a/src/driver/Kconfig +++ b/src/driver/Kconfig @@ -95,6 +95,10 @@ config driver_scd4x bool "Sensirion SCD4x CO2 Sensor" depends on meta_driver_i2c +config driver_veml6075 +bool "VEML6075 UV Sensor" +depends on meta_driver_i2c + config driver_sharp96 bool "sharp LS013B4DN 96x96px Transflective LC Display" depends on ( arch_msp430fr5969lp || arch_msp430fr5994lp ) && meta_driver_spi diff --git a/src/driver/veml6075.cc b/src/driver/veml6075.cc new file mode 100644 index 0000000..a3e41f6 --- /dev/null +++ b/src/driver/veml6075.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2020 Daniel Friesel + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Driver for VEML6075 Ambient Light Sensor. + */ +#include "driver/veml6075.h" +#if defined(CONFIG_meta_driver_hardware_i2c) +#include "driver/i2c.h" +#elif defined(CONFIG_driver_softi2c) +#include "driver/soft_i2c.h" +#endif + +bool VEML6075::init() +{ + txbuf[0] = 0x00; + txbuf[1] = 0x00; // 50ms integration time, normal dynamic + if (i2c.xmit(address, 2, txbuf, 0, rxbuf) != 0) { + return false; + } + return true; +} + +bool VEML6075::readUVCounts(float *uva, float *uvb) +{ + uint16_t uva_counts, uvb_counts, comp_visible, comp_ir; + txbuf[0] = 0x07; + if (i2c.xmit(address, 2, txbuf, 2, rxbuf) != 0) { + return false; + } + uva_counts = ((uint16_t)rxbuf[1] << 8) + rxbuf[0]; + + txbuf[0] = 0x09; + if (i2c.xmit(address, 2, txbuf, 2, rxbuf) != 0) { + return false; + } + uvb_counts = ((uint16_t)rxbuf[1] << 8) + rxbuf[0]; + + txbuf[0] = 0x0a; + if (i2c.xmit(address, 2, txbuf, 2, rxbuf) != 0) { + return false; + } + comp_visible = ((uint16_t)rxbuf[1] << 8) + rxbuf[0]; + + txbuf[0] = 0x0b; + if (i2c.xmit(address, 2, txbuf, 2, rxbuf) != 0) { + return false; + } + comp_ir = ((uint16_t)rxbuf[1] << 8) + rxbuf[0]; + + *uva = uva_counts - uva_a_coef * comp_visible - uva_b_coef * comp_ir; + *uvb = uvb_counts - uvb_c_coef * comp_visible - uvb_d_coef * comp_ir; + + return true; +} + +bool VEML6075::readUV(float *uva, float *uvb) +{ + float uva_counts, uvb_counts; + if (!readUVCounts(&uva_counts, &uvb_counts)) { + return false; + } + + *uva = uva_counts * uva_counts_per_uwcm2; + *uvb = uvb_counts * uvb_counts_per_uwcm2; + return true; +} + +bool VEML6075::readUVI(float *uva, float *uvb) +{ + float uva_counts, uvb_counts; + if (!readUVCounts(&uva_counts, &uvb_counts)) { + return false; + } + + *uva = uva_counts * uva_uvi_response; + *uvb = uvb_counts * uvb_uvi_response; + return true; +} + +VEML6075 veml6075(0x10); |