#include <stdio.h>

#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>

//#define MAIN_LOOP
#define LOCK

#define BME280_CHANNEL 1
#define BME280_ADDRESS 0x76

uint32_t	hum_raw , temp_raw , pres_raw ;
int32_t		t_fine ;
 
uint16_t	dig_T1 ;
int16_t		dig_T2 ;
int16_t		dig_T3 ;

uint16_t	dig_P1 ;
int16_t		dig_P2 ;
int16_t		dig_P3 ;
int16_t		dig_P4 ;
int16_t		dig_P5 ;
int16_t		dig_P6 ;
int16_t		dig_P7 ;
int16_t		dig_P8 ;
int16_t		dig_P9 ;

int8_t		dig_H1 ;
int16_t		dig_H2 ;
int8_t		dig_H3 ;
int16_t		dig_H4 ;
int16_t		dig_H5 ;
int8_t		dig_H6 ;
  
static int dev ;
 
static int init_dev( void )
{
  char filename[ 32 ] ;
  sprintf( filename , "/dev/i2c-%d" , BME280_CHANNEL ) ;
  
  if ( (dev = open( filename , O_RDWR )) < 0 ) {
    fprintf( stderr , "Failed to open the i2c bus.\n" ) ;
    return 1 ;
  }
#ifdef LOCK
  if ( flock( dev , LOCK_EX ) < 0 ) {
    fprintf( stderr , "Failed to lock the i2c bus.\n" ) ;
    return 1 ;
  }
#endif
  if ( ioctl( dev , I2C_SLAVE , BME280_ADDRESS ) < 0 ) {
    fprintf( stderr , "Failed to acquire bus access.\n" ) ;
    dev = -1 ;
    return 1 ;
  }
  return 0 ;
}

void getRegisters( int8_t address , int numData , uint8_t *data ) {
  uint8_t	buf[1] ;
  int		i , adr ;
  
  for ( i = 0 , adr = address ; i < numData ; i++ , adr++ ) {
    buf[0] = adr ;
    if ( write( dev , buf , 1 ) == 1
	 && read( dev , buf , 1 ) == 1 ) {
      *data++ = buf[0] ;
    } else {
      fprintf( stderr , "Failed to write,read.\n" ) ;
      return ;
    }
  }
}

void readTrim()
{
  uint8_t	data[32] , i = 0 ;
  
  getRegisters( 0x88 , 24 , &data[0] ) ;
  i += 24 ;
  
  getRegisters( 0xA1 , 1 , &data[i] ) ;
  i += 1 ;
  
  getRegisters( 0xE1 , 7 , &data[i] ) ;
  i += 7 ;
  
  dig_T1 = (data[1] << 8) | data[0] ;
  dig_T2 = (data[3] << 8) | data[2] ;
  dig_T3 = (data[5] << 8) | data[4] ;
  
  dig_P1 = (data[7] << 8) | data[6] ;
  dig_P2 = (data[9] << 8) | data[8] ;
  dig_P3 = (data[11]<< 8) | data[10] ;
  dig_P4 = (data[13]<< 8) | data[12] ;
  dig_P5 = (data[15]<< 8) | data[14] ;
  dig_P6 = (data[17]<< 8) | data[16] ;
  dig_P7 = (data[19]<< 8) | data[18] ;
  dig_P8 = (data[21]<< 8) | data[20] ;
  dig_P9 = (data[23]<< 8) | data[22] ;
  
  dig_H1 = data[24] ;
  dig_H2 = (data[26]<< 8) | data[25] ;
  dig_H3 = data[27] ;
  dig_H4 = (data[28]<< 4) | (0x0F & data[29]) ;
  dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F) ;
  dig_H6 = data[31] ;   
}

void writeReg( uint8_t address , uint8_t data ) {
  uint8_t	buf[ 2 ]  ;
  
  buf[ 0 ] = address  ;
  buf[ 1 ] = data  ;
  if ( write( dev , buf , 2 ) != 2 ) {
    fprintf( stderr , "Error writeReg\n" ) ;
  }
}

void readData()
{
  int		i = 0 ;
  uint8_t	data[8] ;
  
  getRegisters( 0xF7 , 8 , &data[0] ) ;
  
  pres_raw = data[0] ;
  pres_raw = (pres_raw<<8) | data[1] ;
  pres_raw = (pres_raw<<4) | (data[2] >> 4) ;
  
  temp_raw = data[3] ;
  temp_raw = (temp_raw<<8) | data[4] ;
  temp_raw = (temp_raw<<4) | (data[5] >> 4) ;
  
  hum_raw  = data[6] ;
  hum_raw  = (hum_raw << 8) | data[7] ;
  // printf("TEMP :%x  DegC  PRESS :%x  hPa  HUM :%x \n",temp_raw,pres_raw,hum_raw) ;
}


