A Vishay library for devices VEML6040 R+G+B+W and VEML6075 UVA+UVB optical sensors. Data is stored in a dedicated data structure.

Dependents:   vmel60xx_hello_world

This device library is for use with the Vishay VEML6040 and VEML6075 optical sensors. Ambient light conditions are gathered and stored in a user accessible data structure veml60xx_struct. The library has enough intelligence to determine which device is connected and performs the appropriate functions.

The VEML6040 detects Red, Green, Blue and White light data, which is easily converted to relative Lux intensities.

The VEML6075 detects UVA and UVB light which is converted to a UV Index value.

Since both devices use the same I2C address, they cannot be on the same I2C bus at the same time.

Tested on a K64F

veml60xx.cpp

Committer:
loopsva
Date:
2016-04-29
Revision:
4:0ce65ee5697f
Parent:
3:dda770fa7228

File content as of revision 4:0ce65ee5697f:

// Vishay veml6040 R+G+B+W and veml6075 UVA+UVB optical sensors

#include "veml60xx.h"

//--------------------------------------------------------------------------------------------------------------------------------------//
// Constructor, to allow for user to select i2c frequency

veml60xx::veml60xx(PinName sda, PinName scl,  int i2cFrequency) {
    _i2c_ = new I2C(sda, scl);
    _i2c_->frequency(i2cFrequency);
}

//--------------------------------------------------------------------------------------------------------------------------------------//
// deconstructor

veml60xx::~veml60xx() {
}

//--------------------------------------------------------------------------------------------------------------------------------------//
// Set/initialize the veml60xx config register.

void veml60xx::setConfig(veml60xx_struct& Pntr, uint16_t val) {
    vemlBuffer[0] = VEML60xx_CONF_REG;
    vemlBuffer[1] = val;
    vemlBuffer[2] = 0;
    _i2c_->write(VEML60_WADDR, vemlBuffer, 3, false);
    Pntr.conf_reg = (vemlBuffer[2] << 8)  | vemlBuffer[1];
}

//--------------------------------------------------------------------------------------------------------------------------------------//
// Get veml60xx ID register.  Returns register value
// Note: the veml6040 does have a device ID, but is not in datasheet

uint16_t veml60xx::getID(veml60xx_struct& Pntr) {
    vemlBuffer[0] = VEML6075_CHIP_ID_REG;
    _i2c_->write(VEML60_WADDR, vemlBuffer, 1, true);
    _i2c_->read(VEML60_RADDR, vemlBuffer, 2, false);
    Pntr.id = (vemlBuffer[1] << 8)  | vemlBuffer[0];

    if(((Pntr.id & 0xff) & VEML6075_DEVICE_ID) == VEML6075_DEVICE_ID) {
        Pntr.is6075 = true;
        Pntr.is6040 = false;
    } else
    if(((Pntr.id & 0xff) & VEML6040_DEVICE_ID) == VEML6040_DEVICE_ID) {
        Pntr.is6075 = false;
        Pntr.is6040 = true;
    } else {
        Pntr.is6075 = false;
        Pntr.is6040 = false;
    }
    return(Pntr.id);
}

//--------------------------------------------------------------------------------------------------------------------------------------//
// Get veml60xx config register.  Returns register value

