1

I have bought a GY-86 sensor. it is comprised of an accelerometer/gyroscope (Mpu6050) a magnetometer(HMC5883L) and a barometer(MS5611-01BA01). all these sensors have I2C interface.

I am reading the temperature and pressure from the barometer. the device appears to work fine. It responds to commands accordingly(I can write and read from sensor as described in datasheet). but the problem is that I keep reading a constant value(temp=20.0005625). temperature and pressure do not change. while in fact the the value should be jumping up and down!!!

also regardless of what resolution I use for conversion(256 or 1024 or 4096 bit ADC conversion), I get the same value.

could you please help me? I have raspbian OS. to test the sensor I have written a code in python. I also used i2cset and i2cget commands and got the same result


edited:

I found the problem. I have enclosed my code below. this code is modified. I am coding in python and I use smbus library.

in the previous code I used to call write_byte() function to send a command to sensor and then call read_byte() function to get the response. But the sensor returns more than one byte.

if I call write_byte() to tell the sensor to start the conversion and then call read_byte() 2 times, I get IO Exception the second time. in that case I can read only one byte.

in cases that the sensor returns 2 bytes I can use read_word_data(addr,cmd).

but in some cases the sensor returns 3 bytes. for that used read_block_data(addr,cmd) which returns returns long[]. but when I call this function the program halts and the raspberry does not respond any more.I have to reboot raspberry.

for reading more that 2 bytes there is another function in smbus library. when I call read_i2c_block_data(addr,cmd) it returns an array of size 32. while the sensor is supposed to return only 3 bytes.

example output of read_i2c_block_data: [127,80,66,63,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]

how should I exactly read 3 bytes from i2c interface?

read start_conversion and get_conversion methods

#!/usr/bin/python

import smbus
import time
import sys

class MS5611:

    ms5611_addr= 0x77
    ms5611_d1= 0x40
    ms5611_d2= 0x50
    ms5611_reset=0x1e
    ms5611_adc_read=0

    ms5611_osr_256= 0x00
    ms5611_osr_512= 0x02
    ms5611_osr_1024= 0x04
    ms5611_osr_2048= 0x06
    ms5611_osr_4096= 0x08 

    ms5611_prom_base_addr= 0xa2
    ms5611_prom_reg_count= 6
    ms5611_prom_reg_size= 2

    conversion_time= .1 #time in seconds
    extra_precision=5

    channel=1;
    prom=[]


    def __init__(self,addr):

        self.lastTempConv =0
        self.lastPresConv =0
        self.presCache=-1
        self.tempCache=-1

        self.ms5611_addr=addr
        self.bus=smbus.SMBus(self.channel)
        self.reset()
        self.readPROM()
        print(self.prom)

    def reset(self):
        self.bus.write_byte(self.ms5611_addr,self.ms5611_reset)
        time.sleep(0.01)

    def readPROM(self):
        for i in range(self.ms5611_prom_reg_count):

            temp=self.ms5611_prom_base_addr + (i *self.ms5611_prom_reg_size)
            t1=self.bus.read_word_data(self.ms5611_addr,temp)

#           self.bus.write_byte(self.ms5611_addr,temp)
#           time.sleep(0.01)

#           t1= self.bus.read_byte(self.ms5611_addr)
#           time.sleep(0.01)

            self.prom.append(t1)

        def getConversion(self):
 #               self.bus.write_byte(self.ms5611_addr,self.ms5611_adc_read)
