Driver for the Silicon Labs Si1133 Visible Light/UV sensor

Dependents:   TBSense2_Sensor_Demo mbed-BLE-coragem-teste ... more

Sample library for use with the Silicon Labs Si1133 sensor for visible light intensity (lux) and UV index measurements.

Information

All examples in this repo are considered EXPERIMENTAL QUALITY, meaning this code has been created as one-off proof-of-concept and is suitable as a demonstration for experimental purposes only. This code will not be regularly maintained by Silicon Labs and there is no guarantee that these projects will work across all environments, SDK versions and hardware.

Datasheet

https://www.silabs.com/documents/public/data-sheets/Si1133.pdf

Usage

#include "mbed.h"
#include "Si1133.h"
//Create an Si1133 object
Si1133 sensor(PC4, PC5);
int main()
{
    //Try to open the Si1133
    if (sensor.open()) {
        printf("Device detected!\n");
        while (1) {
            //Print the current light level
            printf("Lux = %.3f\n", (float)sensor.get_light_level());
            //Print the current UV index
            printf("UV index = %.3f\n", (float)sensor.get_uv_index());
            //Sleep for 0.5 seconds
            wait(0.5);
        }
    } else {
        error("Device not detected!\n");
    }
}

Si1133.cpp

Committer:
Steven Cooreman
Date:
16 months ago
Revision:
3:f780ca9105bb
Parent:
2:1e2dd643afa8

File content as of revision 3:f780ca9105bb:

/***************************************************************************//**
 * @file Si1133.cpp
 *******************************************************************************
 * @section License
 * <b>(C) Copyright 2017 Silicon Labs, http://www.silabs.com</b>
 *******************************************************************************
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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
 * 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.
 *
 ******************************************************************************/

#include "Si1133.h"

#define SI1133_I2C_ADDRESS (0xAA) /** Hardcoded address for Si1133 sensor */

/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */

#define X_ORDER_MASK            0x0070
#define Y_ORDER_MASK            0x0007
#define SIGN_MASK               0x0080
#define GET_X_ORDER(m)          ( ((m) & X_ORDER_MASK) >> 4)
#define GET_Y_ORDER(m)          ( ((m) & Y_ORDER_MASK) )
#define GET_SIGN(m)             ( ((m) & SIGN_MASK) >> 7)

#define UV_INPUT_FRACTION       15
#define UV_OUTPUT_FRACTION      12
#define UV_NUMCOEFF             2

#define ADC_THRESHOLD           16000
#define INPUT_FRACTION_HIGH     7
#define INPUT_FRACTION_LOW      15
#define LUX_OUTPUT_FRACTION     12
#define NUMCOEFF_LOW            9
#define NUMCOEFF_HIGH           4

/** @endcond */

/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/***************************************************************************//**
 * @brief
 *    Coefficients for lux calculation
 ******************************************************************************/
const Si1133::LuxCoeff_t Si1133::lk = {
    {   {     0, 209 },           /**< coeff_high[0]   */
        {  1665, 93  },           /**< coeff_high[1]   */
        {  2064, 65  },           /**< coeff_high[2]   */
        { -2671, 234 } },         /**< coeff_high[3]   */
    {   {     0, 0   },           /**< coeff_low[0]    */
        {  1921, 29053 },         /**< coeff_low[1]    */
        { -1022, 36363 },         /**< coeff_low[2]    */
        {  2320, 20789 },         /**< coeff_low[3]    */
        {  -367, 57909 },         /**< coeff_low[4]    */
        { -1774, 38240 },         /**< coeff_low[5]    */
        {  -608, 46775 },         /**< coeff_low[6]    */
        { -1503, 51831 },         /**< coeff_low[7]    */
        { -1886, 58928 } }        /**< coeff_low[8]    */
};

/***************************************************************************//**
 * @brief
 *    Coefficients for UV index calculation
 ******************************************************************************/
const Si1133::Coeff_t Si1133::uk[2] = {
    { 1281, 30902 },            /**< coeff[0]        */
    { -638, 46301 }             /**< coeff[1]        */
};

