/*
	BMP280.cpp
	
	Bosch BMP280 pressure sensor library for the Arduino microcontroller.
	This library uses I2C connection.

	Uses floating-point equations from BMP280 datasheet.

	modified by mhafuzul islam

	version 1.01		 16/9/2014 initial version
	
	Our example code uses the "pizza-eating" license. You can do anything
	you like with this code. No really, anything. If you find it useful,
	buy me italian pizza someday.
*/

#include "BMP280.h"
#include <Wire.h>
#include <stdio.h>
#include <math.h>


BMP280::BMP280()
{
	//do nothing
}
/*
*	Initialize library and coefficient for measurements
*/
char BMP280::begin(int sdaPin, int sclPin)
{
	Wire.begin(sdaPin,sclPin);
	return (readCalibration());
}

char BMP280::begin() 
{
	
	// Start up the Arduino's "wire" (I2C) library:
	Wire.begin();
	return (readCalibration());
}

// The BMP280 includes factory calibration data stored on the device.
// Each device has different numbers, these must be retrieved and
// used in the calculations when taking measurements.

// Retrieve calibration data from device:
char BMP280::readCalibration() {
	
	if (    
		readUInt(0x88, dig_T1) &&
		readInt(0x8A, dig_T2)  &&
		readInt(0x8C, dig_T3)  &&
		readUInt(0x8E, dig_P1) &&
		readInt(0x90, dig_P2)  &&
		readInt(0x92, dig_P3)  &&
		readInt(0x94, dig_P4)  &&
		readInt(0x96, dig_P5)  &&
		readInt(0x98, dig_P6)  &&
		readInt(0x9A, dig_P7)  &&
		readInt(0x9C, dig_P8)  &&
		readInt(0x9E, dig_P9)){
#ifdef _debugSerial
		Serial.print("dig_T1="); Serial.println(dig_T1,2);
		Serial.print("dig_T2="); Serial.println(dig_T2,2);
		Serial.print("dig_T3="); Serial.println(dig_T3,2);
		Serial.print("dig_P1="); Serial.println(dig_P1,2);
		Serial.print("dig_P2="); Serial.println(dig_P2,2);
		Serial.print("dig_P3="); Serial.println(dig_P3,2);
		Serial.print("dig_P4="); Serial.println(dig_P4,2);
		Serial.print("dig_P5="); Serial.println(dig_P5,2);
		Serial.print("dig_P6="); Serial.println(dig_P6,2);
		Serial.print("dig_P7="); Serial.println(dig_P7,2);
		Serial.print("dig_P8="); Serial.println(dig_P8,2);
		Serial.print("dig_P9="); Serial.println(dig_P9,2);
#endif
#ifdef _debugTestData
		dig_T1 = 27504.0;
		dig_T2 = 26435.0;
		dig_T3 = -1000.0;
		dig_P1 = 36477.0;
		dig_P2 = -10685.0;
		dig_P3 = 3024.0;
		dig_P4 = 2855.0;
		dig_P5 = 140.0;
		dig_P6 = -7.0;
		dig_P7 = 15500.0;
		dig_P8 = -14600.0;
		dig_P9 = 6000.0;
		Serial.print("dig_T1="); Serial.println(dig_T1,2);
		Serial.print("dig_T2="); Serial.println(dig_T2,2);
		Serial.print("dig_T3="); Serial.println(dig_T3,2);
		Serial.print("dig_P1="); Serial.println(dig_P1,2);
		Serial.print("dig_P2="); Serial.println(dig_P2,2);
		Serial.print("dig_P3="); Serial.println(dig_P3,2);
		Serial.print("dig_P4="); Serial.println(dig_P4,2);
		Serial.print("dig_P5="); Serial.println(dig_P5,2);
		Serial.print("dig_P6="); Serial.println(dig_P6,2);
		Serial.print("dig_P7="); Serial.println(dig_P7,2);
		Serial.print("dig_P8="); Serial.println(dig_P8,2);
		Serial.print("dig_P9="); Serial.println(dig_P9,2);
#endif
		return (1);
	}
	else 
		return (0);
}

/*
**	Read a signed integer (two bytes) from device
**	@param : address = register to start reading (plus subsequent register)
**	@param : value   = external variable to store data (function modifies value)
*/
char BMP280::readInt(char address, double &value)

{
	unsigned char data[2];	//char is 4bit,1byte

	data[0] = address;
	if (readBytes(data,2))
	{
		value = (double)(int16_t)(((unsigned int)data[1]<<8)|(unsigned int)data[0]); //
		return(1);
	}
	value = 0;
	return(0);
}
/* 
**	Read an unsigned integer (two bytes) from device
**	@param : address = register to start reading (plus subsequent register)
**	@param : value 	 = external variable to store data (function modifies value)
*/

