/*
 * bme280.c
 *
 *  Created on: Nov 30, 2022
 *      Author: thomm
 */


//#include <stdint.h>

/* resources...
 *  https://github.com/boschsensortec/BME280_driver/blob/master/bme280.c
 *  https://github.com/eziya/STM32_HAL_BME280/blob/master/Src/BME280/bme280.c
 *  https://github.com/vuquangtrong/tiva-c/blob/master/7_Sensors/Sensor/BMP280.cpp
 *  https://github.com/khoitd1997/BMP280_driver_TivaC/blob/master/src/TivaC_I2C.c
 *  https://github.com/JVKran/Forced-BME280/blob/master/src/forcedClimate.cpp
 *  https://drive.google.com/file/d/1mXrVDlCyYWgZkumhETqR3W0fhO84YPFa/view
 *  https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf
 *
 *
 * Pressure range: 300 … 1100 hPa (equivalent to +9000…-500m above/below sea level)
        Pressure resolution: 0.01 hPa ( < 10 cm)
        Temperature range: -40 … 85 °C
        Temperature resolution: 0.01 °C
        Humidity range: 0 … 100 %
        Interface: I2C and SPI
        Supply voltage range: 1.71 … 3.6 V

        Pressure is measured in hectoPascals (hPa), also called millibars.
        Standard pressure at sea level is defined as 1013hPa, but we can see large areas of either high or low pressure.


 */

#include <stdint.h>
#include <stdbool.h>
#include "system.h"
#include "user_io.h"
#include "uartstdio.h"  // User local version with larger RX buffer
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"

#include "board_util.h"
#include "i2c0.h"
#include "bme280.h"


extern void delay_msec(unsigned int msec);


// From Schematic: bme280 i2c address: 1110110x
//#define BME280_I2C_ADDRESS  0xEC        // Tiva API requires this to be bit shifted for 7bit address

struct bme280_data bme280_data;



void bme280_dev_test(void)
{
    //i2c0_busScan();

    //bme280_init(&bme280_data);

    bme280_readChipID(1);
    bme280_readStatus(1);
    bme280_readConfig(1);
    bme280_readCtrlMeas(1);

    bme280_test();
}


void bme280_test(void)
{
    char input;
    int32_t temperature;
    int32_t pressure, humidity;

    while(1)
    {
        bme280_sample_fetch(&bme280_data);

        temperature = bme280_get_temperature(&bme280_data);
        pressure = bme280_get_pressure(&bme280_data);
        humidity = bme280_get_humidity(&bme280_data);

        uprintf("bme280 temperature:   %4d.%02d  C\r\n", temperature/100, temperature%100);
        uprintf("bme280 humidity:      %4d       percent RH\r\n", humidity/1024);
        uprintf("bme280 pressure:      %4d.%02d  hPa\r\n", (pressure/256)/100, (pressure/256)%100);

        if( UARTRxBytesAvail() )
        {
            input = get_key();
            break;    // break from while(1)
        }

        delay_msec(1000);
        wait_consoleTx();

    }

}

//int bme280_init(struct bme280_data *data)
void bme280_init(void)
{

    struct bme280_data *data;

    i2c0_init(FALSE);       // FALSE for 100Khz, TRUE for 400Khz bus

    pwr_env_mon_on();       //   // Turn on Load switch for ENV monitoring, PB5

    data = &bme280_data;
    data->chip_id = 0x00;

    bme280_read_byte(BME280_REG_CHIPID, &data->chip_id);

    if (data->chip_id == BME280_CHIPID_VAL)
    {
        //uprintf("BME280 chip detected\r\n");
    } else if (data->chip_id == BMP280_CHIP_ID_MP ||
           data->chip_id == BMP280_CHIP_ID_SAMPLE_1)
    {
        //uprintf("BMP280 chip detected\r\n");
    } else
    {
        // FUTURE: create a bme280 disable flag
        //uprintf("BME280: bad chip id 0x%x\r\n", data->chip_id);
        return;
    }

    bme280_read_compensation(data);

    if (data->chip_id == BME280_CHIPID_VAL)
    {
        bme280_write_byte(BME280_REG_CTRL_HUM, BME280_HUMIDITY_OVER);       // 4x oversample (011 written into reg)
    }


    bme280_setup();


}

