RobTillaart / MS5611

Arduino library for MS5611 temperature and pressure sensor
MIT License
17 stars 5 forks source link

Add flag to enable / disable temperature compensation of pressure. #25

Closed RobTillaart closed 2 years ago

RobTillaart commented 2 years ago

it would make the math faster. Needs investigation what the error will be. (rather simple)

RobTillaart commented 2 years ago

on the other hand, added offset functions to improve accuracy.

ratio-x commented 2 years ago

Providing an adjustable device-specific (more situation-specific) pressure offset is sufficient in my opinion. Furthermore, not providing temperature compensation gives users clarity what to expect from this library.

Measured pressure has always to be calibrated to local-/daytime-specific pressure, because GY-63/MS5611 with high-resolution readings is often used as altimeter and small variance in pressure can have huge impacts to altitude.

Derivation of relative altitude (depending on absolute altitude, pressure, temperature, humidity and whatelse) is very complex. See "International Standard Atmosphere" (ISA). In reverse it's impossible to provide an accurate pressure, accurate absolute altitude or accurate relative altitude steps with only barometric (sensoric) pressure and temperature. In other words, temperature compensation of pressure is also dependent on absolute altitude

Calculations with absolute/relative altitude follow rulesets and formulas specific to the use case (usage in static altitude ./. slightly changed ./. significantly changed altitude), often mixed in some kind of fuzzy logic with other altitude sources (GPS, geodesy, etc.) and should therefore be done in external applications or separated libraries. Otherwise it's unclear, which parts are implemented in MS5611 library.

Also the measured temperature is not reliable in some conditions.

ratio-x commented 2 years ago

On the other hand: If it's simple and optional, why not.

RobTillaart commented 2 years ago

I have seen several libraries (*) or just programs for the sensor. Some of them have a similar flag and the only reason I can think of is that it will be faster. How much faster and if it is worth to trade precision for that is up to the user / application.

Think most users choose this sensor as it has that compensated pressure.

Yes implementation is simple and extra footprint only a few bytes.

*) _Looked at other implementations as the SPI version of this library https://github.com/RobTillaart/MS5611_SPI has a side effect of internal heat build up. Not understood what causes it._

ratio-x commented 2 years ago

What do you mean with "faster"? Do you mean that a temperature-compensated pressure is provided by the MS5611 chip itself? Then of course it should be readable by flag or separate function.

Anything else regarding (abs/rel) altitude should be calculated outside the library.

RobTillaart commented 2 years ago

faster because the library would do 50% or so less floating point operations.

Do you mean that a temperature-compensated pressure is provided by the MS5611 chip itself?

unfortunately not.

Anything else regarding (abs/rel) altitude should be calculated outside the library.

agree, mostly I do not go (far) outside the datasheet. Think for the altitude functions other libraries exist?

ratio-x commented 2 years ago

O.K. Let's then discuss the formula/algorithm.

RobTillaart commented 2 years ago

It is in the datasheet.

RobTillaart commented 2 years ago

the flag would enable the 2nd order compensation -> this part line 185++ would skip 19 floating point operations (compiler may optimize some of them)

  // SECOND ORDER COMPENSATION - PAGE 8/20
  // COMMENT OUT < 2000 CORRECTION IF NOT NEEDED
  // NOTE TEMPERATURE IS IN 0.01 C
  if (_temperature < 2000)
  {
    float T2 = dT * dT * 4.6566128731E-10;
    float t = (_temperature - 2000) * (_temperature - 2000);
    float offset2 = 2.5 * t;
    float sens2 = 1.25 * t;
    // COMMENT OUT < -1500 CORRECTION IF NOT NEEDED
    if (_temperature < -1500)
    {
      t = (_temperature + 1500) * (_temperature + 1500);
      offset2 += 7 * t;
      sens2 += 5.5 * t;
    }
    _temperature -= T2;
    offset -= offset2;
    sens -= sens2;
  }
  // END SECOND ORDER COMPENSATION
ratio-x commented 2 years ago