int32_t calibration_T( int32_t adc_T )
{
  int32_t	var1 , var2 ;
  
  var1 = ((((adc_T >> 3) - ((int32_t)dig_T1<<1))) * ((int32_t)dig_T2)) >> 11 ;
  var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) * ((adc_T>>4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14 ;
  
  t_fine = var1 + var2 ;
  return (t_fine * 5 + 128) >> 8 ;
}

uint32_t calibration_P( int32_t adc_P )
{
  int32_t var1, var2 ;
  uint32_t P ;
  
  var1 = (((int32_t)t_fine)>>1) - (int32_t)64000 ;
  var2 = (((var1>>2) * (var1>>2)) >> 11) * ((int32_t)dig_P6) ;
  var2 = var2 + ((var1*((int32_t)dig_P5))<<1) ;
  var2 = (var2>>2)+(((int32_t)dig_P4)<<16) ;
  var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((int32_t)dig_P2) * var1)>>1))>>18 ;
  var1 = ((((32768+var1))*((int32_t)dig_P1))>>15) ;

  if (var1 == 0) {
    return 0 ;
  }    
  P = (((uint32_t)(((int32_t)1048576)-adc_P)-(var2>>12)))*3125 ;
  if(P<0x80000000) {
    P = (P << 1) / ((uint32_t) var1) ;   
  } else {
    P = (P / (uint32_t)var1) * 2 ;    
  }
  var1 = (((int32_t)dig_P9) * ((int32_t)(((P>>3) * (P>>3))>>13)))>>12 ;
  var2 = (((int32_t)(P>>2)) * ((int32_t)dig_P8))>>13 ;
  P = (uint32_t)((int32_t)P + ((var1 + var2 + dig_P7) >> 4)) ;
  return P ;
}

uint32_t calibration_H( int32_t adc_H )
{
  int32_t v_x1 ;
  
  v_x1 = (t_fine - ((int32_t)76800)) ;
  v_x1 = (((((adc_H << 14) -(((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1)) + 
	    ((int32_t)16384)) >> 15) * (((((((v_x1 * ((int32_t)dig_H6)) >> 10) * 
					    (((v_x1 * ((int32_t)dig_H3)) >> 11) + ((int32_t) 32768))) >> 10) + (( int32_t)2097152)) * 
					 ((int32_t) dig_H2) + 8192) >> 14)) ;
  v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4)) ;
  v_x1 = (v_x1 < 0 ? 0 : v_x1) ;
  v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1) ;
  return (uint32_t)(v_x1 >> 12) ;   
}

int main( int argc , char **argv )
{
  uint8_t	osrs_t = 1 ;             //Temperature oversampling x 1
  uint8_t	osrs_p = 1 ;             //Pressure oversampling x 1
  uint8_t	osrs_h = 1 ;             //Humidity oversampling x 1
  uint8_t	mode   = 3 ;             //Normal mode
  uint8_t	t_sb   = 5 ;             //Tstandby 1000ms
  uint8_t	filter = 0 ;             //Filter off 
  uint8_t	spi3w_en = 0 ;           //3-wire SPI Disable
  
  uint8_t	ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode ;
  uint8_t	config_reg    = (t_sb << 5) | (filter << 2) | spi3w_en ;
  uint8_t	ctrl_hum_reg  = osrs_h ;
  
  
  double	temp_act = 0.0, press_act = 0.0,hum_act=0.0 ;
  int32_t	temp_cal ;
  uint32_t	press_cal , hum_cal ;
  
  if( init_dev() == 1 )  return 1  ; 
  
  writeReg( 0xF2 , ctrl_hum_reg ) ;
  writeReg( 0xF4 , ctrl_meas_reg ) ;
  writeReg( 0xF5 , config_reg ) ;
  readTrim() ;                    //

#if defined( MAIN_LOOP )
  while(1) {
    delay(1000) ;
#endif
    readData() ;
      
    temp_cal = calibration_T(temp_raw) ;
    press_cal = calibration_P(pres_raw) ;
    hum_cal = calibration_H(hum_raw) ;

    temp_act = (double)temp_cal / 100.0 ;
    press_act = (double)press_cal / 100.0 ;
    hum_act = (double)hum_cal / 1024.0 ;
#if 1
    printf( "%5.2f" , temp_act )  ;
    printf( " %5.2f" , hum_act )  ;
    printf( " %7.2f\n" , press_act )  ;
#else
    printf("TEMP :%5.2f DegC  PRESS :%7.2f hPa  HUM :%5.2f \n",temp_act,press_act,hum_act) ;
#endif
#if defined( MAIN_LOOP )
    }
#endif
#ifdef LOCK
  flock( dev , LOCK_UN )  ;
  close( dev )  ;
#endif
  return 0  ;
}