/**************************************************************************//**
* @name Error Codes
* @{
******************************************************************************/
#define SI1133_OK                            0x0000   /**< No errors                  */
#define SI1133_ERROR_I2C_TRANSACTION_FAILED  0x0001   /**< I2C transaction failed     */
#define SI1133_ERROR_SLEEP_FAILED            0x0002   /**< Entering sleep mode failed */
/**@}*/

/** @endcond */

Si1133::Si1133(PinName sda, PinName scl, int hz) : m_I2C(sda, scl)
{
    //Set the I2C bus frequency
    m_I2C.frequency(hz);
}

Si1133::~Si1133(void)
{
    deinit();
}

bool Si1133::open()
{
    //Probe for the Si1133 using a Zero Length Transfer
    if (m_I2C.write(SI1133_I2C_ADDRESS, NULL, 0)) {
        //Return success
        return false;
    }

    // initialize sensor
    if (SI1133_OK == init()) {
        return true;
    }
    return false;
}

/** Measure the current light level (in lux) on the Si1133
 *
 * @returns The current temperature measurement in Lux.
 */
float Si1133::get_light_level()
{
    float lux, uvi;
    measure_lux_uv(&lux, &uvi);
    return lux;
}

/** Measure the current UV Index on the Si1133
 *
 * @returns The current UV index.
 */
float Si1133::get_uv_index()
{
    float lux, uvi;
    measure_lux_uv(&lux, &uvi);
    return uvi;
}

bool Si1133::get_light_and_uv(float *light_level, float *uv_index)
{
    if(measure_lux_uv(light_level, uv_index)) {
        return false;
    }
    return true;
}

/***************************************************************************//**
 * @brief
 *    Reads register from the Si1133 sensor
 *
 * @param[in] reg
 *    The register address to read from in the sensor.
 *
 * @param[out] data
 *    The data read from the sensor
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::read_register(enum Si1133::Register reg, uint8_t *data)
{
    char buf[1];
    buf[0] = (char) reg;

    if (m_I2C.write(SI1133_I2C_ADDRESS, buf, 1, true)) {
        //Return failure
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    if (m_I2C.read(SI1133_I2C_ADDRESS, buf, 1)) {
        //Return failure
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    *data = buf[0];

    return SI1133_OK;
}

/***************************************************************************//**
 * @brief
 *    Writes register in the Si1133 sensor
 *
 * @param[in] reg
 *    The register address to write to in the sensor
 *
 * @param[in] data
 *    The data to write to the sensor
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::write_register(enum Si1133::Register reg, uint8_t data)
{
    char buf[2];
    buf[0] = (char) reg;
    buf[1] = (char) data;

    if (m_I2C.write(SI1133_I2C_ADDRESS, buf, 2)) {
        //Return failure
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    return SI1133_OK;
}

/***************************************************************************//**
 * @brief
 *    Writes a block of data to the Si1133 sensor.
 *
 * @param[in] reg
 *    The first register to begin writing to
 *
 * @param[in] length
 *    The number of bytes to write to the sensor
 *
 * @param[in] data
 *    The data to write to the sensor
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::write_register_block(enum Si1133::Register reg, uint8_t length, uint8_t *data)
{
    char buf[3];
    buf[0] = (char)reg;

    if (length > 2) {
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    memcpy(&buf[1], data, length);

    if (m_I2C.write(SI1133_I2C_ADDRESS, buf, length + 1)) {
        //Return failure
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    return SI1133_OK;
}

/***************************************************************************//**
 * @brief
 *    Reads a block of data from the Si1133 sensor.
 *
 * @param[in] reg
 *    The first register to begin reading from
 *
 * @param[in] length
 *    The number of bytes to write to the sensor
 *
 * @param[out] data
 *    The data read from the sensor
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::read_register_block(enum Si1133::Register reg, uint8_t length, uint8_t *data)
{
    char reg_c = (char)reg;
    if (m_I2C.write(SI1133_I2C_ADDRESS, &reg_c, 1, true)) {
        //Return failure
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    if (m_I2C.read(SI1133_I2C_ADDRESS, (char*) data, length)) {
        //Return failure
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    return SI1133_OK;
}

/***************************************************************************//**
 * @brief
 *    Reads the interrupt status register of the device
 *
 * @param[out] irqStatus
 *    The contentof the IRQ status register
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::get_irq_status(uint8_t *irq_status)
{
    return read_register(REG_IRQ_STATUS, irq_status);
}

/***************************************************************************//**
 * @brief
 *    Waits until the Si1133 is sleeping before proceeding
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::wait_until_sleep(void)
{
  uint32_t ret;
  uint8_t response;
  size_t count = 0;

  /* This loops until the Si1133 is known to be in its sleep state  */
  /* or if an i2c error occurs                                      */
  while ( count < 5 ) {
    ret = read_register(REG_RESPONSE0, &response);
    if ( (response & (uint8_t)RSP0_CHIPSTAT_MASK) == (uint8_t)RSP0_SLEEP ) {
      return SI1133_OK;
    }

    if ( ret != SI1133_OK ) {
      return SI1133_ERROR_SLEEP_FAILED;
    }

    count++;
  }

  return SI1133_ERROR_SLEEP_FAILED;
}