Sorry, there was a fundamental misunderstanding, which was my fault, because I never read the datasheet beyond the i2c topics. I thought that the sensor would spit out compensated pressure and temperature values (based on factory calibration), and reading calibration values are just for interest/fun.

First, lets start with the latest version af the datasheet. The document I'm relying is v 06/2017 and has 22 pages and the pressure and temperature calculation is on page 8/22.

https://www.te.com/commerce/DocumentDelivery/DDEController?Action=showdoc&DocId=Data+Sheet%7FMS5611-01BA03%7FB3%7Fpdf%7FEnglish%7FENG_DS_MS5611-01BA03_B3.pdf%7FCAT-BLPS0036

Page 6:

(I think there is a typo...)

FACTORY CALIBRATION Every module is individually factory calibrated at two temperatures and two pressures. As a result, 6 coefficients necessary to compensate for process pressure variations and temperature variations are calculated and stored in the 128-bit PROM of each module. These bits (partitioned into 6 coefficients) must be read by the microcontroller software and used in the program converting D1 and D2 into compensated pressure and temperature values.

Page 7:

COMMANDS The MS5611-01BA has only five basic commands:

  1. Reset
  2. Read PROM (128 bit of calibration words)
  3. D1 conversion
  4. D2 conversion
  5. Read ADC result (24 bit pressure / temperature)

Page 8:

PRESSURE AND TEMPERATURE CALCULATION

Page 9:

SECOND ORDER TEMPERATURE COMPENSATION

Page 20:

Diagram of Temperature Error Accuracy vs temperature (typical)

(which is a clear argument for 2nd level compensation, because there are many use cases for temperatures below 20 °C)

Summary: The library currently does the calculations described on page 8, right?

The optional flag enables the 2nd order compensation. Your code looks correct so far. I'm not sure, if the rounded value of 1 / 2^31 (0,0000000004656612873077392578125 rouded to 4.6566128731E-10) may have a significant impact.

Where/when would be the offset functions applied, if 2nd order compensation is enabled by flag? After the calculations on page 8 or after 2nd order compensation on page 9? Without flag enabled it is applied after basic calculation on page 8, right? An application generally after the last calculation seems plausible to me.

What is the difference between D1/D2 and ADC reading? I can't understand this from the datasheet.

ratio-x commented 2 years ago

I still don't understand the saving of floating point operations. Calculations on page 8 are still necessary, 2nd order compensation on page 9 is additional to this.

RobTillaart commented 2 years ago

The library currently does the calculations described on page 8, right?

yes

The optional flag enables the 2nd order compensation. Your code looks correct so far. I'm not sure, if the rounded value of 1 / 2^31 (0,0000000004656612873077392578125 rouded to 4.6566128731E-10) may have a significant impact.

The impact will be around the 6th or 7th decimal for float (32 bit)
Only platforms that support double (64 bit) would have higher precision. As the pressure itself is only 6 decimals there is no need to do the math in double..

Where/when would be the offset functions applied

The offsets will be added after last calculations, so after the 2nd order compensation.. I'm working on a SPI version of the lib and it is about 2.5°C too high due to internal heating (no cause found yet). I can compensate for that with the offset. Because the temperature is too high, the pressure is also slightly incorrect as the 2nd order compensation (2OC) is incorrect. (it is probably better to add the Temperature offset before the 2OC of pressure, needs testing ==> future)

What is the difference between D1/D2 ?

_D1 is the raw uncalibrated pressure. _D2 is the raw uncalibrated temperature. These refer to the names as used in the datasheet.

I still don't understand the saving of floating point operations.

Some applications prefer more often data than precise data, seen that for many different sensors, Some people switch between modes, to save time and possible even energy. I still have to quantify how much time is gained, I might be only 100-250 us per call but if that call is done 100x per second it add up. It also gives some compatibility with other libraries that support this flag. Default the library will still be using the 2OC to be backwards compatible AND because I think most people choose this sensor because of its precision.

Hope this answers the questions a bit.

BTW there is a PR - https://github.com/RobTillaart/MS5611/pull/28

ratio-x commented 2 years ago

