/*
HP03.cpp - Arduino library support for HP03 Pressure sensor
Copyright (c)2008 Michael Margolis All right reserved
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Version: 1.0 - July14 2008
*/
// Note that this code uses the return values in Arduino version 0012 wire.c library
extern "C" {
#include <avr/pgmspace.h>
#include <wiring.h>
#include <FrequencyTimer2.h>
#include <Wire.h>
}
#include "HP03.h"
#if !defined(NULL)
#define NULL 0
#endif
// #define PRINT_CALIBRATION_DATA //undefine these to print low level data for debug purposes
// I2C defines
#define MCLK_pin 16 // Arduino uses analog pins 4 and 5 for I2C
#define XCLR_pin 17
#define HP03_I2C_SENSOR_ADDRESS 0x77
#define HP03_I2C_CALIBRATION_ADDRESS 0x50
#define HP03_MEASURE_COMMAND 0xff
#define HP03_RESULT_COMMAND 0xfd
#define HP03_PRESSURE_SUBTYPE 0xf0
#define HP03_TEMPERATURE_SUBTYPE 0xe8
#define HP03_RESULT_SUBTYPE 0xef
// macro to convert two bytes in an arrya to an int
#define GET_BIGENDIAN_WORD(x, y) ((((unsigned int)x[y]) << 8) | x[y+1])
// Pressure to altitude conversion defines
#define MAX_PRESSURE 1100L // in Hpa
#define PRESSURE_ARRAY_ELEMS 80 // number of elements in pressure array
#define PRESSURE_INC 10L // 10Hpa between array values in altitude table
#define HP03_NUM_COEFF 7
#define HP03_NUM_PARAMS 4
enum coefficients { C1,C2,C3,C4,C5,C6,C7};
enum params { AA,BB,CC,DD};
unsigned int coeff[HP03_NUM_COEFF]; // these are marked C1 through C7 in the datasheet
byte sensorParams[HP03_NUM_PARAMS]; // these are marked a,b,c,d in the datasheet
long int pow2(byte value){ // return value raised to the power of 2
long uiData = 2;
while(--value){
uiData <<= 1;
}
return uiData;
}
//********* I2C functions ******************
int HP03class::i2cTransfer(unsigned char Address, const unsigned char *TxBuf, int TxBytes, unsigned char *RxBuf, int RxBytes){
// send bytes to the given address and receive bytes from that address
int ret = WIRE_OK;
if(TxBytes > 0){ // send data if TxBytes greater than 0
if(TxBytes > BUFFER_LENGTH)
ret = WIRE_ERR_NO_BUFFER_SPACE ;
else{
Wire.beginTransmission(Address); // transmit to device
while(TxBytes--){
Wire.send(*TxBuf++);
}
ret = Wire.endTransmission(); // stop transmitting
}
}
if (RxBytes > 0 && ret == WIRE_OK){ // receive given number of byts if send ok
Wire.requestFrom(Address, (uint8_t)RxBytes);
while(Wire.available() && RxBytes--)
{
char c = Wire.receive();
*RxBuf++ = c;
}
}
return ret;
}
int HP03class::i2cSendCommand(unsigned char Address, unsigned char Command){
Wire.beginTransmission(Address); // transmit to device
Wire.send(Command);
return Wire.endTransmission(); // stop transmitting
}
int HP03class::i2cMeasure(unsigned char SubType){
// Start a measurement on the HP03, returns WIRE_OK on success else WIRE error code
unsigned char TxBuf[2];
TxBuf[0] = HP03_MEASURE_COMMAND;
TxBuf[1] = SubType;
return i2cTransfer(HP03_I2C_SENSOR_ADDRESS, TxBuf, sizeof(TxBuf), NULL, 0);
}
int HP03class::i2cResult(unsigned int *Result){
// Get a measurement result from the HP03, returns WIRE_OK on success else WIRE error code
unsigned char TxBuf[2];
unsigned char RxBuf[2];
int Status;
TxBuf[0] = HP03_RESULT_COMMAND;
TxBuf[1] = HP03_RESULT_SUBTYPE;
Status = i2cTransfer(HP03_I2C_SENSOR_ADDRESS, TxBuf, sizeof(TxBuf), RxBuf, sizeof(RxBuf));
*Result = (RxBuf[0] << 8) | RxBuf[1];
return Status;
}
HP03class::HP03class(){
}
boolean HP03class::begin( void ){
// set control pins for output
pinMode(MCLK_pin, OUTPUT);
pinMode(XCLR_pin, OUTPUT);
digitalWrite(XCLR_pin,LOW) ;
/* set up TIMER2 to 32768Hz with 50/50 mark/space ration with output on */
pinMode(11,OUTPUT); // frequencyTimer2 is hard coded to output on this pin
FrequencyTimer2::setPeriod(31); // 31us = 32258hz
FrequencyTimer2::enable();
Wire.begin();
return getCalibrationData(); // returns true if ok, if false then probably sensor not detected
}
boolean HP03class::getCalibrationData(){
// read the calibration values from the sensor, return true if ok, false if error
unsigned char CalData[18];
boolean ret;
ret = (i2cSendCommand(HP03_I2C_CALIBRATION_ADDRESS, 16) == WIRE_OK);
if (ret == true){
if(i2cTransfer(HP03_I2C_CALIBRATION_ADDRESS, NULL, 0, CalData, sizeof(CalData)) == WIRE_OK) {
for (int i = 0; i < HP03_NUM_COEFF; i++)
coeff[i] = GET_BIGENDIAN_WORD(CalData, i*2);
for (int i = 0; i < HP03_NUM_PARAMS; i++)
sensorParams[i] = CalData[( HP03_NUM_COEFF * sizeof(int)) + i];
ret = true;
}
}
#ifdef PRINT_CALIBRATION_DATA
printCalibrationData();
#endif
return ret;
}
#ifdef PRINT_CALIBRATION_DATA
void HP03class::printCalibrationData(){
int Count;
for (Count = 0; Count < HP03_NUM_COEFF; Count++) {
Serial.print("Coeff C");
Serial.print((char)('1' + Count));
Serial.print(" = ");
Serial.println((unsigned long)coeff[Count],DEC);
}
for (Count = 0; Count < HP03_NUM_PARAMS; Count++) {
Serial.print("Param");
Serial.print((char)('A' + Count));
Serial.print(" = ");
Serial.println( (unsigned)sensorParams[Count],DEC);
}
Serial.println();
}
#endif
boolean HP03class::update(){
if(readRawData()){
calculatePressureAndTemperature();
return true;
}
return false;
}
boolean HP03class::readRawData(){
digitalWrite(XCLR_pin,HIGH);
// send temperature measurement command
if ( i2cMeasure(HP03_TEMPERATURE_SUBTYPE) != WIRE_OK) {
digitalWrite(XCLR_pin,LOW);
return false;
}
delay(40);
if( i2cResult(&rawTemperature) != WIRE_OK){
digitalWrite(XCLR_pin,LOW);
return false;
}
if (i2cMeasure(HP03_PRESSURE_SUBTYPE) != WIRE_OK) {
digitalWrite(XCLR_pin,LOW);
return false;
}
delay(40);
if( i2cResult(&rawPressure) != WIRE_OK){
digitalWrite(XCLR_pin,LOW);
return false;
}
return true;
}
void HP03class::calculatePressureAndTemperature(){
double MiddleData1;
double MiddleData2;
double MiddleData3;
double MiddleData4;
double dUT;
double SENS;
double OFF;
double X;
MiddleData1 = (long)rawTemperature - coeff[C5];
MiddleData2 = MiddleData1 * MiddleData1/16384;
if(rawTemperature < coeff[C5])
MiddleData3 = MiddleData2 * sensorParams[BB];
else
MiddleData3 = MiddleData2 * sensorParams[AA];
MiddleData4 = pow2(sensorParams[CC]);
MiddleData4 = MiddleData3 / MiddleData4;
dUT = MiddleData1 - MiddleData4;
//OFF = (C2+(C4-1024)*DUT/Get2_x(14))*4;
MiddleData1 = (long)coeff[C4] - 1024L;
MiddleData2 = pow2(14);
MiddleData3 = dUT * MiddleData1;
MiddleData4 = MiddleData3 / MiddleData2;
MiddleData4 = (long)coeff[C2] + MiddleData4;
OFF = MiddleData4 * 4;
//SENS = C1+C3*DUT/Get2
MiddleData1 = (long)coeff[C3] * dUT;
MiddleData2 = pow2(10);
MiddleData3 = MiddleData1 / MiddleData2;
SENS = coeff[C1] + MiddleData3;
//X = SENS*(D1-7168)/ Get2_x(14)-OFF;
MiddleData1 = pow2(14);
MiddleData2 = (long)rawPressure - 7168;
MiddleData3 = MiddleData2 * SENS;
MiddleData4 = MiddleData3 / MiddleData1;
X = MiddleData4 - OFF;
//Pressure = X*100/Get2_x(5)+C7*10;
MiddleData1 = X * 100;
MiddleData2 = pow2(5);
MiddleData3 = MiddleData1 / MiddleData2;
MiddleData4 = coeff[C7] * 10;
Pressure = MiddleData3 + MiddleData4;
Temperature = 250 + dUT * coeff[C6] / pow2(16) - dUT / pow2(sensorParams[DD]);
}
static prog_int32_t BasicAltitude[PRESSURE_ARRAY_ELEMS] PROGMEM = {
-6983,-6201,-5413,-4620,-3820,-3015,-2203,-1385,-560, 270, //0.1m
1108, 1953, 2805, 3664, 4224, 5403, 6284, 7172, 8068, 8972,
9885, 10805,11734,12671,13617,14572,15537,16510,17494,18486,
19489,20502,21526,22560,23605,24662,25730,26809,27901,29005,
30121,31251,32394,33551,34721,35906,37106,38322,39553,40800,
42060,43345,44644,45961,47296,48652,50027,51423,52841,54281,
55744,57231,58742,60280,61844,63436,65056,66707,68390,70105,
71854,73639,75461,77323,79226,81172,83164,85204,87294,89438};
long altitudeValue_P(int address){
// return the value of the altitude table
return pgm_read_dword(&BasicAltitude[address] );
}
long HP03class::getAltitude(long pressure){ //pressure is 100 times what it should be
int index;
long AltitudeDifference,PressureDifference;
long fraction;
long alt;
index = (MAX_PRESSURE - pressure/100) / PRESSURE_INC + 1;
if (index >= PRESSURE_ARRAY_ELEMS)
index = PRESSURE_ARRAY_ELEMS-1;
AltitudeDifference = altitudeValue_P(index) - altitudeValue_P(index-1);
PressureDifference = pressure - ((MAX_PRESSURE - (index* PRESSURE_INC))*100);
fraction = (AltitudeDifference *1000) - (PressureDifference * AltitudeDifference) ;
alt = ((altitudeValue_P(index-1) * 10) + (fraction/100))/10;
if(distanceUnits == METERS)
return alt;
else
return (long)(alt * 0.328084);
}
HP03class HP03 = HP03class();