Home        Store        Learn        Blog

Bar30 Read() All Zeros

I am trying to interface with the Bar30 sensor via the i2c bus on a TX2 loaded with Ubuntu. I tried to make custom C++ code based on the Arduino code linked on the Bar30 product page. When I run the test program that I made, I can read the correct configuration parameters on initialization, but I read in all zeros for the data bytes. The C++ code is very similar to the Arduino code so I am not sure why I could read the initialization values and not the data. Any help is appreciated, thank you. Since I am a new user and cannot upload files, I will include the text below.

testMS5837.h

#ifndef testMS5837_H
#define testMS5837_H

#include "ms5837.h"
#include <stdio.h>
#include <iostream>

//using namespace std;
int initMS5837(MS5837* sensor);
bool getMS5837Values(MS5837* sensor, float& depth, float& temp, float& pressure, int water_type);
#endif</code>

testMS5837.cpp

#include "testMS5837.h"

int initMS5837(MS5837* sensor) {
	if (!sensor->init()){
    	std::cout << "\tSensor could not be initialized\n";
	   	return 1;
    }
}

bool getMS5837Values(MS5837* sensor, float& depth, float& temp, float& pressure, int water_type){
    // We have to read values from sensor to update depth
    if (!sensor->readSens(sensor->OSR_8192)){
        	std::cout << "\t\tSensor read failed!\n";
		    return 1;
    }
    if (water_type == 0) { // freshwater
	    depth = sensor->depth(); // default is freshwater
    } else { //saltwater
	    sensor->setFluidDensity(sensor->DENSITY_SALTWATER);
	    depth = sensor->depth(); // No nead to readSens() again
    }
    temp = sensor->temperature(sensor->UNITS_Farenheit);
    pressure = sensor->pressure(sensor->UNITS_atm);
    return true;
}

int main() {
    MS5837 sensor = MS5837(1,0);
    initMS5837(&sensor);
    float depth, temp, pressure;
    int water_type = 0;
    getMS5837Values(&sensor, depth, temp, pressure, water_type); 
    std::cout << "Depth = " << depth << " m\n";
    std::cout << "Temp = " << temp	<< " F\n";
    std::cout << "Pressure = " << pressure << " atm \n";

}

ms5837.h

#ifndef MS5837_TEST_SENSOR_H
#define MS5837_TEST_SENSOR_H

extern "C"{
    #include <linux/i2c-dev.h>
    #include <i2c/smbus.h>
    #include <linux/i2c.h>
}

// Converted from ms5837.py in Blue Robotics MS5837-python github
#include <string>
#include <stdio.h>
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <vector>
#include <math.h>

using namespace std;


class MS5837{
    
public:
        // Registers
        __u8 _MS5837_ADDR             = 0x76;  
        __u8 _MS5837_RESET            = 0x1E;
        __u8 _MS5837_ADC_READ         = 0x00;
        __u8 _MS5837_PROM_READ        = 0xA0;
        __u8 _MS5837_CONVERT_D1_8192  = 0x4A;
        __u8 _MS5837_CONVERT_D2_8192  = 0x5A;
        // Oversampling options
        int OSR_8192 = 5;
        // kg/m^3 convenience
        int DENSITY_FRESHWATER = 997;
        int DENSITY_SALTWATER = 1029;
        // Conversion factors (from native unit, mbar)
        float UNITS_Pa     = 100.0f;
        float UNITS_mbar   = 1.0f;
        float UNITS_bar    = 0.001f;
        float UNITS_atm    = 0.000986923f;
        float UNITS_Torr   = 0.750062f;
        float UNITS_psi    = 0.014503773773022f;
        // Valid units
        int UNITS_Centigrade = 1;
        int UNITS_Farenheit  = 2;
        int UNITS_Kelvin     = 3;
        MS5837() {}
        MS5837( int model, int bus);
        ~MS5837(){}            
        bool init();    
        bool readSens(int oversampling=0); // OSR_256
        void setFluidDensity(int denisty);
        // Pressure in requested units
        // mbar * conversion
        float pressure(float conversion=1.0); // UNITS_mbar
        // Temperature in requested units
        // default degrees C
        float temperature(float conversion=1); // UNITS_Centigrade
        // Depth relative to MSL pressure in given fluid density
        float depth();
        // Altitude relative to MSL pressure
        float altitude();
        // Cribbed from datasheet
        void _calculate();
        // Cribbed from datasheet
        uint8_t crc4(uint16_t n_prom[]);

private: 
        int _file;
        float _fluidDensity;
        int32_t P;
        int32_t TEMP;
        uint32_t _D1, _D2;
        int _n_rem ;
        vector<int> _n_prom; 
        uint16_t _C[8];
};    