What is the difference between D1/D2 [and ADC]?

Sorry, this question was unfinished. My interest was what is the difference between D1/D2 and ADC. Are there any pressure/temparature values in the return value of ADC read command (page 7)?

Some more thoughts on calculation precision:

I have two sensors and I'm wondering that pressure values (currently calculated without 2nd order compensation) are 1) so different and 2) so far from expected values, while temperature readings/calculations are so close (both to each other and to expected values). Temperature differs by less than 0.05 °C.

1) The two sensors differ by 1.6 mbar. 2) In comparison to official wheather stations (nearby, measured on a day with a pressure variance of 0,5 mbar) one sensor differs by 5,3 mbar and the other sensor differs by 6,9 mbar (less than "official"). Error consideration: I measured indoor (21 °C) close to the opened window, while official station's outdoor temperature was 5 °C. I have to investigate this outdoor, but the result would not explain the difference of the two sensors.

Is it possible that limitations in 32 bit data types lead to rounding errors? See notes on page 8 (4th column => "Maximal (bit) size of intermediate result during evaluation of variable"). Also section Calculate temperature compensated pressure is above 32 bit (intermediate results) and [2][3][4] min and max have to be defined.

I don't have deep C++ skills, but as far as I can see the library uses float types in calculation which give a wider range numbers, while bit sizes on page 8 are based on int type. (This is confusing, because there are divisonal operations.)

Default the library will still be using the 2OC to be backwards compatible AND because I think most people choose this sensor because of its precision.

I agree. Apart from calculation speed, do you think there is still potential to increase accuracy and/or precision in library's math?

ratio-x commented 2 years ago

Are you sure that the +2.5°C in SPI usage is caused by internal heating? Hard to imagine. Maybe also a math issue?

RobTillaart commented 2 years ago

Are you sure that the +2.5°C in SPI usage is caused by internal heating? Hard to imagine. Maybe also a math issue?

Pretty sure something is heating up and affects the temperature sensor when using SPI mode. Not 100% sure it is in the MS5611 itself, it could be another component on my GY-63 board. I need a thermal camera to see where heat forms, to confirm it is internal or not. Googled quite a bit however there is very little information about using the SPI interface, and even less on heat effects.

Do not have a second board to confirm it is in the SPI mode or it is a faulty board. However in I2C mode I get stable readings so if there is a fault in the board it is limited to SPI.

Maybe also a math issue?

It is no math issue as the math is the same, and identical to the I2C library.

If I connect 3V3 ESP32 I have ~2.5 degrees rise, if I connect 5V UNO I have >10 degrees rise - too much to let it run for longer than 10 seconds. The GY53 board has 3V3 level convertors to handle 5V connections, but somehow the extra voltage the UNO provides causes a larger temp rise.

When I use the very same board in I2C modes on a 5V UNO I get a temperature within 1°C of my room thermostat and other temp sensors. Same for ESP32 and NANO 33 BLE.

Thus so far all tests points to a heating effect due to selecting SPI.

RobTillaart commented 2 years ago

The two sensors differ by 1.6 mbar.

1.6 mbar is 100% within specs, As 1 sensor can have 1.5 mbar error under best conditions, two can be between 0 and 3 mbar apart.

From datasheet image

In comparison to official wheather stations (nearby, measured on a day with a pressure variance of 0,5 mbar) one sensor differs by 5,3 mbar and the other sensor differs by 6,9 mbar (less than "official"). Error consideration: I measured indoor (21 °C) close to the opened window, while official station's outdoor temperature was 5 °C. I have to investigate this outdoor, but the result would not explain the difference of the two sensors.

Factors to consider:

Best test is to go to the local weather station and place your setup besides theirs (as close as allowed). Then you could see how their measurement and yours correlate.

With the offset functions you can "calibrate" your sensors to the weather station, or at least to each other (to some extend).

RobTillaart commented 2 years ago

do you think there is still potential to increase accuracy and/or precision in library's math?

With the offsets a first step was made, it is just a linear offset.

