HP03 pressure sensor library update

In response to requests to update the HP03 library to work with boards like the Mega, here are the latest files. This version uses the Arduino tone function to produce the external clock needed by HP03. You can read more about the HP03 pressure sensor module in this old thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1223819446/0

Here is the test sketch:

// HP03 example sketch
// prints the temperature, presssure and altitude to the serial port
// also shows the maximum deviation in feet from the average altitude
// Created 17 July 2009
// Updated Jun 1 2011 to support Mega (tone funtion used instead of FrequencyTimer2)

#include <HP03.h>
#include <Wire.h>

// Standard boards (168 and 328 chips) use digital pin 11 for the clock, analog pins 2 and 3 for control
// Mega uses digital pin 10 for clock and 18 & 19 for control (I2C are pins 20 & 21)

const int numReadings = 12;    // the number of readings to smooth 
                              // with HP03 conversion rate of 85ms, 12 readings takes one second
                              
long count = 0;               // the number of readings taken

long minA = 9999999;          // used to show how much the altitude deviates
long maxA = 0;
long deviation;    
  
void setup()
{ 
  Serial.begin(9600);  // start serial for output  
  Serial.flush();
  if(HP03.begin() == false)
    Serial.println("Error getting HP03 calibration, check sensor connection");    
}

void loop()
{    
  if(HP03.update() == false)
    Serial.println("Error getting HP03 data, check sensor connection");
  else   
  {
    count++;
    showReadings(); 
  }
}

void showReadings()
{
    Serial.print("Temp ");
    Serial.print(HP03.Temperature/10.0);
    Serial.print(" Pressure ");
    Serial.print(HP03.Pressure/100.0); 
    HP03.distanceUnits = METERS;
    Serial.print("   Altitude: Meters = ");
    Serial.print(HP03.getAltitude(HP03.Pressure)/ 10); 
    HP03.distanceUnits = FEET;
    Serial.print(", Feet  = ");
    long feet = HP03.getAltitude(HP03.Pressure);
    Serial.print(feet);
    feet = smooth(feet);
    if(count >= numReadings)
    {    
      Serial.print(" smoothed = ");
      Serial.print(feet);        
      
      if(feet < minA) minA = feet;
      if(feet > maxA) maxA = feet;          
      Serial.print(", deviation = ");
      Serial.print( (maxA-minA) /2 );      
    }
    Serial.println();
    
    int val = map(HP03.Pressure /10,9750, 10250, 0,255);
    analogWrite(9,val );
    Serial.println( val);     
}

long smooth(long value)
{
static int index = 0; 
static long readings[numReadings]  ;
static boolean isPrimed = false;   // set to true when at least numReadings have been received 
static long total = 0;

   total = total - readings[index];    // remove the oldest value
   readings[index] = value;            // store the new value
   total= total + readings[index];     // add the reading to the total:     
   index = index + 1;                  // advance to the next position in the array:    
  
   // if we're at the end of the array...
   if (index >= numReadings)          // check if this is the end of the array    
   {
      index = 0;                      // and wrap to the beginning if so    
      isPrimed  = true;               // enough readings received for average 
   }
   if(isPrimed)
      return total / numReadings;   
   else
     return value; // return the raw value if not enough samples received;   
}

HP03.h header file to go into the library directory

/*
  HP03.h - 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
  Version:   1.1 - May31  2011 - updated to use tone to work with mega board
  
*/

#ifndef	HP03_H
#define HP03_H

typedef unsigned char boolean;

/*
  define some constants for errors based on the return values
  of the functions in utility/twi.c
*/
#define WIRE_OK 0
#define WIRE_ERR_NO_BUFFER_SPACE 1
#define WIRE_ERR_BAD_ADDRESS 2
#define WIRE_ERR_NOT_IN_TRNSMISSION_MODE 2
#define WIRE_ERR_DATA_NO_ACK 3
#define WIRE_ERR_BUS 4

typedef enum distanceUnits_t {METERS, FEET};