/***************************************************************************//**
 * @brief
 *    Resets the Si1133
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::reset(void)
{
    uint32_t retval;

    /* Do not access the Si1133 earlier than 25 ms from power-up */
    wait_ms(30);

    /* Perform the Reset Command */
    retval = write_register(REG_COMMAND, (uint8_t)CMD_RESET);

    /* Delay for 10 ms. This delay is needed to allow the Si1133   */
    /* to perform internal reset sequence.                         */
    wait_ms(10);

    return retval;
}

/***************************************************************************//**
 * @brief
 *    Helper function to send a command to the Si1133
 *
 * @param[in] command
 *    The command to send to the sensor
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::send_cmd(enum Si1133::Command command)
{
    uint8_t response;
    uint8_t response_stored;
    uint8_t count = 0;
    uint32_t ret;

    /* Get the response register contents */
    ret = read_register(REG_RESPONSE0, &response_stored);
    if ( ret != SI1133_OK ) {
        return ret;
    }

    response_stored = response_stored & (uint8_t)RSP0_COUNTER_MASK;

    /* Double-check the response register is consistent */
    while ( count < 5 ) {
        ret = wait_until_sleep();
        if ( ret != SI1133_OK ) {
            return ret;
        }
        /* Skip if the command is RESET COMMAND COUNTER */
        if ( command == (uint8_t)CMD_RESET_CMD_CTR ) {
            break;
        }

        ret = read_register(REG_RESPONSE0, &response);

        if ( (response & (uint8_t)RSP0_COUNTER_MASK) == response_stored ) {
            break;
        } else {
            if ( ret != SI1133_OK ) {
                return ret;
            } else {
                response_stored = response & (uint8_t)RSP0_COUNTER_MASK;
            }
        }

        count++;
    }

    /* Send the command */
    ret = write_register(REG_COMMAND, command);
    if ( ret != SI1133_OK ) {
        return ret;
    }

    count = 0;
    /* Expect a change in the response register */
    while ( count < 5 ) {
        /* Skip if the command is RESET COMMAND COUNTER */
        if ( command == (uint8_t)CMD_RESET_CMD_CTR ) {
            break;
        }

        ret = read_register(REG_RESPONSE0, &response);
        if ( (response & (uint8_t)RSP0_COUNTER_MASK) != response_stored ) {
            break;
        } else {
            if ( ret != SI1133_OK ) {
                return ret;
            }
        }

        count++;
    }

    return SI1133_OK;
}

