Go Down

Topic: HP03 pressure sensor library update (Read 2622 times) previous topic - next topic

mem

Jun 01, 2011, 07:05 pm Last Edit: Jun 02, 2011, 12:17 pm by mem Reason: 1
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:
Code: [Select]
// 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
Code: [Select]
/*
 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)

mem

#1
Jun 01, 2011, 07:06 pm Last Edit: Jun 02, 2011, 01:18 pm by mem Reason: 1
Here is the HP03.cpp file:
Code: [Select]

/*
 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();


kfahrner

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)

robtillaart

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

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

mem

Quote
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.

mem

Klaus,

Good to hear that you have it working.

Quote
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)
Quote
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.

X3fil

After a while... :)

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

joseant

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

Go Up