void bme280_setup(void)
{
    // Setup the BME280
    bme280_write_byte(BME280_REG_CTRL_MEAS, BME280_CTRL_MEAS_VAL);      // Press Over, Temp Over, MODE Forced

    bme280_write_byte(BME280_REG_CONFIG, BME280_CONFIG_VAL);
}


// called by bme_init
void bme280_read_compensation(struct bme280_data *data)
{

    uint16_t buf[16];       //12
    uint8_t hbuf[8];        //7
    uint8_t bVal;

    while(1)
    {
        // is a copy to registers in progress (bit.0=1)?
        //bme280_read_byte(BME280_REG_STATUS, &bVal);
        bVal = bme280_readStatus(0);
        if ((bVal & 0x01) == 0x00 )
        {
            break;
        }
    }

    bme280_read_buf(BME280_REG_COMP_START, (uint8_t *)buf, sizeof(buf));

    data->dig_t1 = buf[0];
    data->dig_t2 = buf[1];
    data->dig_t3 = buf[2];
    data->dig_p1 = buf[3];
    data->dig_p2 = buf[4];
    data->dig_p3 = buf[5];
    data->dig_p4 = buf[6];
    data->dig_p5 = buf[7];
    data->dig_p6 = buf[8];
    data->dig_p7 = buf[9];
    data->dig_p8 = buf[10];
    data->dig_p9 = buf[11];

    if (data->chip_id == BME280_CHIPID_VAL)
    {
        bme280_read_byte(BME280_REG_HUM_COMP_PART1, &data->dig_h1);
        bme280_read_buf(BME280_REG_HUM_COMP_PART2, hbuf, 7);

        data->dig_h2 = (hbuf[1] << 8) | hbuf[0];
        data->dig_h3 = hbuf[2];
        data->dig_h4 = (hbuf[3] << 4) | (hbuf[4] & 0x0F);
        data->dig_h5 = ((hbuf[4] >> 4) & 0x0F) | (hbuf[5] << 4);
        data->dig_h6 = hbuf[6];
    }
}


/*
 * Compensation code taken from BME280 datasheet, Section 4.2.3
 * "Compensation formula".
 */
void bme280_calc_temperature(struct bme280_data *data, int32_t adc_temp)
{
    int32_t var1, var2;

    var1 = (((adc_temp >> 3) - ((int32_t)data->dig_t1 << 1)) *
        ((int32_t)data->dig_t2)) >> 11;
    var2 = (((((adc_temp >> 4) - ((int32_t)data->dig_t1)) *
          ((adc_temp >> 4) - ((int32_t)data->dig_t1))) >> 12) *
        ((int32_t)data->dig_t3)) >> 14;

    data->t_fine = var1 + var2;
    data->comp_temp = (data->t_fine * 5 + 128) >> 8;
}

void bme280_calc_pressure(struct bme280_data *data, int32_t adc_press)
{
    int64_t var1, var2, p;

    var1 = ((int64_t)data->t_fine) - 128000;
    var2 = var1 * var1 * (int64_t)data->dig_p6;
    var2 = var2 + ((var1 * (int64_t)data->dig_p5) << 17);
    var2 = var2 + (((int64_t)data->dig_p4) << 35);
    var1 = ((var1 * var1 * (int64_t)data->dig_p3) >> 8) +
        ((var1 * (int64_t)data->dig_p2) << 12);
    var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)data->dig_p1) >> 33;

    /* Avoid exception caused by division by zero. */
    if (var1 == 0) {
        data->comp_press = 0;
        return;
    }

    p = 1048576 - adc_press;
    p = (((p << 31) - var2) * 3125) / var1;
    var1 = (((int64_t)data->dig_p9) * (p >> 13) * (p >> 13)) >> 25;
    var2 = (((int64_t)data->dig_p8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((int64_t)data->dig_p7) << 4);

    data->comp_press = (uint32_t)p;
}

