Lib for HP03SA atmospheric pressure sensor. Provides pressure, temperature and altitude data.
Diff: HP03SA.cpp
- Revision:
- 0:61bbd81782de
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HP03SA.cpp Sat Jan 10 19:10:17 2015 +0000 @@ -0,0 +1,321 @@ +/* mbed HP03SA Library, for an I2C Pressure and Temperature sensor which provides derived Altitude values + * Copyright (c) 2015, v01: WH, Initial version, ported in part from Elektor weatherstation (Sept 2011), Author: W. Waetzig + * See http://www.elektor-labs.com/project/usb-long-term-weather-logger-100888.12037.html + * Code also based on several other public sources: + * See http://en.wikipedia.org/wiki/Atmospheric_pressure + * See https://learn.sparkfun.com/tutorials/bmp180-barometric-pressure-sensor-hookup-?_ga=1.94307604.888266135.1310146152 + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mbed.h" +#include "HP03SA.h" + +/** Create an HP03SA interface for mbed pins + * + * @param i2c I2C Bus + * @param XCLR Clock enable control line + */ +HP03SA::HP03SA(I2C *i2c, PinName XCLR) : _i2c(i2c), _xclr(XCLR) { + + _init(); +} + +/** Init the HP03SA + * + */ +void HP03SA::_init() { + char buffer[18]; + + //HP03S initialization + + _xclr=0; //XCLR low for coefficients readout + + // Select C1 register + buffer[0] = 0x10; // Register address for _C1 MSB is 0x10 + _i2c->write(HP03SA_EEPROM_SA, buffer, 1, true); // Repeated Start + // Read calibration parameters + _i2c->read(HP03SA_EEPROM_SA, buffer, 18); + + _C1 = (buffer[0] << 8) | buffer[1]; // _C1 MSB, LSB + _C2 = (buffer[2] << 8) | buffer[3]; // _C2 MSB, LSB + _C3 = (buffer[4] << 8) | buffer[5]; // _C3 MSB, LSB + _C4 = (buffer[6] << 8) | buffer[7]; // _C4 MSB, LSB + _C5 = (buffer[8] << 8) | buffer[9]; // _C5 MSB, LSB + _C6 = (buffer[10] << 8) | buffer[11]; // _C6 MSB, LSB + _C7 = (buffer[12] << 8) | buffer[13]; // _C7 MSB, LSB + _A = buffer[14]; // _A + _B = buffer[15]; // _B + _C = buffer[16]; // _C + _D = buffer[17]; // _D + + _pres_base_x10 = PBASE_X10; + _temp_base_x10 = TBASE_X10; + + // Execute Dummy reads after init() + sample(); + sample(); +} + +/** Read ADC value (either pressure or temperature depending on Control Register setting) + * + * @return int pressure or temperature + */ +int HP03SA::_readADC (void) { + char buffer[2]; + + // Select ADC register + buffer[0] = 0xFD; // Register address for ADC + _i2c->write(HP03SA_BARO_SA, buffer, 1, true); // Repeated Start + // Read ADC + _i2c->read(HP03SA_BARO_SA, buffer, 2); + + return ((buffer[0] << 8) | buffer[1]); // MSB, LSB +} + +/** Read Pressure and Temperature + * + * @return none + */ +void HP03SA::_readTempPres(void) { + char buffer[2]; + + _xclr=1; // ADC-XCLR + + // Read ADC with pressure value + buffer[0] = 0xFF; // Control Register address + buffer[1] = 0xF0; // function = select + start-conversion PRES + _i2c->write(HP03SA_BARO_SA, buffer, 2); + wait_ms(40); // delay for ADC-conversion + _D1 = _readADC(); + + // Read ADC with temperature value + buffer[0] = 0xFF; // Control Register address + buffer[1] = 0xE8; // function = select + start-conversion TEMP + _i2c->write(HP03SA_BARO_SA, buffer, 2); + wait_ms(40); // delay for ADC-conversion + _D2 = _readADC(); + + _xclr=0; // ADC-XCLR +} + +/** Calculate Pressure and Temperature + * + * @return none + */ +void HP03SA::_calcTempPres(void) { + long int _D2_C5; + int _corr; + + // Temperature and Pressure calculations from HP03S datasheet version 1.3 + // with 2nd-order corrections using the parameters _A, _B, _C, _D + // Measured values are: _D1(pressure) and _D2(temperature) + // Correct offset-calculation (_off) + + _D2_C5 = _D2 - _C5; + if (_D2_C5 >= 0) + _corr = _A; // _A + else + _corr = _B; // _B + + _dUT = _D2_C5 - ((_D2_C5 >> 7) * (_D2_C5 >> 7) * (_corr >> _C)); // _C + +// _off = ( (_C4 - 1024) * _dUT / 16384 + _C2 ) * 4; +// _off = ((( (_C4 - 1024) * _dUT ) >> 14 ) + _C2 ) << 2; + _off = ((( (_C4 - 1024) * _dUT ) >> 14 ) + _C2 ) << 2; + +// _sens = _C3 * _dUT / 1024 + _C1 ; + _sens = (( _C3 * _dUT ) >> 10 ) + _C1 ; + +// _x = (_D1 - 7168) * _sens / 16384 - _off; + _x = (((_D1 - 7168) * _sens ) >> 14 ) - _off; + +// _pres = x * 10 / 32 + _C7; + _pres_x10 = (( _x * 10 ) >> 5 ) + _C7; // Pressure in hPa x 10 + +// _temp = _dUT * _C6 / 65536 + 250; + _temp_x10 = (( _dUT * _C6 ) >> 16 ) - (_dUT >> _D) + 250; // _D, Temp in Celsius x 10 +} + + +/** Calculate Altitude + * + * @return none + */ +void HP03SA::_calcAlt(void) { +// Calculation based on ISA model +// http://en.wikipedia.org/wiki/Atmospheric_pressure +// +// h = (1 - pow((P/Pref), RL/gM)) * Tref / L +// +// h altitude above Mean Sea Level in m +// P atmospheric pressure at altitude h +// Pref sea level standard atmospheric pressure 101325 Pa +// L temperature lapse rate, = g/cp for dry air 0.0065 K/m +// Tref sea level standard temperature 288.15 K (= 15.0 + 273.15) +// g Earth-surface gravitational acceleration 9.80665 m/s2 +// M molar mass of dry air 0.0289644 kg/mol +// R universal gas constant 8.31447 J/(mol•K) +// RL/gM 0.19026674 +// gM/RL 5.25578009f + +//ISA calculation +// _alt = 44330.76923f * (1.0f - pow( ((float) _pres_x10 / _pres_base_x10), RLGM)); + _alt = (TBASE_X10 / LBASE_X10) * (1.0f - pow( ((float) _pres_x10 / _pres_base_x10), RLGM)); + +//Correction for temperature +// _temp_base_x10 = TBASE_X10 - ( (float) _alt * LBASE_X10 ); +// _temp_base_x10 = TBASE_X10; + + _alt = (float) _alt * ((float)_temp_x10 + 2731.5f) / _temp_base_x10; +} + +/** Get Pressure and Temperature sample + * + * @return none + */ +void HP03SA::sample(void) { + _readTempPres(); + _calcTempPres(); + _calcAlt(); +} + +/** Get Absolute Atmospheric Pressure in hPa x 10 + * Note: sample() must be called before getAtmPressure() + * Checked using http://luchtdruk.com/luchtdruk-in-nederland.html + * + * @return int Pressure in hPa x 10 + */ +int HP03SA::getAbsPressure(void) { + return _pres_x10; +} + +/** Get Sealevel Atmospheric Pressure in hPa x 10 + * This produces pressure readings that can be used for weather measurements. + * Note: sample() must be called before getSeaPressure() + * + * @param int alt_meter Altitude above Mean Sea Level where measurement is taken + * @return int Pressure at sea level in hPa x 10 + */ +int HP03SA::getSeaPressure(int alt_meter) { +// return ( (float) _pres_x10 / pow (1.0f - ((float) alt_meter / 44330.0), 5.255) ); + return ( (float) _pres_x10 / pow (1.0f - ((float) alt_meter * LBASE_X10 / TBASE_X10), GMRL) ); +} + +/** Get Temperature as int in °Celsius x 10 + * Note: sample() must be called before getTemperature() + * + * @return int Temperature in °Celsius x 10 + */ +int HP03SA::getTemperatureInt(void) { + return _temp_x10; +} + +/** Get Temperature as float in °Celsius + * Note: sample() must be called before getTemperature() + * + * @return float Temperature in °Celsius + */ +float HP03SA::getTemperature(void) { + return (float) _temp_x10 / 10.0f; +} + + +/** Convert Temperature from °Celsius into °Fahrenheit + * + * @param float celsius in °Celsius + * @return float temperature in °Fahrenheit + */ +float HP03SA::celsiusToFahrenheit(float celsius) { + + return ((celsius * 9.0f) / 5.0f) + 32.0f; // Convert to Fahrenheit +} + + +/** Get Altitude in meters using ISA + * Note: sample() must be called before getAltitude() + * + * @return int Altitude in meters above Mean Sea Level (MSL) + */ +int HP03SA::getAltitude(void) { + return _alt; +} + +/** Get Altitude in feet using ISA + * Note: sample() must be called before getAltitudeFt() + * + * @return int Altitude in feet above Mean Sea Level (MSL) + */ +int HP03SA::getAltitudeFt() { + return (float)_alt * 3.28084f; +} + +/** Get Status + * + * @return bool Sensor ready + */ +bool HP03SA::getStatus(void) { + char buffer[2]; + int status; + + _xclr=1; // ADC-XCLR + + // Read ADC with pressure value (dummy operation to check status) + buffer[0] = 0xFF; // Control Register address + status=_i2c->write(HP03SA_BARO_SA, buffer, 1); + + _xclr=0; // ADC-XCLR + + return (status==0); // True when device found +} + +/** Set QNH Pressure to calibrate sensor + * + * @param int pressure_x10 at Mean Sea Level in hPa x 10 + * The getAltitude() reading will be 0 m for the set pressure. + */ +void HP03SA::setPressure(int pressure_x10) { + _pres_base_x10 = (float) pressure_x10; +} + +/** Set QNH Altitude in meter to calibrate sensor + * + * @param int alt_meter Altitude in meters above Mean Sea Level (MSL) for current pressure + * The getAltitude() reading will be 'alt_meter' m for the current pressure. + */ +void HP03SA::setAltitude(int alt_meter) { + +// Compute _temp_base derived from the temp at set altitude and temp lapse +// _temp_base_x10 = ((float) _temp_x10 + 2731.5f) + ((float) alt * LBASE_X10); + +// Compute pressure at Mean Sea Level from the pressure at the set altitude + _pres_base_x10 = (float) _pres_x10 * pow( TBASE_X10 / (TBASE_X10 - ((float) alt_meter * LBASE_X10)), GMRL); +} + +/** Set QNH Altitude in feet to calibrate sensor + * + * @param int alt_feet Altitude in meters above Mean Sea Level (MSL) for current pressure + * The getAltitudeFt() reading will be 'alt_feet' ft for the current pressure. + */ +void HP03SA::setAltitudeFt(int alt_feet){ + setAltitude(alt_feet / 3.28084f); +} + +