class HP03class  // shell class for HP03 glcd code
{
  private:
     // methods to send and receive data via I2C
     int i2cTransfer(unsigned char Address, const unsigned char *TxBuf, int TxBytes, unsigned char *RxBuf, int RxBytes);
     int i2cSendCommand(unsigned char Address, unsigned char Command);
     int i2cMeasure(unsigned char SubType);
     int i2cResult(unsigned int *Result);
  public:
    HP03class();
	float Temperature;
    float Pressure;
    long   Altitude;
	unsigned int rawPressure;     // D1 - raw values are only for test purposes
    unsigned int rawTemperature;  // D2  
    distanceUnits_t distanceUnits;   // METERS or FEET
    boolean begin( void );
    boolean getCalibrationData();
#ifdef PRINT_CALIBRATION_DATA
    void HPO3class::printCalibrationData();
#endif
    boolean update();
    boolean readRawData();
    void calculatePressureAndTemperature();
    long getAltitude(long pressure);  //pressure is 100 times what it should be
};

extern HP03class HP03;    
#endif

Tested with Uno and Mega boards using Arduino 0022
Note that the Uno uses digital pin 11 for the external clock and analog pins 2 and 3 for control (I2C uses analog 4 and 5)
The Mega uses digital pin 10 for the clock and digital pins 18 and 19 for control (I2C uses digital pins 20 and 21)

Here is the HP03.cpp file:

/*
  HP03.cpp - Arduino library support for HP03 Pressure sensor
Copyright (c) Michael Margolis 

  Version:   1.0 - July14 2008
  Version:   1.1 - May31  2011 - updated to use tone to work with mega board
*/

// Note that this code uses the return values in Arduino version 0012 wire.c library


#include <avr/pgmspace.h>
#include <WProgram.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

// Pin defines
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define XCLR_pin 19   // Mega uses pin 19 in addition to I2C pins 20 & 21
#define TONE_PIN 10   // pin used by tone for external clock
#else
#define XCLR_pin 17   // ATmega 328 board uses pin 17 (analog 3) in addition to I2C pins 18 & 19
#define TONE_PIN 11   // pin used by tone for external clock
#endif

#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 pin for output
  pinMode(XCLR_pin, OUTPUT);    
  digitalWrite(XCLR_pin,LOW) ;
  tone(TONE_PIN,32768);  // tone function generates external clock 
  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();

Hi Michael,

Hurrah, it all works. I tried it on the Mega so far.
I am getting 1009 for pressure and 28 meters altitude.
The close-by airport has a QNH of 1018 and my house is 46m above sea level. So I will now study your code and the sensor's various correction factors.
I am a bit puzzled by the control pins 18 and 19 you suggested for the Mega. I have put pull-ups on both, but get a steady low signal on both. When I put a high signal on the sensor's XCLR pin, it all works, at least receiving.
Thank you so much for helping me with this issue - I am much too much a beginner to get this sensor going on my own.

Kind regards Klaus (Perth, Western Australia)

small glitch in the test sketch - // Updated Jun 1 to
should be "Jun 1 2011" I guess

small glitch in the test sketch - // Updated Jun 1 to
should be "Jun 1 2011" I guess

That's not what I would call a glitch, but I have added the year into the comment.

Klaus,

Good to hear that you have it working.

I am a bit puzzled by the control pins 18 and 19 you suggested for the Mega. I have put pull-ups on both, but get a steady low signal on both. When I put a high signal on the sensor's XCLR pin, it all works, at least receiving.

The control pins are outputs, pull-ups should not be used.

In fact, the MCLK pin is a remnant from a very early version of this library that generated the clock signal in software and is not used in this version (the clock comes from a timer on pin 10 on the mega). I have updated the cpp file to remove references to that pin (18 on the Mega)

When I put a high signal on the sensor's XCLR pin, it all works

If the posted code not work for you as is, can you explain how you have the pins connected to the HP03 sensor.

After a while... :slight_smile:

Work this library on a HopeFM HP03M on Arduino 2009 (or NANO) 328?
Info page and DataSheet:
http://www.hoperf.com/sensor/barometer_sensor/hp03m.htm

Thank's a lot!

Simon

hello.

This library is not working with arduino 1.0.1 version. I got errors like:

C:\Program Files (x86)\arduino\libraries\HP03\HP03.cpp:57: error: 'byte' does not name a type
C:\Program Files (x86)\arduino\libraries\HP03\HP03.cpp:59: error: 'byte' was not declared in this scope
C:\Program Files (x86)\arduino\libraries\HP03\HP03.cpp:59: error: expected ',' or ';' before '{' token