If you are able to calibrate your sensor against the weather station, you could use my multimap code to handle non-linear mapping. Should not be in this library.

ratio-x commented 2 years ago

Thank you. I'll do some more testing (offset and 2OC) with 0.3.8, after merge of PR #28. What is the provisioning process for Arduino IDE? Does the IDE access GitHub directly as a source or is there some kind of proxy in between?

RobTillaart commented 2 years ago

The library manager is in between. It can take a few hours to a few days before new versions of libraries are seen. Never timed it

RobTillaart commented 2 years ago

Merged 0.3.8

ratio-x commented 2 years ago

Please allow me one more question about your hash function:

// _deviceID is a simple SHIFT XOR merge of PROM data
_deviceID <<= 4;
_deviceID ^= tmp;

I googled around to understand what's happening there (bitwise left shifting an XORing with all PROM values) and found the Jenkins hash function (https://en.wikipedia.org/wiki/Jenkins_hash_function). Your hash function looks a little less complicated.

I want to write a similar function for creating an ID of the NANO itself. The following sketch

include

void setup() { Serial.begin(115200); while (!Serial); }

void loop() { Serial.print("Device ID 0: "); Serial.println(NRF_FICR->DEVICEID[0], HEX); Serial.print("Device ID 1: "); Serial.println(NRF_FICR->DEVICEID[1], HEX);

Serial.print("Device Address 0: "); Serial.println(NRF_FICR->DEVICEADDR[0], HEX); Serial.print("Device Address 1: "); Serial.println(NRF_FICR->DEVICEADDR[1], HEX);

delay (5000); }

produces following output:

Device ID 0: BB7C7490 Device ID 1: 9654A8D0 Device Address 0: 5DD0E421 Device Address 1: F58A652A

Which hash function would you recommend?

Von: Rob Tillaart @.> Gesendet: Dienstag, 25. Januar 2022 15:32 An: RobTillaart/MS5611 @.> Cc: Mario Krauss @.>; Comment @.> Betreff: Re: [RobTillaart/MS5611] Add flag to enable / disable temperature compensation of pressure. (Issue #25)

Merged 0.3.8

— Reply to this email directly, view it on GitHubhttps://github.com/RobTillaart/MS5611/issues/25#issuecomment-1021243060, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ASV4TM2W6D2H6O6X2Y74IBLUX2X65ANCNFSM5MQHPQFA. You are receiving this because you commented.Message ID: @.**@.>>

RobTillaart commented 2 years ago

My hash is indeed simple.

I want to write a similar function for creating an ID of the NANO itself. The following sketch

The nano already has a device id, so my question is why?

Which hash function would you recommend?

I would recommend you implement both, and analyse the differences

Doing this as an exercise will teach you a lot about hashing.

(I would keep it as simple as possible but not simpler)

ratio-x commented 2 years ago

How can one access the device ID of NANO? I found nothing else than the FICR values. Should be unique enough for my requirements, which are likewise to MS5611: Distinguish between different devices when rolling out 98% same code on multiple devices.

Am 25.01.2022 um 19:49 schrieb Rob Tillaart @.***>:



My hash is indeed simple.

I want to write a similar function for creating an ID of the NANO itself. The following sketch

The nano already has a device id, so my question is why?

Which hash function would you recommend?

I would recommend you implement both, and analyse the differences

Doing this as an exercise will teach you a lot about hashing.

(I would keep it as simple as possible but not simpler)

— Reply to this email directly, view it on GitHubhttps://github.com/RobTillaart/MS5611/issues/25#issuecomment-1021500923, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ASV4TM6EXTANA5ZQY4JXCILUX3WCPANCNFSM5MQHPQFA. You are receiving this because you commented.Message ID: @.***>

RobTillaart commented 2 years ago

How can one access the device ID of NANO? I found nothing else than the FICR values.

No nano expert, I assume this ficr values are unique. The device address is probably the MAC address which is meant to be unique.

Think you need to start reading the datasheet of the NANO to learn the details.

Or maybe this is a solution for you: https://www.arduino.cc/reference/en/libraries/arduinouniqueid/