void bme280_calc_humidity(struct bme280_data *data, int32_t adc_humidity)
{
    int32_t h;

    h = (data->t_fine - ((int32_t)76800));
    h = ((((adc_humidity << 14) - (((int32_t)data->dig_h4) << 20) -
        (((int32_t)data->dig_h5) * h)) + ((int32_t)16384)) >> 15) *
        (((((((h * ((int32_t)data->dig_h6)) >> 10) * (((h *
        ((int32_t)data->dig_h3)) >> 11) + ((int32_t)32768))) >> 10) +
        ((int32_t)2097152)) * ((int32_t)data->dig_h2) + 8192) >> 14);
    h = (h - (((((h >> 15) * (h >> 15)) >> 7) *
        ((int32_t)data->dig_h1)) >> 4));
    h = (h > 419430400 ? 419430400 : h);

    data->comp_humidity = (uint32_t)(h >> 12);
}

int bme280_read_data(void)
{
    bme280_sample_fetch(&bme280_data);

    return 0;
}

int bme280_sample_fetch(struct bme280_data *data)
{
    uint8_t buf[8];
    int32_t adc_press, adc_temp, adc_humidity;
    int size = 6;       // for bmp280
    uint8_t bVal;

    if (data->chip_id == BME280_CHIPID_VAL) {
        size = 8;
    }

    // Operating in FORCED_MODE requires the FORCED mode setting to be written
    bme280_write_byte(BME280_REG_CTRL_MEAS, BME280_CTRL_MEAS_VAL);

    delay_msec(500);

    while(1)
    {
        // is a conversion running (bit.3=1)?
        bme280_read_byte(BME280_REG_STATUS, &bVal);
        if ((bVal & 0x03) == 0x00 )
        {
            break;
        }
    }

    if (bme280_read_buf(BME280_REG_PRESS_MSB, buf, size) < 0)
    {
        return -EIO;
    }

    adc_press = (buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4);
    adc_temp = (buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4);

    bme280_calc_temperature(data, adc_temp);
    bme280_calc_pressure(data, adc_press);

    if (data->chip_id == BME280_CHIPID_VAL) {
        adc_humidity = (buf[6] << 8) | buf[7];
        bme280_calc_humidity(data, adc_humidity);
    }

    return 0;
}

/* returns temperature in 0.01 degC from struct, assumes bme280_sample_fetch has been called*/
int32_t bme280_get_temperature(struct bme280_data *data)
{
    return data->comp_temp;
}

/* returns pressure in 1/256 Pa */
uint32_t bme280_get_pressure(struct bme280_data *data)
{
    return data->comp_press;
}

/* returns humidity in 1/1024 %RH */
uint32_t bme280_get_humidity(struct bme280_data *data)
{
    return data->comp_humidity;
}

/* returns temperature in 0.01 degC from struct, assumes bme280_sample_fetch has been called*/
float bme280_get_fTemperature(void)
{
    int32_t temperature;
    float T;

    temperature = bme280_get_temperature(&bme280_data);
    T = (float)temperature;
    T /= 100;

    //uprintf("bme280 temperature:   %4d.%02d C  %8.2f\r\n", temperature/100, temperature%100, T);

    return(T);
}

/* returns pressure in hPa, assumes bme280_sample_fetch has been called*/
float bme280_get_fPressure(void)
{
    uint32_t pressure;
    float P;

    pressure = bme280_get_pressure(&bme280_data);

    //uprintf("bme280 pressure:      %4d.%02d  hPa\r\n", (pressure/256)/100, (pressure/256)%100);

    P = (float)pressure;
    pressure /= 256;
    pressure /= 100;

    return(P);
}

/* returns humidity in percent relative humidity, assumes bme280_sample_fetch has been called*/
uint32_t bme280_get_uiHumidity(void)
{
    uint32_t humidity;

    humidity = bme280_get_humidity(&bme280_data);

    //uprintf("bme280 humidity:      %4d       percent RH\r\n", humidity/1024);

    return(humidity/1024);
}


// writes 1 byte '_data' to register 'reg_addr'
int bme280_write_byte(uint8_t reg_addr, uint8_t device_data)
{
    i2c0_write_reg_byte(BME280_I2C_8BIT_ADDR_EC>>1, reg_addr, device_data);

    return 0;       // different return val policy for imported code

}