char BMP280::readUInt(char address, double &value)
{
	unsigned char data[2];	//4bit
	data[0] = address;
	if (readBytes(data,2))
	{
		value = (double)(unsigned int)(((unsigned int)data[1]<<8)|(unsigned int)data[0]);
		return(1);
	}
	value = 0;
	return(0);
}
/*
** Read an array of bytes from device
** @param : value  = external array to hold data. Put starting register in values[0].
** @param : length = number of bytes to read
*/

char BMP280::readBytes(unsigned char *values, char length)
{
	char x;

	Wire.beginTransmission(BMP280_ADDR);
	Wire.write(values[0]);
	error = Wire.endTransmission();
	if (error == 0)
	{
		Wire.requestFrom(BMP280_ADDR,length);
		while(Wire.available() != length) ; // wait until bytes are ready
		for(x=0;x<length;x++)
		{
			values[x] = Wire.read();
		}
		return(1);
	}
	return(0);
}
/*
** Write an array of bytes to device
** @param : values = external array of data to write. Put starting register in values[0].
** @param : length = number of bytes to write
*/
char BMP280::writeBytes(unsigned char *values, char length)
{
	Wire.beginTransmission(BMP280_ADDR);
	Wire.write(values,length);
	error = Wire.endTransmission();
	if (error == 0)
		return(1);
	else
		return(0);
}

short BMP280::getOversampling(void)
{
	return oversampling;
}

char BMP280::setOversampling(short oss)
{
	oversampling = oss;
	return (1);
}
/*
**	Begin a measurement cycle.
** Oversampling: 0 to 4, higher numbers are slower, higher-res outputs.
** @returns : delay in ms to wait, or 0 if I2C error.
*/
char BMP280::startMeasurment(void)

{
	unsigned char data[2], result, delay;
	
	data[0] = BMP280_REG_CONTROL;

	switch (oversampling)
	{
		case 0:
			data[1] = BMP280_COMMAND_PRESSURE0;     
			oversampling_t = 1;
			delay = 8;			
		break;
		case 1:
			data[1] = BMP280_COMMAND_PRESSURE1;     
			oversampling_t = 1;
			delay = 10;			
		break;
		case 2:
			data[1] = BMP280_COMMAND_PRESSURE2;		
			oversampling_t = 1;
			delay = 15;
		break;
		case 3:
			data[1] = BMP280_COMMAND_PRESSURE3;
			oversampling_t = 1;
			delay = 24;
		break;
		case 4:
			data[1] = BMP280_COMMAND_PRESSURE4;
			oversampling_t = 1;
			delay = 45;
		break;
		case 16:
			data[1] = BMP280_COMMAND_OVERSAMPLING_MAX;
			oversampling_t = 1;
			delay = 80;	//I cannot find any data about timings in datasheet for x16 pressure and x16 temeprature oversampling
		break;				//I guess this is enough time (maybe it can be even smaller ~60ms)
		default:
			data[1] = BMP280_COMMAND_PRESSURE0;
			delay = 9;
		break;
	}
	result = writeBytes(data, 2);
	if (result) // good write?
		return(delay); // return the delay in ms (rounded up) to wait before retrieving data
	else
		return(0); // or return 0 if there was a problem communicating with the BMP
}

