Tasmota/lib/lib_i2c/BlueRobotics_MS5837_Library/MS5837.cpp

258 lines
6.7 KiB
C++
Raw Normal View History

#include "MS5837.h"
#include <Wire.h>
const uint8_t MS5837_ADDR = 0x76;
const uint8_t MS5837_RESET = 0x1E;
const uint8_t MS5837_ADC_READ = 0x00;
const uint8_t MS5837_PROM_READ = 0xA0;
const uint8_t MS5837_CONVERT_D1_8192 = 0x4A;
const uint8_t MS5837_CONVERT_D2_8192 = 0x5A;
const float MS5837::Pa = 100.0f;
const float MS5837::bar = 0.001f;
const float MS5837::mbar = 1.0f;
const uint8_t MS5837::MS5837_30BA = 0;
const uint8_t MS5837::MS5837_02BA = 1;
const uint8_t MS5837::MS5837_UNRECOGNISED = 255;
const uint8_t MS5837_02BA01 = 0x00; // Sensor version: From MS5837_02BA datasheet Version PROM Word 0
const uint8_t MS5837_02BA21 = 0x15; // Sensor version: From MS5837_02BA datasheet Version PROM Word 0
const uint8_t MS5837_30BA26 = 0x1A; // Sensor version: From MS5837_30BA datasheet Version PROM Word 0
MS5837::MS5837() {
fluidDensity = 1029;
}
bool MS5837::begin(TwoWire &wirePort) {
return (init(wirePort));
}
bool MS5837::init(TwoWire &wirePort) {
_i2cPort = &wirePort; //Grab which port the user wants us to use
// Reset the MS5837, per datasheet
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_RESET);
_i2cPort->endTransmission();
// Wait for reset to complete
delay(10);
// Read calibration values and CRC
for ( uint8_t i = 0 ; i < 7 ; i++ ) {
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_PROM_READ+i*2);
_i2cPort->endTransmission();
2024-11-11 15:05:03 +00:00
_i2cPort->requestFrom(MS5837_ADDR, (uint8_t)2);
C[i] = (_i2cPort->read() << 8) | _i2cPort->read();
}
// Verify that data is correct with CRC
uint8_t crcRead = C[0] >> 12;
uint8_t crcCalculated = crc4(C);
if ( crcCalculated != crcRead ) {
return false; // CRC fail
}
uint8_t version = (C[0] >> 5) & 0x7F; // Extract the sensor version from PROM Word 0
// Set _model according to the sensor version
if (version == MS5837_02BA01)
{
_model = MS5837_02BA;
}
else if (version == MS5837_02BA21)
{
_model = MS5837_02BA;
}
else if (version == MS5837_30BA26)
{
_model = MS5837_30BA;
}
else
{
_model = MS5837_UNRECOGNISED;
}
// The sensor has passed the CRC check, so we should return true even if
// the sensor version is unrecognised.
// (The MS5637 has the same address as the MS5837 and will also pass the CRC check)
// (but will hopefully be unrecognised.)
return true;
}
void MS5837::setModel(uint8_t model) {
_model = model;
}
uint8_t MS5837::getModel() {
return (_model);
}
void MS5837::setFluidDensity(float density) {
fluidDensity = density;
}
void MS5837::read() {
//Check that _i2cPort is not NULL (i.e. has the user forgoten to call .init or .begin?)
if (_i2cPort == NULL)
{
return;
}
// Request D1 conversion
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_CONVERT_D1_8192);
_i2cPort->endTransmission();
delay(20); // Max conversion time per datasheet
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_ADC_READ);
_i2cPort->endTransmission();
2024-11-11 15:05:03 +00:00
_i2cPort->requestFrom(MS5837_ADDR, (uint8_t)3);
D1_pres = 0;
D1_pres = _i2cPort->read();
D1_pres = (D1_pres << 8) | _i2cPort->read();
D1_pres = (D1_pres << 8) | _i2cPort->read();
// Request D2 conversion
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_CONVERT_D2_8192);
_i2cPort->endTransmission();
delay(20); // Max conversion time per datasheet
_i2cPort->beginTransmission(MS5837_ADDR);
_i2cPort->write(MS5837_ADC_READ);
_i2cPort->endTransmission();
2024-11-11 15:05:03 +00:00
_i2cPort->requestFrom(MS5837_ADDR, (uint8_t)3);
D2_temp = 0;
D2_temp = _i2cPort->read();
D2_temp = (D2_temp << 8) | _i2cPort->read();
D2_temp = (D2_temp << 8) | _i2cPort->read();
calculate();
}
void MS5837::calculate() {
// Given C1-C6 and D1, D2, calculated TEMP and P
// Do conversion first and then second order temp compensation
int32_t dT = 0;
int64_t SENS = 0;
int64_t OFF = 0;
int32_t SENSi = 0;
int32_t OFFi = 0;
int32_t Ti = 0;
int64_t OFF2 = 0;
int64_t SENS2 = 0;
// Terms called
dT = D2_temp-uint32_t(C[5])*256l;
if ( _model == MS5837_02BA ) {
SENS = int64_t(C[1])*65536l+(int64_t(C[3])*dT)/128l;
OFF = int64_t(C[2])*131072l+(int64_t(C[4])*dT)/64l;
P = (D1_pres*SENS/(2097152l)-OFF)/(32768l);
} else {
SENS = int64_t(C[1])*32768l+(int64_t(C[3])*dT)/256l;
OFF = int64_t(C[2])*65536l+(int64_t(C[4])*dT)/128l;
P = (D1_pres*SENS/(2097152l)-OFF)/(8192l);
}
// Temp conversion
TEMP = 2000l+int64_t(dT)*C[6]/8388608LL;
//Second order compensation
if ( _model == MS5837_02BA ) {
if((TEMP/100)<20){ //Low temp
Ti = (11*int64_t(dT)*int64_t(dT))/(34359738368LL);
OFFi = (31*(TEMP-2000)*(TEMP-2000))/8;
SENSi = (63*(TEMP-2000)*(TEMP-2000))/32;
}
} else {
if((TEMP/100)<20){ //Low temp
Ti = (3*int64_t(dT)*int64_t(dT))/(8589934592LL);
OFFi = (3*(TEMP-2000)*(TEMP-2000))/2;
SENSi = (5*(TEMP-2000)*(TEMP-2000))/8;
if((TEMP/100)<-15){ //Very low temp
OFFi = OFFi+7*(TEMP+1500l)*(TEMP+1500l);
SENSi = SENSi+4*(TEMP+1500l)*(TEMP+1500l);
}
}
else if((TEMP/100)>=20){ //High temp
Ti = 2*(dT*dT)/(137438953472LL);
OFFi = (1*(TEMP-2000)*(TEMP-2000))/16;
SENSi = 0;
}
}
OFF2 = OFF-OFFi; //Calculate pressure and temp second order
SENS2 = SENS-SENSi;
TEMP = (TEMP-Ti);
if ( _model == MS5837_02BA ) {
P = (((D1_pres*SENS2)/2097152l-OFF2)/32768l);
} else {
P = (((D1_pres*SENS2)/2097152l-OFF2)/8192l);
}
}
float MS5837::pressure(float conversion) {
if ( _model == MS5837_02BA ) {
return P*conversion/100.0f;
}
else {
return P*conversion/10.0f;
}
}
float MS5837::temperature() {
return TEMP/100.0f;
}
// The pressure sensor measures absolute pressure, so it will measure the atmospheric pressure + water pressure
// We subtract the atmospheric pressure to calculate the depth with only the water pressure
// The average atmospheric pressure of 101300 pascal is used for the calcuation, but atmospheric pressure varies
// If the atmospheric pressure is not 101300 at the time of reading, the depth reported will be offset
// In order to calculate the correct depth, the actual atmospheric pressure should be measured once in air, and
// that value should subtracted for subsequent depth calculations.
float MS5837::depth() {
return (pressure(MS5837::Pa)-101300)/(fluidDensity*9.80665f);
}
float MS5837::altitude() {
return (1-pow((pressure()/1013.25f),.190284f))*145366.45f*.3048f;
}
uint8_t MS5837::crc4(uint16_t n_prom[]) {
uint16_t n_rem = 0;
n_prom[0] = ((n_prom[0]) & 0x0FFF);
n_prom[7] = 0;
for ( uint8_t i = 0 ; i < 16; i++ ) {
if ( i%2 == 1 ) {
n_rem ^= (uint16_t)((n_prom[i>>1]) & 0x00FF);
} else {
n_rem ^= (uint16_t)(n_prom[i>>1] >> 8);
}
for ( uint8_t n_bit = 8 ; n_bit > 0 ; n_bit-- ) {
if ( n_rem & 0x8000 ) {
n_rem = (n_rem << 1) ^ 0x3000;
} else {
n_rem = (n_rem << 1);
}
}
}
n_rem = ((n_rem >> 12) & 0x000F);
return n_rem ^ 0x00;
}