Reading pressure and temperature from the MS5837 failing

Hello!

My team and I are trying to integrate the MS5837-30BA sensor into our project, but we are unfortunately having trouble reading pressure and temperature from the sensor. We are using a conga-PA5 Pico-ITX SBC board connected to the sensor via I2C. The conga board provides it’s own functions for reading and writing bytes to the i2c bus (documentation on these functions, starting on page 52) and they work for requesting and reading the values stored in the prom of the sensor as well as writing the d1/d2 conversion commands and the adc read command specified in the datasheet. It only fails right when we try to read the 3 bytes that should result from writing an adc read command. Below are snippets of the code that works for reading and writing to the prom and the code that doesn’t work for reading pressure and temperature. We have been having this problem for a couple weeks now and we are really stuck on how to progress so we would really appreciate any input.

unsigned char pressure_reading_buffer_[3];
unsigned char pressure_addr_ = 0x76<<1;
unsigned char pressure_reset_cmd_ = 0x1E;
unsigned char adc_read_ = 0x00;
unsigned char d1_conversion_cmd_ = 0x40;
unsigned char prom_read_cmd_[7]; //initialized with the commands to read the 7 addresses in the prom
unsigned char prom_contents_raw_[2];

if(!CgosI2CWrite(hCgos_, dwUnit_, pressure_addr_, &pressure_reset_cmd_, 1))
    RCLCPP_INFO(this->get_logger(), "failed to reset presure sensor");

bool readPROM(unsigned int prom_contents_[]){ //This function runs fine and the crc at the end verifies the prom was read correctly
    for(int i = 0; i<7; i++){
        if(!CgosI2CWrite(hCgos_, dwUnit_, pressure_addr_, &prom_read_cmd_[i], 1)){
            RCLCPP_INFO(this->get_logger(), "failed to write address number %d while reading PROM", i);
            return false;
        }
        if(!CgosI2CRead(hCgos_, dwUnit_, (pressure_addr_ | 0x01), prom_contents_raw_, 2)){
            RCLCPP_INFO(this->get_logger(), "failed to read prom contents from address number %d", i);
            return false;
        }
        prom_contents_[i] = ((prom_contents_raw_[0] << 8) | prom_contents_raw_[1]);
    }
    pressure_crc_ = prom_contents_[0] >> 12;
    if(pressure_crc_ == crc4(prom_contents_))
        RCLCPP_INFO(this->get_logger(), "successfully read prom contents");
    else
        RCLCPP_INFO(this->get_logger(), "checksum on prom contents failed");
    return true;
}

bool readPressure(){ //This function fails to read pressure after successfully writing the adc_read command
    if(!CgosI2CWrite(hCgos_, dwUnit_, pressure_addr_, &d1_conversion_cmd_, 1)){
        RCLCPP_INFO(this->get_logger(), "failed while triggering pressure measurement");
        return false;
    }
    sleep(0.02); //sleep for 2 ms
    if(!CgosI2CWrite(hCgos_, dwUnit_, pressure_addr_, &adc_read_, 1)){
        RCLCPP_INFO(this->get_logger(), "failed while requesting pressure conversion");
        return false;
    }
    sleep(0.02)
    if(!CgosI2CRead(hCgos_, dwUnit_, (pressure_addr_ | 0x01), pressure_reading_buffer_, 3)){
        RCLCPP_INFO(this->get_logger(), "failed while reading raw pressure");
        return false;
    }
    RCLCPP_INFO(this->get_logger(), "successfully read raw pressure");
    return true;
}

Hi @kedus,

Comparing your pressure read implementation to the one in our Arduino library, the main difference seems to be the (20ms?) sleep you’ve got between the adc_read command write and the pressure_read. You’re also seemingly missing a semicolon after that extra sleep call, but I’m not sure if that’s relevant or just a typo in copying over snippets.

If that’s not the problem I’m not sure what’s most likely. Do you have any more error information beyond just “the read didn’t work”? Like is it not receiving the right number of bytes in return, or not getting anything back at all, or something else?

Hi @EliotBR,

Thanks for your suggestions. Ok, I see the 20ms delay between the adc_read and pressure_read in unecessary. (Removing it now) The missing semicolon was a mistake while copying. By “the read didn’t work”, I meant that when we attempt to read from the sensor after writing the adc_read command, we just get a NACK bit from the sensor on the SDA line.

I managed to “fix” the issue in the meantime when I accidentally compiled some code with a “mistake” but it finally managed to read a single byte from the sensor. Following this “mistake” I wrote the function below that seemingly should not work but somehow does. I suspect it works because of the undocumented behavior of the Conga’s I2C write functions when fed multiple bytes but told to write only one. Anyway I am happy to leave it as it is, since it apparently works reliably.


d1_conversion_cmd_[0] = 0x00;
d1_conversion_cmd_[1] = 0x00;
d2_conversion_cmd_[0] = 0x40;
d2_conversion_cmd_[1] = 0x40;
unsigned char pressure_reading_buffer_[3];
unsigned char bar_temp_reading_buffer_[3];
unsigned char pressure_addr_ = 0x76<<1;

readPressure(){
    if(!CgosI2CWrite(hCgos_, dwUnit_, pressure_addr_, d2_conversion_cmd_, 1)){
        RCLCPP_INFO(this->get_logger(), "failed while triggering pressure measurement");
        return false;
    }
    sleep(0.02);
    CgosI2CRead(hCgos_, dwUnit_, (pressure_addr_ | 0x01), bar_temp_reading_buffer_, 3);
    if(!CgosI2CWrite(hCgos_, dwUnit_, pressure_addr_, d1_conversion_cmd_, 1)){
        RCLCPP_INFO(this->get_logger(), "failed while triggering pressure measurement");
        return false;
    }
    if(!CgosI2CRead(hCgos_, dwUnit_, (pressure_addr_ | 0x01), pressure_reading_buffer_, 3)){
        RCLCPP_INFO(this->get_logger(), "failed while reading raw pressure");
        return false;
    }
    return true;
}


Not sure if you’ve made multiple typos in copying over your solution, but I don’t think your posted solution code makes sense. You’ve set d1_conversion_cmd_ to contain two of what is actually ADC Read, and your d2_conversion_cmd_ is set to a duplicated Convert D1 command.

The presented readPressure function requests a D2 conversion (temp, although given the definitions that’s actually a D1 conversion request), then sleeps and reads 3 bytes into a temperature buffer, with no confirmation and no ADC read before it (so seemingly it’s reading nothing?), after which a D1 conversion (ADC read?) is requested, followed by reading three bytes into the pressure buffer.

If that is actually the code you’re running then assuming the superfluous unconfirmed read doesn’t cause anything weird, it would seem that you’re running basically a somewhat obfuscated version of your previous code but without the extra sleep between asking it for data and actually receiving the response (which was the suggestion I made in my previous comment).

Out of interest, what’s the signature of your sleep function? I assumed it takes float seconds because that’s what you seemingly passed in, but perhaps that’s not the case? An ADC read with an OSR of 256 (e.g. from Convert D1, 0x40) should take maximum 0.6ms (from the datasheet, page 2), so perhaps the sleep with a float argument is ignored or something (i.e. if it’s cast to an integer, which would be 0), and the extra unconfirmed I2C read takes long enough that the conversion completes, after which the normal ADC read works as expected.


As a side-note, I expect you can likely use CgosI2CWriteReadCombined, to avoid some repetition when getting data from the sensor.