1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! This is a platform agnostic Rust driver for the VEML6075 UVA and UVB 
//! light sensor, based on the [`embedded-hal`] traits.
//!
//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
//!
//! This driver allows you to:
//! - Enable/disable the sensor
//! - Read the UVA measurement
//! - Read the UVB measurement
//! - Read the UVcomp1 measurement
//! - Read the UVcomp2 measurement
//! - Read the device id
//!
//! ## The device
//! The VEML6075 senses UVA and UVB light and incorporates photodiode,
//! amplifiers,and analog / digital circuits into a single chip using a
//! CMOS process. When the UV sensor is applied, it is able to detect
//! UVA and UVB intensity to provide a measure of the signal strength
//! as well as allowing for UVI measurement.
//! The VEML6075 provides excellent temperature compensation capability
//! for keeping the output stable under changing temperature.
//! VEML6075's functionality is easilyoperated via the simple command
//! format of I2C (SMBus compatible) interface protocol.
//! VEML6075's operating voltage ranges from 1.7 V to 3.6 V.
//!
//! Datasheet:
//! - [VEML6075](https://www.vishay.com/docs/84304/veml6075.pdf)
//!
//! Application note:
//! - [VEML6075 AN](https://www.vishay.com/docs/84339/designingveml6075.pdf)
//!
//! //! ## Usage examples (see also examples folder)
//!
//! ### Read UVA and UVB
//!
//! Import this crate and an `embedded_hal` implementation, then instantiate
//! the device:
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate veml6075;
//!
//! use hal::I2cdev;
//! use veml6075::VEML6075;
//!
//! # fn main() {
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut uv_light_sensor = VEML6075::new(dev);
//! uv_light_sensor.enable().unwrap();
//! let _uva_reading = uv_light_sensor.read_uva().unwrap();
//! let _uvb_reading = uv_light_sensor.read_uvb().unwrap();
//! # }
//! ```

#![deny(unsafe_code)]
#![deny(missing_docs)]
#![no_std]

extern crate embedded_hal as hal;
use hal::blocking::i2c::Write;

/// All possible errors in this crate
#[derive(Debug)]
pub enum Error<E> {
    /// I²C bus error
    I2C(E),
}

struct Register;

impl Register {
    const CONFIG    : u8 = 0x00;
    const UVA       : u8 = 0x07;
    const UVB       : u8 = 0x09;
    const UVCOMP1   : u8 = 0x0A;
    const UVCOMP2   : u8 = 0x0B;
    const DEVICE_ID : u8 = 0x0C;
}

struct BitFlags;

impl BitFlags {
    const SHUTDOWN   : u8 = 0b0000_0001;
 }

const DEVICE_ADDRESS : u8 = 0x10;

/// VEML6075 device driver.
#[derive(Debug, Default)]
pub struct VEML6075<I2C> {
    /// The concrete I²C device implementation.
    i2c: I2C,
    /// Configuration register status.
    config: u8,
}

impl<I2C, E> VEML6075<I2C>
where
    I2C: Write<Error = E>
{
    /// Create new instance of the VEML6075 device.
    pub fn new(i2c: I2C) -> Self {
        VEML6075 {
            i2c,
            config : 0x01 // shutdown
        }
    }

    /// Destroy driver instance, return I²C bus instance.
    pub fn destroy(self) -> I2C {
        self.i2c
    }

    /// Enable the sensor.
    pub fn enable(&mut self) -> Result<(), Error<E>> {
        let config = self.config;
        self.write_config(config & !BitFlags::SHUTDOWN)
    }

    /// Disable the sensor (shutdown).
    pub fn disable(&mut self) -> Result<(), Error<E>> {
        let config  = self.config;
        self.write_config(config | BitFlags::SHUTDOWN)
    }

    fn write_config(&mut self, config: u8) -> Result<(), Error<E>> {
        self.i2c
            .write(DEVICE_ADDRESS, &[Register::CONFIG, config, 0])
            .map_err(Error::I2C)?;
        self.config = config;
        Ok(())
    }
}


impl<I2C, E> VEML6075<I2C>
where
    I2C: hal::blocking::i2c::WriteRead<Error = E>
{
    /// Read the UVA sensor data.
    pub fn read_uva(&mut self) -> Result<u16, Error<E>> {
        self.read_register(Register::UVA)
    }

    /// Read the UVB sensor data.
    pub fn read_uvb(&mut self) -> Result<u16, Error<E>> {
        self.read_register(Register::UVB)
    }

    /// Read the UVcomp1 sensor data.
    pub fn read_uvcomp1(&mut self) -> Result<u16, Error<E>> {
        self.read_register(Register::UVCOMP1)
    }

    /// Read the UVcomp2 sensor data.
    pub fn read_uvcomp2(&mut self) -> Result<u16, Error<E>> {
        self.read_register(Register::UVCOMP2)
    }

    /// Read the device ID
    pub fn read_device_id(&mut self) -> Result<u16, Error<E>> {
        self.read_register(Register::DEVICE_ID)
    }

    fn read_register(&mut self, register: u8) -> Result<u16, Error<E>> {
        let mut data = [0; 2];
        self.i2c
            .write_read(DEVICE_ADDRESS, &[register], &mut data)
            .map_err(Error::I2C)?;
        Ok((data[1] as u16) << 8 | data[0] as u16)
    }
}