/*
**	Get the uncalibrated pressure and temperature value.
**  @param : uP = stores the uncalibrated pressure value.(20bit)
**  @param : uT = stores the uncalibrated temperature value.(20bit)
*/
char BMP280::getUnPT(double &uP, double &uT)
{
	unsigned char data[6];
	char result;
	
	data[0] = BMP280_REG_RESULT_PRESSURE; //0xF7 

	result = readBytes(data, 6); // 0xF7; xF8, 0xF9, 0xFA, 0xFB, 0xFC
	if (result) // good read
	{
		double factor = pow(2, 4);
		uP = (double)(data[0] *4096 + data[1]*16 + data[2]/16) ;	//20bit UP
		uT = (double)(data[3]*4096 + data[4]*16 + data[5]/16) ;	//20bit UT
#ifdef _debugSerial
		Serial.print(uT);
		Serial.print(" ");
		Serial.println(uP); 
#endif
#ifdef _debugTestData
		uT = 519888.0;
		uP = 415148.0;
		Serial.print(uT);
		Serial.print(" ");
		Serial.println(uP); 
#endif
	}
	return(result);
}
/*
** Retrieve temperature and pressure.
** @param : T = stores the temperature value in degC.
** @param : P = stores the pressure value in mBar.
*/
char BMP280::getTemperatureAndPressure(double &T,double &P)
{
	double uT ;
	double uP;
	char result = getUnPT(uP,uT);
	if(result!=0){
		// calculate the temperature
		result = calcTemperature(T,uT);
		if(result){
			// calculate the pressure
			result = calcPressure(P,uP);
			if(result)return (1);
			else error = 3 ;	// pressure error ;
			return (9);
		}else 
			error = 2;	// temperature error ;
	}
	else 
		error = 1;
	
	return (9);
}
/*
** temperature calculation
** @param : T  = stores the temperature value after calculation.
** @param : uT = the uncalibrated temperature value.
*/
char BMP280::calcTemperature(double &T, double &adc_T)
//
{
	//Serial.print("adc_T = "); Serial.println(adc_T,DEC);
		
	double var1 = (adc_T/16384.0 - dig_T1/1024.0)*dig_T2;
	double var2 = ((adc_T/131072.0 - dig_T1/8192.0)*(adc_T/131072.0 - dig_T1/8192.0))*dig_T3;
	t_fine = var1+var2;
	T = (var1+var2)/5120.0;
#ifdef _debugSerial
	Serial.print(var1);
	Serial.print(" ");
	Serial.print(var2);
	Serial.print(" ");
	Serial.print(t_fine);
	Serial.print(" ");
	Serial.println(T);
#endif
	
	if(T>100 || T <-100)return 0;
	
	return (1);
}
/*
**	Pressure calculation from uncalibrated pressure value.
**  @param : P  = stores the pressure value.
**  @param : uP = uncalibrated pressure value. 
*/
char BMP280::calcPressure(double &P,double uP)
{
	//char result;
	double var1 , var2 ;
	
	var1 = (t_fine/2.0) - 64000.0;
#ifdef _debugSerial
	Serial.print("var1 = ");Serial.println(var1,2);
#endif
	var2 = var1 * (var1 * dig_P6/32768.0);	//not overflow
#ifdef _debugSerial
	Serial.print("var2 = ");Serial.println(var2,2);
#endif
	var2 = var2 + (var1 * dig_P5 * 2.0);	//overflow
#ifdef _debugSerial
	Serial.print("var2 = ");Serial.println(var2,2);
#endif
		
	var2 = (var2/4.0)+((dig_P4)*65536.0);
#ifdef _debugSerial
	Serial.print("var2 = ");Serial.println(var2,2);
#endif
		
	var1 = (dig_P3 * var1 * var1/524288.0 + dig_P2 * var1) / 524288.0;
#ifdef _debugSerial
	Serial.print("var1 = ");Serial.println(var1,2);
#endif
	var1 = (1.0 + var1/32768.0) * dig_P1;
#ifdef _debugSerial
	Serial.print("var1 = ");Serial.println(var1,2);
#endif
		
	P = 1048576.0- uP;
#ifdef _debugSerial
	Serial.print("p = ");Serial.println(p,2);
#endif
		
	P = (P-(var2/4096.0))*6250.0/var1 ;	//overflow
#ifdef _debugSerial
	Serial.print("p = ");Serial.println(p,2);	
#endif
		
	var1 = dig_P9*P*P/2147483648.0;	//overflow
#ifdef _debugSerial
	Serial.print("var1 = ");Serial.println(var1,2);
#endif

	var2 = P*dig_P8/32768.0;
#ifdef _debugSerial
	Serial.print("var2 = ");Serial.println(var2,2);
#endif
	P = P + (var1+var2+dig_P7)/16.0;
#ifdef _debugSerial
	Serial.print("p = ");Serial.println(p,2);
#endif
		
	P = P/100.0 ;
	
	if(P>1200.0 || P < 800.0)return (0);
	return (1);
}




double BMP280::sealevel(double P, double A)
// Given a pressure P (mb) taken at a specific altitude (meters),
// return the equivalent pressure (mb) at sea level.
// This produces pressure readings that can be used for weather measurements.
{
	return(P/pow(1-(A/44330.0),5.255));
}


double BMP280::altitude(double P, double P0)
// Given a pressure measurement P (mb) and the pressure at a baseline P0 (mb),
// return altitude (meters) above baseline.
{
	return(44330.0*(1-pow(P/P0,1/5.255)));
}


char BMP280::getError(void)
	// If any library command fails, you can retrieve an extended
	// error code using this command. Errors are from the wire library: 
	// 0 = Success
	// 1 = Data too long to fit in transmit buffer
	// 2 = Received NACK on transmit of address
	// 3 = Received NACK on transmit of data
	// 4 = Other error
{
	return(error);
}

