diff --git a/lib/LOLIN_HP303B/README.md b/lib/LOLIN_HP303B/README.md new file mode 100644 index 000000000..24d6c2d1b --- /dev/null +++ b/lib/LOLIN_HP303B/README.md @@ -0,0 +1,4 @@ +# Arduino library for the HP303B +### Installation +- Clone this repository or download&unzip [zip file](https://github.com/wemos/LOLIN_HP303B_Library/archive/master.zip) into Arduino/libraries + diff --git a/lib/LOLIN_HP303B/examples/i2c_background/i2c_background.ino b/lib/LOLIN_HP303B/examples/i2c_background/i2c_background.ino new file mode 100644 index 000000000..1b9ae6e68 --- /dev/null +++ b/lib/LOLIN_HP303B/examples/i2c_background/i2c_background.ino @@ -0,0 +1,98 @@ +#include + +// HP303B Opject +LOLIN_HP303B HP303BPressureSensor = LOLIN_HP303B(); + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + //Call begin to initialize HP303BPressureSensor + //The parameter 0x76 is the bus address. The default address is 0x77 and does not need to be given. + //HP303BPressureSensor.begin(Wire, 0x76); + //Use the commented line below instead to use the default I2C address. + HP303BPressureSensor.begin(); + + //temperature measure rate (value from 0 to 7) + //2^temp_mr temperature measurement results per second + int16_t temp_mr = 2; + //temperature oversampling rate (value from 0 to 7) + //2^temp_osr internal temperature measurements per result + //A higher value increases precision + int16_t temp_osr = 2; + //pressure measure rate (value from 0 to 7) + //2^prs_mr pressure measurement results per second + int16_t prs_mr = 2; + //pressure oversampling rate (value from 0 to 7) + //2^prs_osr internal pressure measurements per result + //A higher value increases precision + int16_t prs_osr = 2; + //startMeasureBothCont enables background mode + //temperature and pressure ar measured automatically + //High precision and hgh measure rates at the same time are not available. + //Consult Datasheet (or trial and error) for more information + int16_t ret = HP303BPressureSensor.startMeasureBothCont(temp_mr, temp_osr, prs_mr, prs_osr); + //Use one of the commented lines below instead to measure only temperature or pressure + //int16_t ret = HP303BPressureSensor.startMeasureTempCont(temp_mr, temp_osr); + //int16_t ret = HP303BPressureSensor.startMeasurePressureCont(prs_mr, prs_osr); + + + if (ret != 0) + { + Serial.print("Init FAILED! ret = "); + Serial.println(ret); + } + else + { + Serial.println("Init complete!"); + } +} + + + +void loop() +{ + unsigned char pressureCount = 20; + int32_t pressure[pressureCount]; + unsigned char temperatureCount = 20; + int32_t temperature[temperatureCount]; + + //This function writes the results of continuous measurements to the arrays given as parameters + //The parameters temperatureCount and pressureCount should hold the sizes of the arrays temperature and pressure when the function is called + //After the end of the function, temperatureCount and pressureCount hold the numbers of values written to the arrays + //Note: The HP303B cannot save more than 32 results. When its result buffer is full, it won't save any new measurement results + int16_t ret = HP303BPressureSensor.getContResults(temperature, temperatureCount, pressure, pressureCount); + + if (ret != 0) + { + Serial.println(); + Serial.println(); + Serial.print("FAIL! ret = "); + Serial.println(ret); + } + else + { + Serial.println(); + Serial.println(); + Serial.print(temperatureCount); + Serial.println(" temperature values found: "); + for (int16_t i = 0; i < temperatureCount; i++) + { + Serial.print(temperature[i]); + Serial.println(" degrees of Celsius"); + } + + Serial.println(); + Serial.print(pressureCount); + Serial.println(" pressure values found: "); + for (int16_t i = 0; i < pressureCount; i++) + { + Serial.print(pressure[i]); + Serial.println(" Pascal"); + } + } + + //Wait some time, so that the HP303B can refill its buffer + delay(10000); +} diff --git a/lib/LOLIN_HP303B/examples/i2c_command/i2c_command.ino b/lib/LOLIN_HP303B/examples/i2c_command/i2c_command.ino new file mode 100644 index 000000000..e629f7d5f --- /dev/null +++ b/lib/LOLIN_HP303B/examples/i2c_command/i2c_command.ino @@ -0,0 +1,75 @@ +#include + +// HP303B Opject +LOLIN_HP303B HP303BPressureSensor; + + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + + //Call begin to initialize HP303BPressureSensor + //The parameter 0x76 is the bus address. The default address is 0x77 and does not need to be given. + //HP303BPressureSensor.begin(Wire, 0x76); + //Use the commented line below instead of the one above to use the default I2C address. + //if you are using the Pressure 3 click Board, you need 0x76 + HP303BPressureSensor.begin(); + + Serial.println("Init complete!"); +} + + + +void loop() +{ + int32_t temperature; + int32_t pressure; + int16_t oversampling = 7; + int16_t ret; + Serial.println(); + + //lets the HP303B perform a Single temperature measurement with the last (or standard) configuration + //The result will be written to the paramerter temperature + //ret = HP303BPressureSensor.measureTempOnce(temperature); + //the commented line below does exactly the same as the one above, but you can also config the precision + //oversampling can be a value from 0 to 7 + //the HP303B will perform 2^oversampling internal temperature measurements and combine them to one result with higher precision + //measurements with higher precision take more time, consult datasheet for more information + ret = HP303BPressureSensor.measureTempOnce(temperature, oversampling); + + if (ret != 0) + { + //Something went wrong. + //Look at the library code for more information about return codes + Serial.print("FAIL! ret = "); + Serial.println(ret); + } + else + { + Serial.print("Temperature: "); + Serial.print(temperature); + Serial.println(" degrees of Celsius"); + } + + //Pressure measurement behaves like temperature measurement + //ret = HP303BPressureSensor.measurePressureOnce(pressure); + ret = HP303BPressureSensor.measurePressureOnce(pressure, oversampling); + if (ret != 0) + { + //Something went wrong. + //Look at the library code for more information about return codes + Serial.print("FAIL! ret = "); + Serial.println(ret); + } + else + { + Serial.print("Pressure: "); + Serial.print(pressure); + Serial.println(" Pascal"); + } + + //Wait some time + delay(500); +} diff --git a/lib/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino b/lib/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino new file mode 100644 index 000000000..d1221109b --- /dev/null +++ b/lib/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino @@ -0,0 +1,112 @@ +#include + +// HP303B Opject +LOLIN_HP303B HP303BPressureSensor = LOLIN_HP303B(); + +void onFifoFull(); + +const unsigned char pressureLength = 50; +unsigned char pressureCount = 0; +int32_t pressure[pressureLength]; +unsigned char temperatureCount = 0; +const unsigned char temperatureLength = 50; +int32_t temperature[temperatureLength]; + + + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + //Call begin to initialize HP303BPressureSensor + //The parameter 0x76 is the bus address. The default address is 0x77 and does not need to be given. + //HP303BPressureSensor.begin(Wire, 0x76); + //Use the commented line below instead to use the default I2C address. + HP303BPressureSensor.begin(); + + int16_t ret = HP303BPressureSensor.setInterruptPolarity(1); + ret = HP303BPressureSensor.setInterruptSources(1, 0, 0); + //clear interrupt flag by reading + HP303BPressureSensor.getIntStatusFifoFull(); + + //initialization of Interrupt for Controller unit + //SDO pin of HP303B has to be connected with interrupt pin + int16_t interruptPin = 3; + pinMode(interruptPin, INPUT); + attachInterrupt(digitalPinToInterrupt(interruptPin), onFifoFull, RISING); + + //start of a continuous measurement just like before + int16_t temp_mr = 3; + int16_t temp_osr = 2; + int16_t prs_mr = 1; + int16_t prs_osr = 3; + ret = HP303BPressureSensor.startMeasureBothCont(temp_mr, temp_osr, prs_mr, prs_osr); + if (ret != 0) + { + Serial.print("Init FAILED! ret = "); + Serial.println(ret); + } + else + { + Serial.println("Init complete!"); + } +} + + +void loop() +{ + //do other stuff + Serial.println("loop running"); + delay(500); + + + //if result arrays are full + //This could also be in the interrupt handler, but it would take too much time for a proper ISR + if (pressureCount == pressureLength && temperatureCount == temperatureLength) + { + //print results + Serial.println(); + Serial.println(); + Serial.print(temperatureCount); + Serial.println(" temperature values found: "); + for (int16_t i = 0; i < temperatureCount; i++) + { + Serial.print(temperature[i]); + Serial.println(" degrees of Celsius"); + } + Serial.println(); + Serial.print(pressureCount); + Serial.println(" pressure values found: "); + for (int16_t i = 0; i < pressureCount; i++) + { + Serial.print(pressure[i]); + Serial.println(" Pascal"); + } + Serial.println(); + Serial.println(); + //reset result counters + pressureCount = 0; + temperatureCount = 0; + } +} + + +//interrupt handler +void onFifoFull() +{ + //message for debugging + Serial.println("Interrupt handler called"); + + //clear interrupt flag by reading + HP303BPressureSensor.getIntStatusFifoFull(); + + //calculate the number of free indexes in the result arrays + unsigned char prs_freespace = pressureLength - pressureCount; + unsigned char temp_freespace = temperatureLength - temperatureCount; + //read the results from HP303B, new results will be added at the end of the arrays + HP303BPressureSensor.getContResults(&temperature[temperatureCount], temp_freespace, &pressure[pressureCount], prs_freespace); + //after reading the result counters are increased by the amount of new results + pressureCount += prs_freespace; + temperatureCount += temp_freespace; +} diff --git a/lib/LOLIN_HP303B/keywords.txt b/lib/LOLIN_HP303B/keywords.txt new file mode 100644 index 000000000..b283317f5 --- /dev/null +++ b/lib/LOLIN_HP303B/keywords.txt @@ -0,0 +1,26 @@ +####################################### +# Syntax Coloring Map For DHT12 +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +DHT12 KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +get KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +cTemp LITERAL1 +fTemp LITERAL1 +humidity LITERAL1 + + + diff --git a/lib/LOLIN_HP303B/library.properties b/lib/LOLIN_HP303B/library.properties new file mode 100644 index 000000000..7f71ad2b2 --- /dev/null +++ b/lib/LOLIN_HP303B/library.properties @@ -0,0 +1,9 @@ +name=LOLIN_HP303B +version=1.0.0 +author=WEMOS.CC +maintainer=WEMOS.CC +sentence=Library for the HP303B.. +paragraph=LOLIN HP303B +category=Device Control +url=https://github.com/wemos/LOLIN_HP303B_Library +architectures=* diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp new file mode 100644 index 000000000..c6f8aeb1d --- /dev/null +++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp @@ -0,0 +1,1685 @@ +#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; +} diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h new file mode 100644 index 000000000..4d04ff6da --- /dev/null +++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h @@ -0,0 +1,146 @@ +#ifndef __LOLIN_HP303B_H +#define __LOLIN_HP303B_H + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include +#include +#include "util/hp303b_consts.h" + +class LOLIN_HP303B +{ +public: + //constructor + LOLIN_HP303B(void); + //destructor + ~LOLIN_HP303B(void); + //begin + uint8_t begin(TwoWire &bus, uint8_t slaveAddress); + uint8_t begin(uint8_t slaveAddress=HP303B__STD_SLAVE_ADDRESS); + uint8_t begin(SPIClass &bus, int32_t chipSelect); + uint8_t begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire); + //end + void end(void); + + //general + uint8_t getProductId(void); + uint8_t getRevisionId(void); + + //Idle Mode + int16_t standby(void); + + //Command Mode + int16_t measureTempOnce(float &result); + int16_t measureTempOnce(float &result, uint8_t slaveAddress); + int16_t measureTempOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate); + int16_t startMeasureTempOnce(void); + int16_t startMeasureTempOnce(uint8_t oversamplingRate); + int16_t measurePressureOnce(float &result); + int16_t measurePressureOnce(float &result, uint8_t slaveAddress); + int16_t measurePressureOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate); + int16_t startMeasurePressureOnce(void); + int16_t startMeasurePressureOnce(uint8_t oversamplingRate); + int16_t getSingleResult(float &result); + + //Background Mode + int16_t startMeasureTempCont(uint8_t measureRate, uint8_t oversamplingRate); + int16_t startMeasurePressureCont(uint8_t measureRate, uint8_t oversamplingRate); + int16_t startMeasureBothCont(uint8_t tempMr, uint8_t tempOsr, uint8_t prsMr, uint8_t prsOsr); + int16_t getContResults(float *tempBuffer, uint8_t &tempCount, float *prsBuffer, uint8_t &prsCount); + + //Interrupt Control + int16_t setInterruptPolarity(uint8_t polarity); + int16_t setInterruptSources(uint8_t fifoFull, uint8_t tempReady, uint8_t prsReady); + int16_t getIntStatusFifoFull(void); + int16_t getIntStatusTempReady(void); + int16_t getIntStatusPrsReady(void); + + //function to fix a hardware problem on some devices + int16_t correctTemp(void); + +private: + //scaling factor table + static const int32_t scaling_facts[HP303B__NUM_OF_SCAL_FACTS]; + + //enum for operating mode + enum Mode + { + IDLE = 0x00, + CMD_PRS = 0x01, + CMD_TEMP = 0x02, + INVAL_OP_CMD_BOTH = 0x03, //invalid + INVAL_OP_CONT_NONE = 0x04, //invalid + CONT_PRS = 0x05, + CONT_TMP = 0x06, + CONT_BOTH = 0x07 + }; + Mode m_opMode; + + //flags + uint8_t m_initFail; + uint8_t m_productID; + uint8_t m_revisionID; + + //settings + uint8_t m_tempMr; + uint8_t m_tempOsr; + uint8_t m_prsMr; + uint8_t m_prsOsr; + uint8_t m_tempSensor; + + //compensation coefficients + int32_t m_c0Half; + int32_t m_c1; + int32_t m_c00; + int32_t m_c10; + int32_t m_c01; + int32_t m_c11; + int32_t m_c20; + int32_t m_c21; + int32_t m_c30; + //last measured scaled temperature + //(necessary for pressure compensation) + double m_lastTempScal; + + //bus specific + uint8_t m_SpiI2c; //0=SPI, 1=I2C + //used for I2C + TwoWire *m_i2cbus; + uint8_t m_slaveAddress; + //used for SPI + SPIClass *m_spibus; + int32_t m_chipSelect; + uint8_t m_threeWire; + + //measurement + uint8_t init(void); + int16_t readcoeffs(void); + int16_t setOpMode(uint8_t background, uint8_t temperature, uint8_t pressure); + int16_t setOpMode(uint8_t opMode); + int16_t configTemp(uint8_t temp_mr, uint8_t temp_osr); + int16_t configPressure(uint8_t prs_mr, uint8_t prs_osr); + uint16_t calcBusyTime(uint16_t temp_rate, uint16_t temp_osr); + int16_t getTemp(float *result); + int16_t getPressure(float *result); + int16_t getFIFOvalue(float *value); + float calcTemp(float raw); + float calcPressure(float raw); + + //bus specific + int16_t readByte(uint8_t regAddress); + int16_t readByteSPI(uint8_t regAddress); + int16_t readBlock(uint8_t regAddress, uint8_t length, uint8_t *buffer); + int16_t readBlockSPI(uint8_t regAddress, uint8_t length, uint8_t *readbuffer); + int16_t writeByte(uint8_t regAddress, uint8_t data); + int16_t writeByte(uint8_t regAddress, uint8_t data, uint8_t check); + int16_t writeByteSpi(uint8_t regAddress, uint8_t data, uint8_t check); + int16_t writeByteBitfield(uint8_t data, uint8_t regAddress, uint8_t mask, uint8_t shift); + int16_t writeByteBitfield(uint8_t data, uint8_t regAddress, uint8_t mask, uint8_t shift, uint8_t check); + int16_t readByteBitfield(uint8_t regAddress, uint8_t mask, uint8_t shift); +}; + +#endif diff --git a/lib/LOLIN_HP303B/src/util/hp303b_consts.h b/lib/LOLIN_HP303B/src/util/hp303b_consts.h new file mode 100644 index 000000000..f93629e93 --- /dev/null +++ b/lib/LOLIN_HP303B/src/util/hp303b_consts.h @@ -0,0 +1,258 @@ +/** + * + * + */ + +#ifndef __HP303B_CONSTS_H_ +#define __HP303B_CONSTS_H_ + + + //general Constants +#define HP303B__PROD_ID 0U +#define HP303B__STD_SLAVE_ADDRESS 0x77U +#define HP303B__SPI_WRITE_CMD 0x00U +#define HP303B__SPI_READ_CMD 0x80U +#define HP303B__SPI_RW_MASK 0x80U +#define HP303B__SPI_MAX_FREQ 100000U + +#define HP303B__LSB 0x01U + +#define HP303B__TEMP_STD_MR 2U +#define HP303B__TEMP_STD_OSR 3U +#define HP303B__PRS_STD_MR 2U +#define HP303B__PRS_STD_OSR 3U +#define HP303B__OSR_SE 3U +//we use 0.1 mS units for time calculations, so 10 units are one millisecond +#define HP303B__BUSYTIME_SCALING 10U +// DPS310 has 10 milliseconds of spare time for each synchronous measurement / per second for asynchronous measurements +// this is for error prevention on friday-afternoon-products :D +// you can set it to 0 if you dare, but there is no warranty that it will still work +#define HP303B__BUSYTIME_FAILSAFE 10U +#define HP303B__MAX_BUSYTIME ((1000U-HP303B__BUSYTIME_FAILSAFE)*HP303B__BUSYTIME_SCALING) +#define HP303B__NUM_OF_SCAL_FACTS 8 + +#define HP303B__SUCCEEDED 0 +#define HP303B__FAIL_UNKNOWN -1 +#define HP303B__FAIL_INIT_FAILED -2 +#define HP303B__FAIL_TOOBUSY -3 +#define HP303B__FAIL_UNFINISHED -4 + + + //Constants for register manipulation + //SPI mode (3 or 4 wire) +#define HP303B__REG_ADR_SPI3W 0x09U +#define HP303B__REG_CONTENT_SPI3W 0x01U + + + //product id +#define HP303B__REG_INFO_PROD_ID HP303B__REG_ADR_PROD_ID, \ + HP303B__REG_MASK_PROD_ID, \ + HP303B__REG_SHIFT_PROD_ID +#define HP303B__REG_ADR_PROD_ID 0x0DU +#define HP303B__REG_MASK_PROD_ID 0x0FU +#define HP303B__REG_SHIFT_PROD_ID 0U + + //revision id +#define HP303B__REG_INFO_REV_ID HP303B__REG_ADR_REV_ID, \ + HP303B__REG_MASK_REV_ID, \ + HP303B__REG_SHIFT_REV_ID +#define HP303B__REG_ADR_REV_ID 0x0DU +#define HP303B__REG_MASK_REV_ID 0xF0U +#define HP303B__REG_SHIFT_REV_ID 4U + + //operating mode +#define HP303B__REG_INFO_OPMODE HP303B__REG_ADR_OPMODE, \ + HP303B__REG_MASK_OPMODE, \ + HP303B__REG_SHIFT_OPMODE +#define HP303B__REG_ADR_OPMODE 0x08U +#define HP303B__REG_MASK_OPMODE 0x07U +#define HP303B__REG_SHIFT_OPMODE 0U + + + //temperature measure rate +#define HP303B__REG_INFO_TEMP_MR HP303B__REG_ADR_TEMP_MR, \ + HP303B__REG_MASK_TEMP_MR, \ + HP303B__REG_SHIFT_TEMP_MR +#define HP303B__REG_ADR_TEMP_MR 0x07U +#define HP303B__REG_MASK_TEMP_MR 0x70U +#define HP303B__REG_SHIFT_TEMP_MR 4U + + //temperature oversampling rate +#define HP303B__REG_INFO_TEMP_OSR HP303B__REG_ADR_TEMP_OSR, \ + HP303B__REG_MASK_TEMP_OSR, \ + HP303B__REG_SHIFT_TEMP_OSR +#define HP303B__REG_ADR_TEMP_OSR 0x07U +#define HP303B__REG_MASK_TEMP_OSR 0x07U +#define HP303B__REG_SHIFT_TEMP_OSR 0U + + //temperature sensor +#define HP303B__REG_INFO_TEMP_SENSOR HP303B__REG_ADR_TEMP_SENSOR, \ + HP303B__REG_MASK_TEMP_SENSOR, \ + HP303B__REG_SHIFT_TEMP_SENSOR +#define HP303B__REG_ADR_TEMP_SENSOR 0x07U +#define HP303B__REG_MASK_TEMP_SENSOR 0x80U +#define HP303B__REG_SHIFT_TEMP_SENSOR 7U + + //temperature sensor recommendation +#define HP303B__REG_INFO_TEMP_SENSORREC HP303B__REG_ADR_TEMP_SENSORREC, \ + HP303B__REG_MASK_TEMP_SENSORREC, \ + HP303B__REG_SHIFT_TEMP_SENSORREC +#define HP303B__REG_ADR_TEMP_SENSORREC 0x28U +#define HP303B__REG_MASK_TEMP_SENSORREC 0x80U +#define HP303B__REG_SHIFT_TEMP_SENSORREC 7U + + //temperature shift enable (if temp_osr>3) +#define HP303B__REG_INFO_TEMP_SE HP303B__REG_ADR_TEMP_SE, \ + HP303B__REG_MASK_TEMP_SE, \ + HP303B__REG_SHIFT_TEMP_SE +#define HP303B__REG_ADR_TEMP_SE 0x09U +#define HP303B__REG_MASK_TEMP_SE 0x08U +#define HP303B__REG_SHIFT_TEMP_SE 3U + + + //pressure measure rate +#define HP303B__REG_INFO_PRS_MR HP303B__REG_ADR_PRS_MR, \ + HP303B__REG_MASK_PRS_MR, \ + HP303B__REG_SHIFT_PRS_MR +#define HP303B__REG_ADR_PRS_MR 0x06U +#define HP303B__REG_MASK_PRS_MR 0x70U +#define HP303B__REG_SHIFT_PRS_MR 4U + + //pressure oversampling rate +#define HP303B__REG_INFO_PRS_OSR HP303B__REG_ADR_PRS_OSR, \ + HP303B__REG_MASK_PRS_OSR, \ + HP303B__REG_SHIFT_PRS_OSR +#define HP303B__REG_ADR_PRS_OSR 0x06U +#define HP303B__REG_MASK_PRS_OSR 0x07U +#define HP303B__REG_SHIFT_PRS_OSR 0U + + //pressure shift enable (if prs_osr>3) +#define HP303B__REG_INFO_PRS_SE HP303B__REG_ADR_PRS_SE, \ + HP303B__REG_MASK_PRS_SE, \ + HP303B__REG_SHIFT_PRS_SE +#define HP303B__REG_ADR_PRS_SE 0x09U +#define HP303B__REG_MASK_PRS_SE 0x04U +#define HP303B__REG_SHIFT_PRS_SE 2U + + + //temperature ready flag +#define HP303B__REG_INFO_TEMP_RDY HP303B__REG_ADR_TEMP_RDY, \ + HP303B__REG_MASK_TEMP_RDY, \ + HP303B__REG_SHIFT_TEMP_RDY +#define HP303B__REG_ADR_TEMP_RDY 0x08U +#define HP303B__REG_MASK_TEMP_RDY 0x20U +#define HP303B__REG_SHIFT_TEMP_RDY 5U + + //pressure ready flag +#define HP303B__REG_INFO_PRS_RDY HP303B__REG_ADR_PRS_RDY, \ + HP303B__REG_MASK_PRS_RDY, \ + HP303B__REG_SHIFT_PRS_RDY +#define HP303B__REG_ADR_PRS_RDY 0x08U +#define HP303B__REG_MASK_PRS_RDY 0x10U +#define HP303B__REG_SHIFT_PRS_RDY 4U + + //pressure value +#define HP303B__REG_ADR_PRS 0x00U +#define HP303B__REG_LEN_PRS 3U + + //temperature value +#define HP303B__REG_ADR_TEMP 0x03U +#define HP303B__REG_LEN_TEMP 3U + + //compensation coefficients +#define HP303B__REG_ADR_COEF 0x10U +#define HP303B__REG_LEN_COEF 18 + + + //FIFO enable +#define HP303B__REG_INFO_FIFO_EN HP303B__REG_ADR_FIFO_EN, \ + HP303B__REG_MASK_FIFO_EN, \ + HP303B__REG_SHIFT_FIFO_EN +#define HP303B__REG_ADR_FIFO_EN 0x09U +#define HP303B__REG_MASK_FIFO_EN 0x02U +#define HP303B__REG_SHIFT_FIFO_EN 1U + + //FIFO flush +#define HP303B__REG_INFO_FIFO_FL HP303B__REG_ADR_FIFO_EN, \ + HP303B__REG_MASK_FIFO_EN, \ + HP303B__REG_SHIFT_FIFO_EN +#define HP303B__REG_ADR_FIFO_FL 0x0CU +#define HP303B__REG_MASK_FIFO_FL 0x80U +#define HP303B__REG_SHIFT_FIFO_FL 7U + + //FIFO empty +#define HP303B__REG_INFO_FIFO_EMPTY HP303B__REG_ADR_FIFO_EMPTY, \ + HP303B__REG_MASK_FIFO_EMPTY, \ + HP303B__REG_SHIFT_FIFO_EMPTY +#define HP303B__REG_ADR_FIFO_EMPTY 0x0BU +#define HP303B__REG_MASK_FIFO_EMPTY 0x01U +#define HP303B__REG_SHIFT_FIFO_EMPTY 0U + + //FIFO full +#define HP303B__REG_INFO_FIFO_FULL HP303B__REG_ADR_FIFO_FULL, \ + HP303B__REG_MASK_FIFO_FULL, \ + HP303B__REG_SHIFT_FIFO_FULL +#define HP303B__REG_ADR_FIFO_FULL 0x0BU +#define HP303B__REG_MASK_FIFO_FULL 0x02U +#define HP303B__REG_SHIFT_FIFO_FULL 1U + + + //INT HL +#define HP303B__REG_INFO_INT_HL HP303B__REG_ADR_INT_HL, \ + HP303B__REG_MASK_INT_HL, \ + HP303B__REG_SHIFT_INT_HL +#define HP303B__REG_ADR_INT_HL 0x09U +#define HP303B__REG_MASK_INT_HL 0x80U +#define HP303B__REG_SHIFT_INT_HL 7U + + //INT FIFO enable +#define HP303B__REG_INFO_INT_EN_FIFO HP303B__REG_ADR_INT_EN_FIFO, \ + HP303B__REG_MASK_INT_EN_FIFO, \ + HP303B__REG_SHIFT_INT_EN_FIFO +#define HP303B__REG_ADR_INT_EN_FIFO 0x09U +#define HP303B__REG_MASK_INT_EN_FIFO 0x40U +#define HP303B__REG_SHIFT_INT_EN_FIFO 6U + + //INT TEMP enable +#define HP303B__REG_INFO_INT_EN_TEMP HP303B__REG_ADR_INT_EN_TEMP, \ + HP303B__REG_MASK_INT_EN_TEMP, \ + HP303B__REG_SHIFT_INT_EN_TEMP +#define HP303B__REG_ADR_INT_EN_TEMP 0x09U +#define HP303B__REG_MASK_INT_EN_TEMP 0x20U +#define HP303B__REG_SHIFT_INT_EN_TEMP 5U + + //INT PRS enable +#define HP303B__REG_INFO_INT_EN_PRS HP303B__REG_ADR_INT_EN_PRS, \ + HP303B__REG_MASK_INT_EN_PRS, \ + HP303B__REG_SHIFT_INT_EN_PRS +#define HP303B__REG_ADR_INT_EN_PRS 0x09U +#define HP303B__REG_MASK_INT_EN_PRS 0x10U +#define HP303B__REG_SHIFT_INT_EN_PRS 4U + + //INT FIFO flag +#define HP303B__REG_INFO_INT_FLAG_FIFO HP303B__REG_ADR_INT_FLAG_FIFO, \ + HP303B__REG_MASK_INT_FLAG_FIFO, \ + HP303B__REG_SHIFT_INT_FLAG_FIFO +#define HP303B__REG_ADR_INT_FLAG_FIFO 0x0AU +#define HP303B__REG_MASK_INT_FLAG_FIFO 0x04U +#define HP303B__REG_SHIFT_INT_FLAG_FIFO 2U + + //INT TMP flag +#define HP303B__REG_INFO_INT_FLAG_TEMP HP303B__REG_ADR_INT_FLAG_TEMP, \ + HP303B__REG_MASK_INT_FLAG_TEMP, \ + HP303B__REG_SHIFT_INT_FLAG_TEMP +#define HP303B__REG_ADR_INT_FLAG_TEMP 0x0AU +#define HP303B__REG_MASK_INT_FLAG_TEMP 0x02U +#define HP303B__REG_SHIFT_INT_FLAG_TEMP 1U + + //INT PRS flag +#define HP303B__REG_INFO_INT_FLAG_PRS HP303B__REG_ADR_INT_FLAG_PRS, \ + HP303B__REG_MASK_INT_FLAG_PRS, \ + HP303B__REG_SHIFT_INT_FLAG_PRS +#define HP303B__REG_ADR_INT_FLAG_PRS 0x0AU +#define HP303B__REG_MASK_INT_FLAG_PRS 0x01U +#define HP303B__REG_SHIFT_INT_FLAG_PRS 0U + + + +#endif /* DPS310_CONSTS_H_ */ diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 04de532d6..c973e30f5 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -578,8 +578,10 @@ void GetFeatures(void) #ifdef USE_TELEGRAM feature6 |= 0x00008000; // xdrv_40_telegram.ino #endif +#ifdef USE_HP303B + feature6 |= 0x00010000; // xsns_73_hp303b.ino +#endif -// feature6 |= 0x00010000; // feature6 |= 0x00020000; // feature6 |= 0x00040000; // feature6 |= 0x00080000; diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino new file mode 100644 index 000000000..1be0a1606 --- /dev/null +++ b/tasmota/xsns_73_hp303b.ino @@ -0,0 +1,176 @@ +/* + xsns_72_hp303b.ino - HP303B digital barometric air pressure sensor support for Tasmota + + Copyright (C) 2020 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifdef USE_I2C +#ifdef USE_HP303B +/*********************************************************************************************\ + * HP303B - Gas (TVOC - Total Volatile Organic Compounds) and Air Quality (CO2) + * + * Source: Lolin LOLIN_HP303B_Library + * + * I2C Address: 0x77 or 0x76 +\*********************************************************************************************/ + +#define XSNS_73 73 +#define XI2C_52 52 // See I2CDEVICES.md + +#include +// HP303B Object +LOLIN_HP303B HP303BSensor = LOLIN_HP303B(); + +#define HP303B_MAX_SENSORS 2 +#define HP303B_START_ADDRESS 0x76 + +struct { +char types[7] = "HP303B"; +uint8_t count = 0; +int16_t oversampling = 7; +} hp303b_cfg; + +struct BHP303B { + uint8_t address; + uint8_t valid = 0; + float temperature = NAN; + float pressure = NAN; +} hp303b_sensor[HP303B_MAX_SENSORS]; +/*********************************************************************************************/ + +bool HP303B_Read(uint8_t hp303b_idx) +{ + if (hp303b_sensor[hp303b_idx].valid) { hp303b_sensor[hp303b_idx].valid--; } + + float t; + float p; + int16_t ret; + + ret = HP303BSensor.measureTempOnce(t, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling); + if (ret != 0) + return false; + + ret = HP303BSensor.measurePressureOnce(p, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling); + if (ret != 0) + return false; + + hp303b_sensor[hp303b_idx].temperature = (float)ConvertTemp(t); + hp303b_sensor[hp303b_idx].pressure = (float)ConvertPressure(p) / 100; //conversion to hPa + + hp303b_sensor[hp303b_idx].valid = SENSOR_MAX_MISS; + return true; +} + +/********************************************************************************************/ + +void HP303B_Detect(void) +{ + for (uint32_t i = 0; i < HP303B_MAX_SENSORS; i++) + { + if (!I2cSetDevice(HP303B_START_ADDRESS + i )) { continue; } + + if (HP303BSensor.begin(HP303B_START_ADDRESS + i)) + { + hp303b_sensor[hp303b_cfg.count].address = HP303B_START_ADDRESS + i; + I2cSetActiveFound(hp303b_sensor[hp303b_cfg.count].address, hp303b_cfg.types); + hp303b_cfg.count++; + } + } +} + +void HP303B_EverySecond(void) +{ + for (uint32_t i = 0; i < hp303b_cfg.count; i++) { + if (uptime &1) { + if (!HP303B_Read(i)) { + AddLogMissed(hp303b_cfg.types, hp303b_sensor[i].valid); + } + } + } +} + +void HP303B_Show(bool json) +{ + for (uint32_t i = 0; i < hp303b_cfg.count; i++) { + char sensor_name[12]; + strlcpy(sensor_name, hp303b_cfg.types, sizeof(sensor_name)); + if (hp303b_cfg.count > 1) { + snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c0x%02X"), sensor_name, IndexSeparator(), hp303b_sensor[i].address); // HP303B-0x76, HP303B-0x77 + } + + if (hp303b_sensor[i].valid) + { + char str_temperature[33]; + dtostrfd(hp303b_sensor[i].temperature, Settings.flag2.temperature_resolution, str_temperature); + char str_pressure[33]; + dtostrfd(hp303b_sensor[i].pressure, Settings.flag2.pressure_resolution, str_pressure); + + if (json) + { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), sensor_name, str_temperature, str_pressure); + ResponseJsonEnd(); + #ifdef USE_DOMOTICZ + // Domoticz and knx only support one temp sensor + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_TEMP, hp303b_sensor[i].temperature); + } + #endif // USE_DOMOTICZ + #ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, str_temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_PRESSURE, sensor_name, str_pressure, PressureUnit().c_str()); + #endif // USE_WEBSERVER + } + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns73(uint8_t function) +{ + if (!I2cEnabled(XI2C_52)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + HP303B_Detect(); + } + else if (hp303b_cfg.count) + { + switch (function) + { + case FUNC_EVERY_SECOND: + HP303B_EverySecond(); + break; + case FUNC_JSON_APPEND: + HP303B_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HP303B_Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_HP303B +#endif // USE_I2C diff --git a/tools/decode-status.py b/tools/decode-status.py index 4228b69df..2ec5b472e 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -206,7 +206,7 @@ a_features = [[ "USE_IAQ","USE_DISPLAY_SEVENSEG","USE_AS3935","USE_PING", "USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","USE_VEML6075", "USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM", - "","","","", + "USE_HP303B","","","", "","","","", "","","","", "","","","USE_WEBCAM"