// reads 8 bits from register 'reg_addr'
//uint8_t bme280_read_byte(uint8_t device_register)
int bme280_read_byte(uint8_t reg_addr, uint8_t *data)
{
  uint8_t retByte;

  retByte = i2c0_read8(BME280_I2C_8BIT_ADDR_EC>>1, reg_addr);

  *data = retByte;

  return 0;
}

int bme280_read_buf(uint8_t reg_addr, uint8_t *data, uint8_t len)
{
    int retVal;

    retVal = i2c0_read(BME280_I2C_8BIT_ADDR_EC>>1, reg_addr, data, len);

    return 0;
}



uint8_t bme280_readChipID(int prnFlg)
{
    uint8_t  chipId;

    bme280_read_byte(BME280_REG_CHIPID, &chipId);

    if(prnFlg) uprintf("\r\nBME280 Chip ID = 0x%02x, reading reg returns: 0x%02x\r\n", BME280_CHIPID_VAL, chipId );

    return(chipId);
}

uint8_t bme280_readStatus(int prnFlg)
{
    uint8_t  status;

    bme280_read_byte(BME280_REG_STATUS, &status);

    if(prnFlg) uprintf("\r\nBME280 Status = 0x%02x\r\n", status );

    return(status);
}


uint8_t bme280_readConfig(int prnFlg)
{
    uint8_t  config;

    bme280_read_byte(BME280_REG_CONFIG, &config);

    if(prnFlg) uprintf("\r\nBME280 Config = 0x%02x\r\n", config );

    return(config);
}


uint8_t bme280_readCtrlMeas(int prnFlg)
{
    uint8_t  bVal;

    bme280_read_byte(BME280_REG_CONFIG, &bVal);

    if(prnFlg) uprintf("\r\nBME280 CTRL MEAS = 0x%02x\r\n", bVal );

    return(bVal);
}











#ifdef JUNKCODE
/*!
 * @brief This API is used to calculate the maximum delay in milliseconds required for the
 * temperature/pressure/humidity(which ever at enabled) measurement to complete.
 */
uint32_t bme280_calc_meas_delay(const struct bme280_settings *settings)
{
    uint32_t max_delay;
    uint8_t temp_osr;
    uint8_t pres_osr;
    uint8_t hum_osr;

    /*Array to map OSR config register value to actual OSR */
    uint8_t osr_sett_to_act_osr[] = { 0, 1, 2, 4, 8, 16 };

    /* Mapping osr settings to the actual osr values e.g. 0b101 -> osr X16  */
    if (settings->osr_t <= 5)
    {
        temp_osr = osr_sett_to_act_osr[settings->osr_t];
    }
    else
    {
        temp_osr = 16;
    }

    if (settings->osr_p <= 5)
    {
        pres_osr = osr_sett_to_act_osr[settings->osr_p];
    }
    else
    {
        pres_osr = 16;
    }

    if (settings->osr_h <= 5)
    {
        hum_osr = osr_sett_to_act_osr[settings->osr_h];
    }
    else
    {
        hum_osr = 16;
    }

    max_delay =
        (uint32_t)((BME280_MEAS_OFFSET + (BME280_MEAS_DUR * temp_osr) +
                    ((BME280_MEAS_DUR * pres_osr) + BME280_PRES_HUM_MEAS_OFFSET) +
                    ((BME280_MEAS_DUR * hum_osr) + BME280_PRES_HUM_MEAS_OFFSET)) / BME280_MEAS_SCALING_FACTOR);

    return max_delay;
}


// BME280 sensor setup function.
void bme280_setup(uint8_t mode, uint8_t T_sampling, uint8_t H_sampling, uint8_t P_sampling, uint8_t filter, uint8_t standby)
{
    uint8_t _ctrl_hum, _ctrl_meas, _config;

    _ctrl_hum = H_sampling;
    _config = ((standby << 5) | (filter << 2)) & 0xFC;
    _ctrl_meas = (T_sampling << 5) | (P_sampling << 2) | mode;

    bme280_write_uint8(BME280_REG_CTRLHUM, _ctrl_hum);
    bme280_write_uint8(BME280_REG_CONFIG,  _config);
    bme280_write_uint8(BME280_REG_CONTROL, _ctrl_meas);
}

/*!
 * @brief This internal API puts the device to sleep mode.
 */