/***************************************************************************//**
 * @brief
 *    Sends a RESET COMMAND COUNTER command to the Si1133
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::reset_cmd_counter(void)
{
  return send_cmd(CMD_RESET_CMD_CTR);
}

/***************************************************************************//**
 * @brief
 *    Sends a FORCE command to the Si1133
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::force_measurement(void)
{
  return send_cmd(CMD_FORCE_CH);
}

/***************************************************************************//**
 * @brief
 *    Sends a START command to the Si1133
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::start_measurement(void)
{
  return send_cmd(CMD_START);
}

/***************************************************************************//**
 * @brief
 *    Sends a PAUSE command to the Si1133
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::pause_measurement(void)
{
  return send_cmd(CMD_PAUSE_CH);
}

/***************************************************************************//**
 * @brief
 *    Writes a byte to an Si1133 Parameter
 *
 * @param[in] address
 *    The parameter address
 *
 * @param[in] value
 *    The byte value to be written to the Si1133 parameter
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 *
 * @note
 *    This function ensures that the Si1133 is idle and ready to
 *    receive a command before writing the parameter. Furthermore,
 *    command completion is checked. If setting parameter is not done
 *    properly, no measurements will occur. This is the most common
 *    error. It is highly recommended that host code make use of this
 *    function.
 ******************************************************************************/
uint32_t Si1133::set_parameter (enum Si1133::Parameter address, uint8_t value)
{
    uint32_t retval;
    uint8_t buffer[2];
    uint8_t response_stored;
    uint8_t response;
    size_t count;

    retval = wait_until_sleep();
    if ( retval != SI1133_OK ) {
        return retval;
    }

    read_register(REG_RESPONSE0, &response_stored);
    response_stored &= (uint8_t)RSP0_COUNTER_MASK;

    buffer[0] = value;
    buffer[1] = 0x80 + ((uint8_t)address & 0x3F);

    retval = write_register_block(REG_HOSTIN0, 2, (uint8_t*) buffer);
    if ( retval != SI1133_OK ) {
        return retval;
    }

    /* Wait for command to finish */
    count = 0;
    /* Expect a change in the response register */
    while ( count < 5 ) {
        retval = read_register(REG_RESPONSE0, &response);
        if ( (response & (uint8_t)RSP0_COUNTER_MASK) != response_stored ) {
            break;
        } else {
            if ( retval != SI1133_OK ) {
                return retval;
            }
        }

        count++;
    }

    if (count >= 5) {
        return SI1133_ERROR_I2C_TRANSACTION_FAILED;
    }

    return SI1133_OK;
}

/***************************************************************************//**
 * @brief
 *    Reads a parameter from the Si1133
 *
 * @param[in] address
 *    The address of the parameter.
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::read_parameter (enum Si1133::Parameter address)
{
    uint8_t retval;
    uint8_t cmd;

    cmd = 0x40 + ((uint8_t)address & 0x3F);

    retval = send_cmd((enum Si1133::Command)cmd);
    if ( retval != SI1133_OK ) {
        return retval;
    }

    read_register(REG_RESPONSE1, &retval);

    return retval;
}

/**************************************************************************//**
 * @brief
 *    Initializes the Si1133 chip
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 *****************************************************************************/
uint32_t Si1133::init (void)
{
    uint32_t retval;

    /* Allow some time for the part to power up */
    wait_ms(5);

    retval = reset();

    wait_ms(10);

    retval += set_parameter(PARAM_CH_LIST, 0x0f);
    retval += set_parameter(PARAM_ADCCONFIG0, 0x78);
    retval += set_parameter(PARAM_ADCSENS0, 0x71);
    retval += set_parameter(PARAM_ADCPOST0, 0x40);
    retval += set_parameter(PARAM_ADCCONFIG1, 0x4d);
    retval += set_parameter(PARAM_ADCSENS1, 0xe1);
    retval += set_parameter(PARAM_ADCPOST1, 0x40);
    retval += set_parameter(PARAM_ADCCONFIG2, 0x41);
    retval += set_parameter(PARAM_ADCSENS2, 0xe1);
    retval += set_parameter(PARAM_ADCPOST2, 0x50);
    retval += set_parameter(PARAM_ADCCONFIG3, 0x4d);
    retval += set_parameter(PARAM_ADCSENS3, 0x87);
    retval += set_parameter(PARAM_ADCPOST3, 0x40);

    retval += write_register(REG_IRQ_ENABLE, 0x0f);

    return retval;
}

