//*****************************************************************************
//
// Function to wait for the ISL29023 transactions to complete.
//
//*****************************************************************************
void
ISL29023AppI2CWait(char *pcFilename, uint_fast32_t ui32Line)
{
    //
    // Put the processor to sleep while we wait for the I2C driver to
    // indicate that the transaction is complete.
    //
    while((g_vui8DataFlag == 0) && (g_vui8ErrorFlag == 0))
    {
        //
        // Do Nothing
        //
    }

    //
    // If an error occurred call the error handler immediately.
    //
    if(g_vui8ErrorFlag)
    {
        ISL29023AppErrorHandler(pcFilename, ui32Line);
    }

    //
    // clear the data flag for next use.
    //
    g_vui8DataFlag = 0;
}
//*****************************************************************************
//
// This task captures data from the ISL29023 sensor and puts it into the shared
// data structure.
//
//*****************************************************************************
static void
ISL29023Task(void *pvParameters)
{
    portTickType xLastWakeTime;
    float fVisible;
    uint8_t ui8Mask;

    //
    // The binary semaphore is created full so we empty it first so we can
    // use it to wait for the AppCallback function.
    //
    xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore, 0);
    xSemaphoreTake(g_xISL29023AdjustRangeSemaphore, 0);

    //
    // Take the I2C semaphore so we can init the sensor. Keep it until all init
    // is complete for this sensor.
    //
    xSemaphoreTake(g_xI2CSemaphore, portMAX_DELAY);

    //
    // Initialize the ISL29023 Driver.
    //
    ISL29023Init(&g_sISL29023Inst, &g_sI2CInst, ISL29023_I2C_ADDRESS,
                 ISL29023AppCallback, &g_sISL29023Inst);

    //
    // Wait for transaction to complete
    //
    xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore, portMAX_DELAY);

    //
    // If an error occurred call the error handler immediately.
    //
    if(g_vui8ISL29023I2CErrorStatus)
    {
        //
        // Give back the I2C Semaphore
        //
        xSemaphoreGive(g_xI2CSemaphore);

        //
        // Call the error handler.
        //
        ISL29023AppErrorHandler(__FILE__, __LINE__);
    }

    //
    // Configure the ISL29023 to measure Visible light continuously. Set a 8
    // sample persistence before the INT pin is asserted. Clears the INT flag.
    // Persistence setting of 8 is sufficient to ignore camera flashes.
    //
    ui8Mask = (ISL29023_CMD_I_OP_MODE_M | ISL29023_CMD_I_INT_PERSIST_M |
               ISL29023_CMD_I_INT_FLAG_M);
    ISL29023ReadModifyWrite(&g_sISL29023Inst, ISL29023_O_CMD_I, ~ui8Mask,
                            (ISL29023_CMD_I_OP_MODE_ALS_CONT |
                             ISL29023_CMD_I_INT_PERSIST_8),
                            ISL29023AppCallback, &g_sISL29023Inst);

    //
    // Wait for transaction to complete
    //
    xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore, portMAX_DELAY);

    //
    // If an error occurred call the error handler immediately.
    //
    if(g_vui8ISL29023I2CErrorStatus)
    {
        //
        // Give back the I2C Semaphore
        //
        xSemaphoreGive(g_xI2CSemaphore);

        //
        // Call the Error handler.
        //
        ISL29023AppErrorHandler(__FILE__, __LINE__);
    }

    //
    // Configure the upper threshold to 80% of maximum value
    //
    g_sISL29023Inst.pui8Data[1] = 0xCC;
    g_sISL29023Inst.pui8Data[2] = 0xCC;
    ISL29023Write(&g_sISL29023Inst, ISL29023_O_INT_HT_LSB,
                  g_sISL29023Inst.pui8Data, 2, ISL29023AppCallback,
                  &g_sISL29023Inst);

    //
    // Wait for transaction to complete
    //
    xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore, portMAX_DELAY);

    //
    // If an error occurred call the error handler immediately.
    //
    if(g_vui8ISL29023I2CErrorStatus)
    {
        //
        // Give back the I2C Semaphore
        //
        xSemaphoreGive(g_xI2CSemaphore);

        //
        // Call the error handler.
        //
        ISL29023AppErrorHandler(__FILE__, __LINE__);
    }

    //
    // Configure the lower threshold to 20% of maximum value
    //
    g_sISL29023Inst.pui8Data[1] = 0x33;
    g_sISL29023Inst.pui8Data[2] = 0x33;
    ISL29023Write(&g_sISL29023Inst, ISL29023_O_INT_LT_LSB,
                  g_sISL29023Inst.pui8Data, 2, ISL29023AppCallback,
                  &g_sISL29023Inst);

    //
    // Wait for transaction to complete
    //
    xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore, portMAX_DELAY);

    //
    // Give back the I2C Semaphore
    //
    xSemaphoreGive(g_xI2CSemaphore);

    //
    // If an error occurred call the error handler immediately.
    //
    if(g_vui8ISL29023I2CErrorStatus)
    {
        ISL29023AppErrorHandler(__FILE__, __LINE__);
    }

    //
    // Get the current time as a reference to start our delays.
    //
    xLastWakeTime = xTaskGetTickCount();

    //
    // Loop forever.
    //
    while(1)
    {
        //
        // Wait for the required amount of time to check back.
        //
        vTaskDelayUntil(&xLastWakeTime, ISL29023_TASK_PERIOD_MS /
                                        portTICK_RATE_MS);

        //
        // Take the I2C semaphore.  Given back in the Interrupt contect in the
        // callback function after I2C transaction is complete.
        //
        xSemaphoreTake(g_xI2CSemaphore, portMAX_DELAY);

        //
        // Go get the latest data from the sensor.
        //
        ISL29023DataRead(&g_sISL29023Inst, ISL29023AppCallback,
                         &g_sISL29023Inst);

        //
        // Wait for the I2C Driver to tell us that transaction is complete.
        //
        xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore, portMAX_DELAY);

        //
        // Give back the I2C Semaphore so other can use the I2C interface.
        //
        xSemaphoreGive(g_xI2CSemaphore);

        //
        // If an error occurred call the error handler immediately.
        //
        if(g_vui8ISL29023I2CErrorStatus)
        {
            ISL29023AppErrorHandler(__FILE__, __LINE__);
        }

        //
        // Get a local floating point copy of the latest light data
        //
        ISL29023DataLightVisibleGetFloat(&g_sISL29023Inst, &fVisible);

        //
        // Check if the intensity of light has crossed a threshold. If so
        // then adjust range of sensor readings to track intensity.
        //
        if(xSemaphoreTake(g_xISL29023AdjustRangeSemaphore, 0) == pdTRUE)
        {
            //
            // Adjust the lux range.
            //
            ISL29023AppAdjustRange(fVisible);

            //
            // Take the I2C semaphore.  Given back in the Interrupt contect in
            // the callback function after I2C transaction is complete.
            //
            xSemaphoreTake(g_xI2CSemaphore, portMAX_DELAY);

            //
            // Now we must manually clear the flag in the ISL29023
            // register.
            //
            ISL29023Read(&g_sISL29023Inst, ISL29023_O_CMD_I,
                         g_sISL29023Inst.pui8Data, 1, ISL29023AppCallback,
                         &g_sISL29023Inst);

            //
            // Wait for the I2C Driver to tell us that transaction is complete.
            //
            xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore,
                           portMAX_DELAY);

            //
            // Give back the I2C Semaphore so other can use the I2C interface.
            //
            xSemaphoreGive(g_xI2CSemaphore);

            //
            // If an error occurred call the error handler immediately.
            //
            if(g_vui8ISL29023I2CErrorStatus)
            {
                ISL29023AppErrorHandler(__FILE__, __LINE__);
            }
        }

        //
        // Publish the data to the global structure for consumption by other
        // tasks.
        //
        xSemaphoreTake(g_xCloudDataSemaphore, portMAX_DELAY);
        g_sISL29023Data.fVisible = fVisible;
        g_sISL29023Data.xTimeStampTicks = xTaskGetTickCount();
        g_sISL29023Data.ui8Range = g_sISL29023Inst.ui8Range;
        xSemaphoreGive(g_xCloudDataSemaphore);

    }
}
//*****************************************************************************
//
// Intensity and Range Tracking Function.  This adjusts the range and interrupt
// thresholds as needed.  Uses an 80/20 rule. If light is greather then 80% of
// maximum value in this range then go to next range up. If less than 20% of
// potential value in this range go the next range down.
//
//*****************************************************************************
void
ISL29023AppAdjustRange(float fVisible)
{
    uint8_t ui8NewRange;

    //
    // Initialize the local variable.
    //
    ui8NewRange = g_sISL29023Inst.ui8Range;

    //
    // Check if we crossed the upper threshold.
    //
    if(fVisible > g_fThresholdHigh[g_sISL29023Inst.ui8Range])
    {
        //
        // The current intensity is over our threshold so adjsut the range
        // accordingly
        //
        if(g_sISL29023Inst.ui8Range < ISL29023_CMD_II_RANGE_64K)
        {
            ui8NewRange = g_sISL29023Inst.ui8Range + 1;
        }
    }

    //
    // Check if we crossed the lower threshold
    //
    if(fVisible < g_fThresholdLow[g_sISL29023Inst.ui8Range])
    {
        //
        // If possible go to the next lower range setting and re-config the
        // thresholds.
        //
        if(g_sISL29023Inst.ui8Range > ISL29023_CMD_II_RANGE_1K)
        {
            ui8NewRange = g_sISL29023Inst.ui8Range - 1;
        }
    }

    //
    // If the desired range value changed then send the new range to the sensor
    //
    if(ui8NewRange != g_sISL29023Inst.ui8Range)
    {
        //
        // Take the I2C semaphore.
        //
        xSemaphoreTake(g_xI2CSemaphore, portMAX_DELAY);

        //
        // Perform a read, modify, write over I2C to set the new range.
        //
        ISL29023ReadModifyWrite(&g_sISL29023Inst, ISL29023_O_CMD_II,
                                ~ISL29023_CMD_II_RANGE_M, ui8NewRange,
                                ISL29023AppCallback, &g_sISL29023Inst);

        //
        // Wait for transaction to complete
        //
        xSemaphoreTake(g_xISL29023TransactionCompleteSemaphore, portMAX_DELAY);

        //
        // Give back the I2C Semaphore
        //
        xSemaphoreGive(g_xI2CSemaphore);

        //
        // If an error occurred call the error handler immediately.
        //
        if(g_vui8ISL29023I2CErrorStatus)
        {
            ISL29023AppErrorHandler(__FILE__, __LINE__);
        }
    }
}