#                t1= self.bus.read_byte(self.ms5611_addr)

                t1= self.bus.read_i2c_block_data(self.ms5611_addr, self.ms5611_adc_read)
        print("inside getconversion: " + str(t1))
                return t1

        def startConversion(self,command):

                self.bus.write_byte(self.ms5611_addr,command)


    def readBlock(self,command):
        return self.bus.read_i2c_block_data(self.ms5611_addr,command)


        def rawTemperature(self,osr):

                now = time.time()
                if self.lastTempConv !=0 and (now - self.lastTempConv) >= self.conversion_time:

                        self.lastTempConv=0
                        return self.getConversion()

        else:

            if self.lastTempConv ==0 and self.lastPresConv==0:
                self.startConversion(self.ms5611_d2 + osr)
                self.lastTempConv  = now

            elif self.lastPresConv!=0 :
                return self.tempCache

            else:
                return None




        def rawPressure(self,osr):

                now = time.time()
                if self.lastPresConv !=0 and (now - self.lastPresConv) >= self.conversion_time:

                        self.lastPresConv=0
                        return self.getConversion()

        else:

            if self.lastPresConv ==0 and self.lastTempConv==0:
                self.startConversion(self.ms5611_d1 + osr)
                self.lastPresConv  = now

            return None



    def getDeltaTemp(self,osr):

        rawTemp = self.rawTemperature(osr)

        if rawTemp != None:
            return rawTemp - (self.prom[4]<<8)
        else:
            return None



    def getTemperature(self,osr):

        dt=self.getDeltaTemp(osr)

        print("inside getTemperatue.dt: "+str(dt))

        if dt!= None:
            return((1<<self.extra_precision)*20001 + ( (dt * self.prom[5]) >> (23-self.extra_precision) ) ) /((1<<self.extra_precision) * 100.0)
        else:
            return None



    def getPressure(self,osr):

        rawPress = self.rawPressure(osr)
        dt = self.getDeltaTemp(osr)

        if(dt==None or rawPress==None):
            return None

        off= (self.prom[1] <<16) + ((self.prom[3] * dt) >> 7)
        sens= (self.prom[0] <<15) + ((self.prom[2] * dt) >>8)

        return ((((rawPress * sens) >> 21) -off) >> (15-self.extra_precision)) / ((1<<self.extra_precision) * 100)
  • See abyz.me.uk/rpi/pigpio/pigs.html In particular i2co, i2cwd, and i2crd. Confirm they work as expected. – joan Sep 17 '18 at 9:46
  • "the sensor returns more than one byte" -> If the result data is a set size, you should only read that much data. I'm not a python user (and oddly, cannot find documentation for the smbus module online) but there must be a function for reading exactly X bytes. "The sensor is only supposed to return X bytes" may be a misinterpretation of "the response is three bytes". In some contexts you may be able to read indefinitely without an IO error. That does not mean this is meaningful information produced by the sensor. Further, by doing that you may be disrupting its function. – goldilocks Sep 17 '18 at 12:26
  • If you can't find the documentation either, feel free to ask a short specific question here asking how to read a fixed number of bytes with the smbus module. – goldilocks Sep 17 '18 at 12:27
  • . You can find smbus doc here: wiki.erazor-zone.de/wiki:linux:python:smbus:doc . I found out every time I call read_i2c_block_data() the 4th item in the array is 63. It must be some kind of CRC. I used this method in a case that the sensor is supposed to return 2 bytes. The method returned an array with first 2 items indicating a value and the rest was 255. So this is just the way this methid works, it returns an array of size 32 – ashkan Sep 18 '18 at 2:48
0

I was wrong (in comments) about the need for a function where you can delimit the number of bytes read yourself. The way the I2C/SMBus protocol block read is supposed to work is that the device indicates the amount of following data to be read in the first byte:

This command reads a block of up to 32 bytes from a device, from a designated register that is specified through the Comm byte. The amount of data is specified by the device in the Count byte.

From: https://www.kernel.org/doc/Documentation/i2c/smbus-protocol

That's is taken care of in the implementation, and the first "count byte" is not part of the data. Here's the definition of the native C userland function that most likely underlies the python library:

/* Returns the number of read bytes */
static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, 
                                              __u8 *values)
{
    union i2c_smbus_data data;
    int i;
    if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                         I2C_SMBUS_BLOCK_DATA,&data))
        return -1;
    else {
        for (i = 1; i <= data.block[0]; i++)
            values[i-1] = data.block[i];
            return data.block[0];
    }
}

You can see how the length of relevant data is used via data.block[0], which is the return value indicating the amount of data copied into the values buffer.

This is just a wrapper on the kernel interface, and although its not clear how much is actually read from the device, presumably it does accord with the count byte.

It seems unlikely that the python wrapper messes that up. So it is possibly the device itself which is at fault. There's no way to tell looking at that code whether an error is passed up if the device gives a garbage value for the count, or whether the kernel then just limits the read to 32 bytes. I'm inclined to believe the former, although that would mean the device said read 32 bytes, even though it is pretty clear from your output that only the first three or four are meaningful.

  • thanks a lot @goldilocks for your kind response. Having looked at the Arduino codes to read the same sensor, I know that the sensor does not send 32 bytes of data. nor it would in any way indicate to the I2C master that it will send 32 bytes. I guess this method just returns an array of size 32, no matter what. the tricky part was that the MSB is stored in first element of the array. so when I used read_word_data() it would return a value that is wrong. the value read_word_data() returns, has placed the MSB byte and LSB byte the wrong way around – ashkan Sep 24 '18 at 8:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.