#endif

ms5837.cpp

#include "ms5837.h"

extern "C"{
    #include <linux/i2c-dev.h>
    #include <i2c/smbus.h>
    #include <linux/i2c.h>
}

using namespace std;

MS5837::MS5837( int model, int bus){
    char filename[20];
    snprintf(filename, 19, "/dev/i2c-%d", bus);
    _file = open(filename, O_RDWR);
    if (_file < 0) {
        std::cout << "Cannot open device\n";
        exit(1);
    } 
    if (ioctl(_file, I2C_SLAVE, _MS5837_ADDR) < 0){
        std::cout << "Cannot open device\n";
        exit(1);
    }
    _fluidDensity = DENSITY_FRESHWATER;
}

bool MS5837::init(){  
    i2c_smbus_write_byte_data(_file, 0x00, _MS5837_RESET);
    sleep(0.01); // Wait for reset to complete
    
    // Read calibration values and CRC
    for (uint8_t i = 0; i < 7; i++){
        i2c_smbus_write_byte_data(_file, 0x00, _MS5837_PROM_READ + 2*i);
    
        __u8 d[2];
        __u8 length = 2;

        __s32 tmp = i2c_smbus_read_i2c_block_data(_file, _MS5837_PROM_READ + 2*i, length, d);
        _C[i] =  d[0] << 8 | d[1]; // SMBus is little-endian for word transfers, we need to swap MSB and LSB      

        std::cout << "Calibration : " << _C[i] << "\n";	
    }
    uint8_t crcRead = _C[0] >> 12;
    uint8_t crcCalculated = crc4(_C);
    if (crcCalculated != crcRead){
        std::cout << "PROM read error, CRC failed!\n";
        return false;
    }
    return true;
}

bool MS5837::readSens(int oversampling){
    // Request D1 conversion (temperature)    
    i2c_smbus_write_byte_data(_file, 0x00, _MS5837_CONVERT_D1_8192);
    sleep(0.02); // Max conversion time per datasheet
    i2c_smbus_write_byte_data(_file, 0x00, _MS5837_ADC_READ);

    _D1 = 0;
    __u8 d[3];
    __u8 length = 3;
    __s32 tmp = i2c_smbus_read_i2c_block_data(_file, _MS5837_ADC_READ, length, d);
    _D1 = d[0] << 16 | d[1] << 8 | d[2];
    std::cout << "(" << int(d[0]) << ", " << int(d[1]) << ", " << int(d[2]) << ")\n";
    std::cout << "D1 = " << _D1 << "\n";    
    // Request D2 conversion (pressure)
   i2c_smbus_write_byte_data(_file, 0x00, _MS5837_CONVERT_D2_8192);
   sleep(0.02); // Max conversion time per datasheet
   i2c_smbus_write_byte_data(_file, 0x00, _MS5837_ADC_READ);	
   _D2 = 0;
   tmp = i2c_smbus_read_i2c_block_data(_file, _MS5837_ADC_READ, length, d);    	
   _D2 = d[0] << 16 | d[1] << 8 | d[2];
   std::cout << "(" << int(d[0]) << ", " << int(d[1]) << ", " << int(d[2]) << ")\n";
   std::cout << "D2 = " << _D2 << "\n";    
    _calculate();
    return true;
}

void MS5837::setFluidDensity(int denisty) 
{
    _fluidDensity = denisty;
}

// Pressure in requested units
// mbar * conversion
float MS5837::pressure(float conversion) 
{
    return P * conversion / 10.0f;
}

// Temperature in requested units
// default degrees C
float MS5837::temperature(float conversion)
{
    float degC = TEMP / 100.0f;
    if (conversion == UNITS_Farenheit) return (9.0/5.0)*degC + 32;
    else if (conversion == UNITS_Kelvin) return degC + 273;
    return degC;
}
    
// Depth relative to MSL pressure in given fluid density
float MS5837::depth()
{
    return (pressure(UNITS_Pa)-101300)/(_fluidDensity*9.80665);
}

// Altitude relative to MSL pressure
float MS5837::altitude() 
{
    return (1-pow((pressure()/1013.25),.190284))*145366.45*.3048;        
}

// Cribbed from datasheet
void MS5837::_calculate()
{
    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;
    dT = _D2-uint32_t(_C[5])*256l;
    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*SENS/(2097152l)-OFF)/(8192l);
    TEMP = 2000l+int64_t(dT)*_C[6]/8388608LL;
    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;
    SENS2 = SENS-SENSi; 
    TEMP = (TEMP-Ti);
    P = (((_D1*SENS2)/2097152l-OFF2)/8192l);   
} 

// Cribbed from datasheet
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;
}