/***************************************************************************//**
 * @brief
 *    Stops the measurements on all channel and waits until the chip
 *    goes to sleep state.
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::deinit (void)
{
    uint32_t retval;

    retval = set_parameter(PARAM_CH_LIST, 0x3f);
    retval += pause_measurement();
    retval += wait_until_sleep();

    return retval;
}

/***************************************************************************//**
 * @brief
 *    Read samples from the Si1133 chip
 *
 * @param[out] samples
 *    Retrieves interrupt status and measurement data for channel 0..3 and
 *    converts the data to int32_t format
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::measure (Si1133::Samples_t *samples)
{
    uint8_t buffer[13];
    uint32_t retval;

    retval = read_register_block(REG_IRQ_STATUS, 13, buffer);

    samples->irq_status = buffer[0];

    samples->ch0 = buffer[1] << 16;
    samples->ch0 |= buffer[2] << 8;
    samples->ch0 |= buffer[3];
    if ( samples->ch0 & 0x800000 ) {
        samples->ch0 |= 0xFF000000;
    }

    samples->ch1 = buffer[4] << 16;
    samples->ch1 |= buffer[5] << 8;
    samples->ch1 |= buffer[6];
    if ( samples->ch1 & 0x800000 ) {
        samples->ch1 |= 0xFF000000;
    }

    samples->ch2 = buffer[7] << 16;
    samples->ch2 |= buffer[8] << 8;
    samples->ch2 |= buffer[9];
    if ( samples->ch2 & 0x800000 ) {
        samples->ch2 |= 0xFF000000;
    }

    samples->ch3 = buffer[10] << 16;
    samples->ch3 |= buffer[11] << 8;
    samples->ch3 |= buffer[12];
    if ( samples->ch3 & 0x800000 ) {
        samples->ch3 |= 0xFF000000;
    }

    return retval;
}

int32_t Si1133::calculate_polynomial_helper (int32_t input, int8_t fraction, uint16_t mag, int8_t shift)
{
    int32_t value;

    if ( shift < 0 ) {
        value = ( (input << fraction) / mag) >> -shift;
    } else {
        value = ( (input << fraction) / mag) << shift;
    }

    return value;
}

int32_t Si1133::calculate_polynomial (int32_t x, int32_t y, uint8_t input_fraction, uint8_t output_fraction, uint8_t num_coeff, const Si1133::Coeff_t *kp)
{
    uint8_t info, x_order, y_order, counter;
    int8_t sign, shift;
    uint16_t mag;
    int32_t output = 0, x1, x2, y1, y2;

    for ( counter = 0; counter < num_coeff; counter++ ) {
        info = kp->info;
        x_order = GET_X_ORDER(info);
        y_order = GET_Y_ORDER(info);

        shift = ( (uint16_t) kp->info & 0xff00) >> 8;
        shift ^= 0x00ff;
        shift += 1;
        shift = -shift;

        mag = kp->mag;

        if ( GET_SIGN(info) ) {
            sign = -1;
        } else {
            sign = 1;
        }

        if ( (x_order == 0) && (y_order == 0) ) {
            output += sign * mag << output_fraction;
        } else {
            if ( x_order > 0 ) {
                x1 = calculate_polynomial_helper(x, input_fraction, mag, shift);
                if ( x_order > 1 ) {
                    x2 = calculate_polynomial_helper(x, input_fraction, mag, shift);
                } else {
                    x2 = 1;
                }
            } else {
                x1 = 1;
                x2 = 1;
            }

            if ( y_order > 0 ) {
                y1 = calculate_polynomial_helper(y, input_fraction, mag, shift);
                if ( y_order > 1 ) {
                    y2 = calculate_polynomial_helper(y, input_fraction, mag, shift);
                } else {
                    y2 = 1;
                }
            } else {
                y1 = 1;
                y2 = 1;
            }

            output += sign * x1 * x2 * y1 * y2;
        }

        kp++;
    }

    if ( output < 0 ) {
        output = -output;
    }

    return output;
}

/***************************************************************************//**
 * @brief
 *    Compute UV index
 *
 * @param[in] uv
 *    UV sensor raw data
 *
 * @param[in] uk
 *    UV calculation coefficients
 *
 * @return
 *    UV index scaled by UV_OUPTUT_FRACTION
 ******************************************************************************/
