#include "LOLIN_HP303B.h" const int32_t LOLIN_HP303B::scaling_facts[HP303B__NUM_OF_SCAL_FACTS] = {524288, 1572864, 3670016, 7864320, 253952, 516096, 1040384, 2088960}; //////// Constructor, Destructor, begin, end //////// /** * Standard Constructor */ LOLIN_HP303B::LOLIN_HP303B(void) { //assume that initialization has failed before it has been done m_initFail = 1U; } /** * Standard Destructor */ LOLIN_HP303B::~LOLIN_HP303B(void) { end(); } /** * Standard I2C begin function * * &bus: I2CBus which connects MC to HP303B * slaveAddress: Address of the HP303B (0x77 or 0x76) */ uint8_t LOLIN_HP303B::begin(TwoWire &bus, uint8_t slaveAddress) { //this flag will show if the initialization was successful m_initFail = 0U; //Set I2C bus connection m_SpiI2c = 1U; m_i2cbus = &bus; m_slaveAddress = slaveAddress; // Init bus m_i2cbus->begin(); delay(50); //startup time of HP303B return init(); } uint8_t LOLIN_HP303B::begin(uint8_t slaveAddress) { return begin(Wire,slaveAddress); } /** * SPI begin function for HP303B with 4-wire SPI */ uint8_t LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect) { return begin(bus, chipSelect, 0U); } /** * Standard SPI begin function * * &bus: SPI bus which connects MC to HP303B * chipSelect: Number of the CS line for the HP303B * threeWire: 1 if HP303B is connected with 3-wire SPI * 0 if HP303B is connected with 4-wire SPI (standard) */ uint8_t LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire) { //this flag will show if the initialization was successful m_initFail = 0U; //Set SPI bus connection m_SpiI2c = 0U; m_spibus = &bus; m_chipSelect = chipSelect; // Init bus m_spibus->begin(); m_spibus->setDataMode(SPI_MODE3); pinMode(m_chipSelect, OUTPUT); digitalWrite(m_chipSelect, HIGH); delay(50); //startup time of HP303B //switch to 3-wire mode if necessary //do not use writeByteBitfield or check option to set SPI mode! //Reading is not possible until SPI-mode is valid if(threeWire) { m_threeWire = 1U; if(writeByte(HP303B__REG_ADR_SPI3W, HP303B__REG_CONTENT_SPI3W)) { m_initFail = 1U; return 0U; } } return init(); } /** * End function for HP303B * Sets the sensor to idle mode */ void LOLIN_HP303B::end(void) { standby(); } //////// Declaration of other public functions starts here //////// /** * returns the Product ID of the connected HP303B sensor */ uint8_t LOLIN_HP303B::getProductId(void) { return m_productID; } /** * returns the Revision ID of the connected HP303B sensor */ uint8_t LOLIN_HP303B::getRevisionId(void) { return m_revisionID; } /** * Sets the HP303B to standby mode * * returns: 0 on success * -2 if object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::standby(void) { //set device to idling mode int16_t ret = setOpMode(IDLE); if(ret != HP303B__SUCCEEDED) { return ret; } //flush the FIFO ret = writeByteBitfield(1U, HP303B__REG_INFO_FIFO_FL); if(ret < 0) { return ret; } //disable the FIFO ret = writeByteBitfield(0U, HP303B__REG_INFO_FIFO_EN); return ret; } /** * performs one temperature measurement and writes result to the given address * * &result: reference to a 32-Bit signed Integer value where the result will be written * It will not be written if result==NULL * returns: 0 on success * -4 if the HP303B is could not finish its measurement in time * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::measureTempOnce(float &result) { return measureTempOnce(result, m_slaveAddress, m_tempOsr); } /** * performs one temperature measurement and writes result to the given address * * &result: reference to a 32-Bit signed Integer value where the result will be written * It will not be written if result==NULL * returns: 0 on success * -4 if the HP303B is could not finish its measurement in time * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::measureTempOnce(float &result, uint8_t slaveAddress) { return measureTempOnce(result, slaveAddress, m_tempOsr); } /** * performs one temperature measurement and writes result to the given address * the desired precision can be set with oversamplingRate * * &result: reference to a 32-Bit signed Integer where the result will be written * It will not be written if result==NULL * oversamplingRate: a value from 0 to 7 that decides about the precision * of the measurement * If this value equals n, the HP303B will perform * 2^n measurements and combine the results * returns: 0 on success * -4 if the HP303B is could not finish its measurement in time * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::measureTempOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate) { //Set I2C bus connection m_slaveAddress = slaveAddress; //Start measurement int16_t ret = startMeasureTempOnce(oversamplingRate); if(ret!=HP303B__SUCCEEDED) { return ret; } //wait until measurement is finished delay(calcBusyTime(0U, m_tempOsr)/HP303B__BUSYTIME_SCALING); delay(HP303B__BUSYTIME_FAILSAFE); ret = getSingleResult(result); if(ret!=HP303B__SUCCEEDED) { standby(); } return ret; } /** * starts a single temperature measurement * * returns: 0 on success * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::startMeasureTempOnce(void) { return startMeasureTempOnce(m_tempOsr); } /** * starts a single temperature measurement * The desired precision can be set with oversamplingRate * * oversamplingRate: a value from 0 to 7 that decides about the precision * of the measurement * If this value equals n, the HP303B will perform * 2^n measurements and combine the results * returns: 0 on success * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::startMeasureTempOnce(uint8_t oversamplingRate) { //abort if device is not in idling mode if(m_opMode!=IDLE) { return HP303B__FAIL_TOOBUSY; } if(oversamplingRate!=m_tempOsr) { //configuration of oversampling rate if(configTemp(0U, oversamplingRate) != HP303B__SUCCEEDED) { return HP303B__FAIL_UNKNOWN; } } //set device to temperature measuring mode return setOpMode(0U, 1U, 0U); } /** * performs one pressure measurement and writes result to the given address * * &result: reference to a 32-Bit signed Integer value where the result will be written * It will not be written if result==NULL * returns: 0 on success * -4 if the HP303B is could not finish its measurement in time * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::measurePressureOnce(float &result) { return measurePressureOnce(result, m_slaveAddress, m_prsOsr); } /** * performs one pressure measurement and writes result to the given address * * &result: reference to a 32-Bit signed Integer value where the result will be written * It will not be written if result==NULL * returns: 0 on success * -4 if the HP303B is could not finish its measurement in time * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::measurePressureOnce(float &result, uint8_t slaveAddress) { return measurePressureOnce(result, slaveAddress, m_prsOsr); } /** * performs one pressure measurement and writes result to the given address * the desired precision can be set with oversamplingRate * * &result: reference to a 32-Bit signed Integer where the result will be written * It will not be written if result==NULL * oversamplingRate: a value from 0 to 7 that decides about the precision * of the measurement * If this value equals n, the HP303B will perform * 2^n measurements and combine the results * returns: 0 on success * -4 if the HP303B is could not finish its measurement in time * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::measurePressureOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate) { //Set I2C bus connection m_slaveAddress = slaveAddress; //start the measurement int16_t ret = startMeasurePressureOnce(oversamplingRate); if(ret != HP303B__SUCCEEDED) { return ret; } //wait until measurement is finished delay(calcBusyTime(0U, m_prsOsr)/HP303B__BUSYTIME_SCALING); delay(HP303B__BUSYTIME_FAILSAFE); ret = getSingleResult(result); if(ret!=HP303B__SUCCEEDED) { standby(); } return ret; } /** * starts a single pressure measurement * * returns: 0 on success * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::startMeasurePressureOnce(void) { return startMeasurePressureOnce(m_prsOsr); } /** * starts a single pressure measurement * The desired precision can be set with oversamplingRate * * oversamplingRate: a value from 0 to 7 that decides about the precision * of the measurement * If this value equals n, the HP303B will perform * 2^n measurements and combine the results * returns: 0 on success * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::startMeasurePressureOnce(uint8_t oversamplingRate) { //abort if device is not in idling mode if(m_opMode != IDLE) { return HP303B__FAIL_TOOBUSY; } //configuration of oversampling rate, lowest measure rate to avoid conflicts if(oversamplingRate != m_prsOsr) { if(configPressure(0U, oversamplingRate)) { return HP303B__FAIL_UNKNOWN; } } //set device to pressure measuring mode return setOpMode(0U, 0U, 1U); } /** * gets the result a single temperature or pressure measurement in °C or Pa * * &result: reference to a 32-Bit signed Integer value where the result will be written * returns: 0 on success * -4 if the HP303B is still busy * -3 if the HP303B is not in command mode * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::getSingleResult(float &result) { //read finished bit for current opMode int16_t rdy; switch(m_opMode) { case CMD_TEMP: //temperature rdy = readByteBitfield(HP303B__REG_INFO_TEMP_RDY); break; case CMD_PRS: //pressure rdy = readByteBitfield(HP303B__REG_INFO_PRS_RDY); break; default: //HP303B not in command mode return HP303B__FAIL_TOOBUSY; } //read new measurement result switch(rdy) { case HP303B__FAIL_UNKNOWN: //could not read ready flag return HP303B__FAIL_UNKNOWN; case 0: //ready flag not set, measurement still in progress return HP303B__FAIL_UNFINISHED; case 1: //measurement ready, expected case LOLIN_HP303B::Mode oldMode = m_opMode; m_opMode = IDLE; //opcode was automatically reseted by HP303B switch(oldMode) { case CMD_TEMP: //temperature return getTemp(&result); //get and calculate the temperature value case CMD_PRS: //pressure return getPressure(&result); //get and calculate the pressure value default: return HP303B__FAIL_UNKNOWN; //should already be filtered above } } return HP303B__FAIL_UNKNOWN; } /** * starts a continuous temperature measurement * The desired precision can be set with oversamplingRate * The desired number of measurements per second can be set with measureRate * * measureRate: a value from 0 to 7 that decides about * the number of measurements per second * If this value equals n, the HP303B will perform * 2^n measurements per second * oversamplingRate: a value from 0 to 7 that decides about * the precision of the measurements * If this value equals m, the HP303B will perform * 2^m internal measurements and combine the results * to one more exact measurement * returns: 0 on success * -4 if measureRate or oversamplingRate is too high * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail * NOTE: If measure rate is n and oversampling rate is m, * the HP303B performs 2^(n+m) internal measurements per second. * The HP303B cannot operate with high precision and high speed * at the same time. * Consult the datasheet for more information. */ int16_t LOLIN_HP303B::startMeasureTempCont(uint8_t measureRate, uint8_t oversamplingRate) { //abort if initialization failed if(m_initFail) { return HP303B__FAIL_INIT_FAILED; } //abort if device is not in idling mode if(m_opMode != IDLE) { return HP303B__FAIL_TOOBUSY; } //abort if speed and precision are too high if(calcBusyTime(measureRate, oversamplingRate) >= HP303B__MAX_BUSYTIME) { return HP303B__FAIL_UNFINISHED; } //update precision and measuring rate if(configTemp(measureRate, oversamplingRate)) { return HP303B__FAIL_UNKNOWN; } //enable result FIFO if(writeByteBitfield(1U, HP303B__REG_INFO_FIFO_EN)) { return HP303B__FAIL_UNKNOWN; } //Start measuring in background mode if(setOpMode(1U, 1U, 0U)) { return HP303B__FAIL_UNKNOWN; } return HP303B__SUCCEEDED; } /** * starts a continuous temperature measurement * The desired precision can be set with oversamplingRate * The desired number of measurements per second can be set with measureRate * * measureRate: a value from 0 to 7 that decides about * the number of measurements per second * If this value equals n, the HP303B will perform * 2^n measurements per second * oversamplingRate: a value from 0 to 7 that decides about the precision * of the measurements * If this value equals m, the HP303B will perform * 2^m internal measurements * and combine the results to one more exact measurement * returns: 0 on success * -4 if measureRate or oversamplingRate is too high * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail * NOTE: If measure rate is n and oversampling rate is m, * the HP303B performs 2^(n+m) internal measurements per second. * The HP303B cannot operate with high precision and high speed * at the same time. * Consult the datasheet for more information. */ int16_t LOLIN_HP303B::startMeasurePressureCont(uint8_t measureRate, uint8_t oversamplingRate) { //abort if initialization failed if(m_initFail) { return HP303B__FAIL_INIT_FAILED; } //abort if device is not in idling mode if(m_opMode != IDLE) { return HP303B__FAIL_TOOBUSY; } //abort if speed and precision are too high if(calcBusyTime(measureRate, oversamplingRate) >= HP303B__MAX_BUSYTIME) { return HP303B__FAIL_UNFINISHED; } //update precision and measuring rate if(configPressure(measureRate, oversamplingRate)) return HP303B__FAIL_UNKNOWN; //enable result FIFO if(writeByteBitfield(1U, HP303B__REG_INFO_FIFO_EN)) { return HP303B__FAIL_UNKNOWN; } //Start measuring in background mode if(setOpMode(1U, 0U, 1U)) { return HP303B__FAIL_UNKNOWN; } return HP303B__SUCCEEDED; } /** * starts a continuous temperature and pressure measurement * The desired precision can be set with tempOsr and prsOsr * The desired number of measurements per second can be set with tempMr and prsMr * * tempMr measure rate for temperature * tempOsr oversampling rate for temperature * prsMr measure rate for pressure * prsOsr oversampling rate for pressure * returns: 0 on success * -4 if precision or speed is too high * -3 if the HP303B is already busy * -2 if the object initialization failed * -1 on other fail * NOTE: High precision and speed for both temperature and pressure * can not be reached at the same time. * Estimated time for temperature and pressure measurement * is the sum of both values. * This sum must not be more than 1 second. * Consult the datasheet for more information. */ int16_t LOLIN_HP303B::startMeasureBothCont(uint8_t tempMr, uint8_t tempOsr, uint8_t prsMr, uint8_t prsOsr) { //abort if initialization failed if(m_initFail) { return HP303B__FAIL_INIT_FAILED; } //abort if device is not in idling mode if(m_opMode!=IDLE) { return HP303B__FAIL_TOOBUSY; } //abort if speed and precision are too high if(calcBusyTime(tempMr, tempOsr) + calcBusyTime(prsMr, prsOsr)>=HP303B__MAX_BUSYTIME) { return HP303B__FAIL_UNFINISHED; } //update precision and measuring rate if(configTemp(tempMr, tempOsr)) { return HP303B__FAIL_UNKNOWN; } //update precision and measuring rate if(configPressure(prsMr, prsOsr)) return HP303B__FAIL_UNKNOWN; //enable result FIFO if(writeByteBitfield(1U, HP303B__REG_INFO_FIFO_EN)) { return HP303B__FAIL_UNKNOWN; } //Start measuring in background mode if(setOpMode(1U, 1U, 1U)) { return HP303B__FAIL_UNKNOWN; } return HP303B__SUCCEEDED; } /** * Gets the results from continuous measurements and writes them to given arrays * * *tempBuffer: The start address of the buffer where the temperature results * are written * If this is NULL, no temperature results will be written out * &tempCount: This has to be a reference to a number which contains * the size of the buffer for temperature results. * When the function ends, it will contain * the number of bytes written to the buffer * *prsBuffer: The start address of the buffer where the pressure results * are written * If this is NULL, no pressure results will be written out * &prsCount: This has to be a reference to a number which contains * the size of the buffer for pressure results. * When the function ends, it will contain * the number of bytes written to the buffer * returns: 0 on success * -3 if HP303B is not in background mode * -2 if the object initialization failed * -1 on other fail */ int16_t LOLIN_HP303B::getContResults(float *tempBuffer, uint8_t &tempCount, float *prsBuffer, uint8_t &prsCount) { if(m_initFail) { return HP303B__FAIL_INIT_FAILED; } //abort if device is not in background mode if(!(m_opMode & INVAL_OP_CONT_NONE)) { return HP303B__FAIL_TOOBUSY; } //prepare parameters for buffer length and count uint8_t tempLen = tempCount; uint8_t prsLen = prsCount; tempCount = 0U; prsCount = 0U; //while FIFO is not empty while(readByteBitfield(HP303B__REG_INFO_FIFO_EMPTY) == 0) { float result; //read next result from FIFO int16_t type = getFIFOvalue(&result); switch(type) { case 0: //temperature //calculate compensated pressure value result = calcTemp(result); //if buffer exists and is not full //write result to buffer and increase temperature result counter if(tempBuffer != NULL) { if(tempCount> HP303B__REG_SHIFT_INT_EN_FIFO; tempReady &= HP303B__REG_MASK_INT_EN_TEMP >> HP303B__REG_SHIFT_INT_EN_TEMP; prsReady &= HP303B__REG_MASK_INT_EN_PRS >> HP303B__REG_SHIFT_INT_EN_PRS; //read old value from register int16_t regData = readByte(HP303B__REG_ADR_INT_EN_FIFO); if(regData <0) { return HP303B__FAIL_UNKNOWN; } uint8_t toWrite = (uint8_t)regData; //update FIFO enable bit toWrite &= ~HP303B__REG_MASK_INT_EN_FIFO; //clear bit toWrite |= fifoFull << HP303B__REG_SHIFT_INT_EN_FIFO; //set new bit //update TempReady enable bit toWrite &= ~HP303B__REG_MASK_INT_EN_TEMP; toWrite |= tempReady << HP303B__REG_SHIFT_INT_EN_TEMP; //update PrsReady enable bit toWrite &= ~HP303B__REG_MASK_INT_EN_PRS; toWrite |= prsReady << HP303B__REG_SHIFT_INT_EN_PRS; //write updated value to register return writeByte(HP303B__REG_ADR_INT_EN_FIFO, toWrite); } /** * Gets the interrupt status flag of the FIFO * * Returns: 1 if the FIFO is full and caused an interrupt * 0 if the FIFO is not full or FIFO interrupt is disabled * -1 on fail */ int16_t LOLIN_HP303B::getIntStatusFifoFull(void) { return readByteBitfield(HP303B__REG_INFO_INT_FLAG_FIFO); } /** * Gets the interrupt status flag that indicates a finished temperature measurement * * Returns: 1 if a finished temperature measurement caused an interrupt * 0 if there is no finished temperature measurement * or interrupts are disabled * -1 on fail */ int16_t LOLIN_HP303B::getIntStatusTempReady(void) { return readByteBitfield(HP303B__REG_INFO_INT_FLAG_TEMP); } /** * Gets the interrupt status flag that indicates a finished pressure measurement * * Returns: 1 if a finished pressure measurement caused an interrupt * 0 if there is no finished pressure measurement * or interrupts are disabled * -1 on fail */ int16_t LOLIN_HP303B::getIntStatusPrsReady(void) { return readByteBitfield(HP303B__REG_INFO_INT_FLAG_PRS); } /** * Function to fix a hardware problem on some devices * You have this problem if you measure a temperature which is too high (e.g. 60°C when temperature is around 20°C) * Call correctTemp() directly after begin() to fix this issue */ int16_t LOLIN_HP303B::correctTemp(void) { if(m_initFail) { return HP303B__FAIL_INIT_FAILED; } writeByte(0x0E, 0xA5); writeByte(0x0F, 0x96); writeByte(0x62, 0x02); writeByte(0x0E, 0x00); writeByte(0x0F, 0x00); //perform a first temperature measurement (again) //the most recent temperature will be saved internally //and used for compensation when calculating pressure float trash; measureTempOnce(trash); return HP303B__SUCCEEDED; } //////// Declaration of private functions starts here //////// /** * Initializes the sensor. * This function has to be called from begin() * and requires a valid bus initialization. */ uint8_t LOLIN_HP303B::init(void) { int16_t prodId = readByteBitfield(HP303B__REG_INFO_PROD_ID); if(prodId != HP303B__PROD_ID) { //Connected device is not a HP303B m_initFail = 1U; return 0U; } m_productID = prodId; int16_t revId = readByteBitfield(HP303B__REG_INFO_REV_ID); if(revId < 0) { m_initFail = 1U; return 0U; } m_revisionID = revId; //find out which temperature sensor is calibrated with coefficients... int16_t sensor = readByteBitfield(HP303B__REG_INFO_TEMP_SENSORREC); if(sensor < 0) { m_initFail = 1U; return 0U; } //...and use this sensor for temperature measurement m_tempSensor = sensor; if(writeByteBitfield((uint8_t)sensor, HP303B__REG_INFO_TEMP_SENSOR) < 0) { m_initFail = 1U; return 0U; } //read coefficients if(readcoeffs() < 0) { m_initFail = 1U; return 0U; } //set to standby for further configuration standby(); //set measurement precision and rate to standard values; configTemp(HP303B__TEMP_STD_MR, HP303B__TEMP_STD_OSR); configPressure(HP303B__PRS_STD_MR, HP303B__PRS_STD_OSR); //perform a first temperature measurement //the most recent temperature will be saved internally //and used for compensation when calculating pressure float trash; measureTempOnce(trash); //make sure the HP303B is in standby after initialization standby(); // Fix IC with a fuse bit problem, which lead to a wrong temperature // Should not affect ICs without this problem correctTemp(); return 1U; } /** * reads the compensation coefficients from the HP303B * this is called once from init(), which is called from begin() * * returns: 0 on success, -1 on fail */ int16_t LOLIN_HP303B::readcoeffs(void) { uint8_t buffer[HP303B__REG_LEN_COEF]; //read COEF registers to buffer int16_t ret = readBlock(HP303B__REG_ADR_COEF, HP303B__REG_LEN_COEF, buffer); //abort if less than REG_LEN_COEF bytes were read if(ret < HP303B__REG_LEN_COEF) { return HP303B__FAIL_UNKNOWN; } //compose coefficients from buffer content m_c0Half = ((uint32_t)buffer[0] << 4) | (((uint32_t)buffer[1] >> 4) & 0x0F); //this construction recognizes non-32-bit negative numbers //and converts them to 32-bit negative numbers with 2's complement if(m_c0Half & ((uint32_t)1 << 11)) { m_c0Half -= (uint32_t)1 << 12; } //c0 is only used as c0*0.5, so c0_half is calculated immediately m_c0Half = m_c0Half / 2U; //now do the same thing for all other coefficients m_c1 = (((uint32_t)buffer[1] & 0x0F) << 8) | (uint32_t)buffer[2]; if(m_c1 & ((uint32_t)1 << 11)) { m_c1 -= (uint32_t)1 << 12; } m_c00 = ((uint32_t)buffer[3] << 12) | ((uint32_t)buffer[4] << 4) | (((uint32_t)buffer[5] >> 4) & 0x0F); if(m_c00 & ((uint32_t)1 << 19)) { m_c00 -= (uint32_t)1 << 20; } m_c10 = (((uint32_t)buffer[5] & 0x0F) << 16) | ((uint32_t)buffer[6] << 8) | (uint32_t)buffer[7]; if(m_c10 & ((uint32_t)1<<19)) { m_c10 -= (uint32_t)1 << 20; } m_c01 = ((uint32_t)buffer[8] << 8) | (uint32_t)buffer[9]; if(m_c01 & ((uint32_t)1 << 15)) { m_c01 -= (uint32_t)1 << 16; } m_c11 = ((uint32_t)buffer[10] << 8) | (uint32_t)buffer[11]; if(m_c11 & ((uint32_t)1 << 15)) { m_c11 -= (uint32_t)1 << 16; } m_c20 = ((uint32_t)buffer[12] << 8) | (uint32_t)buffer[13]; if(m_c20 & ((uint32_t)1 << 15)) { m_c20 -= (uint32_t)1 << 16; } m_c21 = ((uint32_t)buffer[14] << 8) | (uint32_t)buffer[15]; if(m_c21 & ((uint32_t)1 << 15)) { m_c21 -= (uint32_t)1 << 16; } m_c30 = ((uint32_t)buffer[16] << 8) | (uint32_t)buffer[17]; if(m_c30 & ((uint32_t)1 << 15)) { m_c30 -= (uint32_t)1 << 16; } return HP303B__SUCCEEDED; } /** * Sets the Operation Mode of the HP303B * * background: determines the general behavior of the HP303B * 0 enables command mode (only measure on commands) * 1 enables background mode (continuous work in background) * temperature: set 1 to measure temperature * pressure: set 1 to measure pressure * return: 0 on success, -1 on fail * * NOTE! * You cannot set background to 1 without setting temperature and pressure * You cannot set both temperature and pressure when background mode is disabled */ int16_t LOLIN_HP303B::setOpMode(uint8_t background, uint8_t temperature, uint8_t pressure) { uint8_t opMode = (background & HP303B__LSB) << 2U | (temperature & HP303B__LSB) << 1U | (pressure & HP303B__LSB); return setOpMode(opMode); } /** * Sets the Operation Mode of the HP303B * * opMode: the new OpMode that has to be set * return: 0 on success, -1 on fail * * NOTE! * You cannot set background to 1 without setting temperature and pressure * You cannot set both temperature and pressure when background mode is disabled */ int16_t LOLIN_HP303B::setOpMode(uint8_t opMode) { //Filter irrelevant bits opMode &= HP303B__REG_MASK_OPMODE >> HP303B__REG_SHIFT_OPMODE; //Filter invalid OpModes if(opMode == INVAL_OP_CMD_BOTH || opMode == INVAL_OP_CONT_NONE) { return HP303B__FAIL_UNKNOWN; } //Set OpMode if(writeByte(HP303B__REG_ADR_OPMODE, opMode)) { return HP303B__FAIL_UNKNOWN; } m_opMode = (LOLIN_HP303B::Mode)opMode; return HP303B__SUCCEEDED; } /** * Configures temperature measurement * * tempMr: the new measure rate for temperature * This can be a value from 0U to 7U. * Actual measure rate will be 2^tempMr, * so this will be a value from 1 to 128. * tempOsr: the new oversampling rate for temperature * This can be a value from 0U to 7U. * Actual measure rate will be 2^tempOsr, * so this will be a value from 1 to 128. * returns: 0 normally or -1 on fail */ int16_t LOLIN_HP303B::configTemp(uint8_t tempMr, uint8_t tempOsr) { //mask parameters tempMr &= HP303B__REG_MASK_TEMP_MR >> HP303B__REG_SHIFT_TEMP_MR; tempOsr &= HP303B__REG_MASK_TEMP_OSR >> HP303B__REG_SHIFT_TEMP_OSR; //set config register according to parameters uint8_t toWrite = tempMr << HP303B__REG_SHIFT_TEMP_MR; toWrite |= tempOsr << HP303B__REG_SHIFT_TEMP_OSR; //using recommended temperature sensor toWrite |= HP303B__REG_MASK_TEMP_SENSOR & (m_tempSensor << HP303B__REG_SHIFT_TEMP_SENSOR); int16_t ret = writeByte(HP303B__REG_ADR_TEMP_MR, toWrite); //abort immediately on fail if(ret != HP303B__SUCCEEDED) { return HP303B__FAIL_UNKNOWN; } //set TEMP SHIFT ENABLE if oversampling rate higher than eight(2^3) if(tempOsr > HP303B__OSR_SE) { ret=writeByteBitfield(1U, HP303B__REG_INFO_TEMP_SE); } else { ret=writeByteBitfield(0U, HP303B__REG_INFO_TEMP_SE); } if(ret == HP303B__SUCCEEDED) { //save new settings m_tempMr = tempMr; m_tempOsr = tempOsr; } else { //try to rollback on fail avoiding endless recursion //this is to make sure that shift enable and oversampling rate //are always consistent if(tempMr != m_tempMr || tempOsr != m_tempOsr) { configTemp(m_tempMr, m_tempOsr); } } return ret; } /** * Configures pressure measurement * * prsMr: the new measure rate for pressure * This can be a value from 0U to 7U. * Actual measure rate will be 2^prs_mr, * so this will be a value from 1 to 128. * prsOs: the new oversampling rate for temperature * This can be a value from 0U to 7U. * Actual measure rate will be 2^prsOsr, * so this will be a value from 1 to 128. * returns: 0 normally or -1 on fail */ int16_t LOLIN_HP303B::configPressure(uint8_t prsMr, uint8_t prsOsr) { //mask parameters prsMr &= HP303B__REG_MASK_PRS_MR >> HP303B__REG_SHIFT_PRS_MR; prsOsr &= HP303B__REG_MASK_PRS_OSR >> HP303B__REG_SHIFT_PRS_OSR; //set config register according to parameters uint8_t toWrite = prsMr << HP303B__REG_SHIFT_PRS_MR; toWrite |= prsOsr << HP303B__REG_SHIFT_PRS_OSR; int16_t ret = writeByte(HP303B__REG_ADR_PRS_MR, toWrite); //abort immediately on fail if(ret != HP303B__SUCCEEDED) { return HP303B__FAIL_UNKNOWN; } //set PM SHIFT ENABLE if oversampling rate higher than eight(2^3) if(prsOsr > HP303B__OSR_SE) { ret = writeByteBitfield(1U, HP303B__REG_INFO_PRS_SE); } else { ret = writeByteBitfield(0U, HP303B__REG_INFO_PRS_SE); } if(ret == HP303B__SUCCEEDED) { //save new settings m_prsMr = prsMr; m_prsOsr = prsOsr; } else { //try to rollback on fail avoiding endless recursion //this is to make sure that shift enable and oversampling rate //are always consistent if(prsMr != m_prsMr || prsOsr != m_prsOsr) { configPressure(m_prsMr, m_prsOsr); } } return ret; } /** * calculates the time that the HP303B needs for 2^mr measurements * with an oversampling rate of 2^osr * * mr: Measure rate for temperature or pressure * osr: Oversampling rate for temperature or pressure * returns: time that the HP303B needs for this measurement * a value of 10000 equals 1 second * NOTE! The measurement time for temperature and pressure * in sum must not be more than 1 second! * Timing behavior of pressure and temperature sensors * can be considered as equal. */ uint16_t LOLIN_HP303B::calcBusyTime(uint16_t mr, uint16_t osr) { //mask parameters first mr &= HP303B__REG_MASK_TEMP_MR >> HP303B__REG_SHIFT_TEMP_MR; osr &= HP303B__REG_MASK_TEMP_OSR >> HP303B__REG_SHIFT_TEMP_OSR; //formula from datasheet (optimized) return ((uint32_t)20U << mr) + ((uint32_t)16U << (osr + mr)); } /** * Gets the next temperature measurement result in degrees of Celsius * * result: address where the result will be written * returns: 0 on success * -1 on fail; */ int16_t LOLIN_HP303B::getTemp(float *result) { unsigned char buffer[3] = {0}; //read raw pressure data to buffer int16_t i = readBlock(HP303B__REG_ADR_TEMP, HP303B__REG_LEN_TEMP, buffer); if(i != HP303B__REG_LEN_TEMP) { //something went wrong return HP303B__FAIL_UNKNOWN; } //compose raw temperature value from buffer float temp = buffer[0] << 16 | buffer[1] << 8 | buffer[2]; //recognize non-32-bit negative numbers //and convert them to 32-bit negative numbers using 2's complement if(temp > 0x7FFFFF) { temp = temp - 0x1000000; } //return temperature *result = calcTemp(temp); return HP303B__SUCCEEDED; } /** * Gets the next pressure measurement result in Pa * * result: address where the result will be written * returns: 0 on success * -1 on fail; */ int16_t LOLIN_HP303B::getPressure(float *result) { unsigned char buffer[3] = {0}; //read raw pressure data to buffer int16_t i = readBlock(HP303B__REG_ADR_PRS, HP303B__REG_LEN_PRS, buffer); if(i != HP303B__REG_LEN_PRS) { //something went wrong //negative pressure is not allowed return HP303B__FAIL_UNKNOWN; } //compose raw pressure value from buffer float prs = buffer[0] << 16 | buffer[1] << 8 | buffer[2]; //recognize non-32-bit negative numbers //and convert them to 32-bit negative numbers using 2's complement if(prs > 0x7FFFFF) { prs = prs - 0x1000000; } *result = calcPressure(prs); return HP303B__SUCCEEDED; } /** * reads the next raw value from the HP303B FIFO * * value: address where the value will be written * returns: -1 on fail * 0 if result is a temperature raw value * 1 if result is a pressure raw value */ int16_t LOLIN_HP303B::getFIFOvalue(float *value) { //abort on invalid argument if(value == NULL) { return HP303B__FAIL_UNKNOWN; } unsigned char buffer[HP303B__REG_LEN_PRS] = {0}; //always read from pressure raw value register int16_t i = readBlock(HP303B__REG_ADR_PRS, HP303B__REG_LEN_PRS, buffer); if(i != HP303B__REG_LEN_PRS) { //something went wrong //return error code return HP303B__FAIL_UNKNOWN; } //compose raw pressure value from buffer *value = buffer[0] << 16 | buffer[1] << 8 | buffer[2]; //recognize non-32-bit negative numbers //and convert them to 32-bit negative numbers using 2's complement if(*value > 0x7FFFFF) { *value = *value - 0x1000000; } //least significant bit shows measurement type return buffer[2] & HP303B__LSB; } /** * Calculates a scaled and compensated pressure value from raw data * raw: raw temperature value read from HP303B * returns: temperature value in °C */ float LOLIN_HP303B::calcTemp(float raw) { double temp = raw; //scale temperature according to scaling table and oversampling temp /= scaling_facts[m_tempOsr]; //update last measured temperature //it will be used for pressure compensation m_lastTempScal = temp; //Calculate compensated temperature temp = m_c0Half + m_c1 * temp; //return temperature return (float)temp; } /** * Calculates a scaled and compensated pressure value from raw data * raw: raw pressure value read from HP303B * returns: pressure value in Pa */ float LOLIN_HP303B::calcPressure(float raw) { double prs = raw; //scale pressure according to scaling table and oversampling prs /= scaling_facts[m_prsOsr]; //Calculate compensated pressure prs = m_c00 + prs * (m_c10 + prs * (m_c20 + prs * m_c30)) + m_lastTempScal * (m_c01 + prs * (m_c11 + prs * m_c21)); //return pressure return (float)prs; } /** * reads a byte from HP303B * * regAdress: Address that has to be read * returns: register content or -1 on fail */ int16_t LOLIN_HP303B::readByte(uint8_t regAddress) { //delegate to specialized function if HP303B is connected via SPI if(m_SpiI2c==0) { return readByteSPI(regAddress); } m_i2cbus->beginTransmission(m_slaveAddress); m_i2cbus->write(regAddress); m_i2cbus->endTransmission(0); //request 1 byte from slave if(m_i2cbus->requestFrom(m_slaveAddress, 1U, 1U) > 0) { return m_i2cbus->read(); //return this byte on success } else { return HP303B__FAIL_UNKNOWN; //if 0 bytes were read successfully } } /** * reads a byte from HP303B via SPI * this function is automatically called by readByte * if HP303B is connected via SPI * * regAdress: Address that has to be read * returns: register content or -1 on fail */ int16_t LOLIN_HP303B::readByteSPI(uint8_t regAddress) { //this function is only made for communication via SPI if(m_SpiI2c != 0) { return HP303B__FAIL_UNKNOWN; } //mask regAddress regAddress &= ~HP303B__SPI_RW_MASK; //reserve and initialize bus m_spibus->beginTransaction(SPISettings(HP303B__SPI_MAX_FREQ, MSBFIRST, SPI_MODE3)); //enable ChipSelect for HP303B digitalWrite(m_chipSelect, LOW); //send address with read command to HP303B m_spibus->transfer(regAddress | HP303B__SPI_READ_CMD); //receive register content from HP303B uint8_t ret = m_spibus->transfer(0xFF); //send a dummy byte while receiving //disable ChipSelect for HP303B digitalWrite(m_chipSelect, HIGH); //close current SPI transaction m_spibus->endTransaction(); //return received data return ret; } /** * reads a block from HP303B * * regAdress: Address that has to be read * length: Length of data block * buffer: Buffer where data will be stored * returns: number of bytes that have been read successfully * NOTE! This is not always equal to length * due to rx-Buffer overflow etc. */ int16_t LOLIN_HP303B::readBlock(uint8_t regAddress, uint8_t length, uint8_t *buffer) { //delegate to specialized function if HP303B is connected via SPI if(m_SpiI2c == 0) { return readBlockSPI(regAddress, length, buffer); } //do not read if there is no buffer if(buffer == NULL) { return 0; //0 bytes read successfully } m_i2cbus->beginTransmission(m_slaveAddress); m_i2cbus->write(regAddress); m_i2cbus->endTransmission(0); //request length bytes from slave int16_t ret = m_i2cbus->requestFrom(m_slaveAddress, length, 1U); //read all received bytes to buffer for(int16_t count = 0; count < ret; count++) { buffer[count] = m_i2cbus->read(); } return ret; } /** * reads a block from HP303B via SPI * * regAdress: Address that has to be read * length: Length of data block * readbuffer: Buffer where data will be stored * returns: number of bytes that have been read successfully * NOTE! This is not always equal to length * due to rx-Buffer overflow etc. */ int16_t LOLIN_HP303B::readBlockSPI(uint8_t regAddress, uint8_t length, uint8_t *buffer) { //this function is only made for communication via SPI if(m_SpiI2c != 0) { return HP303B__FAIL_UNKNOWN; } //do not read if there is no buffer if(buffer == NULL) { return 0; //0 bytes were read successfully } //mask regAddress regAddress &= ~HP303B__SPI_RW_MASK; //reserve and initialize bus m_spibus->beginTransaction(SPISettings(HP303B__SPI_MAX_FREQ, MSBFIRST, SPI_MODE3)); //enable ChipSelect for HP303B digitalWrite(m_chipSelect, LOW); //send address with read command to HP303B m_spibus->transfer(regAddress | HP303B__SPI_READ_CMD); //receive register contents from HP303B for(uint8_t count = 0; count < length; count++) { buffer[count] = m_spibus->transfer(0xFF);//send a dummy byte while receiving } //disable ChipSelect for HP303B digitalWrite(m_chipSelect, HIGH); //close current SPI transaction m_spibus->endTransaction(); //return received data return length; } /** * writes a given byte to a given register of HP303B without checking * * regAdress: Address of the register that has to be updated * data: Byte that will be written to the register * return: 0 if byte was written successfully * or -1 on fail */ int16_t LOLIN_HP303B::writeByte(uint8_t regAddress, uint8_t data) { return writeByte(regAddress, data, 0U); } /** * writes a given byte to a given register of HP303B * * regAdress: Address of the register that has to be updated * data: Byte that will be written to the register * check: If this is true, register content will be read after writing * to check if update was successful * return: 0 if byte was written successfully * or -1 on fail */ int16_t LOLIN_HP303B::writeByte(uint8_t regAddress, uint8_t data, uint8_t check) { //delegate to specialized function if HP303B is connected via SPI if(m_SpiI2c==0) { return writeByteSpi(regAddress, data, check); } m_i2cbus->beginTransmission(m_slaveAddress); m_i2cbus->write(regAddress); //Write Register number to buffer m_i2cbus->write(data); //Write data to buffer if(m_i2cbus->endTransmission() != 0) //Send buffer content to slave { return HP303B__FAIL_UNKNOWN; } else { if(check == 0) return 0; //no checking if(readByte(regAddress) == data) //check if desired by calling function { return HP303B__SUCCEEDED; } else { return HP303B__FAIL_UNKNOWN; } } } /** * writes a given byte to a given register of HP303B via SPI * * regAdress: Address of the register that has to be updated * data: Byte that will be written to the register * check: If this is true, register content will be read after writing * to check if update was successful * return: 0 if byte was written successfully * or -1 on fail */ int16_t LOLIN_HP303B::writeByteSpi(uint8_t regAddress, uint8_t data, uint8_t check) { //this function is only made for communication via SPI if(m_SpiI2c != 0) { return HP303B__FAIL_UNKNOWN; } //mask regAddress regAddress &= ~HP303B__SPI_RW_MASK; //reserve and initialize bus m_spibus->beginTransaction(SPISettings(HP303B__SPI_MAX_FREQ, MSBFIRST, SPI_MODE3)); //enable ChipSelect for HP303B digitalWrite(m_chipSelect, LOW); //send address with read command to HP303B m_spibus->transfer(regAddress | HP303B__SPI_WRITE_CMD); //write register content from HP303B m_spibus->transfer(data); //disable ChipSelect for HP303B digitalWrite(m_chipSelect, HIGH); //close current SPI transaction m_spibus->endTransaction(); //check if necessary if(check == 0) { //no checking necessary return HP303B__SUCCEEDED; } //checking necessary if(readByte(regAddress) == data) { //check passed return HP303B__SUCCEEDED; } else { //check failed return HP303B__FAIL_UNKNOWN; } } /** * updates some given bits of a given register of HP303B without checking * * regAdress: Address of the register that has to be updated * data: BitValues that will be written to the register * shift: Amount of bits the data byte is shifted (left) before being masked * mask: Masks the bits of the register that have to be updated * Bits with value 1 are updated * Bits with value 0 are not changed * return: 0 if byte was written successfully * or -1 on fail */ int16_t LOLIN_HP303B::writeByteBitfield(uint8_t data, uint8_t regAddress, uint8_t mask, uint8_t shift) { return writeByteBitfield(data, regAddress, mask, shift, 0U); } /** * updates some given bits of a given register of HP303B * * regAdress: Address of the register that has to be updated * data: BitValues that will be written to the register * shift: Amount of bits the data byte is shifted (left) before being masked * mask: Masks the bits of the register that have to be updated * Bits with value 1 are updated * Bits with value 0 are not changed * check: enables/disables check after writing * 0 disables check * if check fails, -1 will be returned * return: 0 if byte was written successfully * or -1 on fail */ int16_t LOLIN_HP303B::writeByteBitfield(uint8_t data, uint8_t regAddress, uint8_t mask, uint8_t shift, uint8_t check) { int16_t old = readByte(regAddress); if(old < 0) { //fail while reading return old; } return writeByte(regAddress, ((uint8_t)old & ~mask)|((data << shift) & mask), check); } /** * reads some given bits of a given register of HP303B * * regAdress: Address of the register that has to be updated * mask: Masks the bits of the register that have to be updated * Bits masked with value 1 are read * Bits masked with value 0 are set 0 * shift: Amount of bits the data byte is shifted (right) after being masked * return: read and processed bits * or -1 on fail */ int16_t LOLIN_HP303B::readByteBitfield(uint8_t regAddress, uint8_t mask, uint8_t shift) { int16_t ret = readByte(regAddress); if(ret<0) { return ret; } return (((uint8_t)ret) & mask) >> shift; }