int8_t bme280_sleep(const struct bme280_dev *dev)
{
    int8_t rslt;
    uint8_t reg_data[4];

    struct bme280_settings settings;


    rslt = bme280_get_regs(BME280_CTRL_HUM_ADDR, reg_data, 4, dev);
    if (rslt == BME280_OK) {
        parse_device_settings(reg_data, &settings);
        rslt = bme280_soft_reset(dev);
        if (rslt == BME280_OK)
            rslt = reload_device_settings(&settings, dev);
    }

    return rslt;
}


/*!
 * @brief This internal API puts the device to sleep mode.
 */
static int8_t put_device_to_sleep(struct bme280_dev *dev)
{
    int8_t rslt;
    uint8_t reg_data[4];
    struct bme280_settings settings;

    rslt = bme280_get_regs(BME280_CTRL_HUM_ADDR, reg_data, 4, dev);

    if (rslt == BME280_OK)
    {
        parse_device_settings(reg_data, &settings);
        rslt = bme280_soft_reset(dev);

        if (rslt == BME280_OK)
        {
            rslt = reload_device_settings(&settings, dev);
        }
    }

    return rslt;
}


/*
 * Compensation code taken from BME280 datasheet, Section 4.2.3
 * "Compensation formula".
 */
static void bme280_compensate_temp(struct bme280_data *data, int32_t adc_temp)
{
    int32_t var1, var2;

    var1 = (((adc_temp >> 3) - ((int32_t)data->dig_t1 << 1)) *
        ((int32_t)data->dig_t2)) >> 11;
    var2 = (((((adc_temp >> 4) - ((int32_t)data->dig_t1)) *
          ((adc_temp >> 4) - ((int32_t)data->dig_t1))) >> 12) *
        ((int32_t)data->dig_t3)) >> 14;

    data->t_fine = var1 + var2;
    data->comp_temp = (data->t_fine * 5 + 128) >> 8;
}

static void bme280_compensate_press(struct bme280_data *data, int32_t adc_press)
{
    int64_t var1, var2, p;

    var1 = ((int64_t)data->t_fine) - 128000;
    var2 = var1 * var1 * (int64_t)data->dig_p6;
    var2 = var2 + ((var1 * (int64_t)data->dig_p5) << 17);
    var2 = var2 + (((int64_t)data->dig_p4) << 35);
    var1 = ((var1 * var1 * (int64_t)data->dig_p3) >> 8) +
        ((var1 * (int64_t)data->dig_p2) << 12);
    var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)data->dig_p1) >> 33;

    /* Avoid exception caused by division by zero. */
    if (var1 == 0) {
        data->comp_press = 0;
        return;
    }

    p = 1048576 - adc_press;
    p = (((p << 31) - var2) * 3125) / var1;
    var1 = (((int64_t)data->dig_p9) * (p >> 13) * (p >> 13)) >> 25;
    var2 = (((int64_t)data->dig_p8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((int64_t)data->dig_p7) << 4);

    data->comp_press = (uint32_t)p;
}

static void bme280_compensate_humidity(struct bme280_data *data,
                       int32_t adc_humidity)
{
    int32_t h;

    h = (data->t_fine - ((int32_t)76800));
    h = ((((adc_humidity << 14) - (((int32_t)data->dig_h4) << 20) -
        (((int32_t)data->dig_h5) * h)) + ((int32_t)16384)) >> 15) *
        (((((((h * ((int32_t)data->dig_h6)) >> 10) * (((h *
        ((int32_t)data->dig_h3)) >> 11) + ((int32_t)32768))) >> 10) +
        ((int32_t)2097152)) * ((int32_t)data->dig_h2) + 8192) >> 14);
    h = (h - (((((h >> 15) * (h >> 15)) >> 7) *
        ((int32_t)data->dig_h1)) >> 4));
    h = (h > 419430400 ? 419430400 : h);

    data->comp_humidity = (uint32_t)(h >> 12);
}


float bme280_calcAltitude(float pressure)
{
    float A, B, C;

    A = pressure / 101325;
    B = 1 / 5.25588;
    C = pow(A, B);
    C = 1.0 - C;
    C = C / 0.0000225577;

    return C;
}


#endif