int32_t Si1133::get_uv (int32_t uv)
{
    int32_t uvi;

    uvi = calculate_polynomial(0, uv, UV_INPUT_FRACTION, UV_OUTPUT_FRACTION, UV_NUMCOEFF, uk);

    return uvi;
}

/***************************************************************************//**
 * @brief
 *    Compute lux value
 *
 * @param[in] vis_high
 *    Visible light sensor raw data
 *
 * @param[in] vis_low
 *    Visible light sensor raw data
 *
 * @param[in] ir
 *    Infrared sensor raw data
 *
 * @param[in] lk
 *    Lux calculation coefficients
 *
 * @return
 *    Lux value scaled by LUX_OUPTUT_FRACTION
 ******************************************************************************/
int32_t Si1133::get_lux (int32_t vis_high, int32_t vis_low, int32_t ir)
{
    int32_t lux;

    if ( (vis_high > ADC_THRESHOLD) || (ir > ADC_THRESHOLD) ) {
        lux = calculate_polynomial(vis_high,
                                   ir,
                                   INPUT_FRACTION_HIGH,
                                   LUX_OUTPUT_FRACTION,
                                   NUMCOEFF_HIGH,
                                   &(lk.coeff_high[0]) );
    } else {
        lux = calculate_polynomial(vis_low,
                                   ir,
                                   INPUT_FRACTION_LOW,
                                   LUX_OUTPUT_FRACTION,
                                   NUMCOEFF_LOW,
                                   &(lk.coeff_low[0]) );
    }

    return lux;
}

/***************************************************************************//**
 * @brief
 *    Measure lux and UV index using the Si1133 sensor
 *
 * @param[out] lux
 *    The measured ambient light illuminace in lux
 *
 * @param[out] uvi
 *    UV index
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::measure_lux_uv (float *lux, float *uvi)
{
    Si1133::Samples_t samples;
    uint32_t retval;
    uint8_t response;

    /* Force measurement */
    retval = force_measurement();

    /* Go to sleep while the sensor does the conversion */
    wait_ms(200);

    /* Check if the measurement finished, if not then wait */
    retval += read_register(REG_IRQ_STATUS, &response);
    while ( response != 0x0F ) {
        wait_ms(5);
        retval += read_register(REG_IRQ_STATUS, &response);
    }

    /* Get the results */
    measure(&samples);

    /* Convert the readings to lux */
    *lux = (float) get_lux(samples.ch1, samples.ch3, samples.ch2);
    *lux = *lux / (1 << LUX_OUTPUT_FRACTION);

    /* Convert the readings to UV index */
    *uvi = (float) get_uv(samples.ch0);
    *uvi = *uvi / (1 << UV_OUTPUT_FRACTION);

    return retval;
}

/***************************************************************************//**
 * @brief
 *    Reads Hardware ID from the SI1133 sensor
 *
 * @param[out] hardwareID
 *    The Hardware ID of the chip (should be 0x33)
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::get_hardware_id (uint8_t *hardware_id)
{
    uint32_t retval;

    retval = read_register(REG_PART_ID, hardware_id);

    return retval;
}

/***************************************************************************//**
 * @brief
 *    Retrieve the sample values from the chip and convert them
 *    to lux and UV index values
 *
 * @param[out] lux
 *    The measured ambient light illuminace in lux
 *
 * @param[out] uvi
 *    UV index
 *
 * @return
 *    Returns zero on OK, non-zero otherwise
 ******************************************************************************/
uint32_t Si1133::get_measurement (float *lux, float *uvi)
{
    Si1133::Samples_t samples;
    uint32_t retval;

    /* Get the results */
    retval = measure(&samples);

    /* Convert the readings to lux */
    *lux = (float) get_lux(samples.ch1, samples.ch3, samples.ch2);
    *lux = *lux / (1 << LUX_OUTPUT_FRACTION);

    /* Convert the readings to UV index */
    *uvi = (float) get_uv(samples.ch0);
    *uvi = *uvi / (1 << UV_OUTPUT_FRACTION);

    return retval;
}