uint16_t veml60xx::getConfig(veml60xx_struct& Pntr) {
    vemlBuffer[0] = VEML60xx_CONF_REG;
    _i2c_->write(VEML60_WADDR, vemlBuffer, 1, true);
    _i2c_->read(VEML60_RADDR, vemlBuffer, 2, false);
    uint16_t rdata = (vemlBuffer[1] << 8)  | vemlBuffer[0];
    Pntr.conf_reg = rdata;
    uint16_t c = rdata & VEML60xx_CONF_BITS_IT;
    if(Pntr.is6040) {
        switch (c) {
            case VEML60xx_CONF_BITS_IT_50m40m: 
                Pntr.lux_step = VEML6040_LUX_STEP * 32.0;
                Pntr.trig_dly = 54;
                break;
            case VEML60xx_CONF_BITS_IT_100m80m:
                Pntr.lux_step = VEML6040_LUX_STEP * 16.0;
                Pntr.trig_dly = 102;
                break;
            case VEML60xx_CONF_BITS_IT_200m160m:
                Pntr.lux_step = VEML6040_LUX_STEP * 8.0;
                Pntr.trig_dly = 204;
                break;
            case VEML60xx_CONF_BITS_IT_400m320m:
                Pntr.lux_step = VEML6040_LUX_STEP * 4.0;
                Pntr.trig_dly = 408;
                break;
            case VEML60xx_CONF_BITS_IT_800m640m:
                Pntr.lux_step = VEML6040_LUX_STEP * 2.0;
                Pntr.trig_dly = 816;
                break;
            case VEML6040_CONF_BITS_IT_1280m:
                Pntr.lux_step = VEML6040_LUX_STEP * 1.0;
                Pntr.trig_dly = 1400;
                break;
            default:
                Pntr.lux_step = 0.0;
                Pntr.trig_dly = 1400;
                break;
        }
    }
    if(Pntr.is6075) {
        switch (c) {
            case VEML60xx_CONF_BITS_IT_50m40m: 
                Pntr.uva_step = VEML6075_UVA_RESP * 2.0;
                Pntr.uvb_step = VEML6075_UVB_RESP * 2.0;
                Pntr.trig_dly = 80;
                break;
            case VEML60xx_CONF_BITS_IT_100m80m:
                Pntr.uva_step = VEML6075_UVA_RESP * 1.0;    //reference integration/dwell time (100mS) from app note
                Pntr.uvb_step = VEML6075_UVB_RESP * 1.0;
                Pntr.trig_dly = 320;
                break;
            case VEML60xx_CONF_BITS_IT_200m160m:
                Pntr.uva_step = VEML6075_UVA_RESP * 0.5;
                Pntr.uvb_step = VEML6075_UVB_RESP * 0.5;
                Pntr.trig_dly = 640;
                break;
            case VEML60xx_CONF_BITS_IT_400m320m:
                Pntr.uva_step = VEML6075_UVA_RESP * 0.25;
                Pntr.uvb_step = VEML6075_UVB_RESP * 0.25;
                Pntr.trig_dly = 1280;
                break;
            case VEML60xx_CONF_BITS_IT_800m640m:
                Pntr.uva_step = VEML6075_UVA_RESP * 0.125;
                Pntr.uvb_step = VEML6075_UVB_RESP * 0.125;
                Pntr.trig_dly = 2000;
                break;
            default:
                Pntr.uva_step = 0.0;
                Pntr.uvb_step = 0.0;
                Pntr.trig_dly = 2000;
                break;
        }
    }
    return(rdata);
}    

//--------------------------------------------------------------------------------------------------------------------------------------//
// If AF set to trigger mode, start a conversion process trigger
// Returns: 0=trigger set, 1=already triggered,  2=no AF bit - cannot set trigger

uint16_t veml60xx::startAccess(veml60xx_struct& Pntr) {
    uint16_t val = getConfig(Pntr);
    if((val & VEML60xx_CONF_BITS_AF) == VEML60xx_CONF_BITS_AF) {
        if((val & VEML60xx_CONF_BITS_TRIG) == VEML60xx_CONF_BITS_TRIG) return(1);
        val |= VEML60xx_CONF_BITS_TRIG;
        setConfig(Pntr, val);
        return(0);
    }
    return(2);
}       
    
//--------------------------------------------------------------------------------------------------------------------------------------//
// Get VEML60xx values.  Saves raw data is data structure.  Returns 0 if successful, !0 if status is (something else);
// mode: false = VEML6075, true = VEML6040

uint16_t veml60xx::getRawData(veml60xx_struct& Pntr) {
    Pntr.conf_reg = getConfig(Pntr);
    Pntr.id = getID(Pntr);
        
    if(Pntr.is6075) {
        vemlBuffer[0] = VEML6075_UVA_DATA_REG;
        _i2c_->write(VEML60_WADDR, vemlBuffer, 1, true);
        _i2c_->read(VEML60_RADDR, vemlBuffer, 2, false);
        Pntr.uva_d = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    }
    
    vemlBuffer[0] = VEML6075_DUMMY_REG;
    _i2c_->write(VEML60_WADDR, vemlBuffer, 1, true);
    _i2c_->read(VEML60_RADDR, vemlBuffer, 2, false);
    if(Pntr.is6075) {
        Pntr.dummy_d = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    } else 
    if(Pntr.is6040) {
        Pntr.r_d = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    }
    
    vemlBuffer[0] = VEML6075_UVB_DATA_REG;
    _i2c_->write(VEML60_WADDR, vemlBuffer, 1, true);
    _i2c_->read(VEML60_RADDR, vemlBuffer, 2, false);
    if(Pntr.is6075) {
        Pntr.uvb_d = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    } else 
    if(Pntr.is6040) {
        Pntr.g_d = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    }
       
    vemlBuffer[0] = VEML6075_UV_COMP1_REG;
    _i2c_->write(VEML60_WADDR, vemlBuffer, 1, true);
    _i2c_->read(VEML60_RADDR, vemlBuffer, 2, false);
    if(Pntr.is6075) {
        Pntr.uv_c1 = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    } else 
    if(Pntr.is6040) {
        Pntr.b_d = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    }
        
    vemlBuffer[0] = VEML6075_UV_COMP2_REG;
    _i2c_->write(VEML60_WADDR, vemlBuffer, 1, true);
    _i2c_->read(VEML60_RADDR, vemlBuffer, 2, false);
    if(Pntr.is6075) {
        Pntr.uv_c1 = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    } else
    if(Pntr.is6040) {
        Pntr.w_d = ((vemlBuffer[1] << 8)  | vemlBuffer[0]);
    }
    
    return(0);
}

