#include "MS5837.h" #include 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(); _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(); _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(); _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; }