//--------------------------------------------------------------------------------------------------------------------------------------//
// Convert raw data into real UVA, UVB and UVI numbers

void veml60xx::convertRawData(veml60xx_struct& Pntr) {
    if(Pntr.is6075) {
        //Eq (1)
        Pntr.uva_comp = (double)(Pntr.uva_d - Pntr.dummy_d) - 
                        VEML6075_UVA_COEF_A * (double)(Pntr.uv_c1 - Pntr.dummy_d) - 
                        VEML6075_UVA_COEF_B * (double)(Pntr.uv_c2 - Pntr.dummy_d);
        
        //Eq (2)
        Pntr.uvb_comp = (double)(Pntr.uvb_d - Pntr.dummy_d) - 
                        VEML6075_UVB_COEF_C * (double)(Pntr.uv_c1 - Pntr.dummy_d) - 
                        VEML6075_UVB_COEF_D * (double)(Pntr.uv_c2 - Pntr.dummy_d);
        //Eq (3)
        Pntr.uv_index = ((Pntr.uva_comp * Pntr.uva_step) + (Pntr.uvb_comp * Pntr.uvb_step))/2.0;
        
        if(Pntr.uva_comp < 0.0) Pntr.uva_comp = 0.0;
        if(Pntr.uvb_comp < 0.0) Pntr.uvb_comp = 0.0;
        if(Pntr.uv_index < 0.0) Pntr.uv_index = 0.0;
    }
    
    if(Pntr.is6040) {
        Pntr.r_lux = (double)Pntr.r_d * Pntr.lux_step;
        Pntr.g_lux = (double)Pntr.g_d * Pntr.lux_step;
        Pntr.b_lux = (double)Pntr.b_d * Pntr.lux_step;
        Pntr.w_lux = (double)Pntr.w_d * Pntr.lux_step;
    }
}
    

//--------------------------------------------------------------------------------------------------------------------------------------//
// If there is a lux over/underflow, automatically adjust the CONF_BITS_IT by +-1 to compensate accordingly
// if returns true, the Lux integration/dwell time (IT bits in CONF reg) was changed.

bool veml60xx::autoAdjustLux(veml60xx_struct& Pntr) {
    getRawData(Pntr);
    uint16_t rdata = Pntr.conf_reg;
    uint16_t lux = rdata & VEML60xx_CONF_BITS_IT;
    if(Pntr.is6040) {
        if((Pntr.r_d < 255) && (Pntr.g_d < 255) && (Pntr.b_d < 255) && (Pntr.w_d < 255)) {
            if(lux == VEML6040_CONF_BITS_IT_1280m) return false;
            lux += VEML60xx_CONF_BITS_IT_100m80m;
            rdata = (rdata & ~VEML60xx_CONF_BITS_IT) | lux;
            setConfig(Pntr, rdata);
            return true;
        } else 
        if((Pntr.r_d == 65535) || (Pntr.g_d == 65535) || (Pntr.b_d == 65535) || (Pntr.w_d == 65535)) {
            if(lux == VEML60xx_CONF_BITS_IT_50m40m) return false;
            lux -= VEML60xx_CONF_BITS_IT_100m80m;
            rdata = (rdata & ~VEML60xx_CONF_BITS_IT) | lux;
            setConfig(Pntr, rdata);
            return true;
        }
    } else
    if(Pntr.is6075) {
        if((Pntr.uva_d < 255) && (Pntr.uvb_d < 255)) {
            if(lux == VEML60xx_CONF_BITS_IT_800m640m) return false;
            lux += VEML60xx_CONF_BITS_IT_100m80m;
            rdata = (rdata & ~VEML60xx_CONF_BITS_IT) | lux;
            setConfig(Pntr, rdata);
            return true;
        } else 
        if((Pntr.uva_d == 65535) || (Pntr.uvb_d == 65535)) {
            if(lux == VEML60xx_CONF_BITS_IT_50m40m) return false;
            lux -= VEML60xx_CONF_BITS_IT_100m80m;
            rdata = (rdata & ~VEML60xx_CONF_BITS_IT) | lux;
            setConfig(Pntr, rdata);
            return true;
        }
    }
    return false;
}